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 */