diff --git a/Installer.xcodeproj/project.pbxproj b/Installer.xcodeproj/project.pbxproj
index 238b6d6b8..5bf37db7f 100644
--- a/Installer.xcodeproj/project.pbxproj
+++ b/Installer.xcodeproj/project.pbxproj
@@ -207,7 +207,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "set -euxo pipefail\n\necho BUILT_PRODUCTS_DIR $BUILT_PRODUCTS_DIR\nls \"$BUILT_PRODUCTS_DIR\"\n\nmkdir -p \"$TEMP_DIR\"\n\nSDL_PKG=\"$BUILT_PRODUCTS_DIR/ProjectM-SDL.pkg\"\nMUSIC_PLUGIN_PKG=\"$BUILT_PRODUCTS_DIR/ProjectM-MusicPlugin.pkg\"\n\n#productbuild --timestamp --sign '5926VBQM6Y' --package $SDL_PKG --package $MUSIC_PLUGIN_PKG \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\"\nproductbuild --timestamp --sign '5926VBQM6Y' --distribution mac/Distribution.xml --package-path \"$BUILT_PRODUCTS_DIR\" \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\"\n#productbuild --package \"$SDL_PKG\" --package \"$MUSIC_PLUGIN_PKG\" \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\"\n\necho \"Created installer package $BUILT_PRODUCTS_DIR/ProjectM.pkg\"\n\ncp -rp \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\" \"$SRCROOT/\"\n";
+ shellScript = "set -euxo pipefail\n\necho BUILT_PRODUCTS_DIR $BUILT_PRODUCTS_DIR\nls \"$BUILT_PRODUCTS_DIR\"\n\nmkdir -p \"$TEMP_DIR\"\n\nSDL_PKG=\"$BUILT_PRODUCTS_DIR/ProjectM-SDL.pkg\"\nMUSIC_PLUGIN_PKG=\"$BUILT_PRODUCTS_DIR/ProjectM-MusicPlugin.pkg\"\n\n\n#productbuild --timestamp --sign '5926VBQM6Y' --package $SDL_PKG --package $MUSIC_PLUGIN_PKG \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\"\nproductbuild --timestamp --sign '5926VBQM6Y' --distribution mac/Distribution.xml --package-path \"$BUILT_PRODUCTS_DIR\" \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\"\n#productbuild --package \"$SDL_PKG\" --package \"$MUSIC_PLUGIN_PKG\" \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\"\n\necho \"Created installer package $BUILT_PRODUCTS_DIR/ProjectM.pkg\"\n\ncp -rp \"$BUILT_PRODUCTS_DIR/ProjectM.pkg\" \"$SRCROOT/\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
diff --git a/msvc/projectMSDL.vcxproj b/msvc/projectMSDL.vcxproj
index dcce504f9..60a2400f0 100644
--- a/msvc/projectMSDL.vcxproj
+++ b/msvc/projectMSDL.vcxproj
@@ -142,6 +142,9 @@
+
+
+
@@ -181,6 +184,10 @@
+
+
+
+
diff --git a/src/libprojectM/libprojectM.xcodeproj/project.pbxproj b/src/libprojectM/libprojectM.xcodeproj/project.pbxproj
index 2910365a5..219f5d2fa 100644
--- a/src/libprojectM/libprojectM.xcodeproj/project.pbxproj
+++ b/src/libprojectM/libprojectM.xcodeproj/project.pbxproj
@@ -244,7 +244,6 @@
1668542B2105E4BD0042793A /* Renderable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Renderable.cpp; path = Renderer/Renderable.cpp; sourceTree = ""; };
1668542C2105E4BD0042793A /* PipelineContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PipelineContext.cpp; path = Renderer/PipelineContext.cpp; sourceTree = ""; };
1668542D2105E4BD0042793A /* MilkdropWaveform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MilkdropWaveform.cpp; path = Renderer/MilkdropWaveform.cpp; sourceTree = ""; };
- 168404F425D82ED70001F02C /* StaticGlShaders.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = StaticGlShaders.cpp; path = Renderer/StaticGlShaders.cpp; sourceTree = ""; };
168404FE25D82FB80001F02C /* Intrinsics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Intrinsics.h; sourceTree = ""; };
1687172320C33DF300947E7E /* TextureManager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TextureManager.hpp; path = Renderer/TextureManager.hpp; sourceTree = ""; };
1687172420C33DF300947E7E /* Renderable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Renderable.hpp; path = Renderer/Renderable.hpp; sourceTree = ""; };
@@ -446,7 +445,6 @@
isa = PBXGroup;
children = (
168A57C62516226900E802A0 /* StaticGlShaders.cpp */,
- 168404F425D82ED70001F02C /* StaticGlShaders.cpp */,
166854412105E4C20042793A /* etc */,
1687172220C33DDF00947E7E /* headers */,
168C689F20BB265D000AFC1B /* hlslparser */,
diff --git a/src/libprojectM/projectM-opengl.h b/src/libprojectM/projectM-opengl.h
index 001008cbe..a9d4cd48b 100644
--- a/src/libprojectM/projectM-opengl.h
+++ b/src/libprojectM/projectM-opengl.h
@@ -11,7 +11,7 @@
// Enable openGL extra checks, better not be enabled in release build
#define OGL_DEBUG 0
-// Unlock FPS for rendering benchmarks, it disables Vblank/Vsync and prints drawned frames count within a 5s test run
+// Unlock FPS for rendering benchmarks, it disables Vblank/Vsync and prints drawn frame count within a 5s test run
#define UNLOCK_FPS 0
// If a shader compilation failure occurs, it dumps shader source into /tmp instead of stderr
diff --git a/src/libprojectM/projectM.cpp b/src/libprojectM/projectM.cpp
index f438ad31f..f86b1c6d4 100644
--- a/src/libprojectM/projectM.cpp
+++ b/src/libprojectM/projectM.cpp
@@ -154,7 +154,7 @@ bool projectM::writeConfig(const std::string & configFile, const Settings & sett
config.add("Easter Egg Parameter", settings.easterEgg);
config.add("Shuffle Enabled", settings.shuffleEnabled);
config.add("Soft Cut Ratings Enabled", settings.softCutRatingsEnabled);
- std::fstream file(configFile.c_str());
+ std::fstream file(configFile.c_str(), std::ios_base::trunc | std::ios_base::out);
if (file) {
file << config;
return true;
@@ -164,7 +164,7 @@ bool projectM::writeConfig(const std::string & configFile, const Settings & sett
-void projectM::readConfig (const std::string & configFile )
+void projectM::readConfig (const std::string & configFile)
{
std::cout << "[projectM] config file: " << configFile << std::endl;
diff --git a/src/projectM-sdl/Makefile.am b/src/projectM-sdl/Makefile.am
index b681c3039..f6e89ab7b 100644
--- a/src/projectM-sdl/Makefile.am
+++ b/src/projectM-sdl/Makefile.am
@@ -8,7 +8,9 @@ ${my_CFLAGS} \
${SDL_CFLAGS}
bin_PROGRAMS = projectMSDL
-projectMSDL_SOURCES = pmSDL.cpp projectM_SDL_main.cpp pmSDL.hpp
+projectMSDL_SOURCES = pmSDL.cpp projectM_SDL_main.cpp pmSDL.hpp \
+ setup.cpp setup.hpp loopback.cpp loopback.hpp \
+ audioCapture.cpp audioCapture.hpp
projectMSDL_LDADD =
#projectMSDL_LDADD += -lasan
projectMSDL_LDADD += ${SDL_LIBS} ../libprojectM/libprojectM.la
diff --git a/src/projectM-sdl/SDLprojectM.xcodeproj/project.pbxproj b/src/projectM-sdl/SDLprojectM.xcodeproj/project.pbxproj
index e0b59aca6..7017d6876 100644
--- a/src/projectM-sdl/SDLprojectM.xcodeproj/project.pbxproj
+++ b/src/projectM-sdl/SDLprojectM.xcodeproj/project.pbxproj
@@ -17,9 +17,12 @@
168F715921124C0E001806E7 /* config.inp in Support files */ = {isa = PBXBuildFile; fileRef = 16B52AAA21105A6900830F34 /* config.inp */; };
168F715A21124C14001806E7 /* fonts in Support files */ = {isa = PBXBuildFile; fileRef = C3D30B8F1BF02BE5009AAACD /* fonts */; };
168F718021126256001806E7 /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 168F717F21126256001806E7 /* AppIcon.icns */; };
+ 168FECCD25EA83F800E3E133 /* loopback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 168FECCB25EA83F800E3E133 /* loopback.cpp */; };
+ 168FECD425EA86E900E3E133 /* setup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 168FECD225EA86E900E3E133 /* setup.cpp */; };
169BC64024CC3B56007B7829 /* presets in Copy Files */ = {isa = PBXBuildFile; fileRef = C307DFD31D565B57002F6B9E /* presets */; };
169BC64224CC3FCA007B7829 /* presets in Support files */ = {isa = PBXBuildFile; fileRef = C307DFD31D565B57002F6B9E /* presets */; };
169BC65024CC8401007B7829 /* fonts in Copy Files */ = {isa = PBXBuildFile; fileRef = C3D30B8F1BF02BE5009AAACD /* fonts */; };
+ 16CFA3AC25EABCB100E7893C /* audioCapture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 16CFA3AA25EABCB100E7893C /* audioCapture.cpp */; };
C345215C1BF025A9001707D2 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C345215B1BF025A9001707D2 /* OpenGL.framework */; };
/* End PBXBuildFile section */
@@ -144,12 +147,18 @@
168F714921120210001806E7 /* ProjectM.app */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ProjectM.app; sourceTree = BUILT_PRODUCTS_DIR; };
168F714B21120211001806E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
168F717F21126256001806E7 /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = ""; };
+ 168FECCB25EA83F800E3E133 /* loopback.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = loopback.cpp; sourceTree = ""; };
+ 168FECCC25EA83F800E3E133 /* loopback.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = loopback.hpp; sourceTree = ""; };
+ 168FECD225EA86E900E3E133 /* setup.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = setup.cpp; sourceTree = ""; };
+ 168FECD325EA86E900E3E133 /* setup.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = setup.hpp; sourceTree = ""; };
169501FE1F7009E9008FAF86 /* pmSDL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pmSDL.cpp; sourceTree = ""; };
169501FF1F7009E9008FAF86 /* pmSDL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = pmSDL.hpp; sourceTree = ""; };
169BC64B24CC8353007B7829 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL2.framework; sourceTree = ""; };
169BC64D24CC83CD007B7829 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL2.framework; sourceTree = ""; };
16B52AA8211054E900830F34 /* projectMSDL-pkg.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "projectMSDL-pkg.plist"; sourceTree = ""; };
16B52AAA21105A6900830F34 /* config.inp */ = {isa = PBXFileReference; lastKnownFileType = text; path = config.inp; sourceTree = ""; };
+ 16CFA3AA25EABCB100E7893C /* audioCapture.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = audioCapture.cpp; sourceTree = ""; };
+ 16CFA3AB25EABCB100E7893C /* audioCapture.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = audioCapture.hpp; sourceTree = ""; };
C307DFD31D565B57002F6B9E /* presets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = presets; path = ../../presets; sourceTree = ""; };
C34521441BF02294001707D2 /* SDLprojectM */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SDLprojectM; sourceTree = BUILT_PRODUCTS_DIR; };
C345214E1BF022A5001707D2 /* projectM_SDL_main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = projectM_SDL_main.cpp; sourceTree = SOURCE_ROOT; };
@@ -233,6 +242,12 @@
C345213B1BF02293001707D2 = {
isa = PBXGroup;
children = (
+ 16CFA3AA25EABCB100E7893C /* audioCapture.cpp */,
+ 16CFA3AB25EABCB100E7893C /* audioCapture.hpp */,
+ 168FECD225EA86E900E3E133 /* setup.cpp */,
+ 168FECD325EA86E900E3E133 /* setup.hpp */,
+ 168FECCB25EA83F800E3E133 /* loopback.cpp */,
+ 168FECCC25EA83F800E3E133 /* loopback.hpp */,
C60BD8E1259CF3CA0038831F /* SDLprojectM.entitlements */,
169501FF1F7009E9008FAF86 /* pmSDL.hpp */,
169501FE1F7009E9008FAF86 /* pmSDL.cpp */,
@@ -412,6 +427,9 @@
files = (
16355BFE2143C0F400B3748F /* projectM_SDL_main.cpp in Sources */,
16355BFF2143C0F600B3748F /* pmSDL.cpp in Sources */,
+ 168FECCD25EA83F800E3E133 /* loopback.cpp in Sources */,
+ 168FECD425EA86E900E3E133 /* setup.cpp in Sources */,
+ 16CFA3AC25EABCB100E7893C /* audioCapture.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/src/projectM-sdl/audioCapture.cpp b/src/projectM-sdl/audioCapture.cpp
new file mode 100644
index 000000000..b7d25e897
--- /dev/null
+++ b/src/projectM-sdl/audioCapture.cpp
@@ -0,0 +1,152 @@
+#include "audioCapture.hpp"
+
+int projectMSDL::initAudioInput() {
+ // params for audio input
+ SDL_AudioSpec want, have;
+
+ // requested format
+ // https://wiki.libsdl.org/SDL_AudioSpec#Remarks
+ SDL_zero(want);
+ want.freq = 44100;
+ want.format = AUDIO_F32; // float
+ want.channels = 2; // mono might be better?
+ // lower might reduce latency
+ want.samples = PCM::maxsamples;
+ want.callback = projectMSDL::audioInputCallbackF32;
+ want.userdata = this;
+
+ audioDeviceID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(selectedAudioDevice, true), true, &want, &have, 0);
+
+ if (audioDeviceID == 0) {
+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio capture device: %s", SDL_GetError());
+ return 0;
+ }
+
+ // read characteristics of opened capture device
+ SDL_Log("Opened audio capture device index=%i devId=%i: %s", selectedAudioDevice, audioDeviceID, SDL_GetAudioDeviceName(selectedAudioDevice, true));
+ std::string deviceToast = SDL_GetAudioDeviceName(selectedAudioDevice, true); // Example: Microphone rear
+ deviceToast += " selected";
+ projectM::setToastMessage(deviceToast);
+#ifdef DEBUG
+ SDL_Log("Samples: %i, frequency: %i, channels: %i, format: %i", have.samples, have.freq, have.channels, have.format);
+#endif
+ audioChannelsCount = have.channels;
+ audioSampleRate = have.freq;
+ audioSampleCount = have.samples;
+ audioFormat = have.format;
+ audioInputDevice = audioDeviceID;
+
+ return 1;
+}
+
+void projectMSDL::audioInputCallbackF32(void *userdata, unsigned char *stream, int len) {
+ projectMSDL *app = (projectMSDL *) userdata;
+// printf("\nLEN: %i\n", len);
+// for (int i = 0; i < 64; i++)
+// printf("%X ", stream[i]);
+ // stream is (i think) samples*channels floats (native byte order) of len BYTES
+ if (app->audioChannelsCount == 1)
+ app->pcm()->addPCMfloat((float *)stream, len/sizeof(float));
+ else if (app->audioChannelsCount == 2)
+ app->pcm()->addPCMfloat_2ch((float *)stream, len/sizeof(float));
+ else {
+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Multichannel audio not supported");
+ SDL_Quit();
+ }
+}
+
+void projectMSDL::audioInputCallbackS16(void *userdata, unsigned char *stream, int len) {
+ // printf("LEN: %i\n", len);
+ projectMSDL *app = (projectMSDL *) userdata;
+ short pcm16[2][512];
+
+ for (int i = 0; i < 512; i++) {
+ for (int j = 0; j < app->audioChannelsCount; j++) {
+ pcm16[j][i] = stream[i+j];
+ }
+ }
+ app->pcm()->addPCM16(pcm16);
+}
+
+int projectMSDL::toggleAudioInput() {
+ // trigger a toggle with CMD-I or CTRL-I
+ if (wasapi) { // we are currently on WASAPI, so we are going to revert to a microphone/line-in input.
+ if (this->openAudioInput())
+ this->beginAudioCapture();
+ CurAudioDevice = 0;
+ selectedAudioDevice = CurAudioDevice;
+ this->wasapi = false; // Track wasapi as off so projectMSDL will stop listening to WASAPI loopback in pmSDL_main.
+ }
+ else {
+ this->endAudioCapture(); // end current audio capture.
+ CurAudioDevice++; // iterate device index
+ if (CurAudioDevice >= NumAudioDevices) { // We reached outside the boundaries of available audio devices.
+ CurAudioDevice = 0; // Return to first audio device in the index.
+#ifdef WASAPI_LOOPBACK
+ // If we are at the boundary and WASAPI is enabled then let's load WASAPI instead.
+ projectM::setToastMessage("Loopback audio selected");
+ SDL_Log("Loopback audio selected");
+ this->fakeAudio = false; // disable fakeAudio in case it was enabled.
+ this->wasapi = true; // Track wasapi as on so projectMSDL will listen to it.
+#else
+ if (NumAudioDevices == 1) // If WASAPI_LOOPBACK was not enabled and there is only one audio device, it's pointless to toggle anything.
+ {
+ SDL_Log("There is only one audio capture device. There is nothing to toggle at this time.");
+ return 1;
+ }
+ // If WASAPI_LOOPBACK is not enabled and we have multiple input devices, return to device index 0 and let's listen to that device.
+ selectedAudioDevice = CurAudioDevice;
+ initAudioInput();
+ this->beginAudioCapture();
+#endif
+ }
+ else {
+ // This is a normal scenario where we move forward in the audio device index.
+ selectedAudioDevice = CurAudioDevice;
+ initAudioInput();
+ this->beginAudioCapture();
+ }
+ }
+ return 1;
+}
+
+int projectMSDL::openAudioInput() {
+ fakeAudio = false; // if we are opening an audio input then there is no need for fake audio.
+ // get audio driver name (static)
+#ifdef DEBUG
+ const char* driver_name = SDL_GetCurrentAudioDriver();
+ SDL_Log("Using audio driver: %s\n", driver_name);
+#endif
+
+ // get audio input device
+ NumAudioDevices = SDL_GetNumAudioDevices(true); // capture, please
+
+ CurAudioDevice = 0;
+ if (NumAudioDevices == 0) {
+ SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "No audio capture devices found");
+ projectM::setToastMessage("No audio capture devices found: using simulated audio");
+ fakeAudio = true;
+ return 0;
+ }
+#ifdef DEBUG
+ for (unsigned int i = 0; i < NumAudioDevices; i++) {
+ SDL_Log("Found audio capture device %d: %s", i, SDL_GetAudioDeviceName(i, true));
+ }
+#endif
+
+ // default selected Audio Device to 0.
+ selectedAudioDevice = 0;
+ initAudioInput();
+
+ return 1;
+}
+
+void projectMSDL::beginAudioCapture() {
+ // allocate a buffer to store PCM data for feeding in
+ SDL_PauseAudioDevice(audioDeviceID, false);
+}
+
+void projectMSDL::endAudioCapture() {
+ SDL_PauseAudioDevice(audioDeviceID, true);
+ SDL_CloseAudioDevice(audioDeviceID);
+}
diff --git a/src/projectM-sdl/audioCapture.hpp b/src/projectM-sdl/audioCapture.hpp
new file mode 100644
index 000000000..abf08c598
--- /dev/null
+++ b/src/projectM-sdl/audioCapture.hpp
@@ -0,0 +1,6 @@
+#ifndef audioCapture_hpp
+#define audioCapture_hpp
+
+#include "pmSDL.hpp"
+
+#endif /* audioCapture_hpp */
diff --git a/src/projectM-sdl/config.inp b/src/projectM-sdl/config.inp
index ebb101147..20844d407 100644
--- a/src/projectM-sdl/config.inp
+++ b/src/projectM-sdl/config.inp
@@ -1,28 +1,23 @@
# config.inp
# Configuration File for projectM
-#Texture Size = 1024 # Size of internal rendering texture
-
-Mesh X = 708 # Width of PerPixel Equation mesh
-Mesh Y = 400 # Height of PerPixel Equation mesh
-FPS = 60 # Frames Per Second
-Fullscreen = false
-Window Width = 512 # startup window width
-Window Height = 512 # startup window height
-
+Mesh X = 708 # Width of PerPixel Equation mesh
+Mesh Y = 400 # Height of PerPixel Equation mesh
+FPS = 60 # Frames Per Second
Smooth Transition Duration = 1 # in seconds
-Preset Duration = 10 # in seconds
-
-Hard Cuts Enabled = false # Hard Cuts are preset transitions that occur when your music becomes louder. They only occur after a hard cut duration threshold has passed.
-Hard Cut Duration = 60 # Number of seconds before you become eligible for a hard cut.
-Hard Cut Sensitivity = 1.0 # Volume sensitivity before a hard cut is triggered.
-
-Beat Sensitivity = 1.0 # Beat Sensitivity impacts how reactive your visualizations are to volume, bass, mid-range, and treble. Default 1.0. Range: 0 - 5 (from "dead" to VERY reactive).
+Preset Duration = 10 # in seconds
+Hard Cuts Enabled = false # Hard Cuts are preset transitions that occur when your music becomes louder. They only occur after a hard cut duration threshold has passed.
+Hard Cut Duration = 60 # Number of seconds before you become eligible for a hard cut.
+Hard Cut Sensitivity = 1.0 # Volume sensitivity before a hard cut is triggered.
+Beat Sensitivity = 1.0 # Beat Sensitivity impacts how reactive your visualizations are to volume, bass, mid-range, and treble. Default 1.0. Range: 0 - 5 (from "dead" to VERY reactive).
+# mostly ignored in projectM-SDL
+#Texture Size = 1024 # Size of internal rendering texture
+Window Width = 512 # startup window width
+Window Height = 512 # startup window height
+Fullscreen = false
Easter Egg Parameter = 1
-
Aspect Correction = true # Custom Shape Aspect Correction
-
Preset Path = presets # preset location
Title Font = Vera.ttf
Menu Font = VeraMono.ttf
diff --git a/src/projectM-sdl/loopback.cpp b/src/projectM-sdl/loopback.cpp
new file mode 100644
index 000000000..c9fe1c088
--- /dev/null
+++ b/src/projectM-sdl/loopback.cpp
@@ -0,0 +1,228 @@
+// Handles audio loopback
+
+#include "loopback.hpp"
+
+
+// ref https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
+#ifdef WASAPI_LOOPBACK
+
+IAudioCaptureClient *pAudioCaptureClient;
+UINT32 foo = 0;
+PUINT32 pnFrames = &foo;
+UINT32 nBlockAlign = 0;
+UINT32 nPasses = 0;
+bool bFirstPacket = true;
+
+HRESULT get_default_device(IMMDevice **ppMMDevice) {
+ HRESULT hr = S_OK;
+ IMMDeviceEnumerator *pMMDeviceEnumerator;
+
+ // activate a device enumerator
+ hr = CoCreateInstance(
+ __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&pMMDeviceEnumerator
+ );
+ if (FAILED(hr)) {
+ ERR(L"CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x", hr);
+ return false;
+ }
+
+ // get the default render endpoint
+ hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, ppMMDevice);
+ if (FAILED(hr)) {
+ ERR(L"IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x%08x", hr);
+ return false;
+ }
+
+ return S_OK;
+}
+#endif /** WASAPI_LOOPBACK */
+
+bool initLoopback()
+{
+#ifdef WASAPI_LOOPBACK
+ HRESULT hr;
+
+ hr = CoInitialize(NULL);
+ if (FAILED(hr)) {
+ ERR(L"CoInitialize failed: hr = 0x%08x", hr);
+ }
+
+
+ IMMDevice *pMMDevice(NULL);
+ // open default device if not specified
+ if (NULL == pMMDevice) {
+ hr = get_default_device(&pMMDevice);
+ if (FAILED(hr)) {
+ return false;
+ }
+ }
+
+ bool bInt16 = false;
+
+ // activate an IAudioClient
+ IAudioClient *pAudioClient;
+ hr = pMMDevice->Activate(
+ __uuidof(IAudioClient),
+ CLSCTX_ALL, NULL,
+ (void**)&pAudioClient
+ );
+ if (FAILED(hr)) {
+ ERR(L"IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr);
+ return false;
+ }
+
+ // get the default device periodicity
+ REFERENCE_TIME hnsDefaultDevicePeriod;
+ hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
+ if (FAILED(hr)) {
+ ERR(L"IAudioClient::GetDevicePeriod failed: hr = 0x%08x", hr);
+ return false;
+ }
+
+ // get the default device format
+ WAVEFORMATEX *pwfx;
+ hr = pAudioClient->GetMixFormat(&pwfx);
+ if (FAILED(hr)) {
+ ERR(L"IAudioClient::GetMixFormat failed: hr = 0x%08x", hr);
+ return false;
+ }
+
+ if (bInt16) {
+ // coerce int-16 wave format
+ // can do this in-place since we're not changing the size of the format
+ // also, the engine will auto-convert from float to int for us
+ switch (pwfx->wFormatTag) {
+ case WAVE_FORMAT_IEEE_FLOAT:
+ pwfx->wFormatTag = WAVE_FORMAT_PCM;
+ pwfx->wBitsPerSample = 16;
+ pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+ break;
+
+ case WAVE_FORMAT_EXTENSIBLE:
+ {
+ // naked scope for case-local variable
+ PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast(pwfx);
+ if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
+ pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ pEx->Samples.wValidBitsPerSample = 16;
+ pwfx->wBitsPerSample = 16;
+ pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+ }
+ else {
+ ERR(L"%s", L"Don't know how to coerce mix format to int-16");
+ return E_UNEXPECTED;
+ }
+ }
+ break;
+
+ default:
+ ERR(L"Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16", pwfx->wFormatTag);
+ return E_UNEXPECTED;
+ }
+ }
+
+ nBlockAlign = pwfx->nBlockAlign;
+ *pnFrames = 0;
+
+ // call IAudioClient::Initialize
+ // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK
+ // do not work together...
+ // the "data ready" event never gets set
+ // so we're going to do a timer-driven loop
+ hr = pAudioClient->Initialize(
+ AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_LOOPBACK,
+ 0, 0, pwfx, 0
+ );
+ if (FAILED(hr)) {
+ ERR(L"pAudioClient->Initialize error");
+ return false;
+ }
+
+ // activate an IAudioCaptureClient
+ hr = pAudioClient->GetService(
+ __uuidof(IAudioCaptureClient),
+ (void**)&pAudioCaptureClient
+ );
+ if (FAILED(hr)) {
+ ERR(L"pAudioClient->GetService error");
+ return false;
+ }
+
+ // call IAudioClient::Start
+ hr = pAudioClient->Start();
+ if (FAILED(hr)) {
+ ERR(L"pAudioClient->Start error");
+ return false;
+ }
+
+ bool bDone = false;
+#endif /** WASAPI_LOOPBACK */
+
+ return true;
+}
+
+void configureLoopback(projectMSDL *app) {
+#ifdef WASAPI_LOOPBACK
+ // Default to WASAPI loopback if it was enabled at compilation.
+ app->wasapi = true;
+ // Notify that loopback capture was started.
+ SDL_Log("Opened audio capture loopback.");
+#endif
+}
+
+bool processLoopbackFrame(projectMSDL *app) {
+#ifdef WASAPI_LOOPBACK
+ HRESULT hr;
+
+ if (app->wasapi) {
+ // drain data while it is available
+ nPasses++;
+ UINT32 nNextPacketSize;
+ for (
+ hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize);
+ SUCCEEDED(hr) && nNextPacketSize > 0;
+ hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize)
+ ) {
+ // get the captured data
+ BYTE *pData;
+ UINT32 nNumFramesToRead;
+ DWORD dwFlags;
+
+ hr = pAudioCaptureClient->GetBuffer(
+ &pData,
+ &nNumFramesToRead,
+ &dwFlags,
+ NULL,
+ NULL
+ );
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ LONG lBytesToWrite = nNumFramesToRead * nBlockAlign;
+
+ /** Add the waveform data */
+ app->pcm()->addPCMfloat((float *)pData, nNumFramesToRead);
+
+ *pnFrames += nNumFramesToRead;
+
+ hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ bFirstPacket = false;
+ }
+
+ if (FAILED(hr)) {
+ return false;
+ }
+ }
+#endif /** WASAPI_LOOPBACK */
+
+}
diff --git a/src/projectM-sdl/loopback.hpp b/src/projectM-sdl/loopback.hpp
new file mode 100644
index 000000000..79ad0edd0
--- /dev/null
+++ b/src/projectM-sdl/loopback.hpp
@@ -0,0 +1,14 @@
+
+
+#ifndef loopback_hpp
+#define loopback_hpp
+
+#include "pmSDL.hpp"
+
+class projectMSDL;
+
+bool initLoopback();
+void configureLoopback(projectMSDL *app);
+bool processLoopbackFrame(projectMSDL *app);
+
+#endif /* loopback_hpp */
diff --git a/src/projectM-sdl/pmSDL.cpp b/src/projectM-sdl/pmSDL.cpp
index 1b638bd19..72afccddd 100644
--- a/src/projectM-sdl/pmSDL.cpp
+++ b/src/projectM-sdl/pmSDL.cpp
@@ -29,163 +29,6 @@
*/
#include "pmSDL.hpp"
-#include
-#include
-#include "Renderer/ShaderEngine.hpp"
-#include "Renderer/StaticGlShaders.h"
-
-
-void projectMSDL::audioInputCallbackF32(void *userdata, unsigned char *stream, int len) {
- projectMSDL *app = (projectMSDL *) userdata;
-// printf("\nLEN: %i\n", len);
-// for (int i = 0; i < 64; i++)
-// printf("%X ", stream[i]);
- // stream is (i think) samples*channels floats (native byte order) of len BYTES
- if (app->audioChannelsCount == 1)
- app->pcm()->addPCMfloat((float *)stream, len/sizeof(float));
- else if (app->audioChannelsCount == 2)
- app->pcm()->addPCMfloat_2ch((float *)stream, len/sizeof(float));
- else {
- SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Multichannel audio not supported");
- SDL_Quit();
- }
-}
-
-void projectMSDL::audioInputCallbackS16(void *userdata, unsigned char *stream, int len) {
- // printf("LEN: %i\n", len);
- projectMSDL *app = (projectMSDL *) userdata;
- short pcm16[2][512];
-
- for (int i = 0; i < 512; i++) {
- for (int j = 0; j < app->audioChannelsCount; j++) {
- pcm16[j][i] = stream[i+j];
- }
- }
- app->pcm()->addPCM16(pcm16);
-}
-
-int projectMSDL::toggleAudioInput() {
- // trigger a toggle with CMD-I or CTRL-I
- if (wasapi) { // we are currently on WASAPI, so we are going to revert to a microphone/line-in input.
- if (this->openAudioInput())
- this->beginAudioCapture();
- CurAudioDevice = 0;
- selectedAudioDevice = CurAudioDevice;
- this->wasapi = false; // Track wasapi as off so projectMSDL will stop listening to WASAPI loopback in pmSDL_main.
- }
- else {
- this->endAudioCapture(); // end current audio capture.
- CurAudioDevice++; // iterate device index
- if (CurAudioDevice >= NumAudioDevices) { // We reached outside the boundaries of available audio devices.
- CurAudioDevice = 0; // Return to first audio device in the index.
-#ifdef WASAPI_LOOPBACK
- // If we are at the boundary and WASAPI is enabled then let's load WASAPI instead.
- projectM::setToastMessage("Loopback audio selected");
- SDL_Log("Loopback audio selected");
- this->fakeAudio = false; // disable fakeAudio in case it was enabled.
- this->wasapi = true; // Track wasapi as on so projectMSDL will listen to it.
-#else
- if (NumAudioDevices == 1) // If WASAPI_LOOPBACK was not enabled and there is only one audio device, it's pointless to toggle anything.
- {
- SDL_Log("There is only one audio capture device. There is nothing to toggle at this time.");
- return 1;
- }
- // If WASAPI_LOOPBACK is not enabled and we have multiple input devices, return to device index 0 and let's listen to that device.
- selectedAudioDevice = CurAudioDevice;
- initAudioInput();
- this->beginAudioCapture();
-#endif
- }
- else {
- // This is a normal scenario where we move forward in the audio device index.
- selectedAudioDevice = CurAudioDevice;
- initAudioInput();
- this->beginAudioCapture();
- }
- }
- return 1;
-}
-
-int projectMSDL::initAudioInput() {
-
-
- // params for audio input
- SDL_AudioSpec want, have;
-
- // requested format
- // https://wiki.libsdl.org/SDL_AudioSpec#Remarks
- SDL_zero(want);
- want.freq = 44100;
- want.format = AUDIO_F32; // float
- want.channels = 2;
- want.samples = PCM::maxsamples;
- want.callback = projectMSDL::audioInputCallbackF32;
- want.userdata = this;
-
- audioDeviceID = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(selectedAudioDevice, true), true, &want, &have, 0);
-
- if (audioDeviceID == 0) {
- SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio capture device: %s", SDL_GetError());
- return 0;
- }
-
- // read characteristics of opened capture device
- SDL_Log("Opened audio capture device index=%i devId=%i: %s", selectedAudioDevice, audioDeviceID, SDL_GetAudioDeviceName(selectedAudioDevice, true));
- std::string deviceToast = SDL_GetAudioDeviceName(selectedAudioDevice, true); // Example: Microphone rear
- deviceToast += " selected";
- projectM::setToastMessage(deviceToast);
-#ifdef DEBUG
- SDL_Log("Samples: %i, frequency: %i, channels: %i, format: %i", have.samples, have.freq, have.channels, have.format);
-#endif
- audioChannelsCount = have.channels;
- audioSampleRate = have.freq;
- audioSampleCount = have.samples;
- audioFormat = have.format;
- audioInputDevice = audioDeviceID;
-
- return 1;
-}
-
-int projectMSDL::openAudioInput() {
- fakeAudio = false; // if we are opening an audio input then there is no need for fake audio.
- // get audio driver name (static)
-#ifdef DEBUG
- const char* driver_name = SDL_GetCurrentAudioDriver();
- SDL_Log("Using audio driver: %s\n", driver_name);
-#endif
-
- // get audio input device
- NumAudioDevices = SDL_GetNumAudioDevices(true); // capture, please
-
- CurAudioDevice = 0;
- if (NumAudioDevices == 0) {
- SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "No audio capture devices found");
- projectM::setToastMessage("No audio capture devices found: using simulated audio");
- fakeAudio = true;
- return 0;
- }
-#ifdef DEBUG
- for (unsigned int i = 0; i < NumAudioDevices; i++) {
- SDL_Log("Found audio capture device %d: %s", i, SDL_GetAudioDeviceName(i, true));
- }
-#endif
-
- // default selected Audio Device to 0.
- selectedAudioDevice = 0;
- initAudioInput();
-
- return 1;
-}
-
-void projectMSDL::beginAudioCapture() {
- // allocate a buffer to store PCM data for feeding in
- SDL_PauseAudioDevice(audioDeviceID, false);
-}
-
-void projectMSDL::endAudioCapture() {
- SDL_PauseAudioDevice(audioDeviceID, true);
- SDL_CloseAudioDevice(audioDeviceID);
-}
void projectMSDL::setHelpText(const std::string & helpText) {
projectM::setHelpText(helpText);
@@ -568,23 +411,22 @@ void projectMSDL::renderFrame() {
SDL_GL_SwapWindow(win);
}
-projectMSDL::projectMSDL(Settings settings, int flags) : projectM(settings, flags) {
+projectMSDL::projectMSDL(SDL_GLContext glCtx, Settings settings, int flags) : projectM(settings, flags), glCtx(glCtx) {
width = getWindowWidth();
height = getWindowHeight();
done = 0;
isFullScreen = false;
}
-projectMSDL::projectMSDL(std::string config_file, int flags) : projectM(config_file, flags) {
+projectMSDL::projectMSDL(SDL_GLContext glCtx, std::string config_file, int flags) : projectM(config_file, flags), glCtx(glCtx) {
width = getWindowWidth();
height = getWindowHeight();
done = 0;
isFullScreen = false;
}
-void projectMSDL::init(SDL_Window *window, SDL_GLContext *_glCtx, const bool _renderToTexture) {
+void projectMSDL::init(SDL_Window *window, const bool _renderToTexture) {
win = window;
- glCtx = _glCtx;
projectM_resetGL(width, height);
#ifdef WASAPI_LOOPBACK
diff --git a/src/projectM-sdl/pmSDL.hpp b/src/projectM-sdl/pmSDL.hpp
index 3df80fdc4..1dcc6a0a2 100644
--- a/src/projectM-sdl/pmSDL.hpp
+++ b/src/projectM-sdl/pmSDL.hpp
@@ -39,12 +39,29 @@
#define TEST_ALL_PRESETS 0
#define STEREOSCOPIC_SBS 0
+// projectM
#include "projectM-opengl.h"
#include
+#include "Renderer/ShaderEngine.hpp"
+#include "Renderer/StaticGlShaders.h"
#include
+
+// projectM SDL
+#include "setup.hpp"
+#include "loopback.hpp"
+#include "audioCapture.hpp"
+
+
+#if defined _MSC_VER
+# include
+#endif
+
+#include
+#include
#include
#include
-
+#include
+#include
#ifdef WASAPI_LOOPBACK
#include
@@ -60,8 +77,6 @@
#endif /** WASAPI_LOOPBACK */
-
-
#ifdef WIN32
#define SDL_MAIN_HANDLED
#include "SDL.h"
@@ -69,6 +84,7 @@
#include
#endif /** WIN32 */
+
// DATADIR_PATH should be set by the root Makefile if this is being
// built with autotools.
#ifndef DATADIR_PATH
@@ -84,17 +100,18 @@
#endif
class projectMSDL : public projectM {
+
public:
-
-
bool done;
bool mouseDown = false;
bool wasapi = false; // Used to track if wasapi is currently active. This bool will allow us to run a WASAPI app and still toggle to microphone inputs.
bool fakeAudio = false; // Used to track fake audio, so we can turn it off and on.
bool stretch = false; // used for toggling stretch mode
- projectMSDL(Settings settings, int flags);
- projectMSDL(std::string config_file, int flags);
- void init(SDL_Window *window, SDL_GLContext *glCtx, const bool renderToTexture = false);
+ SDL_GLContext glCtx;
+
+ projectMSDL(SDL_GLContext glCtx, Settings settings, int flags);
+ projectMSDL(SDL_GLContext glCtx, std::string config_file, int flags);
+ void init(SDL_Window *window, const bool renderToTexture = false);
int openAudioInput();
int toggleAudioInput();
int initAudioInput();
@@ -120,7 +137,6 @@ public:
private:
SDL_Window *win;
- SDL_GLContext *glCtx;
bool isFullScreen;
SDL_AudioDeviceID audioInputDevice;
unsigned int width, height;
diff --git a/src/projectM-sdl/projectM_SDL_main.cpp b/src/projectM-sdl/projectM_SDL_main.cpp
index 5111829fb..f9838ec77 100644
--- a/src/projectM-sdl/projectM_SDL_main.cpp
+++ b/src/projectM-sdl/projectM_SDL_main.cpp
@@ -1,568 +1,64 @@
/**
-* projectM -- Milkdrop-esque visualisation SDK
-* Copyright (C)2003-2019 projectM Team
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 2.1 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-* See 'LICENSE.txt' included within this release
-*
-* projectM-sdl
-* This is an implementation of projectM using libSDL2
-*
-* main.cpp
-* Authors: Created by Mischa Spiegelmock on 6/3/15.
-*
-*
-* RobertPancoast77@gmail.com :
-* experimental Stereoscopic SBS driver functionality
-* WASAPI looback implementation
-*
-*
-*/
-
-#if defined _MSC_VER
-# include
-#endif
-
-#include
-#include
-#include
+ * projectM -- Milkdrop-esque visualisation SDK
+ * Copyright (C)2003-2021 projectM Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * See 'LICENSE.txt' included within this release
+ *
+ * projectM-sdl
+ * This is an implementation of projectM using libSDL2
+ *
+ * main.cpp
+ * Authors: Created by Mischa Spiegelmock on 6/3/15.
+ *
+ *
+ * RobertPancoast77@gmail.com :
+ * experimental Stereoscopic SBS driver functionality
+ * WASAPI looback implementation
+ *
+ *
+ */
#include "pmSDL.hpp"
-#if OGL_DEBUG
-void DebugLog(GLenum source,
- GLenum type,
- GLuint id,
- GLenum severity,
- GLsizei length,
- const GLchar* message,
- const void* userParam) {
-
- /*if (type != GL_DEBUG_TYPE_OTHER)*/
- {
- std::cerr << " -- \n" << "Type: " <<
- type << "; Source: " <<
- source <<"; ID: " << id << "; Severity: " <<
- severity << "\n" << message << "\n";
- }
- }
-#endif
-
-// return path to config file to use
-std::string getConfigFilePath(std::string datadir_path) {
- char* home = NULL;
- std::string projectM_home;
- std::string projectM_config = DATADIR_PATH;
-
- projectM_config = datadir_path;
-
-#ifdef _MSC_VER
- home=getenv("USERPROFILE");
-#else
- home=getenv("HOME");
-#endif
+static int mainLoop(void *userData) {
+ projectMSDL **appRef = (projectMSDL **)userData;
+ auto app = *appRef;
- projectM_home = std::string(home);
- projectM_home += "/.projectM";
-
- // Create the ~/.projectM directory. If it already exists, mkdir will do nothing
-#if defined _MSC_VER
- _mkdir(projectM_home.c_str());
-#else
- mkdir(projectM_home.c_str(), 0755);
-#endif
-
- projectM_home += "/config.inp";
- projectM_config += "/config.inp";
-
- std::ifstream f_home(projectM_home);
- std::ifstream f_config(projectM_config);
-
- if (f_config.good() && !f_home.good()) {
- std::ifstream f_src;
- std::ofstream f_dst;
-
- f_src.open(projectM_config, std::ios::in | std::ios::binary);
- f_dst.open(projectM_home, std::ios::out | std::ios::binary);
- f_dst << f_src.rdbuf();
- f_dst.close();
- f_src.close();
- return std::string(projectM_home);
- } else if (f_home.good()) {
- SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "created ~/.projectM/config.inp successfully\n");
- return std::string(projectM_home);
- } else if (f_config.good()) {
- SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot create ~/.projectM/config.inp, using default config file\n");
- return std::string(projectM_config);
- } else {
- SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n");
- return "";
- }
-}
-
-// ref https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/
-#ifdef WASAPI_LOOPBACK
-
-HRESULT get_default_device(IMMDevice **ppMMDevice) {
- HRESULT hr = S_OK;
- IMMDeviceEnumerator *pMMDeviceEnumerator;
-
- // activate a device enumerator
- hr = CoCreateInstance(
- __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
- __uuidof(IMMDeviceEnumerator),
- (void**)&pMMDeviceEnumerator
- );
- if (FAILED(hr)) {
- ERR(L"CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x", hr);
- return hr;
- }
-
- // get the default render endpoint
- hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, ppMMDevice);
- if (FAILED(hr)) {
- ERR(L"IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x%08x", hr);
- return hr;
- }
-
- return S_OK;
-}
-
-#endif /** WASAPI_LOOPBACK */
-
-int main(int argc, char *argv[]) {
-#ifndef WIN32
-srand((int)(time(NULL)));
-#endif
-
-#ifdef WASAPI_LOOPBACK
- HRESULT hr;
-
- hr = CoInitialize(NULL);
- if (FAILED(hr)) {
- ERR(L"CoInitialize failed: hr = 0x%08x", hr);
- }
-
-
- IMMDevice *pMMDevice(NULL);
- // open default device if not specified
- if (NULL == pMMDevice) {
- hr = get_default_device(&pMMDevice);
- if (FAILED(hr)) {
- return hr;
- }
- }
-
- bool bInt16 = false;
- UINT32 foo = 0;
- PUINT32 pnFrames = &foo;
-
- // activate an IAudioClient
- IAudioClient *pAudioClient;
- hr = pMMDevice->Activate(
- __uuidof(IAudioClient),
- CLSCTX_ALL, NULL,
- (void**)&pAudioClient
- );
- if (FAILED(hr)) {
- ERR(L"IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr);
- return hr;
- }
-
- // get the default device periodicity
- REFERENCE_TIME hnsDefaultDevicePeriod;
- hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
- if (FAILED(hr)) {
- ERR(L"IAudioClient::GetDevicePeriod failed: hr = 0x%08x", hr);
- return hr;
- }
-
- // get the default device format
- WAVEFORMATEX *pwfx;
- hr = pAudioClient->GetMixFormat(&pwfx);
- if (FAILED(hr)) {
- ERR(L"IAudioClient::GetMixFormat failed: hr = 0x%08x", hr);
- return hr;
- }
-
- if (bInt16) {
- // coerce int-16 wave format
- // can do this in-place since we're not changing the size of the format
- // also, the engine will auto-convert from float to int for us
- switch (pwfx->wFormatTag) {
- case WAVE_FORMAT_IEEE_FLOAT:
- pwfx->wFormatTag = WAVE_FORMAT_PCM;
- pwfx->wBitsPerSample = 16;
- pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
- pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
- break;
-
- case WAVE_FORMAT_EXTENSIBLE:
- {
- // naked scope for case-local variable
- PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast(pwfx);
- if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
- pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- pEx->Samples.wValidBitsPerSample = 16;
- pwfx->wBitsPerSample = 16;
- pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
- pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
- }
- else {
- ERR(L"%s", L"Don't know how to coerce mix format to int-16");
- return E_UNEXPECTED;
- }
- }
- break;
-
- default:
- ERR(L"Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16", pwfx->wFormatTag);
- return E_UNEXPECTED;
- }
- }
-
- UINT32 nBlockAlign = pwfx->nBlockAlign;
- *pnFrames = 0;
-
- // call IAudioClient::Initialize
- // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK
- // do not work together...
- // the "data ready" event never gets set
- // so we're going to do a timer-driven loop
- hr = pAudioClient->Initialize(
- AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_STREAMFLAGS_LOOPBACK,
- 0, 0, pwfx, 0
- );
- if (FAILED(hr)) {
- ERR(L"pAudioClient->Initialize error");
- return hr;
- }
-
- // activate an IAudioCaptureClient
- IAudioCaptureClient *pAudioCaptureClient;
- hr = pAudioClient->GetService(
- __uuidof(IAudioCaptureClient),
- (void**)&pAudioCaptureClient
- );
- if (FAILED(hr)) {
- ERR(L"pAudioClient->GetService error");
- return hr;
- }
-
- // call IAudioClient::Start
- hr = pAudioClient->Start();
- if (FAILED(hr)) {
- ERR(L"pAudioClient->Start error");
- return hr;
- }
-
- bool bDone = false;
- bool bFirstPacket = true;
- UINT32 nPasses = 0;
-
-#endif /** WASAPI_LOOPBACK */
-
#if UNLOCK_FPS
- setenv("vblank_mode", "0", 1);
+ auto start = startUnlockedFPSCounter();
#endif
- SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
-
- if (! SDL_VERSION_ATLEAST(2, 0, 5)) {
- SDL_Log("SDL version 2.0.5 or greater is required. You have %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
- return 1;
- }
-
- // default window size to usable bounds (e.g. minus menubar and dock)
- SDL_Rect initialWindowBounds;
-#if SDL_VERSION_ATLEAST(2, 0, 5)
- // new and better
- SDL_GetDisplayUsableBounds(0, &initialWindowBounds);
-#else
- SDL_GetDisplayBounds(0, &initialWindowBounds);
-#endif
- int width = initialWindowBounds.w;
- int height = initialWindowBounds.h;
-
-#ifdef USE_GLES
- // use GLES 2.0 (this may need adjusting)
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
-
-#else
- // Disabling compatibility profile
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
-
-#endif
-
- SDL_Window *win = SDL_CreateWindow("projectM", 0, 0, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
-
- SDL_GL_GetDrawableSize(win,&width,&height);
-
-#if STEREOSCOPIC_SBS
-
- // enable stereo
- if (SDL_GL_SetAttribute(SDL_GL_STEREO, 1) == 0)
- {
- SDL_Log("SDL_GL_STEREO: true");
- }
-
- // requires fullscreen mode
- SDL_ShowCursor(false);
- SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN);
-
-#endif
-
-
- SDL_GLContext glCtx = SDL_GL_CreateContext(win);
-
-
- SDL_Log("GL_VERSION: %s", glGetString(GL_VERSION));
- SDL_Log("GL_SHADING_LANGUAGE_VERSION: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
- SDL_Log("GL_VENDOR: %s", glGetString(GL_VENDOR));
-
- SDL_SetWindowTitle(win, "projectM Visualizer");
-
- SDL_GL_MakeCurrent(win, glCtx); // associate GL context with main window
- int avsync = SDL_GL_SetSwapInterval(-1); // try to enable adaptive vsync
- if (avsync == -1) { // adaptive vsync not supported
- SDL_GL_SetSwapInterval(1); // enable updates synchronized with vertical retrace
- }
-
-
- projectMSDL *app;
-
- std::string base_path = DATADIR_PATH;
- SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using data directory: %s\n", base_path.c_str());
-
- // load configuration file
- std::string configFilePath = getConfigFilePath(base_path);
-
- if (! configFilePath.empty()) {
- // found config file, use it
- app = new projectMSDL(configFilePath, 0);
- SDL_Log("Using config from %s", configFilePath.c_str());
- } else {
- base_path = SDL_GetBasePath();
- SDL_Log("Config file not found, using built-in settings. Data directory=%s\n", base_path.c_str());
-
- // Get max refresh rate from attached displays to use as built-in max FPS.
- int i = 0;
- int maxRefreshRate = 0;
- SDL_DisplayMode current;
- for (i = 0; i < SDL_GetNumVideoDisplays(); ++i)
- {
- if (SDL_GetCurrentDisplayMode(i, ¤t) == 0)
- {
- if (current.refresh_rate > maxRefreshRate) maxRefreshRate = current.refresh_rate;
- }
- }
- if (maxRefreshRate <= 60) maxRefreshRate = 60;
-
- float heightWidthRatio = (float)height / (float)width;
- projectM::Settings settings;
- settings.windowWidth = width;
- settings.windowHeight = height;
- settings.meshX = 128;
- settings.meshY = settings.meshX * heightWidthRatio;
- settings.fps = maxRefreshRate;
- settings.smoothPresetDuration = 3; // seconds
- settings.presetDuration = 22; // seconds
- settings.hardcutEnabled = true;
- settings.hardcutDuration = 60;
- settings.hardcutSensitivity = 1.0;
- settings.beatSensitivity = 1.0;
- settings.aspectCorrection = 1;
- settings.shuffleEnabled = 1;
- settings.softCutRatingsEnabled = 1; // ???
- // get path to our app, use CWD or resource dir for presets/fonts/etc
- settings.presetURL = base_path + "presets";
-// settings.presetURL = base_path + "presets/presets_shader_test";
- settings.menuFontURL = base_path + "fonts/Vera.ttf";
- settings.titleFontURL = base_path + "fonts/Vera.ttf";
- // init with settings
- app = new projectMSDL(settings, 0);
- }
-
- // If our config or hard-coded settings create a resolution smaller than the monitors, then resize the SDL window to match.
- if (height > app->getWindowHeight() || width > app->getWindowWidth()) {
- SDL_SetWindowSize(win, app->getWindowWidth(),app->getWindowHeight());
- SDL_SetWindowPosition(win, (width / 2)-(app->getWindowWidth()/2), (height / 2)-(app->getWindowHeight()/2));
- } else if (height < app->getWindowHeight() || width < app->getWindowWidth()) {
- // If our config is larger than our monitors resolution then reduce it.
- SDL_SetWindowSize(win, width, height);
- SDL_SetWindowPosition(win, 0, 0);
- }
-
- // Create a help menu specific to SDL
- std::string modKey = "CTRL";
-
-#if __APPLE__
-modKey = "CMD";
-#endif
-
- std::string sdlHelpMenu = "\n"
- "F1: This help menu""\n"
- "F3: Show preset name""\n"
- "F4: Show details and statistics""\n"
- "F5: Show FPS""\n"
- "L or SPACE: Lock/Unlock Preset""\n"
- "R: Random preset""\n"
- "N: Next preset""\n"
- "P: Previous preset""\n"
- "UP: Increase Beat Sensitivity""\n"
- "DOWN: Decrease Beat Sensitivity""\n"
-#ifdef PROJECTM_TOUCH_ENABLED
- "Left Click: Drop Random Waveform on Screen""\n"
- "Right Click: Remove Random Waveform""\n" +
- modKey + "+Right Click: Remove All Random Waveforms""\n"
-#endif
- + modKey + "+I: Audio Input (listen to next device)""\n" +
- modKey + "+M: Change Monitor""\n" +
- modKey + "+S: Stretch Monitors""\n" +
- modKey + "+F: Fullscreen""\n" +
- modKey + "+Q: Quit";
- app->setHelpText(sdlHelpMenu.c_str());
- app->init(win, &glCtx);
-
-#if STEREOSCOPIC_SBS
- app->toggleFullScreen();
-#endif
-
-#if OGL_DEBUG && !USE_GLES
- glEnable(GL_DEBUG_OUTPUT);
- glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
- glDebugMessageCallback(DebugLog, NULL);
-#endif
-
-#if !FAKE_AUDIO && !WASAPI_LOOPBACK
- // get an audio input device
- if (app->openAudioInput())
- app->beginAudioCapture();
-#endif
-
-#ifdef WASAPI_LOOPBACK
- // Default to WASAPI loopback if it was enabled at compilation.
- app->wasapi = true;
- // Notify that loopback capture was started.
- SDL_Log("Opened audio capture loopback.");
-#endif
-
-#if TEST_ALL_PRESETS
- uint buildErrors = 0;
- for(unsigned int i = 0; i < app->getPlaylistSize(); i++) {
- std::cout << i << "\t" << app->getPresetName(i) << std::endl;
- app->selectPreset(i);
- if (app->getErrorLoadingCurrentPreset()) {
- buildErrors++;
- }
- }
-
- if (app->getPlaylistSize()) {
- fprintf(stdout, "Preset loading errors: %d/%d [%d%%]\n", buildErrors, app->getPlaylistSize(), (buildErrors*100) / app->getPlaylistSize());
- }
-
- delete app;
-
- return PROJECTM_SUCCESS;
-#endif
-
-#if UNLOCK_FPS
- int32_t frame_count = 0;
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- int64_t start = ( ((int64_t)now.tv_sec * 1000L) + (now.tv_nsec / 1000000L) );
-#endif
-
- // standard main loop
+ // frame rate limiter
int fps = app->settings().fps;
- printf("fps: %d\n", fps);
if (fps <= 0)
fps = 60;
const Uint32 frame_delay = 1000/fps;
Uint32 last_time = SDL_GetTicks();
+
+ // loop
while (! app->done) {
+ // render
app->renderFrame();
-#if FAKE_AUDIO
- app->fakeAudio = true;
-#endif
- // fakeAudio can also be enabled dynamically.
- if (app->fakeAudio )
- {
- app->addFakePCM();
- }
-#ifdef WASAPI_LOOPBACK
- if (app->wasapi) {
- // drain data while it is available
- nPasses++;
- UINT32 nNextPacketSize;
- for (
- hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize);
- SUCCEEDED(hr) && nNextPacketSize > 0;
- hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize)
- ) {
- // get the captured data
- BYTE *pData;
- UINT32 nNumFramesToRead;
- DWORD dwFlags;
-
- hr = pAudioCaptureClient->GetBuffer(
- &pData,
- &nNumFramesToRead,
- &dwFlags,
- NULL,
- NULL
- );
- if (FAILED(hr)) {
- return hr;
- }
-
- LONG lBytesToWrite = nNumFramesToRead * nBlockAlign;
-
- /** Add the waveform data */
- app->pcm()->addPCMfloat((float *)pData, nNumFramesToRead);
-
- *pnFrames += nNumFramesToRead;
-
- hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead);
- if (FAILED(hr)) {
- return hr;
- }
-
- bFirstPacket = false;
- }
-
- if (FAILED(hr)) {
- return hr;
- }
- }
-#endif /** WASAPI_LOOPBACK */
-
+
+ if (app->fakeAudio)
+ app->addFakePCM();
+ processLoopbackFrame(app);
+
#if UNLOCK_FPS
- frame_count++;
- clock_gettime(CLOCK_MONOTONIC_RAW, &now);
- if (( ((int64_t)now.tv_sec * 1000L) + (now.tv_nsec / 1000000L) ) - start > 5000) {
- SDL_GL_DeleteContext(glCtx);
- delete(app);
- fprintf(stdout, "Frames[%d]\n", frame_count);
- exit(0);
- }
+ advanceUnlockedFPSCounterFrame(start);
#else
app->pollEvent();
Uint32 elapsed = SDL_GetTicks() - last_time;
@@ -572,19 +68,42 @@ modKey = "CMD";
#endif
}
- SDL_GL_DeleteContext(glCtx);
-#if !FAKE_AUDIO
- if (!app->wasapi) // not currently using WASAPI, so we need to endAudioCapture.
- app->endAudioCapture();
-#endif
-
- // Write back config with current app settings (if we loaded from a config file to begin with)
- if (!configFilePath.empty()) {
- projectM::writeConfig(configFilePath, app->settings());
- }
- delete app;
-
return PROJECTM_SUCCESS;
}
+int main(int argc, char *argv[]) {
+ projectMSDL *app = setupSDLApp();
+
+ int status = mainLoop(&app);
+
+// SDL_Thread *mainLoopThread;
+// int threadReturnValue;
+//
+// mainLoopThread = SDL_CreateThread(mainLoop, "MainLoop", &app);
+//
+// if (NULL == mainLoopThread) {
+// printf("SDL_CreateThread failed: %s\n", SDL_GetError());
+// return PROJECTM_ERROR;
+// } else {
+// SDL_WaitThread(mainLoopThread, &threadReturnValue);
+// printf("Thread returned value: %d\n", threadReturnValue);
+// }
+
+ // Write back config with current app settings (if we loaded from a config file to begin with)
+ std::string configFilePath = getConfigFilePath(DATADIR_PATH);
+ if (!configFilePath.empty()) {
+ projectM::writeConfig(configFilePath, app->settings());
+ }
+
+ // cleanup
+ SDL_GL_DeleteContext(app->glCtx);
+#if !FAKE_AUDIO
+ if (!app->wasapi) // not currently using WASAPI, so we need to endAudioCapture.
+ app->endAudioCapture();
+#endif
+ delete app;
+
+ return status;
+}
+
diff --git a/src/projectM-sdl/setup.cpp b/src/projectM-sdl/setup.cpp
new file mode 100644
index 000000000..96b237ab6
--- /dev/null
+++ b/src/projectM-sdl/setup.cpp
@@ -0,0 +1,334 @@
+#include "setup.hpp"
+
+#include
+#include
+
+#if OGL_DEBUG
+void debugGL(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const void* userParam) {
+
+ /*if (type != GL_DEBUG_TYPE_OTHER)*/
+ {
+ std::cerr << " -- \n" << "Type: " <<
+ type << "; Source: " <<
+ source <<"; ID: " << id << "; Severity: " <<
+ severity << "\n" << message << "\n";
+ }
+}
+#endif
+
+// return path to config file to use
+std::string getConfigFilePath(std::string datadir_path) {
+ char* home = NULL;
+ std::string projectM_home;
+ std::string projectM_config = DATADIR_PATH;
+
+ projectM_config = datadir_path;
+
+#ifdef _MSC_VER
+ home=getenv("USERPROFILE");
+#else
+ home=getenv("HOME");
+#endif
+
+ projectM_home = std::string(home);
+ projectM_home += "/.projectM";
+
+ // Create the ~/.projectM directory. If it already exists, mkdir will do nothing
+#if defined _MSC_VER
+ _mkdir(projectM_home.c_str());
+#else
+ mkdir(projectM_home.c_str(), 0755);
+#endif
+
+ projectM_home += "/config.inp";
+ projectM_config += "/config.inp";
+
+ std::ifstream f_home(projectM_home);
+ std::ifstream f_config(projectM_config);
+ std::cout << "f_home " << f_home.good() << "\n";
+
+ if (f_config.good() && !f_home.good()) {
+ std::ifstream f_src;
+ std::ofstream f_dst;
+
+ f_src.open(projectM_config, std::ios::in | std::ios::binary);
+ f_dst.open(projectM_home, std::ios::out | std::ios::binary);
+ f_dst << f_src.rdbuf();
+ f_dst.close();
+ f_src.close();
+ return std::string(projectM_home);
+ } else if (f_home.good()) {
+ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Read ~/.projectM/config.inp\n");
+ return std::string(projectM_home);
+ } else if (f_config.good()) {
+ SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Cannot create ~/.projectM/config.inp, using %s\n", projectM_config.c_str());
+ return std::string(projectM_config);
+ } else {
+ SDL_LogWarn(SDL_LOG_CATEGORY_ERROR, "Using implementation defaults, your system is really messed up, I'm surprised we even got this far\n");
+ return "";
+ }
+}
+
+
+void seedRand() {
+#ifndef WIN32
+ srand((int)(time(NULL)));
+#endif
+}
+
+void initGL() {
+#ifdef USE_GLES
+ // use GLES 2.0 (this may need adjusting)
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+#else
+ // Disabling compatibility profile
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+#endif
+}
+
+void dumpOpenGLInfo() {
+ SDL_Log("- GL_VERSION: %s", glGetString(GL_VERSION));
+ SDL_Log("- GL_SHADING_LANGUAGE_VERSION: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
+ SDL_Log("- GL_VENDOR: %s", glGetString(GL_VENDOR));
+}
+
+void initStereoscopicView(SDL_Window *win) {
+#if STEREOSCOPIC_SB
+ // enable stereo
+ if (SDL_GL_SetAttribute(SDL_GL_STEREO, 1) == 0)
+ {
+ SDL_Log("SDL_GL_STEREO: true");
+ }
+
+ // requires fullscreen mode
+ SDL_ShowCursor(false);
+ SDL_SetWindowFullscreen(win, SDL_WINDOW_FULLSCREEN);
+#endif
+}
+
+void enableGLDebugOutput() {
+#if OGL_DEBUG && !USE_GLES
+ glEnable(GL_DEBUG_OUTPUT);
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+ glDebugMessageCallback(debugGL, NULL);
+#endif
+}
+
+void testAllPresets(projectMSDL *app) {
+ uint buildErrors = 0;
+ for(unsigned int i = 0; i < app->getPlaylistSize(); i++) {
+ std::cout << i << "\t" << app->getPresetName(i) << std::endl;
+ app->selectPreset(i);
+ if (app->getErrorLoadingCurrentPreset()) {
+ buildErrors++;
+ }
+ }
+
+ if (app->getPlaylistSize()) {
+ fprintf(stdout, "Preset loading errors: %d/%d [%d%%]\n", buildErrors, app->getPlaylistSize(), (buildErrors*100) / app->getPlaylistSize());
+ }
+
+ delete app;
+}
+
+// initialize SDL, openGL, config
+projectMSDL *setupSDLApp() {
+ projectMSDL *app;
+ seedRand();
+
+ if (!initLoopback())
+ {
+ SDL_Log("Failed to initialize audio loopback devide.");
+ exit(1);
+ }
+
+#if UNLOCK_FPS
+ setenv("vblank_mode", "0", 1);
+#endif
+
+ SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
+
+ if (! SDL_VERSION_ATLEAST(2, 0, 5)) {
+ SDL_Log("SDL version 2.0.5 or greater is required. You have %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
+ exit(1);
+ }
+
+ // default window size to usable bounds (e.g. minus menubar and dock)
+ SDL_Rect initialWindowBounds;
+#if SDL_VERSION_ATLEAST(2, 0, 5)
+ // new and better
+ SDL_GetDisplayUsableBounds(0, &initialWindowBounds);
+#else
+ SDL_GetDisplayBounds(0, &initialWindowBounds);
+#endif
+ int width = initialWindowBounds.w;
+ int height = initialWindowBounds.h;
+
+ initGL();
+
+ SDL_Window *win = SDL_CreateWindow("projectM", 0, 0, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
+ SDL_GL_GetDrawableSize(win,&width,&height);
+
+ initStereoscopicView(win);
+
+ SDL_GLContext glCtx = SDL_GL_CreateContext(win);
+ dumpOpenGLInfo();
+
+ SDL_SetWindowTitle(win, "projectM");
+
+ SDL_GL_MakeCurrent(win, glCtx); // associate GL context with main window
+ int avsync = SDL_GL_SetSwapInterval(-1); // try to enable adaptive vsync
+ if (avsync == -1) { // adaptive vsync not supported
+ SDL_GL_SetSwapInterval(1); // enable updates synchronized with vertical retrace
+ }
+
+ std::string base_path = DATADIR_PATH;
+ SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using data directory: %s\n", base_path.c_str());
+
+ // load configuration file
+ std::string configFilePath = getConfigFilePath(base_path);
+
+ if (! configFilePath.empty()) {
+ // found config file, use it
+ app = new projectMSDL(glCtx, configFilePath, 0);
+ SDL_Log("Using config from %s", configFilePath.c_str());
+ } else {
+ // use some sane defaults if config file not found
+ base_path = SDL_GetBasePath();
+ SDL_Log("Config file not found, using built-in settings. Data directory=%s\n", base_path.c_str());
+
+ // Get max refresh rate from attached displays to use as built-in max FPS.
+ int i = 0;
+ int maxRefreshRate = 0;
+ SDL_DisplayMode current;
+ for (i = 0; i < SDL_GetNumVideoDisplays(); ++i)
+ {
+ if (SDL_GetCurrentDisplayMode(i, ¤t) == 0)
+ {
+ if (current.refresh_rate > maxRefreshRate) maxRefreshRate = current.refresh_rate;
+ }
+ }
+ if (maxRefreshRate <= 60) maxRefreshRate = 60;
+
+ float heightWidthRatio = (float)height / (float)width;
+ projectM::Settings settings;
+ settings.windowWidth = width;
+ settings.windowHeight = height;
+ settings.meshX = 128;
+ settings.meshY = settings.meshX * heightWidthRatio;
+ settings.fps = maxRefreshRate;
+ settings.smoothPresetDuration = 3; // seconds
+ settings.presetDuration = 22; // seconds
+ settings.hardcutEnabled = true;
+ settings.hardcutDuration = 60;
+ settings.hardcutSensitivity = 1.0;
+ settings.beatSensitivity = 1.0;
+ settings.aspectCorrection = 1;
+ settings.shuffleEnabled = 1;
+ settings.softCutRatingsEnabled = 1; // ???
+ // get path to our app, use CWD or resource dir for presets/fonts/etc
+ settings.presetURL = base_path + "presets";
+ // settings.presetURL = base_path + "presets/presets_shader_test";
+ settings.menuFontURL = base_path + "fonts/Vera.ttf";
+ settings.titleFontURL = base_path + "fonts/Vera.ttf";
+ // init with settings
+ app = new projectMSDL(glCtx, settings, 0);
+ }
+
+ // center window and full desktop screen
+ SDL_DisplayMode dm;
+ if (SDL_GetDesktopDisplayMode(0, &dm) == 0) {
+ width = dm.w;
+ height = dm.h;
+ } else {
+ SDL_Log("SDL_GetDesktopDisplayMode failed: %s", SDL_GetError());
+ }
+ SDL_SetWindowPosition(win, initialWindowBounds.x, initialWindowBounds.y);
+ SDL_SetWindowSize(win, width, height);
+ app->resize(width, height);
+
+ // Create a help menu specific to SDL
+ std::string modKey = "CTRL";
+
+#if __APPLE__
+ modKey = "CMD";
+#endif
+
+ std::string sdlHelpMenu = "\n"
+ "F1: This help menu""\n"
+ "F3: Show preset name""\n"
+ "F4: Show details and statistics""\n"
+ "F5: Show FPS""\n"
+ "L or SPACE: Lock/Unlock Preset""\n"
+ "R: Random preset""\n"
+ "N: Next preset""\n"
+ "P: Previous preset""\n"
+ "UP: Increase Beat Sensitivity""\n"
+ "DOWN: Decrease Beat Sensitivity""\n"
+#ifdef PROJECTM_TOUCH_ENABLED
+ "Left Click: Drop Random Waveform on Screen""\n"
+ "Right Click: Remove Random Waveform""\n" +
+ modKey + "+Right Click: Remove All Random Waveforms""\n"
+#endif
+ + modKey + "+I: Audio Input (listen to next device)""\n" +
+ modKey + "+M: Change Monitor""\n" +
+ modKey + "+S: Stretch Monitors""\n" +
+ modKey + "+F: Fullscreen""\n" +
+ modKey + "+Q: Quit";
+ app->setHelpText(sdlHelpMenu.c_str());
+ app->init(win);
+
+#if STEREOSCOPIC_SBS
+ app->toggleFullScreen();
+#endif
+#if FAKE_AUDIO
+ app->fakeAudio = true;
+#endif
+
+ enableGLDebugOutput();
+ configureLoopback(app);
+
+#if !FAKE_AUDIO && !WASAPI_LOOPBACK
+ // get an audio input device
+ if (app->openAudioInput())
+ app->beginAudioCapture();
+#endif
+
+#if TEST_ALL_PRESETS
+ testAllPresets(app);
+ return 0;
+#endif
+
+ return app;
+}
+
+int64_t startUnlockedFPSCounter() {
+ using namespace std::chrono;
+ auto currentTime = steady_clock::now();
+ auto currentTimeMs = time_point_cast(currentTime);
+ auto elapsedMs = currentTime.time_since_epoch();
+
+ return elapsedMs.count();
+}
+
+void advanceUnlockedFPSCounterFrame(int64_t startFrame) {
+ static int32_t frameCount = 0;
+
+ frameCount++;
+ auto currentElapsedMs = startUnlockedFPSCounter();
+ if (currentElapsedMs - startFrame > 5000)
+ {
+ printf("Frames[%d]\n", frameCount);
+ exit(0);
+ }
+}
diff --git a/src/projectM-sdl/setup.hpp b/src/projectM-sdl/setup.hpp
new file mode 100644
index 000000000..87b6acd8f
--- /dev/null
+++ b/src/projectM-sdl/setup.hpp
@@ -0,0 +1,27 @@
+#ifndef setup_hpp
+#define setup_hpp
+
+#include "pmSDL.hpp"
+
+class projectMSDL;
+
+void debugGL(GLenum source,
+ GLenum type,
+ GLuint id,
+ GLenum severity,
+ GLsizei length,
+ const GLchar* message,
+ const void* userParam);
+
+std::string getConfigFilePath(std::string datadir_path);
+void seedRand();
+void initGL();
+void dumpOpenGLInfo();
+void initStereoscopicView(SDL_Window *win);
+void enableGLDebugOutput();
+void testAllPresets(projectMSDL *app);
+projectMSDL *setupSDLApp();
+int64_t startUnlockedFPSCounter();
+void advanceUnlockedFPSCounterFrame(int64_t startFrame);
+
+#endif /* setup_hpp */