diff --git a/README.md b/README.md
index a3f668c16b..07e073784f 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ Its purpose in life is to read an audio input and to produce mesmerizing visuals
### Available For
* [macOS, Linux (binary)](https://github.com/projectM-visualizer/projectm/releases)
* [Windows Store (PC, XBOX, Phone)](https://www.microsoft.com/store/apps/9NDCVH0VCWJN)
+* [Windows (standalone binary)](https://github.com/projectM-visualizer/projectm/releases) (Requires the latest [Visual C++ redistributable](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads))
* [Android](https://play.google.com/store/apps/details?id=com.psperl.projectM)
* [iOS](https://itunes.apple.com/us/app/projectm-music-visualizer/id530922227?mt=8&ign-mpt=uo%3D4)
* [iTunes plugin (macOS)](https://github.com/projectM-visualizer/projectm/releases/)
diff --git a/msvc/projectM.ico b/msvc/projectM.ico
new file mode 100644
index 0000000000..2f7fe13993
Binary files /dev/null and b/msvc/projectM.ico differ
diff --git a/msvc/projectM.sln b/msvc/projectM.sln
index 81c7ad23c6..cf70e0a902 100644
--- a/msvc/projectM.sln
+++ b/msvc/projectM.sln
@@ -62,27 +62,17 @@ Global
{013DE011-EC24-3643-A8EE-F2609E7E4741}.Release|x64.ActiveCfg = Release|x64
{013DE011-EC24-3643-A8EE-F2609E7E4741}.Release|x64.Build.0 = Release|x64
{55A71B6A-5C7E-30D5-8210-302A8D2080DB}.Debug|x64.ActiveCfg = Debug|x64
- {55A71B6A-5C7E-30D5-8210-302A8D2080DB}.Debug|x64.Build.0 = Debug|x64
{55A71B6A-5C7E-30D5-8210-302A8D2080DB}.Release|x64.ActiveCfg = Release|x64
- {55A71B6A-5C7E-30D5-8210-302A8D2080DB}.Release|x64.Build.0 = Release|x64
{9260C46C-6BC9-396F-9310-6BAAD56A7801}.Debug|x64.ActiveCfg = Debug|x64
{9260C46C-6BC9-396F-9310-6BAAD56A7801}.Release|x64.ActiveCfg = Release|x64
{7A203034-A4D7-3A2B-9138-CB125F9B35E6}.Debug|x64.ActiveCfg = Debug|x64
- {7A203034-A4D7-3A2B-9138-CB125F9B35E6}.Debug|x64.Build.0 = Debug|x64
{7A203034-A4D7-3A2B-9138-CB125F9B35E6}.Release|x64.ActiveCfg = Release|x64
- {7A203034-A4D7-3A2B-9138-CB125F9B35E6}.Release|x64.Build.0 = Release|x64
{6E418BC8-5407-3A37-96BD-5201D47DE753}.Debug|x64.ActiveCfg = Debug|x64
- {6E418BC8-5407-3A37-96BD-5201D47DE753}.Debug|x64.Build.0 = Debug|x64
{6E418BC8-5407-3A37-96BD-5201D47DE753}.Release|x64.ActiveCfg = Release|x64
- {6E418BC8-5407-3A37-96BD-5201D47DE753}.Release|x64.Build.0 = Release|x64
{B7C4937F-A36D-3B6C-A8AC-CA99772AE5EC}.Debug|x64.ActiveCfg = Debug|x64
- {B7C4937F-A36D-3B6C-A8AC-CA99772AE5EC}.Debug|x64.Build.0 = Debug|x64
{B7C4937F-A36D-3B6C-A8AC-CA99772AE5EC}.Release|x64.ActiveCfg = Release|x64
- {B7C4937F-A36D-3B6C-A8AC-CA99772AE5EC}.Release|x64.Build.0 = Release|x64
{27DDCE71-E33B-3521-92B5-9918356D78A1}.Debug|x64.ActiveCfg = Debug|x64
- {27DDCE71-E33B-3521-92B5-9918356D78A1}.Debug|x64.Build.0 = Debug|x64
{27DDCE71-E33B-3521-92B5-9918356D78A1}.Release|x64.ActiveCfg = Release|x64
- {27DDCE71-E33B-3521-92B5-9918356D78A1}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/msvc/projectMSDL.vcxproj b/msvc/projectMSDL.vcxproj
index 4b38770bb7..b5377f5a88 100644
--- a/msvc/projectMSDL.vcxproj
+++ b/msvc/projectMSDL.vcxproj
@@ -89,7 +89,7 @@
%(IgnoreSpecificDefaultLibraries)
projectM-sdl/Debug/projectMSDL.lib
projectM-sdl/Debug/projectMSDL.pdb
- Console
+ Windows
false
@@ -133,7 +133,7 @@
%(IgnoreSpecificDefaultLibraries)
projectM-sdl/Release/projectMSDL.lib
projectM-sdl/Release/projectMSDL.pdb
- Console
+ Windows
false
@@ -176,7 +176,7 @@
-
+
true
@@ -210,6 +210,7 @@
+
diff --git a/src/libprojectM/PresetFactory.cpp b/src/libprojectM/PresetFactory.cpp
index aa4fc98c0a..c7b2178d8e 100644
--- a/src/libprojectM/PresetFactory.cpp
+++ b/src/libprojectM/PresetFactory.cpp
@@ -16,7 +16,9 @@ std::string PresetFactory::protocol(const std::string & url, std::string & path)
else {
path = url.substr(pos + 3, url.length());
// std::cout << "[PresetFactory] path is " << path << std::endl;
+#ifdef DEBUG
std::cout << "[PresetFactory] url is " << url << std::endl;
+#endif
return url.substr(0, pos);
}
diff --git a/src/libprojectM/Renderer/ShaderEngine.cpp b/src/libprojectM/Renderer/ShaderEngine.cpp
index b5910ee436..05afded149 100644
--- a/src/libprojectM/Renderer/ShaderEngine.cpp
+++ b/src/libprojectM/Renderer/ShaderEngine.cpp
@@ -765,7 +765,9 @@ GLuint ShaderEngine::compilePresetShader(const PresentShaderType shaderType, Sha
}
if (ret != GL_FALSE) {
+#ifdef DEBUG
std::cerr << "Successful compilation of " << shaderTypeString << std::endl;
+#endif
} else {
std::cerr << "Compilation error (step3) of " << shaderTypeString << std::endl;
diff --git a/src/projectM-sdl/pmSDL.cpp b/src/projectM-sdl/pmSDL.cpp
index 379fc157d7..98978d56ce 100644
--- a/src/projectM-sdl/pmSDL.cpp
+++ b/src/projectM-sdl/pmSDL.cpp
@@ -61,27 +61,45 @@ void projectMSDL::audioInputCallbackS16(void *userdata, unsigned char *stream, i
app->pcm()->addPCM16(pcm16);
}
-SDL_AudioDeviceID projectMSDL::selectAudioInput(int _count) {
- // TODO: implement some sort of UI allowing the user to select which audio input device they would like to use
-
-
- // ask the user which capture device to use
- // printf("Please select which audio input to use:\n");
- printf("Detected devices:\n");
- for (int i = 0; i < _count; i++) {
- printf(" %i: 🎤%s\n", i, SDL_GetAudioDeviceName(i, true));
- }
-
- return 0;
-}
-
int projectMSDL::toggleAudioInput() {
-
- CurAudioDevice++;
- if (CurAudioDevice >= NumAudioDevices)
+ // 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;
- initAudioInput();
+ 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;
}
@@ -105,15 +123,17 @@ int projectMSDL::initAudioInput() {
if (audioDeviceID == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to open audio capture device: %s", SDL_GetError());
- SDL_Quit();
+ 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 = "Listening to ";
- deviceToast += 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;
@@ -124,9 +144,12 @@ int projectMSDL::initAudioInput() {
}
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
unsigned int i;
@@ -135,23 +158,18 @@ int projectMSDL::openAudioInput() {
CurAudioDevice = 0;
if (NumAudioDevices == 0) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "No audio capture devices found");
- SDL_Quit();
+ projectM::setToastMessage("No audio capture devices found: using simulated audio");
+ fakeAudio = true;
+ return 0;
}
+#ifdef DEBUG
for (i = 0; i < NumAudioDevices; i++) {
SDL_Log("Found audio capture device %d: %s", i, SDL_GetAudioDeviceName(i, true));
}
+#endif
- // device to open
+ // default selected Audio Device to 0.
selectedAudioDevice = 0;
- if (NumAudioDevices > 1) {
- // need to choose which input device to use
- selectedAudioDevice = selectAudioInput(CurAudioDevice);
- if (selectedAudioDevice > NumAudioDevices) {
- SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "No audio input device specified.");
- SDL_Quit();
- }
- }
-
initAudioInput();
return 1;
@@ -164,6 +182,7 @@ void projectMSDL::beginAudioCapture() {
void projectMSDL::endAudioCapture() {
SDL_PauseAudioDevice(audioDeviceID, true);
+ SDL_CloseAudioDevice(audioDeviceID);
}
void projectMSDL::setHelpText(const std::string & helpText) {
@@ -465,6 +484,10 @@ void projectMSDL::init(SDL_Window *window, SDL_GLContext *_glCtx, const bool _re
glCtx = _glCtx;
projectM_resetGL(width, height);
+#ifdef WASAPI_LOOPBACK
+ wasapi = true;
+#endif
+
// are we rendering to a texture?
renderToTexture = _renderToTexture;
if (renderToTexture) {
diff --git a/src/projectM-sdl/pmSDL.hpp b/src/projectM-sdl/pmSDL.hpp
index 5d25e2fbc6..531f33c137 100644
--- a/src/projectM-sdl/pmSDL.hpp
+++ b/src/projectM-sdl/pmSDL.hpp
@@ -88,6 +88,8 @@ class projectMSDL : public projectM {
bool done;
+ 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.
projectMSDL(Settings settings, int flags);
projectMSDL(std::string config_file, int flags);
void init(SDL_Window *window, SDL_GLContext *glCtx, const bool renderToTexture = false);
@@ -135,7 +137,6 @@ class projectMSDL : public projectM {
static void audioInputCallbackS16(void *userdata, unsigned char *stream, int len);
void keyHandler(SDL_Event *);
- SDL_AudioDeviceID selectAudioInput(int _count);
void renderTexture();
};
diff --git a/src/projectM-sdl/projectM_SDL_main.cpp b/src/projectM-sdl/projectM_SDL_main.cpp
index b926bf86b1..876b3f884f 100644
--- a/src/projectM-sdl/projectM_SDL_main.cpp
+++ b/src/projectM-sdl/projectM_SDL_main.cpp
@@ -234,7 +234,7 @@ srand((int)(time(NULL)));
ERR(L"pAudioClient->Start error");
return hr;
}
-
+
bool bDone = false;
bool bFirstPacket = true;
UINT32 nPasses = 0;
@@ -376,6 +376,7 @@ modKey = "CMD";
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"
@@ -404,9 +405,16 @@ modKey = "CMD";
#endif
#if !FAKE_AUDIO && !WASAPI_LOOPBACK
- // get an audio input device
- if (app->openAudioInput())
- app->beginAudioCapture();
+ // 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
@@ -445,52 +453,58 @@ modKey = "CMD";
while (! app->done) {
app->renderFrame();
#if FAKE_AUDIO
- app->addFakePCM();
+ app->fakeAudio = true;
#endif
+ // fakeAudio can also be enabled dynamically.
+ if (app->fakeAudio )
+ {
+ app->addFakePCM();
+ }
#ifdef WASAPI_LOOPBACK
- // 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;
+ 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;
}
- 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 UNLOCK_FPS
@@ -512,8 +526,9 @@ modKey = "CMD";
}
SDL_GL_DeleteContext(glCtx);
-#if !FAKE_AUDIO && !WASAPI_LOOPBACK
- app->endAudioCapture();
+#if !FAKE_AUDIO
+ if (!app->wasapi) // not currently using WASAPI, so we need to endAudioCapture.
+ app->endAudioCapture();
#endif
delete app;