Split SDL project up into more managable pieces (#473)

* Enable text for SDL app on mac

* fix screenshots

* brew readme

* wip

* Split SDL project up into more managable pieces

* clean

* clean

* Fixed Windows build, made (unused) FPS counter portable to non-POSIX platforms.

Co-authored-by: Kai Blaschke <kai.blaschke@kb-dev.net>
This commit is contained in:
Mischa Spiegelmock
2021-04-21 10:09:51 +03:00
committed by GitHub
parent 4184f237bd
commit f9cfdfed77
17 changed files with 914 additions and 756 deletions

View File

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

View File

@ -142,6 +142,9 @@
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)../src\projectM-sdl\projectM_SDL_main.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)../src\projectM-sdl\pmSDL.cpp" />
<ClCompile Include="..\src\projectM-sdl\audioCapture.cpp" />
<ClCompile Include="..\src\projectM-sdl\loopback.cpp" />
<ClCompile Include="..\src\projectM-sdl\setup.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="\MilkdropPresetFactory.vcxproj">
@ -181,6 +184,10 @@
</Image>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\projectM-sdl\audioCapture.hpp" />
<ClInclude Include="..\src\projectM-sdl\loopback.hpp" />
<ClInclude Include="..\src\projectM-sdl\pmSDL.hpp" />
<ClInclude Include="..\src\projectM-sdl\setup.hpp" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>

View File

@ -244,7 +244,6 @@
1668542B2105E4BD0042793A /* Renderable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Renderable.cpp; path = Renderer/Renderable.cpp; sourceTree = "<group>"; };
1668542C2105E4BD0042793A /* PipelineContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PipelineContext.cpp; path = Renderer/PipelineContext.cpp; sourceTree = "<group>"; };
1668542D2105E4BD0042793A /* MilkdropWaveform.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MilkdropWaveform.cpp; path = Renderer/MilkdropWaveform.cpp; sourceTree = "<group>"; };
168404F425D82ED70001F02C /* StaticGlShaders.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = StaticGlShaders.cpp; path = Renderer/StaticGlShaders.cpp; sourceTree = "<group>"; };
168404FE25D82FB80001F02C /* Intrinsics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Intrinsics.h; sourceTree = "<group>"; };
1687172320C33DF300947E7E /* TextureManager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TextureManager.hpp; path = Renderer/TextureManager.hpp; sourceTree = "<group>"; };
1687172420C33DF300947E7E /* Renderable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Renderable.hpp; path = Renderer/Renderable.hpp; sourceTree = "<group>"; };
@ -446,7 +445,6 @@
isa = PBXGroup;
children = (
168A57C62516226900E802A0 /* StaticGlShaders.cpp */,
168404F425D82ED70001F02C /* StaticGlShaders.cpp */,
166854412105E4C20042793A /* etc */,
1687172220C33DDF00947E7E /* headers */,
168C689F20BB265D000AFC1B /* hlslparser */,

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 = "<group>"; };
168F717F21126256001806E7 /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = "<group>"; };
168FECCB25EA83F800E3E133 /* loopback.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = loopback.cpp; sourceTree = "<group>"; };
168FECCC25EA83F800E3E133 /* loopback.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = loopback.hpp; sourceTree = "<group>"; };
168FECD225EA86E900E3E133 /* setup.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = setup.cpp; sourceTree = "<group>"; };
168FECD325EA86E900E3E133 /* setup.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = setup.hpp; sourceTree = "<group>"; };
169501FE1F7009E9008FAF86 /* pmSDL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pmSDL.cpp; sourceTree = "<group>"; };
169501FF1F7009E9008FAF86 /* pmSDL.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = pmSDL.hpp; sourceTree = "<group>"; };
169BC64B24CC8353007B7829 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL2.framework; sourceTree = "<group>"; };
169BC64D24CC83CD007B7829 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SDL2.framework; sourceTree = "<group>"; };
16B52AA8211054E900830F34 /* projectMSDL-pkg.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "projectMSDL-pkg.plist"; sourceTree = "<group>"; };
16B52AAA21105A6900830F34 /* config.inp */ = {isa = PBXFileReference; lastKnownFileType = text; path = config.inp; sourceTree = "<group>"; };
16CFA3AA25EABCB100E7893C /* audioCapture.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = audioCapture.cpp; sourceTree = "<group>"; };
16CFA3AB25EABCB100E7893C /* audioCapture.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = audioCapture.hpp; sourceTree = "<group>"; };
C307DFD31D565B57002F6B9E /* presets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = presets; path = ../../presets; sourceTree = "<group>"; };
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;
};

View File

@ -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);
}

View File

@ -0,0 +1,6 @@
#ifndef audioCapture_hpp
#define audioCapture_hpp
#include "pmSDL.hpp"
#endif /* audioCapture_hpp */

View File

@ -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

View File

@ -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<PWAVEFORMATEXTENSIBLE>(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 */
}

View File

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

View File

@ -29,163 +29,6 @@
*/
#include "pmSDL.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#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

View File

@ -39,12 +39,29 @@
#define TEST_ALL_PRESETS 0
#define STEREOSCOPIC_SBS 0
// projectM
#include "projectM-opengl.h"
#include <projectM.hpp>
#include "Renderer/ShaderEngine.hpp"
#include "Renderer/StaticGlShaders.h"
#include <sdltoprojectM.h>
// projectM SDL
#include "setup.hpp"
#include "loopback.hpp"
#include "audioCapture.hpp"
#if defined _MSC_VER
# include <direct.h>
#endif
#include <fstream>
#include <string>
#include <iostream>
#include <sys/stat.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#ifdef WASAPI_LOOPBACK
#include <stdio.h>
@ -60,8 +77,6 @@
#endif /** WASAPI_LOOPBACK */
#ifdef WIN32
#define SDL_MAIN_HANDLED
#include "SDL.h"
@ -69,6 +84,7 @@
#include <SDL2/SDL.h>
#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;

View File

@ -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 <direct.h>
#endif
#include <fstream>
#include <iostream>
#include <string>
* 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<PWAVEFORMATEXTENSIBLE>(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, &current) == 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;
}

334
src/projectM-sdl/setup.cpp Normal file
View File

@ -0,0 +1,334 @@
#include "setup.hpp"
#include <chrono>
#include <cmath>
#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, &current) == 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<milliseconds>(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);
}
}

View File

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