From 110a5ef89da2f8b32c9ae1e45330b58728085a4e Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Sun, 30 Aug 2020 02:47:31 +0200 Subject: [PATCH 01/32] move stuff around so that it at least compiles on linux (but it's absolutely unusable) --- .gitignore | 1 + CMakeLists.txt | 18 ++++++++---- WaveSabreCore/CMakeLists.txt | 27 ++++++++++++------ WaveSabreCore/include/WaveSabreCore.h | 2 ++ .../{Adultery.h => AdulteryWin32.h} | 0 WaveSabreCore/include/WaveSabreCore/Devices.h | 8 ++++-- .../{Specimen.h => SpecimenWin32.h} | 0 .../{Thunder.h => ThunderWin32.h} | 0 WaveSabreCore/include/WaveSabreCore/Twister.h | 2 +- .../src/{Adultery.cpp => AdulteryWin32.cpp} | 6 ++-- WaveSabreCore/src/Cathedral.cpp | 2 +- WaveSabreCore/src/Helpers.cpp | 3 +- WaveSabreCore/src/Leveller.cpp | 2 +- WaveSabreCore/src/ResampleBuffer.cpp | 2 +- .../src/{Specimen.cpp => SpecimenWin32.cpp} | 6 ++-- WaveSabreCore/src/SynthDevice.cpp | 4 +-- .../src/{Thunder.cpp => ThunderWin32.cpp} | 4 +-- WaveSabreCore/src/Twister.cpp | 8 +++--- WaveSabrePlayerLib/CMakeLists.txt | 28 +++++++++++++------ .../include/WaveSabrePlayerLib.h | 4 ++- .../WaveSabrePlayerLib/PreRenderPlayer.h | 5 ++++ ...RealtimePlayer.h => RealtimePlayerWin32.h} | 3 +- .../include/WaveSabrePlayerLib/SongRenderer.h | 12 ++++++-- WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 20 +++++++++++++ ...timePlayer.cpp => RealtimePlayerWin32.cpp} | 2 +- WaveSabrePlayerLib/src/SongRenderer.Track.cpp | 4 ++- WaveSabrePlayerLib/src/SongRenderer.cpp | 23 ++++++++++++++- WaveSabrePlayerLib/src/WavWriter.cpp | 8 +++++- WaveSabreStandAlonePlayer/CMakeLists.txt | 4 +++ WaveSabreStandAlonePlayer/main.cpp | 21 +++++++++++++- 30 files changed, 175 insertions(+), 54 deletions(-) rename WaveSabreCore/include/WaveSabreCore/{Adultery.h => AdulteryWin32.h} (100%) rename WaveSabreCore/include/WaveSabreCore/{Specimen.h => SpecimenWin32.h} (100%) rename WaveSabreCore/include/WaveSabreCore/{Thunder.h => ThunderWin32.h} (100%) rename WaveSabreCore/src/{Adultery.cpp => AdulteryWin32.cpp} (99%) rename WaveSabreCore/src/{Specimen.cpp => SpecimenWin32.cpp} (99%) rename WaveSabreCore/src/{Thunder.cpp => ThunderWin32.cpp} (99%) rename WaveSabrePlayerLib/include/WaveSabrePlayerLib/{RealtimePlayer.h => RealtimePlayerWin32.h} (99%) rename WaveSabrePlayerLib/src/{RealtimePlayer.cpp => RealtimePlayerWin32.cpp} (96%) diff --git a/.gitignore b/.gitignore index 7defc56f..41405e54 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ Release/ Vsts/build/ Tests/PlayerTest/build/ build/ +cmake-build/ Vst3.x/* !Vst3.x/README Data/data.aps diff --git a/CMakeLists.txt b/CMakeLists.txt index fa768de4..1930c62d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,19 +8,27 @@ if(MSVC) string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) endif() -set(VSTSDK3_DIR "./Vst3.x/" CACHE PATH "VSTSDK location") +if(WIN32) + set(VSTSDK3_DIR "./Vst3.x/" CACHE PATH "VSTSDK location") +endif() # shared code -add_subdirectory(MSVCRT) +if(WIN32) + add_subdirectory(MSVCRT) +endif() add_subdirectory(WaveSabreCore) add_subdirectory(WaveSabrePlayerLib) # binaries +if(WIN32) add_subdirectory(Tests/PlayerTest) +endif() add_subdirectory(WaveSabreStandAlonePlayer) # VSTs -if(VSTSDK3_DIR) - add_subdirectory(WaveSabreVstLib) - add_subdirectory(Vsts) +if(WIN32) + if(VSTSDK3_DIR) + add_subdirectory(WaveSabreVstLib) + add_subdirectory(Vsts) + endif() endif() diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 31ca1e2e..69bc737b 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -1,5 +1,4 @@ add_library(WaveSabreCore - include/WaveSabreCore/Adultery.h include/WaveSabreCore/AllPass.h include/WaveSabreCore/AllPassDelay.h include/WaveSabreCore/BiquadFilter.h @@ -12,7 +11,6 @@ add_library(WaveSabreCore include/WaveSabreCore/Echo.h include/WaveSabreCore/Envelope.h include/WaveSabreCore/Falcon.h - include/WaveSabreCore/GmDls.h include/WaveSabreCore/Helpers.h include/WaveSabreCore/Leveller.h include/WaveSabreCore/MxcsrFlagGuard.h @@ -21,12 +19,9 @@ add_library(WaveSabreCore include/WaveSabreCore/Scissor.h include/WaveSabreCore/Slaughter.h include/WaveSabreCore/Smasher.h - include/WaveSabreCore/Specimen.h include/WaveSabreCore/StateVariableFilter.h include/WaveSabreCore/SynthDevice.h - include/WaveSabreCore/Thunder.h include/WaveSabreCore/Twister.h - src/Adultery.cpp src/AllPass.cpp src/AllPassDelay.cpp src/BiquadFilter.cpp @@ -39,7 +34,6 @@ add_library(WaveSabreCore src/Echo.cpp src/Envelope.cpp src/Falcon.cpp - src/GmDls.cpp src/Helpers.cpp src/Leveller.cpp src/MxcsrFlagGuard.cpp @@ -48,13 +42,24 @@ add_library(WaveSabreCore src/Scissor.cpp src/Slaughter.cpp src/Smasher.cpp - src/Specimen.cpp src/StateVariableFilter.cpp src/SynthDevice.cpp - src/Thunder.cpp src/Twister.cpp) -target_link_libraries(WaveSabreCore Msacm32.lib) +if(WIN32) + add_library(WaveSabreCore + include/WaveSabreCore/AdulteryWin32.h + include/WaveSabreCore/GmDls.h + include/WaveSabreCore/SpecimenWin32.h + include/WaveSabreCore/ThunderWin32.h + src/AdulteryWin32.cpp + src/GmDls.cpp + src/SpecimenWin32.cpp + src/ThunderWin32.cpp) + + target_link_libraries(WaveSabreCore Msacm32.lib) +endif() + target_include_directories(WaveSabreCore PUBLIC include) if(MSVC) @@ -69,4 +74,8 @@ if(MSVC) target_compile_options(WaveSabreCore PUBLIC $<$:/Zc:sizedDealloc->) endif() +else() + # assuming GCC or clang for now + + set(CMAKE_CXX_FLAGS "-fpermissive") endif() diff --git a/WaveSabreCore/include/WaveSabreCore.h b/WaveSabreCore/include/WaveSabreCore.h index 4f179e1e..02d3df7c 100644 --- a/WaveSabreCore/include/WaveSabreCore.h +++ b/WaveSabreCore/include/WaveSabreCore.h @@ -12,7 +12,9 @@ #include "WaveSabreCore/AllPassDelay.h" #include "WaveSabreCore/Comb.h" #include "WaveSabreCore/ResampleBuffer.h" +#if defined(WIN32) || defined(_WIN32) #include "WaveSabreCore/GmDls.h" +#endif #include "WaveSabreCore/MxcsrFlagGuard.h" #include "WaveSabreCore/Devices.h" diff --git a/WaveSabreCore/include/WaveSabreCore/Adultery.h b/WaveSabreCore/include/WaveSabreCore/AdulteryWin32.h similarity index 100% rename from WaveSabreCore/include/WaveSabreCore/Adultery.h rename to WaveSabreCore/include/WaveSabreCore/AdulteryWin32.h diff --git a/WaveSabreCore/include/WaveSabreCore/Devices.h b/WaveSabreCore/include/WaveSabreCore/Devices.h index 06bc217b..bf9bb116 100644 --- a/WaveSabreCore/include/WaveSabreCore/Devices.h +++ b/WaveSabreCore/include/WaveSabreCore/Devices.h @@ -3,9 +3,11 @@ #include "Falcon.h" #include "Slaughter.h" -#include "Thunder.h" -#include "Adultery.h" -#include "Specimen.h" +#if defined(WIN32) || defined(_WIN32) +#include "ThunderWin32.h" +#include "AdulteryWin32.h" +#include "SpecimenWin32.h" +#endif #include "Scissor.h" #include "Leveller.h" diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/SpecimenWin32.h similarity index 100% rename from WaveSabreCore/include/WaveSabreCore/Specimen.h rename to WaveSabreCore/include/WaveSabreCore/SpecimenWin32.h diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/ThunderWin32.h similarity index 100% rename from WaveSabreCore/include/WaveSabreCore/Thunder.h rename to WaveSabreCore/include/WaveSabreCore/ThunderWin32.h diff --git a/WaveSabreCore/include/WaveSabreCore/Twister.h b/WaveSabreCore/include/WaveSabreCore/Twister.h index 536eeb7c..14fb4fde 100644 --- a/WaveSabreCore/include/WaveSabreCore/Twister.h +++ b/WaveSabreCore/include/WaveSabreCore/Twister.h @@ -65,7 +65,7 @@ namespace WaveSabreCore ResampleBuffer leftBuffer; ResampleBuffer rightBuffer; - + StateVariableFilter lowCutFilter[2], highCutFilter[2]; }; } diff --git a/WaveSabreCore/src/Adultery.cpp b/WaveSabreCore/src/AdulteryWin32.cpp similarity index 99% rename from WaveSabreCore/src/Adultery.cpp rename to WaveSabreCore/src/AdulteryWin32.cpp index 9447f324..7a0d87be 100644 --- a/WaveSabreCore/src/Adultery.cpp +++ b/WaveSabreCore/src/AdulteryWin32.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -240,7 +240,7 @@ namespace WaveSabreCore case ParamIndices::VoicesUnisono: return Helpers::UnisonoToParam(VoicesUnisono); case ParamIndices::VoicesDetune: return VoicesDetune; case ParamIndices::VoicesPan: return VoicesPan; - + case ParamIndices::VoiceMode: return Helpers::VoiceModeToParam(GetVoiceMode()); case ParamIndices::SlideTime: return Slide; @@ -319,7 +319,7 @@ namespace WaveSabreCore modEnv.Sustain = adultery->modSustain; modEnv.Release = adultery->modRelease; modEnv.Trigger(); - + samplePlayer.SampleData = adultery->sampleData; samplePlayer.SampleLength = adultery->sampleLength; samplePlayer.SampleLoopStart = adultery->sampleLoopStart; diff --git a/WaveSabreCore/src/Cathedral.cpp b/WaveSabreCore/src/Cathedral.cpp index e2951355..00452012 100644 --- a/WaveSabreCore/src/Cathedral.cpp +++ b/WaveSabreCore/src/Cathedral.cpp @@ -79,7 +79,7 @@ namespace WaveSabreCore outL += combLeft[i].Process(input); outR += combRight[i].Process(input); } - + // Feed through allpasses in series for (int i = 0; i < numAllPasses; i++) { diff --git a/WaveSabreCore/src/Helpers.cpp b/WaveSabreCore/src/Helpers.cpp index 3027da9e..440f3463 100644 --- a/WaveSabreCore/src/Helpers.cpp +++ b/WaveSabreCore/src/Helpers.cpp @@ -6,6 +6,7 @@ #if defined(_MSC_VER) && defined(_M_IX86) // TODO: make assembly equivalent for x64 (use intrinsic ?) +// TODO: also, GCC/clang equivalents please -poro static __declspec(naked) double __vectorcall fpuPow(double x, double y) { __asm @@ -365,7 +366,7 @@ namespace WaveSabreCore { return (Spread)(int)(param * 2.0f); } - + float Helpers::SpreadToParam(Spread spread) { return (float)spread / 2.0f; diff --git a/WaveSabreCore/src/Leveller.cpp b/WaveSabreCore/src/Leveller.cpp index 59ba5941..23c9ddd2 100644 --- a/WaveSabreCore/src/Leveller.cpp +++ b/WaveSabreCore/src/Leveller.cpp @@ -124,7 +124,7 @@ namespace WaveSabreCore case ParamIndices::HighCutFreq: return Helpers::FrequencyToParam(highCutFreq); case ParamIndices::HighCutQ: return Helpers::QToParam(highCutQ); - + case ParamIndices::Master: return master; } } diff --git a/WaveSabreCore/src/ResampleBuffer.cpp b/WaveSabreCore/src/ResampleBuffer.cpp index a1759baa..3dbfbb6f 100644 --- a/WaveSabreCore/src/ResampleBuffer.cpp +++ b/WaveSabreCore/src/ResampleBuffer.cpp @@ -50,7 +50,7 @@ namespace WaveSabreCore { int samplePos = (currentPosition + (int)position) % length; // actual sample position determined float fraction = position - floorf(position); // fractional - + float s0 = buffer[samplePos]; float s1 = (samplePos > 0) ? buffer[samplePos - 1] : buffer[length - 1]; return s0 + fraction * (s1 - s0); diff --git a/WaveSabreCore/src/Specimen.cpp b/WaveSabreCore/src/SpecimenWin32.cpp similarity index 99% rename from WaveSabreCore/src/Specimen.cpp rename to WaveSabreCore/src/SpecimenWin32.cpp index eb0abd88..1474a3f4 100644 --- a/WaveSabreCore/src/Specimen.cpp +++ b/WaveSabreCore/src/SpecimenWin32.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -269,7 +269,7 @@ namespace WaveSabreCore acmStreamPrepareHeader(stream, &streamHeader, 0); acmStreamConvert(stream, &streamHeader, 0); - + acmStreamClose(stream, 0); acmDriverClose(driver, 0); @@ -374,7 +374,7 @@ namespace WaveSabreCore this->velocity = (float)velocity / 128.0f; } - + void Specimen::SpecimenVoice::NoteOff() { ampEnv.Off(); diff --git a/WaveSabreCore/src/SynthDevice.cpp b/WaveSabreCore/src/SynthDevice.cpp index c8544a25..5d7b8b29 100644 --- a/WaveSabreCore/src/SynthDevice.cpp +++ b/WaveSabreCore/src/SynthDevice.cpp @@ -223,7 +223,7 @@ namespace WaveSabreCore { return voiceMode; } - + SynthDevice::Voice::Voice() { IsOn = false; @@ -248,7 +248,7 @@ namespace WaveSabreCore { slideActive = true; destinationNote = note; - + double slideTime = 10.f * Helpers::Pow(this->GetSynthDevice()->Slide,4.0); slideDelta = ((double)note - currentNote) / (Helpers::CurrentSampleRate * slideTime); slideSamples = (int)(Helpers::CurrentSampleRate * slideTime); diff --git a/WaveSabreCore/src/Thunder.cpp b/WaveSabreCore/src/ThunderWin32.cpp similarity index 99% rename from WaveSabreCore/src/Thunder.cpp rename to WaveSabreCore/src/ThunderWin32.cpp index c40d9094..23e1b7fb 100644 --- a/WaveSabreCore/src/Thunder.cpp +++ b/WaveSabreCore/src/ThunderWin32.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -103,7 +103,7 @@ namespace WaveSabreCore acmStreamPrepareHeader(stream, &streamHeader, 0); acmStreamConvert(stream, &streamHeader, 0); - + acmStreamClose(stream, 0); acmDriverClose(driver, 0); diff --git a/WaveSabreCore/src/Twister.cpp b/WaveSabreCore/src/Twister.cpp index 5c3c1813..52f2a0a8 100644 --- a/WaveSabreCore/src/Twister.cpp +++ b/WaveSabreCore/src/Twister.cpp @@ -14,9 +14,9 @@ namespace WaveSabreCore spread = Spread::Mono; vibratoFreq = Helpers::ParamToVibratoFreq(0.0f); vibratoAmount = 0.0f; - + vibratoPhase = 0.0; - + lowCutFreq = 20.0f; highCutFreq = 20000.0f- 20.0f; @@ -166,8 +166,8 @@ namespace WaveSabreCore { switch ((ParamIndices)index) { - case ParamIndices::Type: - default: + case ParamIndices::Type: + default: return type / 3.0f; case ParamIndices::Amount: return amount; diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index 6a0149d4..783cd98c 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -1,24 +1,30 @@ add_library(WaveSabrePlayerLib - include/WaveSabrePlayerLib/CriticalSection.h include/WaveSabrePlayerLib/PreRenderPlayer.h include/WaveSabrePlayerLib/WavWriter.h - include/WaveSabrePlayerLib/DirectSoundRenderThread.h - include/WaveSabrePlayerLib/RealtimePlayer.h include/WaveSabrePlayerLib/IPlayer.h include/WaveSabrePlayerLib/SongRenderer.h - src/CriticalSection.cpp - src/DirectSoundRenderThread.cpp src/IPlayer.cpp src/PreRenderPlayer.cpp - src/RealtimePlayer.cpp src/SongRenderer.cpp src/SongRenderer.Track.cpp src/WavWriter.cpp) target_link_libraries(WaveSabrePlayerLib - WaveSabreCore - winmm.lib - dsound.lib) + WaveSabreCore) + +if(WIN32) + add_library(WaveSabrePlayerLib + include/WaveSabrePlayerLib/CriticalSection.h + include/WaveSabrePlayerLib/DirectSoundRenderThread.h + include/WaveSabrePlayerLib/RealtimePlayerWin32.h + src/CriticalSection.cpp + src/DirectSoundRenderThread.cpp + src/RealtimePlayerWin32.cpp) + + target_link_libraries(WaveSabrePlayerLib + winmm.lib + dsound.lib) +endif() target_include_directories(WaveSabrePlayerLib PUBLIC include) @@ -28,4 +34,8 @@ if(MSVC) $<$:/EHs-c->) set_property(TARGET WaveSabrePlayerLib APPEND_STRING PROPERTY STATIC_LIBRARY_FLAGS_MINSIZEREL " /LTCG") +else() + # assuming GCC or clang for now + + set(CMAKE_CXX_FLAGS "-fpermissive") endif() diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h index 91a38c22..7d94f4f7 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h @@ -2,7 +2,9 @@ #define __WAVESABREPLAYERLIB_H__ #include "WaveSabrePlayerLib/IPlayer.h" -#include "WaveSabrePlayerLib/RealtimePlayer.h" +#if defined(WIN32) || defined(_WIN32) +#include "WaveSabrePlayerLib/RealtimePlayerWin32.h" +#endif #include "WaveSabrePlayerLib/PreRenderPlayer.h" #include "WaveSabrePlayerLib/WavWriter.h" diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h index efe30cb1..f9a04054 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h @@ -3,7 +3,10 @@ #include "IPlayer.h" #include "SongRenderer.h" + +#if defined(WIN32) || defined(_WIN32) #include "DirectSoundRenderThread.h" +#endif namespace WaveSabrePlayerLib { @@ -35,7 +38,9 @@ namespace WaveSabrePlayerLib int playbackBufferSizeMs; int playbackBufferIndex; +#if defined(WIN32) || defined(_WIN32) DirectSoundRenderThread *renderThread; +#endif }; } diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayerWin32.h similarity index 99% rename from WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h rename to WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayerWin32.h index 1efa0e80..02b871e9 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayerWin32.h @@ -3,6 +3,7 @@ #include "IPlayer.h" #include "SongRenderer.h" + #include "DirectSoundRenderThread.h" namespace WaveSabrePlayerLib @@ -14,7 +15,7 @@ namespace WaveSabrePlayerLib virtual ~RealtimePlayer(); virtual void Play(); - + virtual int GetTempo() const; virtual int GetSampleRate() const; virtual double GetLength() const; diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h index f9c6a122..bd2880c1 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h @@ -1,7 +1,9 @@ #ifndef __WAVESABREPLAYERLIB_SONGRENDERER_H__ #define __WAVESABREPLAYERLIB_SONGRENDERER_H__ +#if defined(WIN32) || defined(_WIN32) #include "CriticalSection.h" +#endif #include @@ -89,7 +91,7 @@ namespace WaveSabrePlayerLib Track(SongRenderer *songRenderer, DeviceFactory factory); ~Track(); - + void Run(int numSamples); private: @@ -156,7 +158,9 @@ namespace WaveSabrePlayerLib int renderThreadIndex; } RenderThreadData; +#if defined(WIN32) || defined(_WIN32) static DWORD WINAPI renderThreadProc(LPVOID lpParameter); +#endif bool renderThreadWork(int renderThreadIndex); @@ -172,7 +176,7 @@ namespace WaveSabrePlayerLib int bpm; int sampleRate; double length; - + int numDevices; WaveSabreCore::Device **devices; @@ -184,13 +188,17 @@ namespace WaveSabrePlayerLib TrackRenderState *trackRenderStates; int numRenderThreads; +#if defined(WIN32) || defined(_WIN32) HANDLE *additionalRenderThreads; +#endif bool renderThreadShutdown; int renderThreadNumFloatSamples; unsigned int renderThreadsRunning; +#if defined(WIN32) || defined(_WIN32) HANDLE *renderThreadStartEvents; HANDLE renderDoneEvent; +#endif }; } diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index cd8f3815..22b29f55 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -1,5 +1,7 @@ #include +#include + namespace WaveSabrePlayerLib { PreRenderPlayer::PreRenderPlayer(const SongRenderer::Song *song, int numRenderThreads, ProgressCallback callback, void *data, int playbackBufferSizeMs) @@ -37,25 +39,33 @@ namespace WaveSabrePlayerLib this->playbackBufferSizeMs = playbackBufferSizeMs; +#if defined(WIN32) || defined(_WIN32) renderThread = nullptr; +#endif } PreRenderPlayer::~PreRenderPlayer() { +#if defined(WIN32) || defined(_WIN32) if (renderThread) delete renderThread; +#endif delete [] renderBuffer; } void PreRenderPlayer::Play() { +#if defined(WIN32) || defined(_WIN32) if (renderThread) delete renderThread; +#endif playbackBufferIndex = 0; +#if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs); +#endif } int PreRenderPlayer::GetTempo() const @@ -75,10 +85,14 @@ namespace WaveSabrePlayerLib double PreRenderPlayer::GetSongPos() const { +#if defined(WIN32) || defined(_WIN32) if (!renderThread) return 0.0; return max(((double)renderThread->GetPlayPositionMs() - (double)playbackBufferSizeMs) / 1000.0, 0.0); +#else + return 0.0/0.0; +#endif } void PreRenderPlayer::renderCallback(SongRenderer::Sample *buffer, int numSamples, void *data) @@ -92,7 +106,13 @@ namespace WaveSabrePlayerLib return; } + int samplesToTake; +#if defined(WIN32) || defined(_WIN32) int samplesToTake = min(numSamples, samplesLeft); +#else + // sigh. + samplesToTake = (numSamples > samplesLeft) ? samplesLeft : numSamples; +#endif if (samplesToTake) { memcpy(buffer, player->renderBuffer + player->playbackBufferIndex, samplesToTake * sizeof(SongRenderer::Sample)); diff --git a/WaveSabrePlayerLib/src/RealtimePlayer.cpp b/WaveSabrePlayerLib/src/RealtimePlayerWin32.cpp similarity index 96% rename from WaveSabrePlayerLib/src/RealtimePlayer.cpp rename to WaveSabrePlayerLib/src/RealtimePlayerWin32.cpp index 7976b03c..0610af39 100644 --- a/WaveSabrePlayerLib/src/RealtimePlayer.cpp +++ b/WaveSabrePlayerLib/src/RealtimePlayerWin32.cpp @@ -1,4 +1,4 @@ -#include +#include namespace WaveSabrePlayerLib { diff --git a/WaveSabrePlayerLib/src/SongRenderer.Track.cpp b/WaveSabrePlayerLib/src/SongRenderer.Track.cpp index 61f1f361..d51c8d2f 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.Track.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.Track.cpp @@ -1,5 +1,7 @@ #include +#include + using namespace WaveSabreCore; namespace WaveSabrePlayerLib @@ -58,7 +60,7 @@ namespace WaveSabrePlayerLib if (NumReceives) delete [] Receives; - + if (numDevices) { delete[] devicesIndicies; diff --git a/WaveSabrePlayerLib/src/SongRenderer.cpp b/WaveSabrePlayerLib/src/SongRenderer.cpp index 4923ac72..3560433a 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.cpp @@ -1,5 +1,8 @@ #include +#include +#include + using namespace WaveSabreCore; namespace WaveSabrePlayerLib @@ -37,7 +40,7 @@ namespace WaveSabrePlayerLib for (int m = 0; m < numEvents; m++) { midiLanes[i]->events[m].TimeStamp = readInt(); - byte note = readByte(); + uint8_t note = readByte(); if ((note & 0x80) == 0x00) { midiLanes[i]->events[m].Type = (EventType)0; @@ -65,13 +68,16 @@ namespace WaveSabrePlayerLib this->numRenderThreads = numRenderThreads; renderThreadShutdown = false; +#if defined(WIN32) || defined(_WIN32) renderThreadStartEvents = new HANDLE[numRenderThreads]; for (int i = 0; i < numRenderThreads; i++) renderThreadStartEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); renderDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); +#endif if (numRenderThreads > 1) { +#if defined(WIN32) || defined(_WIN32) additionalRenderThreads = new HANDLE[numRenderThreads - 1]; for (int i = 0; i < numRenderThreads - 1; i++) { @@ -81,6 +87,7 @@ namespace WaveSabrePlayerLib additionalRenderThreads[i] = CreateThread(0, 0, renderThreadProc, (LPVOID)renderThreadData, 0, 0); SetThreadPriority(additionalRenderThreads[i], THREAD_PRIORITY_HIGHEST); } +#endif } } @@ -91,12 +98,14 @@ namespace WaveSabrePlayerLib if (numRenderThreads > 1) { +#if defined(WIN32) || defined(_WIN32) for (int i = 0; i < numRenderThreads; i++) SetEvent(renderThreadStartEvents[i]); WaitForMultipleObjects(numRenderThreads - 1, additionalRenderThreads, TRUE, INFINITE); for (int i = 0; i < numRenderThreads - 1; i++) CloseHandle(additionalRenderThreads[i]); delete [] additionalRenderThreads; +#endif } for (int i = 0; i < numDevices; i++) delete devices[i]; @@ -109,11 +118,13 @@ namespace WaveSabrePlayerLib delete [] tracks; delete [] trackRenderStates; +#if defined(WIN32) || defined(_WIN32) for (int i = 0; i < numRenderThreads; i++) CloseHandle(renderThreadStartEvents[i]); CloseHandle(renderDoneEvent); delete [] renderThreadStartEvents; +#endif } void SongRenderer::RenderSamples(Sample *buffer, int numSamples) @@ -127,6 +138,7 @@ namespace WaveSabrePlayerLib // Dispatch work renderThreadsRunning = numRenderThreads; +#if defined(WIN32) || defined(_WIN32) for (int i = 0; i < numRenderThreads; i++) SetEvent(renderThreadStartEvents[i]); @@ -134,6 +146,7 @@ namespace WaveSabrePlayerLib // Wait for render threads to complete their work WaitForSingleObject(renderDoneEvent, INFINITE); +#endif // Copy final output float **masterTrackBuffers = tracks[numTracks - 1]->Buffers; @@ -146,6 +159,7 @@ namespace WaveSabrePlayerLib } } +#if defined(WIN32) || defined(_WIN32) DWORD WINAPI SongRenderer::renderThreadProc(LPVOID lpParameter) { auto renderThreadData = (RenderThreadData *)lpParameter; @@ -160,10 +174,13 @@ namespace WaveSabrePlayerLib return 0; } +#endif bool SongRenderer::renderThreadWork(int renderThreadIndex) { +#if defined(WIN32) || defined(_WIN32) WaitForSingleObject(renderThreadStartEvents[renderThreadIndex], INFINITE); +#endif if (renderThreadShutdown) return false; @@ -195,6 +212,7 @@ namespace WaveSabrePlayerLib // We have a free track that we can work on, yay! // Let's try to mark it so that no other thread takes it +#if defined(WIN32) || defined(_WIN32) if ((TrackRenderState)InterlockedCompareExchange((unsigned int *)&trackRenderStates[i], (unsigned int)TrackRenderState::Rendering, (unsigned int)TrackRenderState::Idle) == TrackRenderState::Idle) { // We marked it successfully, so now we'll do the work @@ -203,11 +221,14 @@ namespace WaveSabrePlayerLib trackRenderStates[i] = TrackRenderState::Finished; break; } +#endif } } +#if defined(WIN32) || defined(_WIN32) if (!InterlockedDecrement(&renderThreadsRunning)) SetEvent(renderDoneEvent); +#endif return true; } diff --git a/WaveSabrePlayerLib/src/WavWriter.cpp b/WaveSabrePlayerLib/src/WavWriter.cpp index fdca1417..def6f5ea 100644 --- a/WaveSabrePlayerLib/src/WavWriter.cpp +++ b/WaveSabrePlayerLib/src/WavWriter.cpp @@ -1,6 +1,12 @@ #include +#if defined(WIN32) || defined(_WIN32) #include +#endif + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM (0x0001) +#endif namespace WaveSabrePlayerLib { @@ -25,7 +31,7 @@ namespace WaveSabrePlayerLib auto file = fopen(fileName, "wb"); int dataSubChunkSize = numSamples * bitsPerSample / 8; - + // RIFF header fputs("RIFF", file); writeInt(36 + dataSubChunkSize, file); diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 5b301ec4..85e74b69 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -12,4 +12,8 @@ if(MSVC) target_compile_definitions(WaveSabreStandAlonePlayer PRIVATE $<$:_NO_CRT_STDIO_INLINE>) endif() +else() + # assuming GCC or clang for now + + set(CMAKE_CXX_FLAGS "-fpermissive") endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index e5676a22..4968b29c 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -2,6 +2,7 @@ #include using namespace WaveSabrePlayerLib; +#include #include WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) @@ -10,7 +11,9 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) { case SongRenderer::DeviceId::Falcon: return new WaveSabreCore::Falcon(); case SongRenderer::DeviceId::Slaughter: return new WaveSabreCore::Slaughter(); +#if defined(WIN32) || defined(_WIN32) case SongRenderer::DeviceId::Thunder: return new WaveSabreCore::Thunder(); +#endif case SongRenderer::DeviceId::Scissor: return new WaveSabreCore::Scissor(); case SongRenderer::DeviceId::Leveller: return new WaveSabreCore::Leveller(); case SongRenderer::DeviceId::Crusher: return new WaveSabreCore::Crusher(); @@ -19,8 +22,10 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) case SongRenderer::DeviceId::Chamber: return new WaveSabreCore::Chamber(); case SongRenderer::DeviceId::Twister: return new WaveSabreCore::Twister(); case SongRenderer::DeviceId::Cathedral: return new WaveSabreCore::Cathedral(); +#if defined(WIN32) || defined(_WIN32) case SongRenderer::DeviceId::Adultery: return new WaveSabreCore::Adultery(); case SongRenderer::DeviceId::Specimen: return new WaveSabreCore::Specimen(); +#endif } return nullptr; } @@ -38,6 +43,13 @@ int main(int argc, char **argv) { bool writeWav = argc >= 3 && !strcmp(argv[2], "-w"); bool preRender = argc == 3 && !strcmp(argv[2], "-p"); +#if !defined(WIN32) && !defined(_WIN32) + if (!preRender) { + printf("W: realtime playback not yet supported on non-Windows " + "platforms. Writing WAV instead...\n"); + preRender = true; + } +#endif const int numRenderThreads = 3; @@ -93,13 +105,16 @@ int main(int argc, char **argv) printf("\n\n"); } +#if defined(WIN32) || defined(_WIN32) else { player = new RealtimePlayer(&song, numRenderThreads); } +#endif printf("Realtime player activated. Press ESC to quit.\n"); player->Play(); +#if defined(WIN32) || defined(_WIN32) while (!GetAsyncKeyState(VK_ESCAPE)) { auto songPos = player->GetSongPos(); @@ -111,6 +126,10 @@ int main(int argc, char **argv) Sleep(10); } +#else + // good enough for now + fgetc(stdin); +#endif printf("\n"); delete player; @@ -118,4 +137,4 @@ int main(int argc, char **argv) free(buffer); return 0; -} \ No newline at end of file +} From 2ce9ca6e19782d04c9303f033d32b8d08beab70b Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Sun, 30 Aug 2020 04:15:49 +0200 Subject: [PATCH 02/32] add inline assembly stuff (from earlier porting tries), enable optimizations, fix error printing in the standalone segfaulting (bad ferris!) --- WaveSabreCore/CMakeLists.txt | 4 +- WaveSabreCore/src/Helpers.cpp | 100 +++++++++++++++++++++-- WaveSabrePlayerLib/CMakeLists.txt | 4 +- WaveSabreStandAlonePlayer/CMakeLists.txt | 4 +- WaveSabreStandAlonePlayer/main.cpp | 36 ++++++++ 5 files changed, 136 insertions(+), 12 deletions(-) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 69bc737b..bc1dfb99 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -77,5 +77,7 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-fpermissive") + set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + # -fpermissive + stuff to make the code smaller + set(CMAKE_CXX_FLAGS "-fpermissive -g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreCore/src/Helpers.cpp b/WaveSabreCore/src/Helpers.cpp index 440f3463..27debf07 100644 --- a/WaveSabreCore/src/Helpers.cpp +++ b/WaveSabreCore/src/Helpers.cpp @@ -4,11 +4,27 @@ #define _USE_MATH_DEFINES #include -#if defined(_MSC_VER) && defined(_M_IX86) // TODO: make assembly equivalent for x64 (use intrinsic ?) -// TODO: also, GCC/clang equivalents please -poro -static __declspec(naked) double __vectorcall fpuPow(double x, double y) +#if defined(_MSC_VER) && defined(_M_IX86) + #define ASM_MATH_AVAILABLE (1) + #define ASMVECTORCALL __declspec(naked) __vectorcall +#elif defined(__GNUC__) + #define ASM_MATH_AVAILABLE (1) + #if defined(__x86_64) || defined(__i386__) + #define ASMVECTORCALL inline __attribute__((__always_inline__)) + #else + #define ASMVECTORCALL inline + #endif +#else /* nor MSVC nor GCC/clang */ + #define ASM_MATH_AVAILABLE (0) +#endif + +// XXX: this pow() implementation seems to suck for y > 4, regardless of float/double + +#if ASM_MATH_AVAILABLE == 1 +static ASMVECTORCALL double fpuPow(double x, double y) { +#if defined(_MSC_VER) && defined(_M_IX86) __asm { sub esp, 8 @@ -53,10 +69,38 @@ static __declspec(naked) double __vectorcall fpuPow(double x, double y) ret } +#elif defined(__GNUC__) + #if defined(__x86_64__) || defined(__i386__) + // not writing the *entire* function body in assembly actually helps + // gcc and clang with inlining and LTO + if (y == 0.0) return 1; + if (x == 0.0) return 0; + + asm volatile("fyl2x\n" + "fld %%st(0)\n" + "frndint\n" + "fsubr %%st(0), %%st(1)\n" + "fxch %%st(1)\n" + "fchs\n" + "f2xm1\n" + "fld1\n" + "faddp %%st(0), %%st(1)\n" + "fscale\n" + "fstp %%st(1)\n" + :"+t"(x) // output regs ('+': inout) + :"u"(y) // input regs + : // destroyed regs + ); + return x; + #else /* not x86 */ + return pow(x, y); // __builtin_pow only accepts an integer exponent :/ + #endif /* GNUC, platform */ +#endif /* MSVC/GNUC */ } -static __declspec(naked) float __vectorcall fpuPowF(float x, float y) +static ASMVECTORCALL float fpuPowF(float x, float y) { +#if defined(_MSC_VER) && defined(_M_IX86) __asm { sub esp, 8 @@ -101,10 +145,38 @@ static __declspec(naked) float __vectorcall fpuPowF(float x, float y) ret } +#elif defined(__GNUC__) + #if defined (__x86_64__) || defined(__i386__) + // not writing the *entire* function body in assembly actually helps + // gcc and clang with inlining and LTO + if (y == 0) return 1; + if (x == 0) return 0; + + asm volatile("fyl2x\n" + "fld %%st(0)\n" + "frndint\n" + "fsubr %%st(0), %%st(1)\n" + "fxch %%st(1)\n" + "fchs\n" + "f2xm1\n" + "fld1\n" + "faddp %%st(0), %%st(1)\n" + "fscale\n" + "fstp %%st(1)\n" + :"+t"(x) // output regs ('+': inout) + :"u"(y) // input regs + : // destroyed regs + ); + return x; + #else /* not x86_64 */ + return powf(x, y); // __builtin_pow only accepts an integer exponent :/ + #endif /* GNUC, platform */ +#endif /* MSVC/GNUC */ } -static __declspec(naked) double __vectorcall fpuCos(double x) +static ASMVECTORCALL double fpuCos(double x) { +#if defined(_MSC_VER) && defined(_M_IX86) __asm { sub esp, 8 @@ -119,8 +191,18 @@ static __declspec(naked) double __vectorcall fpuCos(double x) ret } +#elif defined(__GNUC__) + #if defined(__x86_64__) || defined(__i386__) + // not writing the *entire* function body in assembly actually helps + // gcc and clang with inlining and LTO + asm volatile("fcos\n":"+t"(x)::); + return x; + #else /* x86_64 */ + return __builtin_cos(x); + #endif /* GNUC, platform */ +#endif /* MSVC/GNUC */ } -#endif // defined(_MSC_VER) && defined(_M_IX86) +#endif // ASM_MATH_AVAILABLE == 1 namespace WaveSabreCore { @@ -139,7 +221,7 @@ namespace WaveSabreCore for (int i = 0; i < fastCosTabSize + 1; i++) { double phase = double(i) * ((M_PI * 2) / fastCosTabSize); -#if defined(_MSC_VER) && defined(_M_IX86) +#if ASM_MATH_AVAILABLE == 1 fastCosTab[i] = fpuCos(phase); #else fastCosTab[i] = cos(phase); @@ -154,7 +236,7 @@ namespace WaveSabreCore double Helpers::Pow(double x, double y) { -#if defined(_MSC_VER) && defined(_M_IX86) +#if ASM_MATH_AVAILABLE == 1 return fpuPow(x, y); #else return pow(x, y); @@ -163,7 +245,7 @@ namespace WaveSabreCore float Helpers::PowF(float x, float y) { -#if defined(_MSC_VER) && defined(_M_IX86) +#if ASM_MATH_AVAILABLE == 1 return fpuPowF(x, y); #else return powf(x, y); diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index 783cd98c..16baaad3 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -37,5 +37,7 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-fpermissive") + set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + # -fpermissive + stuff to make the code smaller + set(CMAKE_CXX_FLAGS "-fpermissive -g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 85e74b69..ffaf55af 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -15,5 +15,7 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-fpermissive") + set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + # -fpermissive + stuff to make the code smaller + set(CMAKE_CXX_FLAGS "-fpermissive -g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 4968b29c..5236b604 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -1,9 +1,11 @@ #include +//#include #include using namespace WaveSabrePlayerLib; #include #include +//#include WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) { @@ -41,6 +43,40 @@ void progressCallback(double progress, void *data) int main(int argc, char **argv) { + /*double minerr = 1.0/0, maxerr = -1.0/0; + double stddev = 0, avg = 0; + + const int N = 1000; + const double scaler = 0.001; + + //for (int y = 1; y <= N; ++y) + int y = 1.0/12; + for (int x = 1; x <= N; ++x) { + double xd = x * scaler, + yd = y * scaler; + + double pf = WaveSabreCore::Helpers::Pow(xd, yd), + pm = pow(xd, yd); // from libm + + double err = abs(pf - pm); + //err = err / pm; // fuck it, relative error + + if (err < minerr) minerr = err; + if (err > maxerr) maxerr = err; + + avg += err; + stddev += err*err; + printf("%f\n",err); + } + + avg /= N*N; + stddev /= N*N - 1; + stddev = sqrt(stddev); + + printf("#min=%f avg=%f max=%f stddev=%f\n", minerr, avg, maxerr, stddev); + + return 0;*/ + bool writeWav = argc >= 3 && !strcmp(argv[2], "-w"); bool preRender = argc == 3 && !strcmp(argv[2], "-p"); #if !defined(WIN32) && !defined(_WIN32) From 31885d135e799ea78f634734f83da5e0457b6046 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Sun, 30 Aug 2020 05:08:09 +0200 Subject: [PATCH 03/32] factor out the actual platform-specific renderthread/player stuff --- WaveSabrePlayerLib/CMakeLists.txt | 8 ++++--- .../DirectSoundRenderThread.h | 9 ++++--- .../WaveSabrePlayerLib/IRenderThread.h | 19 +++++++++++++++ .../WaveSabrePlayerLib/PreRenderPlayer.h | 9 ++----- ...RealtimePlayerWin32.h => RealtimePlayer.h} | 5 ++-- WaveSabrePlayerLib/src/IRenderThread.cpp | 8 +++++++ WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 17 +++++++------ ...timePlayerWin32.cpp => RealtimePlayer.cpp} | 23 ++++++++++++++---- WaveSabreStandAlonePlayer/main.cpp | 24 ++++++++++--------- 9 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h rename WaveSabrePlayerLib/include/WaveSabrePlayerLib/{RealtimePlayerWin32.h => RealtimePlayer.h} (90%) create mode 100644 WaveSabrePlayerLib/src/IRenderThread.cpp rename WaveSabrePlayerLib/src/{RealtimePlayerWin32.cpp => RealtimePlayer.cpp} (69%) diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index 16baaad3..6debaa58 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -1,10 +1,14 @@ add_library(WaveSabrePlayerLib + include/WaveSabrePlayerLib/IRenderThread.h include/WaveSabrePlayerLib/PreRenderPlayer.h include/WaveSabrePlayerLib/WavWriter.h include/WaveSabrePlayerLib/IPlayer.h + include/WaveSabrePlayerLib/RealtimePlayer.h include/WaveSabrePlayerLib/SongRenderer.h src/IPlayer.cpp + src/IRenderThread.cpp src/PreRenderPlayer.cpp + src/RealtimePlayer.cpp src/SongRenderer.cpp src/SongRenderer.Track.cpp src/WavWriter.cpp) @@ -16,10 +20,8 @@ if(WIN32) add_library(WaveSabrePlayerLib include/WaveSabrePlayerLib/CriticalSection.h include/WaveSabrePlayerLib/DirectSoundRenderThread.h - include/WaveSabrePlayerLib/RealtimePlayerWin32.h src/CriticalSection.cpp - src/DirectSoundRenderThread.cpp - src/RealtimePlayerWin32.cpp) + src/DirectSoundRenderThread.cpp) target_link_libraries(WaveSabrePlayerLib winmm.lib diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h index f54d1d71..2f1bf2d5 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h @@ -3,21 +3,20 @@ #include "SongRenderer.h" #include "CriticalSection.h" +#include "IRenderThread" #include #include namespace WaveSabrePlayerLib { - class DirectSoundRenderThread + class DirectSoundRenderThread : public IRenderThread { public: - typedef void (*RenderCallback)(SongRenderer::Sample *buffer, int numSamples, void *data); - DirectSoundRenderThread(RenderCallback callback, void *callbackData, int sampleRate, int bufferSizeMs = 1000); - ~DirectSoundRenderThread(); + virtual ~DirectSoundRenderThread(); - int GetPlayPositionMs(); + virtual int GetPlayPositionMs(); private: static DWORD WINAPI threadProc(LPVOID lpParameter); diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h new file mode 100644 index 00000000..69f4f428 --- /dev/null +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h @@ -0,0 +1,19 @@ +#ifndef __WAVESABREPLAYERLIB_IRENDERTHREAD_H__ +#define __WAVESABREPLAYERLIB_IRENDERTHREAD_H__ + +#include "SongRenderer.h" + +namespace WaveSabrePlayerLib +{ + class IRenderThread + { + public: + typedef void (*RenderCallback)(SongRenderer::Sample *buffer, int numSamples, void *data); + + virtual ~IRenderThread(); + + virtual int GetPlayPositionMs() = 0; + }; +} + +#endif diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h index f9a04054..90b5fdd4 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h @@ -2,12 +2,9 @@ #define __WAVESABREPLAYERLIB_PRERENDERPLAYER_H__ #include "IPlayer.h" +#include "IRenderThread.h" #include "SongRenderer.h" -#if defined(WIN32) || defined(_WIN32) -#include "DirectSoundRenderThread.h" -#endif - namespace WaveSabrePlayerLib { class PreRenderPlayer : public IPlayer @@ -38,9 +35,7 @@ namespace WaveSabrePlayerLib int playbackBufferSizeMs; int playbackBufferIndex; -#if defined(WIN32) || defined(_WIN32) - DirectSoundRenderThread *renderThread; -#endif + IRenderThread *renderThread; }; } diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayerWin32.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h similarity index 90% rename from WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayerWin32.h rename to WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h index 02b871e9..e83eed2a 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayerWin32.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h @@ -2,10 +2,9 @@ #define __WAVESABREPLAYERLIB_REALTIMEPLAYER_H__ #include "IPlayer.h" +#include "IRenderThread.h" #include "SongRenderer.h" -#include "DirectSoundRenderThread.h" - namespace WaveSabrePlayerLib { class RealtimePlayer : public IPlayer @@ -29,7 +28,7 @@ namespace WaveSabrePlayerLib int bufferSizeMs; SongRenderer *songRenderer; - DirectSoundRenderThread *renderThread; + IRenderThread *renderThread; }; } diff --git a/WaveSabrePlayerLib/src/IRenderThread.cpp b/WaveSabrePlayerLib/src/IRenderThread.cpp new file mode 100644 index 00000000..7e27b939 --- /dev/null +++ b/WaveSabrePlayerLib/src/IRenderThread.cpp @@ -0,0 +1,8 @@ +#include + +namespace WaveSabrePlayerLib +{ + IRenderThread::~IRenderThread() + { + } +} diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index 22b29f55..2e42c6e5 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -1,5 +1,9 @@ #include +#if defined(WIN32) || defined(_WIN32) +#include +#endif + #include namespace WaveSabrePlayerLib @@ -39,27 +43,21 @@ namespace WaveSabrePlayerLib this->playbackBufferSizeMs = playbackBufferSizeMs; -#if defined(WIN32) || defined(_WIN32) renderThread = nullptr; -#endif } PreRenderPlayer::~PreRenderPlayer() { -#if defined(WIN32) || defined(_WIN32) if (renderThread) delete renderThread; -#endif delete [] renderBuffer; } void PreRenderPlayer::Play() { -#if defined(WIN32) || defined(_WIN32) if (renderThread) delete renderThread; -#endif playbackBufferIndex = 0; @@ -85,13 +83,14 @@ namespace WaveSabrePlayerLib double PreRenderPlayer::GetSongPos() const { -#if defined(WIN32) || defined(_WIN32) if (!renderThread) return 0.0; - return max(((double)renderThread->GetPlayPositionMs() - (double)playbackBufferSizeMs) / 1000.0, 0.0); + double v = ((double)renderThread->GetPlayPositionMs() - (double)playbackBufferSizeMs) / 1000.0; +#if defined(WIN32) || defined(_WIN32) + return max(v, 0.0); #else - return 0.0/0.0; + return (v > 0.0) ? v : 0.0; #endif } diff --git a/WaveSabrePlayerLib/src/RealtimePlayerWin32.cpp b/WaveSabrePlayerLib/src/RealtimePlayer.cpp similarity index 69% rename from WaveSabrePlayerLib/src/RealtimePlayerWin32.cpp rename to WaveSabrePlayerLib/src/RealtimePlayer.cpp index 0610af39..7f6480df 100644 --- a/WaveSabrePlayerLib/src/RealtimePlayerWin32.cpp +++ b/WaveSabrePlayerLib/src/RealtimePlayer.cpp @@ -1,4 +1,8 @@ -#include +#include + +#if defined(WIN32) || defined(_WIN32) +#include +#endif namespace WaveSabrePlayerLib { @@ -27,7 +31,9 @@ namespace WaveSabrePlayerLib delete songRenderer; songRenderer = new SongRenderer(song, numRenderThreads); +#if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs); +#endif } int RealtimePlayer::GetTempo() const @@ -50,14 +56,23 @@ namespace WaveSabrePlayerLib if (!renderThread) return 0.0; - return max(((double)renderThread->GetPlayPositionMs() - (double)bufferSizeMs) / 1000.0, 0.0); + double v = ((double)renderThread->GetPlayPositionMs() - (double)bufferSizeMs) / 1000.0; +#if defined(WIN32) || defined(_WIN32) + return max(v, 0.0); +#else + return (v > 0.0) ? v : 0.0; +#endif } void RealtimePlayer::renderCallback(SongRenderer::Sample *buffer, int numSamples, void *data) { auto player = (RealtimePlayer *)data; const int stepSize = 100 * SongRenderer::NumChannels; - for (int i = 0; i < numSamples; i += stepSize) - player->songRenderer->RenderSamples(buffer + i, min(numSamples - i, stepSize)); + for (int i = 0; i < numSamples; i += stepSize) { + int v = numSamples - i; + if (v > stepSize) v = stepSize; + + player->songRenderer->RenderSamples(buffer + i, v); + } } } diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 5236b604..7ffb1846 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -80,14 +80,18 @@ int main(int argc, char **argv) bool writeWav = argc >= 3 && !strcmp(argv[2], "-w"); bool preRender = argc == 3 && !strcmp(argv[2], "-p"); #if !defined(WIN32) && !defined(_WIN32) - if (!preRender) { - printf("W: realtime playback not yet supported on non-Windows " - "platforms. Writing WAV instead...\n"); - preRender = true; + if (!writeWav) { + printf("W: playback not yet supported on non-Windows platforms." + "Writing WAV instead...\n"); + writeWav = true; } #endif +#if defined(WIN32) || defined(_WIN32) const int numRenderThreads = 3; +#else + const int numRenderThreads = 1; // TODO +#endif FILE * pFile; long lSize; @@ -130,6 +134,7 @@ int main(int argc, char **argv) } else { +#if defined(WIN32) || defined(_WIN32) IPlayer *player; if (preRender) @@ -141,16 +146,14 @@ int main(int argc, char **argv) printf("\n\n"); } -#if defined(WIN32) || defined(_WIN32) else { player = new RealtimePlayer(&song, numRenderThreads); } -#endif printf("Realtime player activated. Press ESC to quit.\n"); + player->Play(); -#if defined(WIN32) || defined(_WIN32) while (!GetAsyncKeyState(VK_ESCAPE)) { auto songPos = player->GetSongPos(); @@ -162,13 +165,12 @@ int main(int argc, char **argv) Sleep(10); } -#else - // good enough for now - fgetc(stdin); -#endif printf("\n"); delete player; +#else + printf("???\n"); +#endif } free(buffer); From c0689417da312c31433569d050a1f059417b4cd7 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Sun, 30 Aug 2020 05:31:02 +0200 Subject: [PATCH 04/32] remove need for -fpermissive flag, optional foreground work stuff for renderthreads (unused yet, but will be useful for piping to alsa + select(2)/poll(2)) --- WaveSabreCore/CMakeLists.txt | 4 ++-- WaveSabrePlayerLib/CMakeLists.txt | 4 ++-- .../include/WaveSabrePlayerLib/DirectSoundRenderThread.h | 2 ++ WaveSabrePlayerLib/include/WaveSabrePlayerLib/IPlayer.h | 1 + .../include/WaveSabrePlayerLib/IRenderThread.h | 2 ++ .../include/WaveSabrePlayerLib/PreRenderPlayer.h | 1 + .../include/WaveSabrePlayerLib/RealtimePlayer.h | 1 + WaveSabrePlayerLib/src/DirectSoundRenderThread.cpp | 7 ++++++- WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 6 ++++++ WaveSabrePlayerLib/src/RealtimePlayer.cpp | 6 ++++++ WaveSabreStandAlonePlayer/CMakeLists.txt | 4 ++-- WaveSabreStandAlonePlayer/main.cpp | 1 + 12 files changed, 32 insertions(+), 7 deletions(-) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index bc1dfb99..d728bbb5 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -78,6 +78,6 @@ else() # assuming GCC or clang for now set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - # -fpermissive + stuff to make the code smaller - set(CMAKE_CXX_FLAGS "-fpermissive -g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + # stuff to make the code smaller + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index 6debaa58..bed3df54 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -40,6 +40,6 @@ else() # assuming GCC or clang for now set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - # -fpermissive + stuff to make the code smaller - set(CMAKE_CXX_FLAGS "-fpermissive -g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + # stuff to make the code smaller + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h index 2f1bf2d5..a45873e9 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h @@ -18,6 +18,8 @@ namespace WaveSabrePlayerLib virtual int GetPlayPositionMs(); + virtual void DoForegroundWork(); + private: static DWORD WINAPI threadProc(LPVOID lpParameter); diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IPlayer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IPlayer.h index 589a9903..9cf16d3b 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IPlayer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IPlayer.h @@ -9,6 +9,7 @@ namespace WaveSabrePlayerLib virtual ~IPlayer(); virtual void Play() = 0; + virtual void DoForegroundWork() = 0; virtual int GetTempo() const = 0; virtual int GetSampleRate() const = 0; diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h index 69f4f428..96239bff 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/IRenderThread.h @@ -13,6 +13,8 @@ namespace WaveSabrePlayerLib virtual ~IRenderThread(); virtual int GetPlayPositionMs() = 0; + + virtual void DoForegroundWork() = 0; }; } diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h index 90b5fdd4..e670b2d0 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/PreRenderPlayer.h @@ -16,6 +16,7 @@ namespace WaveSabrePlayerLib virtual ~PreRenderPlayer(); virtual void Play(); + virtual void DoForegroundWork(); virtual int GetTempo() const; virtual int GetSampleRate() const; diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h index e83eed2a..ed10e61c 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/RealtimePlayer.h @@ -14,6 +14,7 @@ namespace WaveSabrePlayerLib virtual ~RealtimePlayer(); virtual void Play(); + virtual void DoForegroundWork(); virtual int GetTempo() const; virtual int GetSampleRate() const; diff --git a/WaveSabrePlayerLib/src/DirectSoundRenderThread.cpp b/WaveSabrePlayerLib/src/DirectSoundRenderThread.cpp index fd50a115..8de2ada6 100644 --- a/WaveSabrePlayerLib/src/DirectSoundRenderThread.cpp +++ b/WaveSabrePlayerLib/src/DirectSoundRenderThread.cpp @@ -26,6 +26,11 @@ namespace WaveSabrePlayerLib WaitForSingleObject(thread, INFINITE); } + void DirectSoundRenderThread::DoForegroundWork() + { + // nop. + } + int DirectSoundRenderThread::GetPlayPositionMs() { if (!buffer) @@ -124,4 +129,4 @@ namespace WaveSabrePlayerLib return 0; } -} \ No newline at end of file +} diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index 2e42c6e5..2028199b 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -54,6 +54,12 @@ namespace WaveSabrePlayerLib delete [] renderBuffer; } + void PreRenderPlayer::DoForegroundWork() + { + if (renderThread) + renderThread->DoForegroundWork(); + } + void PreRenderPlayer::Play() { if (renderThread) diff --git a/WaveSabrePlayerLib/src/RealtimePlayer.cpp b/WaveSabrePlayerLib/src/RealtimePlayer.cpp index 7f6480df..642af431 100644 --- a/WaveSabrePlayerLib/src/RealtimePlayer.cpp +++ b/WaveSabrePlayerLib/src/RealtimePlayer.cpp @@ -23,6 +23,12 @@ namespace WaveSabrePlayerLib delete songRenderer; } + void RealtimePlayer::DoForegroundWork() + { + if (renderThread) + renderThread->DoForegroundWork(); + } + void RealtimePlayer::Play() { if (renderThread) diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index ffaf55af..2cda5b23 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -16,6 +16,6 @@ else() # assuming GCC or clang for now set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - # -fpermissive + stuff to make the code smaller - set(CMAKE_CXX_FLAGS "-fpermissive -g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + # stuff to make the code smaller + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 7ffb1846..92d2851e 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -163,6 +163,7 @@ int main(int argc, char **argv) int hundredths = (int)(songPos * 100.0) % 100; printf("\r %.1i:%.2i.%.2i", minutes, seconds, hundredths); + player->DoForegroundWork(); Sleep(10); } printf("\n"); From d781da566039d344a2b06c66ba66e75b76a42540 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Mon, 31 Aug 2020 19:07:36 +0200 Subject: [PATCH 05/32] w00t the WAV writer works now --- CMakeLists.txt | 8 +- WaveSabreCore/CMakeLists.txt | 7 +- WaveSabrePlayerLib/CMakeLists.txt | 39 ++- .../WaveSabrePlayerLib/AplayRenderThread.h | 59 +++++ .../WaveSabrePlayerLib/CriticalSection.h | 11 + .../DirectSoundRenderThread.h | 2 +- .../include/WaveSabrePlayerLib/SongRenderer.h | 12 +- WaveSabrePlayerLib/src/AplayRenderThread.cpp | 238 ++++++++++++++++++ WaveSabrePlayerLib/src/CriticalSection.cpp | 20 ++ WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 4 + WaveSabrePlayerLib/src/RealtimePlayer.cpp | 4 + WaveSabrePlayerLib/src/SongRenderer.cpp | 64 ++++- WaveSabrePlayerLib/src/WavWriter.cpp | 1 + WaveSabreStandAlonePlayer/CMakeLists.txt | 5 +- WaveSabreStandAlonePlayer/main.cpp | 11 + 15 files changed, 462 insertions(+), 23 deletions(-) create mode 100644 WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h create mode 100644 WaveSabrePlayerLib/src/AplayRenderThread.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1930c62d..98d2155b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,12 @@ project("WaveSabre") cmake_minimum_required(VERSION 3.11) set_property(GLOBAL PROPERTY USE_FOLDERS ON) +if(NOT WIN32) + option(USE_PTHREADS "Use Pthreads for threading" OFF) + option(ENABLE_APLAY "Enable aplay(1) backend for audio playback" ON) + option(ENABLE_SDL2 "Enable SDL2_Audio backend for audio playback" ON) +endif() + if(MSVC) # disable exceptions globally (will be added back for VSTs) string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) @@ -21,7 +27,7 @@ add_subdirectory(WaveSabrePlayerLib) # binaries if(WIN32) -add_subdirectory(Tests/PlayerTest) + add_subdirectory(Tests/PlayerTest) endif() add_subdirectory(WaveSabreStandAlonePlayer) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index d728bbb5..6beffbbd 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -47,7 +47,7 @@ add_library(WaveSabreCore src/Twister.cpp) if(WIN32) - add_library(WaveSabreCore + target_sources(WaveSabreCore PUBLIC include/WaveSabreCore/AdulteryWin32.h include/WaveSabreCore/GmDls.h include/WaveSabreCore/SpecimenWin32.h @@ -77,7 +77,8 @@ if(MSVC) else() # assuming GCC or clang for now - set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + #set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index bed3df54..d9057dd5 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -1,10 +1,12 @@ add_library(WaveSabrePlayerLib + include/WaveSabrePlayerLib/CriticalSection.h include/WaveSabrePlayerLib/IRenderThread.h include/WaveSabrePlayerLib/PreRenderPlayer.h include/WaveSabrePlayerLib/WavWriter.h include/WaveSabrePlayerLib/IPlayer.h include/WaveSabrePlayerLib/RealtimePlayer.h include/WaveSabrePlayerLib/SongRenderer.h + src/CriticalSection.cpp src/IPlayer.cpp src/IRenderThread.cpp src/PreRenderPlayer.cpp @@ -13,19 +15,43 @@ add_library(WaveSabrePlayerLib src/SongRenderer.Track.cpp src/WavWriter.cpp) -target_link_libraries(WaveSabrePlayerLib +target_link_libraries(WaveSabrePlayerLib PUBLIC WaveSabreCore) if(WIN32) - add_library(WaveSabrePlayerLib - include/WaveSabrePlayerLib/CriticalSection.h + message(STATUS "Win32: using DirectSound renderthread") + target_sources(WaveSabrePlayerLib PRIVATE include/WaveSabrePlayerLib/DirectSoundRenderThread.h - src/CriticalSection.cpp src/DirectSoundRenderThread.cpp) target_link_libraries(WaveSabrePlayerLib winmm.lib dsound.lib) +else() + if(ENABLE_APLAY) + message(STATUS "Enabling aplay(1)-based backend") + target_compile_definitions(WaveSabrePlayerLib PRIVATE HAVE_APLAY=1) + target_sources(WaveSabrePlayerLib PRIVATE + include/WaveSabrePlayerLib/AplayRenderThread.h + src/AplayRenderThread.cpp) + endif() + + if(USE_PTHREADS) + set_property(TARGET WaveSabrePlayerLib PROPERTY CXX_STANDARD 11) # need a recent one for stdatomic.h + + message(STATUS "Trying to look for pthreads...") + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + message(STATUS "Found pthreads, enabling pthreads for background rendering threads.") + target_link_libraries(WaveSabrePlayerLib PRIVATE Threads::Threads) + target_compile_definitions(WaveSabrePlayerLib PRIVATE HAVE_PTHREAD=1) + else() + message(STATUS "pthreads not found, using synchronous fallback.") + endif() + else() + message(STATUS "pthreads disabled explicitely.") + endif() endif() target_include_directories(WaveSabrePlayerLib PUBLIC include) @@ -39,7 +65,8 @@ if(MSVC) else() # assuming GCC or clang for now - set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h new file mode 100644 index 00000000..e872e19e --- /dev/null +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h @@ -0,0 +1,59 @@ +#ifndef __WAVESABREPLAYERLIB_APLAYRENDERTHREAD_H__ +#define __WAVESABREPLAYERLIB_APLAYRENDERTHREAD_H__ + +#include "SongRenderer.h" +#include "CriticalSection.h" +#include "IRenderThread.h" + +#include + +#include +#include + +#if HAVE_PTHREADS +#include +#endif + +namespace WaveSabrePlayerLib +{ + class AplayRenderThread : public IRenderThread + { + public: + // 'pthread': if true, spawn a separate thread using pthreads and do + // the poll/select/send-to-alsa loop there, if not, you have to + // periodically call DoForegroundWork, but, pthread won't be needed + AplayRenderThread(RenderCallback callback, void *callbackData, + int sampleRate, int bufferSizeMs = 1000, bool pthread = false); + virtual ~AplayRenderThread(); + + virtual int GetPlayPositionMs(); + + virtual void DoForegroundWork(); + + private: + static void AplayProc(int readend, int rate); + static void* AplayWriterProc(void* ud); + + void GetBufferTick(bool block = false); + + RenderCallback callback; + void *callbackData; + int sampleRate; + int bufferSizeMs; + int bufferSizeBytes; + int samplesWritten; + SongRenderer::Sample *sampleBuffer; + const SongRenderer::Sample *bufferToWrite; + size_t bufferBytesLeft; + size_t writeBytesMax; + + int aupipe; + pid_t aplay; +#if HAVE_PTHREAD + pthread_t writer; + bool writerStarted; +#endif + }; +} + +#endif diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/CriticalSection.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/CriticalSection.h index 84ce77e5..6ac1d25a 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/CriticalSection.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/CriticalSection.h @@ -1,7 +1,14 @@ #ifndef __WAVESABREPLAYERLIB_CRITICALSECTION__ #define __WAVESABREPLAYERLIB_CRITICALSECTION__ +#if defined(WIN32) || defined(_WIN32) #include +#elif HAVE_PTHREAD +#include +#include + +#include +#endif namespace WaveSabrePlayerLib { @@ -24,7 +31,11 @@ namespace WaveSabrePlayerLib CriticalSectionGuard Enter(); private: +#if defined(WIN32) || defined(_WIN32) CRITICAL_SECTION criticalSection; +#elif HAVE_PTHREAD + pthread_mutex_t pmutex; +#endif }; } diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h index a45873e9..a7a4dc90 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/DirectSoundRenderThread.h @@ -3,7 +3,7 @@ #include "SongRenderer.h" #include "CriticalSection.h" -#include "IRenderThread" +#include "IRenderThread.h" #include #include diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h index bd2880c1..3eab8787 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SongRenderer.h @@ -1,9 +1,7 @@ #ifndef __WAVESABREPLAYERLIB_SONGRENDERER_H__ #define __WAVESABREPLAYERLIB_SONGRENDERER_H__ -#if defined(WIN32) || defined(_WIN32) #include "CriticalSection.h" -#endif #include @@ -160,6 +158,8 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) static DWORD WINAPI renderThreadProc(LPVOID lpParameter); +#elif HAVE_PTHREAD + static void* renderThreadProc(void* lpParameter); #endif bool renderThreadWork(int renderThreadIndex); @@ -190,14 +190,20 @@ namespace WaveSabrePlayerLib int numRenderThreads; #if defined(WIN32) || defined(_WIN32) HANDLE *additionalRenderThreads; +#elif HAVE_PTHREAD + pthread_t *additionalRenderThreads; #endif bool renderThreadShutdown; int renderThreadNumFloatSamples; - unsigned int renderThreadsRunning; #if defined(WIN32) || defined(_WIN32) + unsigned int renderThreadsRunning; HANDLE *renderThreadStartEvents; HANDLE renderDoneEvent; +#elif HAVE_PTHREAD + std::atomic_int renderThreadsRunning; + sem_t *renderThreadStartEvents; + sem_t renderDoneEvent; #endif }; } diff --git a/WaveSabrePlayerLib/src/AplayRenderThread.cpp b/WaveSabrePlayerLib/src/AplayRenderThread.cpp new file mode 100644 index 00000000..bdee6e38 --- /dev/null +++ b/WaveSabrePlayerLib/src/AplayRenderThread.cpp @@ -0,0 +1,238 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if HAVE_PTHREAD +#include +#endif + +static unsigned is_le(void) +{ + const union { unsigned u; unsigned char c[4]; } one = { 1 }; + return one.c[0]; +} + +namespace WaveSabrePlayerLib +{ + AplayRenderThread::AplayRenderThread(RenderCallback callback, + void *callbackData, int sampleRate, int bufferSizeMs, + bool pthread) + : callback(callback) + , callbackData(callbackData) + , sampleRate(sampleRate) + , bufferSizeMs(bufferSizeMs) + , samplesWritten(0) + , writeBytesMax(PIPE_BUF) +#if HAVE_PTHREAD + , writerStarted(false) +#endif + { +#if !defined(HAVE_PTHREAD) || !HAVE_PTHREAD + pthread = false; +#endif + + bufferSizeBytes = sampleRate * SongRenderer::BlockAlign * bufferSizeMs / 1000; + bufferToWrite = (SongRenderer::Sample*)malloc(bufferSizeBytes); + + int fdpair[2]; + int rv = pipe(fdpair); + assert(rv == 0 && "Couldn't set up a pipe for aplay."); + + int readend = fdpair[0], writeend = fdpair[1]; + + this->aupipe = writeend; + + if (!pthread) { + // set the write end of the pipe to nonblocking, so we can limit the + // number of bytes to write to the pipe to the pipe size + int flags = fcntl(writeend, F_GETFL); + assert(flags != -1 && "Couldn't get pipe write-end fd flags."); + flags = fcntl(writeend, F_SETFL, flags|O_NONBLOCK); + assert(flags == 0 && "Couldn't set pipe write-end fd NONBLOCK flag."); + } + + pid_t child = fork(); // TODO: use vfork, needs shuffling stuff around + assert(child >= 0 && "Couldn't fork."); + + if (child == 0) { + // child process + close(writeend); // don't need this + AplayProc(readend, sampleRate); + } else { + // parent process + close(readend); // don't need this + this->aplay = child; + } + +#if HAVE_PTHREAD + if (pthread) { + rv = pthread_create(&writer, NULL, AplayWriterProc, this); + assert(rv == 0 && "Couldn't create background writer thread"); + + writerStarted = true; + } +#endif + } + + AplayRenderThread::~AplayRenderThread() + { + struct timespec ts; + ts.tv_sec = 1; + ts.tv_nsec = 0; + +#if HAVE_PTHREAD + if (writerStarted) { + pthread_cancel(writer); + if (pthread_timedjoin_np(writer, NULL, &ts) != 0) { + pthread_kill(writer, SIGTERM); + if (pthread_timedjoin_np(writer, NULL, &ts) != 0) { + pthread_kill(writer, SIGKILL); + pthread_join(writer, NULL); + } + } + writerStarted = false; + } +#endif + + if (this->aplay > 1) { + int stat; + kill(this->aplay, SIGTERM); + + // wait a second for aplay to ack the SIGTERM + sigset_t sset; + sigemptyset(&sset); + sigaddset(&sset, SIGCHLD); + if (sigtimedwait(&sset, NULL, &ts) < 0) { + // not acked? then you shall die. + kill(this->aplay, SIGKILL); + } + + waitpid(this->aplay, &stat, 0); + + this->aupipe = -1; + this->aplay = -1; + } + } + + void AplayRenderThread::DoForegroundWork() + { + GetBufferTick(false); + } + + int AplayRenderThread::GetPlayPositionMs() + { + return (samplesWritten * 1000) / sampleRate; + } + + void AplayRenderThread::AplayProc(int readend, int rate) + { + // stdin becomes readend (properly sets up the pipe) + // doing this instead of using /proc/$pid/fd is more portable + dup2(readend, STDIN_FILENO); + + // format aplay args + char* progpath = "/usr/bin/aplay"; + char* arg0 = progpath; + char arg1[strlen("-r48000")+1]; + char arg2[strlen("-fS16_LE")+1]; + + snprintf(arg1, sizeof(arg1), "-r%d", rate); + snprintf(arg2, sizeof(arg2), "-fS%d_%sE", + (int)sizeof(SongRenderer::Sample)*CHAR_BIT, + (is_le() ? "L" : "B")); + + // now we're ready to start aplay! + char *const args[] = {arg0, arg1, arg2, NULL}; + int rv = execve("/usr/bin/aplay", args, environ); + assert(rv >= 0 && "Failed to run aplay!"); + } + + void AplayRenderThread::GetBufferTick(bool block) + { + while (true) { + struct pollfd pfd; + pfd.fd = this->aupipe; + pfd.events = POLLOUT; + pfd.revents = 0; + + int res = poll(&pfd, 1, (block ? (-1) : 0)); + assert(res >= 0 && "poll(2) failed."); + + // can't write? let's try again later then + if (!res || (pfd.revents & POLLERR)) + break; + + assert(!(pfd.revents & POLLHUP) && "aplay suddenly stopped?"); + + if (bufferBytesLeft <= 0) { + callback(sampleBuffer, bufferSizeBytes/sizeof(SongRenderer::Sample), callbackData); + bufferToWrite = sampleBuffer; + bufferBytesLeft = bufferSizeBytes; + } + + size_t toWrite = bufferBytesLeft; + if (toWrite > writeBytesMax) + toWrite = writeBytesMax; + + bool haderr = false; + while (true) { + ssize_t ret = write(aupipe, bufferToWrite, toWrite); + + if (ret < 0) { + haderr = true; + int err = errno; + if (err == EAGAIN || err == EWOULDBLOCK) { + toWrite >>= 1; // AIMD + continue; + } else { + assert(0 && "Couldn't write to the aplay pipe."); + } + } else if (toWrite > (size_t)ret) { + haderr = true; // inhibit AI + toWrite >>= 1; // MD + } + + bufferBytesLeft -= (size_t)ret; + bufferToWrite += ret / sizeof(SongRenderer::Sample); + samplesWritten += ret / sizeof(SongRenderer::Sample); + + break; + } + + writeBytesMax = toWrite; + if (!haderr) { + // AIMD + writeBytesMax += 0x100; + if (writeBytesMax > PIPE_BUF) + writeBytesMax = PIPE_BUF; + } + } + } + +#if HAVE_PTHREAD + void* AplayRenderThread::AplayWriterProc(void* ud) + { + auto self = (AplayRenderThread*)ud; + + while (self->aupipe >= 0) { + self->GetBufferTick(true); + //pthread_yield(); // poll and write are cancellation points anyway + } + + self->writerStarted = false; + return NULL; + } +#endif +} diff --git a/WaveSabrePlayerLib/src/CriticalSection.cpp b/WaveSabrePlayerLib/src/CriticalSection.cpp index 937a4ae8..f2224eb9 100644 --- a/WaveSabrePlayerLib/src/CriticalSection.cpp +++ b/WaveSabrePlayerLib/src/CriticalSection.cpp @@ -5,22 +5,42 @@ namespace WaveSabrePlayerLib CriticalSection::CriticalSectionGuard::CriticalSectionGuard(CriticalSection *criticalSection) : criticalSection(criticalSection) { +#if defined(WIN32) || defined(_WIN32) EnterCriticalSection(&criticalSection->criticalSection); +#elif HAVE_PTHREAD + pthread_mutex_lock(&criticalSection->pmutex); +#endif } CriticalSection::CriticalSectionGuard::~CriticalSectionGuard() { +#if defined(WIN32) || defined(_WIN32) LeaveCriticalSection(&criticalSection->criticalSection); +#elif HAVE_PTHREAD + pthread_mutex_unlock(&criticalSection->pmutex); +#endif } + CriticalSection::CriticalSection() +#if HAVE_PTHREAD + : pmutex(PTHREAD_MUTEX_INITIALIZER) +#endif { +#if defined(WIN32) || defined(_WIN32) InitializeCriticalSection(&criticalSection); +#elif HAVE_PTHREAD + pthread_mutex_init(&pmutex, NULL); +#endif } CriticalSection::~CriticalSection() { +#if defined(WIN32) || defined(_WIN32) DeleteCriticalSection(&criticalSection); +#elif HAVE_PTHREAD + pthread_mutex_destroy(&pmutex); +#endif } CriticalSection::CriticalSectionGuard CriticalSection::Enter() diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index 2028199b..8d222eaa 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -2,6 +2,8 @@ #if defined(WIN32) || defined(_WIN32) #include +#elif HAVE_APLAY +#include #endif #include @@ -69,6 +71,8 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs); +#elif HAVE_APLAY + renderThread = new AplayRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs); #endif } diff --git a/WaveSabrePlayerLib/src/RealtimePlayer.cpp b/WaveSabrePlayerLib/src/RealtimePlayer.cpp index 642af431..ee051b07 100644 --- a/WaveSabrePlayerLib/src/RealtimePlayer.cpp +++ b/WaveSabrePlayerLib/src/RealtimePlayer.cpp @@ -2,6 +2,8 @@ #if defined(WIN32) || defined(_WIN32) #include +#elif HAVE_APLAY +#include #endif namespace WaveSabrePlayerLib @@ -39,6 +41,8 @@ namespace WaveSabrePlayerLib songRenderer = new SongRenderer(song, numRenderThreads); #if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs); +#elif HAVE_APLAY + renderThread = new AplayRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs); #endif } diff --git a/WaveSabrePlayerLib/src/SongRenderer.cpp b/WaveSabrePlayerLib/src/SongRenderer.cpp index 3560433a..74c560e1 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.cpp @@ -73,21 +73,36 @@ namespace WaveSabrePlayerLib for (int i = 0; i < numRenderThreads; i++) renderThreadStartEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL); renderDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); +#elif HAVE_PTHREAD + renderThreadStartEvents = new sem_t[numRenderThreads]; + for (int i = 0; i < numRenderThreads; i++) + sem_init(&renderThreadStartEvents[i], 0, 0); + sem_init(&renderDoneEvent, 0, 0); #endif if (numRenderThreads > 1) { #if defined(WIN32) || defined(_WIN32) additionalRenderThreads = new HANDLE[numRenderThreads - 1]; +#elif HAVE_PTHREAD + additionalRenderThreads = new pthread_t[numRenderThreads - 1]; +#endif + for (int i = 0; i < numRenderThreads - 1; i++) { auto renderThreadData = new RenderThreadData(); renderThreadData->songRenderer = this; renderThreadData->renderThreadIndex = i + 1; + +#if defined(WIN32) || defined(_WIN32) additionalRenderThreads[i] = CreateThread(0, 0, renderThreadProc, (LPVOID)renderThreadData, 0, 0); SetThreadPriority(additionalRenderThreads[i], THREAD_PRIORITY_HIGHEST); - } +#elif HAVE_PTHREAD + pthread_create(&additionalRenderThreads[i], NULL, renderThreadProc, + renderThreadData); + pthread_setschedprio(additionalRenderThreads[i], -15); #endif + } } } @@ -104,8 +119,12 @@ namespace WaveSabrePlayerLib WaitForMultipleObjects(numRenderThreads - 1, additionalRenderThreads, TRUE, INFINITE); for (int i = 0; i < numRenderThreads - 1; i++) CloseHandle(additionalRenderThreads[i]); - delete [] additionalRenderThreads; +#elif HAVE_PTHREAD + for (int i = 0; i < numRenderThreads; i++) + sem_post(&renderThreadStartEvents[i]); + // TODO: wait for threads, stop threads #endif + delete [] additionalRenderThreads; } for (int i = 0; i < numDevices; i++) delete devices[i]; @@ -122,9 +141,12 @@ namespace WaveSabrePlayerLib for (int i = 0; i < numRenderThreads; i++) CloseHandle(renderThreadStartEvents[i]); CloseHandle(renderDoneEvent); - - delete [] renderThreadStartEvents; +#elif HAVE_PTHREAD + for (int i = 0; i < numRenderThreads; i++) + sem_destroy(&renderThreadStartEvents[i]); + sem_destroy(&renderDoneEvent); #endif + delete [] renderThreadStartEvents; } void SongRenderer::RenderSamples(Sample *buffer, int numSamples) @@ -141,11 +163,18 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) for (int i = 0; i < numRenderThreads; i++) SetEvent(renderThreadStartEvents[i]); +#elif HAVE_PTHREAD + for (int i = 0; i < numRenderThreads; i++) + sem_post(&renderThreadStartEvents[i]); +#endif renderThreadWork(0); +#if defined(WIN32) || defined(_WIN32) // Wait for render threads to complete their work WaitForSingleObject(renderDoneEvent, INFINITE); +#elif HAVE_PTHREAD + sem_wait(&renderDoneEvent); #endif // Copy final output @@ -161,6 +190,9 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) DWORD WINAPI SongRenderer::renderThreadProc(LPVOID lpParameter) +#elif HAVE_PTHREAD + void* SongRenderer::renderThreadProc(void* lpParameter) +#endif { auto renderThreadData = (RenderThreadData *)lpParameter; @@ -174,12 +206,13 @@ namespace WaveSabrePlayerLib return 0; } -#endif bool SongRenderer::renderThreadWork(int renderThreadIndex) { #if defined(WIN32) || defined(_WIN32) WaitForSingleObject(renderThreadStartEvents[renderThreadIndex], INFINITE); +#elif HAVE_PTHREAD + sem_wait(&renderThreadStartEvents[renderThreadIndex]); #endif if (renderThreadShutdown) @@ -213,7 +246,20 @@ namespace WaveSabrePlayerLib // We have a free track that we can work on, yay! // Let's try to mark it so that no other thread takes it #if defined(WIN32) || defined(_WIN32) - if ((TrackRenderState)InterlockedCompareExchange((unsigned int *)&trackRenderStates[i], (unsigned int)TrackRenderState::Rendering, (unsigned int)TrackRenderState::Idle) == TrackRenderState::Idle) + if ((TrackRenderState)InterlockedCompareExchange( + (unsigned int *)&trackRenderStates[i], + (unsigned int)TrackRenderState::Rendering, + (unsigned int)TrackRenderState::Idle) + == TrackRenderState::Idle) +#elif HAVE_PTHREAD + int xv = (int)TrackRenderState::Idle; + if (std::atomic_compare_exchange_strong( + (std::atomic_int *)&trackRenderStates[i], + &xv, (int)TrackRenderState::Rendering) + ) +#else +#error "AAAAAAAAAAAAAAAA" +#endif { // We marked it successfully, so now we'll do the work tracks[i]->Run(renderThreadNumFloatSamples); @@ -221,13 +267,17 @@ namespace WaveSabrePlayerLib trackRenderStates[i] = TrackRenderState::Finished; break; } -#endif } } + //asm volatile("int3\n"); #if defined(WIN32) || defined(_WIN32) if (!InterlockedDecrement(&renderThreadsRunning)) SetEvent(renderDoneEvent); +#elif HAVE_PTHREAD + // returns the value *before* the call + if (std::atomic_fetch_sub(&renderThreadsRunning, 1) == 1) + sem_post(&renderDoneEvent); #endif return true; diff --git a/WaveSabrePlayerLib/src/WavWriter.cpp b/WaveSabrePlayerLib/src/WavWriter.cpp index def6f5ea..4b96eee1 100644 --- a/WaveSabrePlayerLib/src/WavWriter.cpp +++ b/WaveSabrePlayerLib/src/WavWriter.cpp @@ -56,6 +56,7 @@ namespace WaveSabrePlayerLib SongRenderer::Sample buf[stepSize]; for (int i = 0; i < numSamples; i += stepSize) { + //asm volatile("int3\n"); songRenderer->RenderSamples(buf, stepSize); for (int j = 0; j < stepSize; j++) writeShort(buf[j], file); diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 2cda5b23..2ed55929 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -15,7 +15,8 @@ if(MSVC) else() # assuming GCC or clang for now - set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 92d2851e..958e74c7 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -7,6 +7,15 @@ using namespace WaveSabrePlayerLib; #include //#include +#if defined(_MSC_VER) +// MSVC WHYYYYYY +extern "C" FILE* __cdecl __iob_func(void) +{ + static FILE _iob[] = {*stdin, *stdout, *stderr}; + return _iob; +} +#endif + WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) { switch (id) @@ -29,6 +38,7 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) case SongRenderer::DeviceId::Specimen: return new WaveSabreCore::Specimen(); #endif } + fprintf(stderr, "ack, unknown device %d!\n", id); return nullptr; } @@ -128,6 +138,7 @@ int main(int argc, char **argv) auto fileName = argc >= 4 ? argv[3] : "out.wav"; printf("Rendering...\n"); + //asm volatile("int3\n"); wavWriter.Write(fileName, progressCallback, nullptr); printf("\n\nWAV file written to \"%s\". Enjoy.\n", fileName); From 941f03541ab0f6fbc57b6562174248f40b5a9c58 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Mon, 31 Aug 2020 19:23:13 +0200 Subject: [PATCH 06/32] prerender player on linux (untested) --- .../include/WaveSabrePlayerLib.h | 4 +-- WaveSabreStandAlonePlayer/main.cpp | 26 ++++++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h index 7d94f4f7..91a38c22 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib.h @@ -2,9 +2,7 @@ #define __WAVESABREPLAYERLIB_H__ #include "WaveSabrePlayerLib/IPlayer.h" -#if defined(WIN32) || defined(_WIN32) -#include "WaveSabrePlayerLib/RealtimePlayerWin32.h" -#endif +#include "WaveSabrePlayerLib/RealtimePlayer.h" #include "WaveSabrePlayerLib/PreRenderPlayer.h" #include "WaveSabrePlayerLib/WavWriter.h" diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 958e74c7..04b2a69a 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -1,11 +1,14 @@ #include -//#include +#include #include using namespace WaveSabrePlayerLib; #include #include //#include +#if !defined(WIN32) && !defined(_WIN32) +#include +#endif #if defined(_MSC_VER) // MSVC WHYYYYYY @@ -90,11 +93,12 @@ int main(int argc, char **argv) bool writeWav = argc >= 3 && !strcmp(argv[2], "-w"); bool preRender = argc == 3 && !strcmp(argv[2], "-p"); #if !defined(WIN32) && !defined(_WIN32) - if (!writeWav) { + /*if (!writeWav) { printf("W: playback not yet supported on non-Windows platforms." "Writing WAV instead...\n"); writeWav = true; - } + }*/ + preRender = 1; #endif #if defined(WIN32) || defined(_WIN32) @@ -145,7 +149,6 @@ int main(int argc, char **argv) } else { -#if defined(WIN32) || defined(_WIN32) IPlayer *player; if (preRender) @@ -162,10 +165,18 @@ int main(int argc, char **argv) player = new RealtimePlayer(&song, numRenderThreads); } +#if defined(WIN32) || defined(_WIN32) printf("Realtime player activated. Press ESC to quit.\n"); +#else + printf("Realtime player activated. Press ^C to quit.\n"); +#endif player->Play(); +#if defined(WIN32) || defined(_WIN32) while (!GetAsyncKeyState(VK_ESCAPE)) +#else + while (true) +#endif { auto songPos = player->GetSongPos(); if (songPos >= player->GetLength()) break; @@ -175,14 +186,15 @@ int main(int argc, char **argv) printf("\r %.1i:%.2i.%.2i", minutes, seconds, hundredths); player->DoForegroundWork(); +#if defined(WIN32) || defined(_WIN32) Sleep(10); +#else + sleep( 1); +#endif } printf("\n"); delete player; -#else - printf("???\n"); -#endif } free(buffer); From 78a9e68e1957229f43f570d40c775ba7dc89869d Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Mon, 31 Aug 2020 22:51:39 +0200 Subject: [PATCH 07/32] fix cmake error --- WaveSabrePlayerLib/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index d9057dd5..eb435634 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -24,7 +24,7 @@ if(WIN32) include/WaveSabrePlayerLib/DirectSoundRenderThread.h src/DirectSoundRenderThread.cpp) - target_link_libraries(WaveSabrePlayerLib + target_link_libraries(WaveSabrePlayerLib PUBLIC winmm.lib dsound.lib) else() From 8e3c7adc7ecb45232e19d054ea958079af4a2013 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Mon, 31 Aug 2020 23:30:07 +0200 Subject: [PATCH 08/32] fix more cmake errors --- WaveSabreCore/CMakeLists.txt | 21 ++++++++++--------- .../{AdulteryWin32.h => Adultery.h} | 0 WaveSabreCore/include/WaveSabreCore/Devices.h | 6 +++--- .../{SpecimenWin32.h => Specimen.h} | 6 ++++++ .../{ThunderWin32.h => Thunder.h} | 6 ++++++ .../src/{AdulteryWin32.cpp => Adultery.cpp} | 11 +++++++++- WaveSabreCore/src/GmDls.cpp | 6 ++++++ .../src/{SpecimenWin32.cpp => Specimen.cpp} | 20 +++++++++++++++++- .../src/{ThunderWin32.cpp => Thunder.cpp} | 18 +++++++++++++++- 9 files changed, 78 insertions(+), 16 deletions(-) rename WaveSabreCore/include/WaveSabreCore/{AdulteryWin32.h => Adultery.h} (100%) rename WaveSabreCore/include/WaveSabreCore/{SpecimenWin32.h => Specimen.h} (94%) rename WaveSabreCore/include/WaveSabreCore/{ThunderWin32.h => Thunder.h} (90%) rename WaveSabreCore/src/{AdulteryWin32.cpp => Adultery.cpp} (97%) rename WaveSabreCore/src/{SpecimenWin32.cpp => Specimen.cpp} (97%) rename WaveSabreCore/src/{ThunderWin32.cpp => Thunder.cpp} (93%) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 6beffbbd..56ace623 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(WaveSabreCore + include/WaveSabreCore/Adultery.h include/WaveSabreCore/AllPass.h include/WaveSabreCore/AllPassDelay.h include/WaveSabreCore/BiquadFilter.h @@ -19,9 +20,12 @@ add_library(WaveSabreCore include/WaveSabreCore/Scissor.h include/WaveSabreCore/Slaughter.h include/WaveSabreCore/Smasher.h + include/WaveSabreCore/Specimen.h include/WaveSabreCore/StateVariableFilter.h include/WaveSabreCore/SynthDevice.h + include/WaveSabreCore/Thunder.h include/WaveSabreCore/Twister.h + src/Adultery.cpp src/AllPass.cpp src/AllPassDelay.cpp src/BiquadFilter.cpp @@ -42,22 +46,19 @@ add_library(WaveSabreCore src/Scissor.cpp src/Slaughter.cpp src/Smasher.cpp + src/Specimen.cpp src/StateVariableFilter.cpp src/SynthDevice.cpp - src/Twister.cpp) + src/Thunder.cpp + src/Twister.cpp + ) if(WIN32) - target_sources(WaveSabreCore PUBLIC - include/WaveSabreCore/AdulteryWin32.h + target_sources(WaveSabreCore PRIVATE include/WaveSabreCore/GmDls.h - include/WaveSabreCore/SpecimenWin32.h - include/WaveSabreCore/ThunderWin32.h - src/AdulteryWin32.cpp - src/GmDls.cpp - src/SpecimenWin32.cpp - src/ThunderWin32.cpp) + src/GmDls.cppà) - target_link_libraries(WaveSabreCore Msacm32.lib) + target_link_libraries(WaveSabreCore PUBLIC Msacm32.lib) endif() target_include_directories(WaveSabreCore PUBLIC include) diff --git a/WaveSabreCore/include/WaveSabreCore/AdulteryWin32.h b/WaveSabreCore/include/WaveSabreCore/Adultery.h similarity index 100% rename from WaveSabreCore/include/WaveSabreCore/AdulteryWin32.h rename to WaveSabreCore/include/WaveSabreCore/Adultery.h diff --git a/WaveSabreCore/include/WaveSabreCore/Devices.h b/WaveSabreCore/include/WaveSabreCore/Devices.h index bf9bb116..30e829e8 100644 --- a/WaveSabreCore/include/WaveSabreCore/Devices.h +++ b/WaveSabreCore/include/WaveSabreCore/Devices.h @@ -4,9 +4,9 @@ #include "Falcon.h" #include "Slaughter.h" #if defined(WIN32) || defined(_WIN32) -#include "ThunderWin32.h" -#include "AdulteryWin32.h" -#include "SpecimenWin32.h" +#include "Thunder.h" +#include "Adultery.h" +#include "Specimen.h" #endif #include "Scissor.h" diff --git a/WaveSabreCore/include/WaveSabreCore/SpecimenWin32.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h similarity index 94% rename from WaveSabreCore/include/WaveSabreCore/SpecimenWin32.h rename to WaveSabreCore/include/WaveSabreCore/Specimen.h index 86c3d7ec..69b561ba 100644 --- a/WaveSabreCore/include/WaveSabreCore/SpecimenWin32.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -6,6 +6,7 @@ #include "StateVariableFilter.h" #include "SamplePlayer.h" +#if defined(WIN32) || defined(_WIN32) #include #include @@ -13,6 +14,7 @@ #define _UNICODE #endif #include +#endif /* WIN32 */ namespace WaveSabreCore { @@ -70,7 +72,9 @@ namespace WaveSabreCore virtual void SetChunk(void *data, int size); virtual int GetChunk(void **data); +#if defined(WIN32) || defined(_WIN32) void LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat); +#endif private: class SpecimenVoice : public Voice @@ -99,10 +103,12 @@ namespace WaveSabreCore float velocity; }; +#if defined(WIN32) || defined(_WIN32) static BOOL __stdcall driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport); static BOOL __stdcall formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport); static HACMDRIVERID driverId; +#endif char *chunkData; diff --git a/WaveSabreCore/include/WaveSabreCore/ThunderWin32.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h similarity index 90% rename from WaveSabreCore/include/WaveSabreCore/ThunderWin32.h rename to WaveSabreCore/include/WaveSabreCore/Thunder.h index 44e5d4b6..e163b83b 100644 --- a/WaveSabreCore/include/WaveSabreCore/ThunderWin32.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -3,6 +3,7 @@ #include "SynthDevice.h" +#if defined(WIN32) || defined(_WIN32) #include #include @@ -10,6 +11,7 @@ #define _UNICODE #endif #include +#endif /* WIN32 */ namespace WaveSabreCore { @@ -24,7 +26,9 @@ namespace WaveSabreCore virtual void SetChunk(void *data, int size); virtual int GetChunk(void **data); +#if defined(WIN32) || defined(_WIN32) void LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat); +#endif private: class ThunderVoice : public Voice @@ -43,10 +47,12 @@ namespace WaveSabreCore int samplePos; }; +#if defined(WIN32) || defined(_WIN32) static BOOL __stdcall driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport); static BOOL __stdcall formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport); static HACMDRIVERID driverId; +#endif char *chunkData; diff --git a/WaveSabreCore/src/AdulteryWin32.cpp b/WaveSabreCore/src/Adultery.cpp similarity index 97% rename from WaveSabreCore/src/AdulteryWin32.cpp rename to WaveSabreCore/src/Adultery.cpp index 7a0d87be..a278daf8 100644 --- a/WaveSabreCore/src/AdulteryWin32.cpp +++ b/WaveSabreCore/src/Adultery.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -98,6 +98,15 @@ namespace WaveSabreCore if (sampleIndex >= 0) { auto gmDls = GmDls::Load(); + if (gmDls == nullptr) { + sampleLength = 1; + sampleData = new float[sampleLength]; + sampleData[0] = 0.0f; + sampleLoopStart = 0; + sampleLoopLength = 1; + + break; + } // Seek to wave pool chunk's data auto ptr = gmDls + GmDls::WaveListOffset; diff --git a/WaveSabreCore/src/GmDls.cpp b/WaveSabreCore/src/GmDls.cpp index 1ca8e566..043073ee 100644 --- a/WaveSabreCore/src/GmDls.cpp +++ b/WaveSabreCore/src/GmDls.cpp @@ -1,6 +1,8 @@ #include +#if defined(WIN32) || defined(_WIN32) #include +#endif static char *gmDlsPaths[2] = { @@ -12,6 +14,7 @@ namespace WaveSabreCore { unsigned char *GmDls::Load() { +#if defined(WIN32) || defined(_WIN32) HANDLE gmDlsFile = INVALID_HANDLE_VALUE; for (int i = 0; gmDlsFile == INVALID_HANDLE_VALUE; i++) { @@ -26,5 +29,8 @@ namespace WaveSabreCore CloseHandle(gmDlsFile); return gmDls; +#else + return nullptr; +#endif } } diff --git a/WaveSabreCore/src/SpecimenWin32.cpp b/WaveSabreCore/src/Specimen.cpp similarity index 97% rename from WaveSabreCore/src/SpecimenWin32.cpp rename to WaveSabreCore/src/Specimen.cpp index 1474a3f4..2f9a7cc9 100644 --- a/WaveSabreCore/src/SpecimenWin32.cpp +++ b/WaveSabreCore/src/Specimen.cpp @@ -1,11 +1,13 @@ -#include +#include #include #include namespace WaveSabreCore { +#if defined(WIN32) || defined(_WIN32) HACMDRIVERID Specimen::driverId = NULL; +#endif Specimen::Specimen() : SynthDevice(0) @@ -152,6 +154,7 @@ namespace WaveSabreCore void Specimen::SetChunk(void *data, int size) { +#if defined(WIN32) || defined(_WIN32) if (!size) return; // Read header @@ -182,10 +185,18 @@ namespace WaveSabreCore auto numChunkParams = (int)((size - sizeof(int) - (paramDataPtr - (char *)data)) / sizeof(float)); for (int i = 0; i < numChunkParams; i++) SetParam(i, ((float *)paramDataPtr)[i]); +#else + sampleLength = 1; + sampleData = new float[1]; + sampleData[0] = 0.0f; + sampleLoopStart = 0; + sampleLoopLength = 1; +#endif } int Specimen::GetChunk(void **data) { +#if defined(WIN32) || defined(_WIN32) if (!compressedData) return 0; // Figure out size of chunk @@ -226,8 +237,12 @@ namespace WaveSabreCore *data = chunkData; return size; +#else + return 0; +#endif } +#if defined(WIN32) || defined(_WIN32) void Specimen::LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat) { this->compressedSize = compressedSize; @@ -283,6 +298,7 @@ namespace WaveSabreCore delete [] uncompressedData; } +#endif Specimen::SpecimenVoice::SpecimenVoice(Specimen *specimen) { @@ -391,6 +407,7 @@ namespace WaveSabreCore samplePlayer.CalcPitch(GetNote() - 60 + Detune + specimen->fineTune * 2.0f - 1.0f + SpecimenVoice::coarseDetune(specimen->coarseTune)); } +#if defined(WIN32) || defined(_WIN32) BOOL __stdcall Specimen::driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport) { if (Specimen::driverId) return 1; @@ -427,4 +444,5 @@ namespace WaveSabreCore } return 1; } +#endif } diff --git a/WaveSabreCore/src/ThunderWin32.cpp b/WaveSabreCore/src/Thunder.cpp similarity index 93% rename from WaveSabreCore/src/ThunderWin32.cpp rename to WaveSabreCore/src/Thunder.cpp index 23e1b7fb..79a77caa 100644 --- a/WaveSabreCore/src/ThunderWin32.cpp +++ b/WaveSabreCore/src/Thunder.cpp @@ -1,11 +1,13 @@ -#include +#include #include #include namespace WaveSabreCore { +#if defined(WIN32) || defined(_WIN32) HACMDRIVERID Thunder::driverId = NULL; +#endif Thunder::Thunder() : SynthDevice(0) @@ -38,15 +40,22 @@ namespace WaveSabreCore void Thunder::SetChunk(void *data, int size) { +#if defined(WIN32) || defined(_WIN32) if (!size) return; auto h = (ChunkHeader *)data; auto waveFormat = (WAVEFORMATEX *)((char *)data + sizeof(ChunkHeader)); auto compressedData = (char *)waveFormat + sizeof(WAVEFORMATEX) + waveFormat->cbSize; LoadSample(compressedData, h->CompressedSize, h->UncompressedSize, waveFormat); +#else + sampleLength = 1; + sampleData = new float[1]; + sampleData[0] = 0.0f; +#endif } int Thunder::GetChunk(void **data) { +#if defined(WIN32) || defined(_WIN32) if (!compressedData) return 0; ChunkHeader h; h.CompressedSize = compressedSize; @@ -60,8 +69,12 @@ namespace WaveSabreCore *(int *)(chunkData + chunkSize - sizeof(int)) = chunkSize; *data = chunkData; return chunkSize; +#else + return 0; +#endif } +#if defined(WIN32) || defined(_WIN32) void Thunder::LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat) { this->compressedSize = compressedSize; @@ -114,6 +127,7 @@ namespace WaveSabreCore delete [] uncompressedData; } +#endif Thunder::ThunderVoice::ThunderVoice(Thunder *thunder) { @@ -147,6 +161,7 @@ namespace WaveSabreCore samplePos = 0; } +#if defined(WIN32) || defined(_WIN32) BOOL __stdcall Thunder::driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport) { if (Thunder::driverId) return 1; @@ -183,4 +198,5 @@ namespace WaveSabreCore } return 1; } +#endif } From 284eec2486cacb3b8f0e8ef19f0f02791c050b21 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 00:08:40 +0200 Subject: [PATCH 09/32] aplay prerender works, tackling multithread now --- WaveSabreCore/CMakeLists.txt | 6 ++-- WaveSabrePlayerLib/CMakeLists.txt | 6 ++-- WaveSabrePlayerLib/src/AplayRenderThread.cpp | 33 +++++++++++++++----- WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 2 +- WaveSabrePlayerLib/src/RealtimePlayer.cpp | 2 +- WaveSabreStandAlonePlayer/CMakeLists.txt | 6 ++-- WaveSabreStandAlonePlayer/main.cpp | 5 +-- 7 files changed, 39 insertions(+), 21 deletions(-) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 56ace623..6a34f070 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -78,8 +78,8 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-g -Og") + #set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - #set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index eb435634..9abae009 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -65,8 +65,8 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-g -Og") + #set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/src/AplayRenderThread.cpp b/WaveSabrePlayerLib/src/AplayRenderThread.cpp index bdee6e38..3bd75d2c 100644 --- a/WaveSabrePlayerLib/src/AplayRenderThread.cpp +++ b/WaveSabrePlayerLib/src/AplayRenderThread.cpp @@ -34,6 +34,7 @@ namespace WaveSabrePlayerLib , sampleRate(sampleRate) , bufferSizeMs(bufferSizeMs) , samplesWritten(0) + , bufferBytesLeft(0) , writeBytesMax(PIPE_BUF) #if HAVE_PTHREAD , writerStarted(false) @@ -78,6 +79,7 @@ namespace WaveSabrePlayerLib #if HAVE_PTHREAD if (pthread) { + //printf("create pthread!\n"); rv = pthread_create(&writer, NULL, AplayWriterProc, this); assert(rv == 0 && "Couldn't create background writer thread"); @@ -128,7 +130,13 @@ namespace WaveSabrePlayerLib void AplayRenderThread::DoForegroundWork() { - GetBufferTick(false); + //printf("\nfgw\n"); +#if HAVE_PTHREAD + if (!writerStarted) +#else + if (true) +#endif + GetBufferTick(false); } int AplayRenderThread::GetPlayPositionMs() @@ -143,24 +151,24 @@ namespace WaveSabrePlayerLib dup2(readend, STDIN_FILENO); // format aplay args - char* progpath = "/usr/bin/aplay"; - char* arg0 = progpath; - char arg1[strlen("-r48000")+1]; char arg2[strlen("-fS16_LE")+1]; + char arg3[strlen("-r44100")+1]; - snprintf(arg1, sizeof(arg1), "-r%d", rate); snprintf(arg2, sizeof(arg2), "-fS%d_%sE", (int)sizeof(SongRenderer::Sample)*CHAR_BIT, (is_le() ? "L" : "B")); + snprintf(arg3, sizeof(arg3), "-r%d", rate); // now we're ready to start aplay! - char *const args[] = {arg0, arg1, arg2, NULL}; + //printf("executing /usr/bin/aplay -traw -c2 %s %s\n", arg2, arg3); + char *const args[] = {"/usr/bin/aplay", "-traw", "-c2", arg2, arg3, NULL}; int rv = execve("/usr/bin/aplay", args, environ); assert(rv >= 0 && "Failed to run aplay!"); } void AplayRenderThread::GetBufferTick(bool block) { + //printf("tick! buffer cap 0x%zx\n", bufferSizeBytes); while (true) { struct pollfd pfd; pfd.fd = this->aupipe; @@ -180,6 +188,7 @@ namespace WaveSabrePlayerLib callback(sampleBuffer, bufferSizeBytes/sizeof(SongRenderer::Sample), callbackData); bufferToWrite = sampleBuffer; bufferBytesLeft = bufferSizeBytes; + //printf("new buffer! 0x%zX bytes\n", bufferSizeBytes); } size_t toWrite = bufferBytesLeft; @@ -197,14 +206,21 @@ namespace WaveSabrePlayerLib toWrite >>= 1; // AIMD continue; } else { - assert(0 && "Couldn't write to the aplay pipe."); + printf("Couldn't write to the aplay pipe: %s.\n", strerror(err)); + assert(0); } - } else if (toWrite > (size_t)ret) { + + break; + } + + if (toWrite > (size_t)ret) { haderr = true; // inhibit AI toWrite >>= 1; // MD } bufferBytesLeft -= (size_t)ret; + //printf("written buffer %p size 0x%zx, wrote 0x%zx bytes, left: 0x%zx\n", + // bufferToWrite, toWrite, (size_t)ret, bufferBytesLeft); bufferToWrite += ret / sizeof(SongRenderer::Sample); samplesWritten += ret / sizeof(SongRenderer::Sample); @@ -224,6 +240,7 @@ namespace WaveSabrePlayerLib #if HAVE_PTHREAD void* AplayRenderThread::AplayWriterProc(void* ud) { + //printf("writer proc!\n"); auto self = (AplayRenderThread*)ud; while (self->aupipe >= 0) { diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index 8d222eaa..3f52bc71 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -72,7 +72,7 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs); #elif HAVE_APLAY - renderThread = new AplayRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs); + renderThread = new AplayRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs, true); #endif } diff --git a/WaveSabrePlayerLib/src/RealtimePlayer.cpp b/WaveSabrePlayerLib/src/RealtimePlayer.cpp index ee051b07..a0e3b6bf 100644 --- a/WaveSabrePlayerLib/src/RealtimePlayer.cpp +++ b/WaveSabrePlayerLib/src/RealtimePlayer.cpp @@ -42,7 +42,7 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs); #elif HAVE_APLAY - renderThread = new AplayRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs); + renderThread = new AplayRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs, true); #endif } diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 2ed55929..1c62e6da 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -15,8 +15,8 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-g -Og") + #set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 04b2a69a..5ed66621 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -98,13 +98,14 @@ int main(int argc, char **argv) "Writing WAV instead...\n"); writeWav = true; }*/ - preRender = 1; + //writeWav = true; + preRender = true; #endif #if defined(WIN32) || defined(_WIN32) const int numRenderThreads = 3; #else - const int numRenderThreads = 1; // TODO + const int numRenderThreads = 3; // TODO #endif FILE * pFile; From b8cb3c3db8a4f7efdde39dfbebb9e1f9e98ceb83 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 00:18:11 +0200 Subject: [PATCH 10/32] aplay somewhat working, debugging misc issues now --- WaveSabreCore/CMakeLists.txt | 5 +++-- WaveSabreCore/src/GmDls.cpp | 2 +- WaveSabreCore/src/Helpers.cpp | 30 +++++++++++++++++++++++------- WaveSabreStandAlonePlayer/main.cpp | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 6a34f070..c43be3f5 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -12,6 +12,7 @@ add_library(WaveSabreCore include/WaveSabreCore/Echo.h include/WaveSabreCore/Envelope.h include/WaveSabreCore/Falcon.h + include/WaveSabreCore/GmDls.h include/WaveSabreCore/Helpers.h include/WaveSabreCore/Leveller.h include/WaveSabreCore/MxcsrFlagGuard.h @@ -38,6 +39,7 @@ add_library(WaveSabreCore src/Echo.cpp src/Envelope.cpp src/Falcon.cpp + src/GmDls.cpp src/Helpers.cpp src/Leveller.cpp src/MxcsrFlagGuard.cpp @@ -55,8 +57,7 @@ add_library(WaveSabreCore if(WIN32) target_sources(WaveSabreCore PRIVATE - include/WaveSabreCore/GmDls.h - src/GmDls.cppà) + ) target_link_libraries(WaveSabreCore PUBLIC Msacm32.lib) endif() diff --git a/WaveSabreCore/src/GmDls.cpp b/WaveSabreCore/src/GmDls.cpp index 043073ee..44656a8b 100644 --- a/WaveSabreCore/src/GmDls.cpp +++ b/WaveSabreCore/src/GmDls.cpp @@ -4,7 +4,7 @@ #include #endif -static char *gmDlsPaths[2] = +static char const *gmDlsPaths[2] = { "drivers/gm.dls", "drivers/etc/gm.dls" diff --git a/WaveSabreCore/src/Helpers.cpp b/WaveSabreCore/src/Helpers.cpp index 27debf07..64e600e6 100644 --- a/WaveSabreCore/src/Helpers.cpp +++ b/WaveSabreCore/src/Helpers.cpp @@ -76,7 +76,14 @@ static ASMVECTORCALL double fpuPow(double x, double y) if (y == 0.0) return 1; if (x == 0.0) return 0; - asm volatile("fyl2x\n" + if (y >= 1.99 && y <= 2.01) return x*x; + if (y >= 3.99 && y <= 4.01) { + x = x*x; + return x*x; + } + + return pow(x,y); + /*asm volatile("fyl2x\n" "fld %%st(0)\n" "frndint\n" "fsubr %%st(0), %%st(1)\n" @@ -91,7 +98,7 @@ static ASMVECTORCALL double fpuPow(double x, double y) :"u"(y) // input regs : // destroyed regs ); - return x; + return x;*/ #else /* not x86 */ return pow(x, y); // __builtin_pow only accepts an integer exponent :/ #endif /* GNUC, platform */ @@ -152,7 +159,15 @@ static ASMVECTORCALL float fpuPowF(float x, float y) if (y == 0) return 1; if (x == 0) return 0; - asm volatile("fyl2x\n" + if (y >= 1.99f && y <= 2.01f) return x*x; + if (y >= 3.99f && y <= 4.01f) { + x = x*x; + return x*x; + } + + return powf(x,y); + + /*asm volatile("fyl2x\n" "fld %%st(0)\n" "frndint\n" "fsubr %%st(0), %%st(1)\n" @@ -167,7 +182,7 @@ static ASMVECTORCALL float fpuPowF(float x, float y) :"u"(y) // input regs : // destroyed regs ); - return x; + return x;*/ #else /* not x86_64 */ return powf(x, y); // __builtin_pow only accepts an integer exponent :/ #endif /* GNUC, platform */ @@ -195,10 +210,11 @@ static ASMVECTORCALL double fpuCos(double x) #if defined(__x86_64__) || defined(__i386__) // not writing the *entire* function body in assembly actually helps // gcc and clang with inlining and LTO - asm volatile("fcos\n":"+t"(x)::); - return x; + //asm volatile("fcos\n":"+t"(x)::); + //return x; + return cos(x); #else /* x86_64 */ - return __builtin_cos(x); + return cos(x);//return __builtin_cos(x); #endif /* GNUC, platform */ #endif /* MSVC/GNUC */ } diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 5ed66621..fa8f1927 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -98,7 +98,7 @@ int main(int argc, char **argv) "Writing WAV instead...\n"); writeWav = true; }*/ - //writeWav = true; + writeWav = true; preRender = true; #endif From f3204489f115156a19ac70f138ed39e327a54146 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 00:24:20 +0200 Subject: [PATCH 11/32] asm maths issues debugging ep.1 --- WaveSabreCore/src/Helpers.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/WaveSabreCore/src/Helpers.cpp b/WaveSabreCore/src/Helpers.cpp index 64e600e6..6f14ef8b 100644 --- a/WaveSabreCore/src/Helpers.cpp +++ b/WaveSabreCore/src/Helpers.cpp @@ -7,14 +7,16 @@ // TODO: make assembly equivalent for x64 (use intrinsic ?) #if defined(_MSC_VER) && defined(_M_IX86) #define ASM_MATH_AVAILABLE (1) - #define ASMVECTORCALL __declspec(naked) __vectorcall + #define ASMNAKED __declspec(naked) + #define ASMVECTORCALL __vectorcall #elif defined(__GNUC__) #define ASM_MATH_AVAILABLE (1) #if defined(__x86_64) || defined(__i386__) - #define ASMVECTORCALL inline __attribute__((__always_inline__)) + #define ASMNAKED inline __attribute__((__always_inline__)) #else - #define ASMVECTORCALL inline + #define ASMNAKED inline #endif + #define ASMVECTORCALL #else /* nor MSVC nor GCC/clang */ #define ASM_MATH_AVAILABLE (0) #endif @@ -22,7 +24,7 @@ // XXX: this pow() implementation seems to suck for y > 4, regardless of float/double #if ASM_MATH_AVAILABLE == 1 -static ASMVECTORCALL double fpuPow(double x, double y) +static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) { #if defined(_MSC_VER) && defined(_M_IX86) __asm @@ -105,7 +107,7 @@ static ASMVECTORCALL double fpuPow(double x, double y) #endif /* MSVC/GNUC */ } -static ASMVECTORCALL float fpuPowF(float x, float y) +static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) { #if defined(_MSC_VER) && defined(_M_IX86) __asm @@ -189,7 +191,7 @@ static ASMVECTORCALL float fpuPowF(float x, float y) #endif /* MSVC/GNUC */ } -static ASMVECTORCALL double fpuCos(double x) +static ASMNAKED double ASMVECTORCALL fpuCos(double x) { #if defined(_MSC_VER) && defined(_M_IX86) __asm @@ -210,11 +212,11 @@ static ASMVECTORCALL double fpuCos(double x) #if defined(__x86_64__) || defined(__i386__) // not writing the *entire* function body in assembly actually helps // gcc and clang with inlining and LTO - //asm volatile("fcos\n":"+t"(x)::); - //return x; - return cos(x); + asm volatile("fcos\n":"+t"(x)::); + return x; + //return cos(x); #else /* x86_64 */ - return cos(x);//return __builtin_cos(x); + /*return cos(x);*/return __builtin_cos(x); #endif /* GNUC, platform */ #endif /* MSVC/GNUC */ } From 0b5ebc64f6ad3e148744ac455936a3b5c4aa5d9e Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 01:00:02 +0200 Subject: [PATCH 12/32] asm maths issues debugging ep.2 --- WaveSabreCore/src/Helpers.cpp | 132 +++++++++++++++++---- WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 2 +- WaveSabreStandAlonePlayer/main.cpp | 18 +-- 3 files changed, 113 insertions(+), 39 deletions(-) diff --git a/WaveSabreCore/src/Helpers.cpp b/WaveSabreCore/src/Helpers.cpp index 6f14ef8b..d82ce7d4 100644 --- a/WaveSabreCore/src/Helpers.cpp +++ b/WaveSabreCore/src/Helpers.cpp @@ -11,11 +11,11 @@ #define ASMVECTORCALL __vectorcall #elif defined(__GNUC__) #define ASM_MATH_AVAILABLE (1) - #if defined(__x86_64) || defined(__i386__) - #define ASMNAKED inline __attribute__((__always_inline__)) - #else + //#if defined(__x86_64) || defined(__i386__) + // #define ASMNAKED inline __attribute__((__always_inline__/*__no_inline__,__naked__*/)) + //#else #define ASMNAKED inline - #endif + //#endif #define ASMVECTORCALL #else /* nor MSVC nor GCC/clang */ #define ASM_MATH_AVAILABLE (0) @@ -72,20 +72,62 @@ static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) ret } #elif defined(__GNUC__) - #if defined(__x86_64__) || defined(__i386__) + //#if defined(__x86_64__) || defined(__i386__) + /*asm volatile("push %%rax\n" // subq $8, %rsp + "xorpd %%xmm2, %%xmm2\n" + "comisd %%xmm2, %%xmm1\n" + "jne 1f\n" + + "fld1\n" + "jmp 3f\n" + + "1:\n" + "comisd %%xmm2, %%xmm0\n" + "jne 2f\n" + + "fldz\n" + "jmp 3f\n" + + "2:\n" + "movsd %%xmm1, (%%rsp)\n" + "fldl (%%rsp)\n" + "movsd %%xmm0, (%%rsp)\n" + "fldl (%%rsp)\n" + + "fyl2x\n" + "fld %%st(0)\n" + "frndint\n" + "fsubr %%st(0), %%st(1)\n" + "fxch %%st(1)\n" + "fchs\n" + "f2xm1\n" + "fld1\n" + "faddp %%st(0), %%st(1)\n" + "fscale\n" + "fstp %%st(1)\n" + + "3:\n" + "fstpl (%%rsp)\n" + "movsd (%%rsp), %%xmm0\n" + "pop %%rax\n" // addq $8, %%rsp + "ret\n" + :// no output + :// no input + :"xmm2" // clobbered + );*/ // not writing the *entire* function body in assembly actually helps // gcc and clang with inlining and LTO - if (y == 0.0) return 1; - if (x == 0.0) return 0; + /*if (y == 0.0) return 1.0; + if (x == 0.0) return 0.0; - if (y >= 1.99 && y <= 2.01) return x*x; - if (y >= 3.99 && y <= 4.01) { + if (y >= 1.9999 && y <= 2.0001) return x*x; + if (y >= 3.9999 && y <= 4.0001) { x = x*x; return x*x; } - return pow(x,y); - /*asm volatile("fyl2x\n" + //return pow(x,y); + asm volatile("fyl2x\n" "fld %%st(0)\n" "frndint\n" "fsubr %%st(0), %%st(1)\n" @@ -101,9 +143,9 @@ static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) : // destroyed regs ); return x;*/ - #else /* not x86 */ + //#else /* not x86 */ return pow(x, y); // __builtin_pow only accepts an integer exponent :/ - #endif /* GNUC, platform */ + //#endif /* GNUC, platform */ #endif /* MSVC/GNUC */ } @@ -155,21 +197,62 @@ static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) ret } #elif defined(__GNUC__) - #if defined (__x86_64__) || defined(__i386__) + //#if defined (__x86_64__) || defined(__i386__) + /*asm volatile("push %%rax\n" // subq $8, %rsp + "xorps %%xmm2, %%xmm2\n" + "comiss %%xmm2, %%xmm1\n" + "jne 1f\n" + + "fld1\n" + "jmp 3f\n" + + "1:\n" + "comiss %%xmm2, %%xmm0\n" + "jne 2f\n" + + "fldz\n" + "jmp 3f\n" + + "2:\n" + "movss %%xmm1, (%%rsp)\n" + "flds (%%rsp)\n" + "movss %%xmm0, (%%rsp)\n" + "flds (%%rsp)\n" + + "fyl2x\n" + "fld %%st(0)\n" + "frndint\n" + "fsubr %%st(0), %%st(1)\n" + "fxch %%st(1)\n" + "fchs\n" + "f2xm1\n" + "fld1\n" + "faddp %%st(0), %%st(1)\n" + "fscale\n" + "fstp %%st(1)\n" + + "3:\n" + "fstps (%%rsp)\n" + "movss (%%rsp), %%xmm0\n" + "pop %%rax\n" // addq $8, %%rsp + "ret\n" + :// no output + :// no input + :"xmm2" // clobbered + );*/ // not writing the *entire* function body in assembly actually helps // gcc and clang with inlining and LTO - if (y == 0) return 1; - if (x == 0) return 0; + /*if (y == 0.0f) return 1.0f; + if (x == 0.0f) return 0.0f; - if (y >= 1.99f && y <= 2.01f) return x*x; - if (y >= 3.99f && y <= 4.01f) { + if (y >= 1.9999f && y <= 2.0001f) return x*x; + if (y >= 3.9999f && y <= 4.0001f) { x = x*x; return x*x; } - return powf(x,y); - - /*asm volatile("fyl2x\n" + //return powf(x,y); + asm volatile("fyl2x\n" "fld %%st(0)\n" "frndint\n" "fsubr %%st(0), %%st(1)\n" @@ -185,9 +268,9 @@ static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) : // destroyed regs ); return x;*/ - #else /* not x86_64 */ + //#else /* not x86_64 */ return powf(x, y); // __builtin_pow only accepts an integer exponent :/ - #endif /* GNUC, platform */ + //#endif /* GNUC, platform */ #endif /* MSVC/GNUC */ } @@ -214,9 +297,8 @@ static ASMNAKED double ASMVECTORCALL fpuCos(double x) // gcc and clang with inlining and LTO asm volatile("fcos\n":"+t"(x)::); return x; - //return cos(x); #else /* x86_64 */ - /*return cos(x);*/return __builtin_cos(x); + return __builtin_cos(x); #endif /* GNUC, platform */ #endif /* MSVC/GNUC */ } diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index 3f52bc71..0a73189d 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -117,7 +117,7 @@ namespace WaveSabrePlayerLib int samplesToTake; #if defined(WIN32) || defined(_WIN32) - int samplesToTake = min(numSamples, samplesLeft); + samplesToTake = min(numSamples, samplesLeft); #else // sigh. samplesToTake = (numSamples > samplesLeft) ? samplesLeft : numSamples; diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index fa8f1927..80abaff1 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -5,20 +5,12 @@ using namespace WaveSabrePlayerLib; #include #include -//#include +#include + #if !defined(WIN32) && !defined(_WIN32) #include #endif -#if defined(_MSC_VER) -// MSVC WHYYYYYY -extern "C" FILE* __cdecl __iob_func(void) -{ - static FILE _iob[] = {*stdin, *stdout, *stderr}; - return _iob; -} -#endif - WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) { switch (id) @@ -62,8 +54,8 @@ int main(int argc, char **argv) const int N = 1000; const double scaler = 0.001; - //for (int y = 1; y <= N; ++y) - int y = 1.0/12; + for (int y = 1; y <= N; ++y) + //int y = 1.0/12; for (int x = 1; x <= N; ++x) { double xd = x * scaler, yd = y * scaler; @@ -113,7 +105,7 @@ int main(int argc, char **argv) unsigned char * buffer; size_t result; - pFile = fopen(argv[1], "rb"); + pFile = (argc < 2) ? NULL : fopen(argv[1], "rb"); if (pFile == NULL) { printf("File error\n"); exit(1); } // obtain file size: From 3cc35a7d904014743d55cfbc0a316ee74960ed9b Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 01:04:16 +0200 Subject: [PATCH 13/32] right, windows... --- WaveSabreStandAlonePlayer/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 80abaff1..763363d3 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -33,7 +33,7 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) case SongRenderer::DeviceId::Specimen: return new WaveSabreCore::Specimen(); #endif } - fprintf(stderr, "ack, unknown device %d!\n", id); + printf("ack, unknown device %d!\n", id); return nullptr; } From 8743101c3f0f303a4d4d4911ac41eebc3ab80239 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 01:32:30 +0200 Subject: [PATCH 14/32] properly fix all aplay RT stuff --- WaveSabreCore/CMakeLists.txt | 6 +++--- WaveSabrePlayerLib/CMakeLists.txt | 6 +++--- .../include/WaveSabrePlayerLib/AplayRenderThread.h | 2 +- WaveSabrePlayerLib/src/AplayRenderThread.cpp | 9 +++++---- WaveSabreStandAlonePlayer/CMakeLists.txt | 6 +++--- WaveSabreStandAlonePlayer/main.cpp | 7 ++++--- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index c43be3f5..1270b0b9 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -79,8 +79,8 @@ if(MSVC) else() # assuming GCC or clang for now - #set(CMAKE_CXX_FLAGS "-g -Og") + set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + #set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index 9abae009..eb435634 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -65,8 +65,8 @@ if(MSVC) else() # assuming GCC or clang for now - #set(CMAKE_CXX_FLAGS "-g -Og") + set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h index e872e19e..1f177db0 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h @@ -41,7 +41,7 @@ namespace WaveSabrePlayerLib int sampleRate; int bufferSizeMs; int bufferSizeBytes; - int samplesWritten; + int bytesWritten; SongRenderer::Sample *sampleBuffer; const SongRenderer::Sample *bufferToWrite; size_t bufferBytesLeft; diff --git a/WaveSabrePlayerLib/src/AplayRenderThread.cpp b/WaveSabrePlayerLib/src/AplayRenderThread.cpp index 3bd75d2c..3c99d71e 100644 --- a/WaveSabrePlayerLib/src/AplayRenderThread.cpp +++ b/WaveSabrePlayerLib/src/AplayRenderThread.cpp @@ -33,7 +33,7 @@ namespace WaveSabrePlayerLib , callbackData(callbackData) , sampleRate(sampleRate) , bufferSizeMs(bufferSizeMs) - , samplesWritten(0) + , bytesWritten(0) , bufferBytesLeft(0) , writeBytesMax(PIPE_BUF) #if HAVE_PTHREAD @@ -45,7 +45,8 @@ namespace WaveSabrePlayerLib #endif bufferSizeBytes = sampleRate * SongRenderer::BlockAlign * bufferSizeMs / 1000; - bufferToWrite = (SongRenderer::Sample*)malloc(bufferSizeBytes); + sampleBuffer = (SongRenderer::Sample*)malloc(bufferSizeBytes); + bufferToWrite = sampleBuffer; int fdpair[2]; int rv = pipe(fdpair); @@ -141,7 +142,7 @@ namespace WaveSabrePlayerLib int AplayRenderThread::GetPlayPositionMs() { - return (samplesWritten * 1000) / sampleRate; + return (int)(bytesWritten / SongRenderer::BlockAlign * 1000 / sampleRate); } void AplayRenderThread::AplayProc(int readend, int rate) @@ -222,7 +223,7 @@ namespace WaveSabrePlayerLib //printf("written buffer %p size 0x%zx, wrote 0x%zx bytes, left: 0x%zx\n", // bufferToWrite, toWrite, (size_t)ret, bufferBytesLeft); bufferToWrite += ret / sizeof(SongRenderer::Sample); - samplesWritten += ret / sizeof(SongRenderer::Sample); + bytesWritten += ret; break; } diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 1c62e6da..2ed55929 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -15,8 +15,8 @@ if(MSVC) else() # assuming GCC or clang for now - #set(CMAKE_CXX_FLAGS "-g -Og") + set(CMAKE_CXX_FLAGS "-g -Og") # stuff to make the code smaller - set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 763363d3..3a51b41e 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -90,14 +90,14 @@ int main(int argc, char **argv) "Writing WAV instead...\n"); writeWav = true; }*/ - writeWav = true; - preRender = true; + //writeWav = true; + //preRender = true; #endif #if defined(WIN32) || defined(_WIN32) const int numRenderThreads = 3; #else - const int numRenderThreads = 3; // TODO + const int numRenderThreads = 3; #endif FILE * pFile; @@ -182,6 +182,7 @@ int main(int argc, char **argv) #if defined(WIN32) || defined(_WIN32) Sleep(10); #else + fflush(stdout); sleep( 1); #endif } From 4831bfc37b7cafad3ce91fd384fe592d7acd5309 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 02:17:03 +0200 Subject: [PATCH 15/32] wow, an argument parser that's actually helpful! --- WaveSabreStandAlonePlayer/main.cpp | 165 ++++++++++++++++++++++++----- 1 file changed, 137 insertions(+), 28 deletions(-) diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 3a51b41e..cf525117 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -46,6 +46,118 @@ void progressCallback(double progress, void *data) printf("]"); } +struct player_args +{ + const char* infile; + const char* outfile; + int nthreads; + bool prerender; + bool wavwriter; +}; + +static void print_usage(const char* prgm) +{ + printf("%s: WaveSabre standalone song player/renderer\n" + "Usage:\n" +#if defined(WIN32) || defined(_WIN32) + "\t%s [/p|/prerender] [/w|/wav [out.wav]] [/t|/threads 3] \n" +#else + "\t%s [-p|--prerender] [-w|--wav [out.wav]] [-t|--threads 3] \n" +#endif + "\n" + "Arguments:\n" + "\tprerender prerender the song instead of rendering while playing.\n" + "\twav instead of playing back the song, write a WAV file. By default,\n" + "\t the output file is called `out.wav', but another may be supplied\n" + "\t immediately after this argument.\n" + "\tthreads the amount of threads to use in parallel for rendering. By\n" + "\t default, this number is 3.\n" + "\tinput.bin the input song file, exported by the WaveSabre exporter.\n" + , prgm, prgm); + exit(0); +} +static void parse_args(struct player_args* args, int argc, char** argv) +{ + if (argc < 2) { + print_usage(argv[0]); + // unreachable + } + + args->infile = NULL; + args->outfile = NULL; + args->nthreads = 3; + args->prerender = false; + args->wavwriter = false; + + char argpfix; +#if defined(WIN32) || defined(_WIN32) + argpfix = '/'; +#else + argpfix = '-'; +#endif + + for (int i = 1; i < argc; ++i) { + if (argv[i][0] == argpfix) { + const char* as = &argv[i][1]; + + if (!strcmp(as, "h") || !strcmp(as, "-help") || !strcmp(as, "help")) { + print_usage(argv[0]); + // unreachable + } + // TODO: option for printing version info + if (!strcmp(as, "p") || !strcmp(as, "-prerender") || !strcmp(as, "prerender")) { + args->prerender = true; + continue; + } + if (!strcmp(as, "w") || !strcmp(as, "-wav") || !strcmp(as, "wav")) { + args->wavwriter = true; + + if (i+1 < argc && argv[i+1][0] != argpfix) { + args->outfile = argv[i+1]; + ++i; + } + continue; + } + if (!strcmp(as, "t") || !strcmp(as, "-threads") || !strcmp(as, "threads")) { + if (i+1 == argc) { + printf("Expecting thread amount\n"); + exit(2); + } + + ++i; + int res = sscanf(argv[i], "%d", &args->nthreads); + if (res != 1 || args->nthreads < 0) { + printf("Can't parse thread amount '%s'\n", argv[i]); + exit(2); + } + + continue; + } + + printf("Unrecognised option '%s', ignoring...\n", argv[i]); + } else { + if (args->infile != NULL) { + printf("Unexpected argument '%s'\n", argv[i]); + exit(2); + } + + args->infile = argv[i]; + } + } + + if (args->infile == NULL) { + if (args->outfile != NULL) { + // oops, was probably meant as input file path + args->infile = args->outfile; + args->outfile = NULL; + } + } + + if (args->outfile == NULL && args->wavwriter) { + args->outfile = "out.wav"; + } +} + int main(int argc, char **argv) { /*double minerr = 1.0/0, maxerr = -1.0/0; @@ -82,31 +194,19 @@ int main(int argc, char **argv) return 0;*/ - bool writeWav = argc >= 3 && !strcmp(argv[2], "-w"); - bool preRender = argc == 3 && !strcmp(argv[2], "-p"); -#if !defined(WIN32) && !defined(_WIN32) - /*if (!writeWav) { - printf("W: playback not yet supported on non-Windows platforms." - "Writing WAV instead...\n"); - writeWav = true; - }*/ - //writeWav = true; - //preRender = true; -#endif - -#if defined(WIN32) || defined(_WIN32) - const int numRenderThreads = 3; -#else - const int numRenderThreads = 3; -#endif + struct player_args args; + parse_args(&args, argc, argv); FILE * pFile; long lSize; unsigned char * buffer; size_t result; - pFile = (argc < 2) ? NULL : fopen(argv[1], "rb"); - if (pFile == NULL) { printf("File error\n"); exit(1); } + pFile = fopen(args.infile, "rb"); + if (pFile == NULL) { + printf("Can't open input file '%s'\n", args.infile); + exit(1); + } // obtain file size: fseek(pFile, 0, SEEK_END); @@ -118,7 +218,10 @@ int main(int argc, char **argv) // copy the file into the buffer: result = fread(buffer, 1, lSize, pFile); - if (result != lSize) { printf("Reading error\n"); exit(3); } + if (result != lSize) { + printf("Can't read from input file '%s'\n", args.infile); + exit(3); + } // terminate fclose(pFile); @@ -127,35 +230,41 @@ int main(int argc, char **argv) song.blob = buffer; song.factory = SongFactory; - if (writeWav) + if (args.wavwriter) { - WavWriter wavWriter(&song, numRenderThreads); + FILE* outf = fopen(args.outfile, "wb"); + if (!outf) { + printf("Can't open output file '%s'\n", args.outfile); + exit(4); + } + fclose(outf); // close again because WavWriter wants a file path + + WavWriter wavWriter(&song, args.nthreads); printf("WAV writer activated.\n"); - auto fileName = argc >= 4 ? argv[3] : "out.wav"; printf("Rendering...\n"); //asm volatile("int3\n"); - wavWriter.Write(fileName, progressCallback, nullptr); + wavWriter.Write(args.outfile, progressCallback, nullptr); - printf("\n\nWAV file written to \"%s\". Enjoy.\n", fileName); + printf("\n\nWAV file written to \"%s\". Enjoy.\n", args.outfile); } else { IPlayer *player; - if (preRender) + if (args.prerender) { printf("Prerender activated.\n"); printf("Rendering...\n"); - player = new PreRenderPlayer(&song, numRenderThreads, progressCallback, nullptr); + player = new PreRenderPlayer(&song, args.nthreads, progressCallback, nullptr); printf("\n\n"); } else { - player = new RealtimePlayer(&song, numRenderThreads); + player = new RealtimePlayer(&song, args.nthreads); } #if defined(WIN32) || defined(_WIN32) From 1ca8809a1f3c0106da66a2c090e04af957a5983c Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 02:30:47 +0200 Subject: [PATCH 16/32] proper thread stopping --- WaveSabrePlayerLib/src/SongRenderer.cpp | 34 ++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/WaveSabrePlayerLib/src/SongRenderer.cpp b/WaveSabrePlayerLib/src/SongRenderer.cpp index 74c560e1..53758570 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.cpp @@ -3,6 +3,10 @@ #include #include +#if HAVE_PTHREAD +#include +#endif + using namespace WaveSabreCore; namespace WaveSabrePlayerLib @@ -122,7 +126,30 @@ namespace WaveSabrePlayerLib #elif HAVE_PTHREAD for (int i = 0; i < numRenderThreads; i++) sem_post(&renderThreadStartEvents[i]); - // TODO: wait for threads, stop threads + + // properly stop threads + { + struct timespec to; + to.tv_sec = 0; + // .1sec milli micro + to.tv_nsec = 100 * 1000 * 1000; + bool badAddThrds[numRenderThreads - 1] = { false }; + for (int i = 0; i < numRenderThreads - 1; i++) { + if (pthread_timedjoin_np(additionalRenderThreads[i], NULL, &to)) { + badAddThrds[i] = true; + } + } + + for (int i = 0; i < numRenderThreads - 1; i++) { + if (badAddThrds[i]) { + pthread_kill(additionalRenderThreads[i], SIGTERM); + if (pthread_timedjoin_np(additionalRenderThreads[i], NULL, &to)) { + pthread_kill(additionalRenderThreads[i], SIGKILL); + pthread_join(additionalRenderThreads[i], NULL); + } + } + } + } #endif delete [] additionalRenderThreads; } @@ -268,6 +295,11 @@ namespace WaveSabrePlayerLib break; } } + + // might take a wihle before more work becomes available +#if HAVE_PTHREAD + pthread_yield(); +#endif } //asm volatile("int3\n"); From b1bfd75fbb7b5e2c71f0d2841a93665dd48f31dd Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 04:33:13 +0200 Subject: [PATCH 17/32] SDL backend --- WaveSabreCore/CMakeLists.txt | 12 +- WaveSabrePlayerLib/CMakeLists.txt | 40 ++++- .../WaveSabrePlayerLib/AplayRenderThread.h | 4 +- .../WaveSabrePlayerLib/SDL2RenderThread.h | 62 +++++++ WaveSabrePlayerLib/src/AplayRenderThread.cpp | 15 +- WaveSabrePlayerLib/src/PreRenderPlayer.cpp | 4 + WaveSabrePlayerLib/src/RealtimePlayer.cpp | 4 + WaveSabrePlayerLib/src/SDL2RenderThread.cpp | 155 ++++++++++++++++++ WaveSabrePlayerLib/src/SongRenderer.cpp | 16 +- WaveSabrePlayerLib/src/WavWriter.cpp | 1 - WaveSabreStandAlonePlayer/CMakeLists.txt | 29 +++- WaveSabreStandAlonePlayer/main.cpp | 5 +- 12 files changed, 307 insertions(+), 40 deletions(-) create mode 100644 WaveSabrePlayerLib/include/WaveSabrePlayerLib/SDL2RenderThread.h create mode 100644 WaveSabrePlayerLib/src/SDL2RenderThread.cpp diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 1270b0b9..eb4f7689 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -79,8 +79,12 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-g -Og") - # stuff to make the code smaller - #set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + if(CMAKE_BUILD_TYPE EQUAL Debug) + target_compile_options(WaveSabreCore PUBLIC -g -Og) + else() + #set_property(TARGET WaveSabreCore PROPERTY INTERPROCEDURAL_OPTIMIZATION ON) + target_compile_options(WaveSabreCore + PUBLIC -O2 -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics + PRIVATE -ffast-math -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections) + endif() endif() diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index eb435634..3d010b89 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -34,6 +34,26 @@ else() target_sources(WaveSabrePlayerLib PRIVATE include/WaveSabrePlayerLib/AplayRenderThread.h src/AplayRenderThread.cpp) + else() + message(STATUS "aplay(1) backend disabled") + endif() + + if(ENABLE_SDL2) + message(STATUS "Enabling SDL2 backend") + + find_package(SDL2) + if(SDL2_FOUND) + message(STATUS "Found SDL2, enabling SDL2 backend") + target_link_libraries(WaveSabrePlayerLib PUBLIC SDL2::SDL2 SDL2::SDL2main) + target_compile_definitions(WaveSabrePlayerLib PUBLIC HAVE_SDL2=1) + target_sources(WaveSabrePlayerLib PRIVATE + include/WaveSabrePlayerLib/SDL2RenderThread.h + src/SDL2RenderThread.cpp) + else() + message(WARNING "SDL2 not found, disabling SDL2 backend") + endif() + else() + message(STATUS "SDL2 backend disabled") endif() if(USE_PTHREADS) @@ -47,7 +67,7 @@ else() target_link_libraries(WaveSabrePlayerLib PRIVATE Threads::Threads) target_compile_definitions(WaveSabrePlayerLib PRIVATE HAVE_PTHREAD=1) else() - message(STATUS "pthreads not found, using synchronous fallback.") + message(WARNING "pthreads not found, using synchronous fallback.") endif() else() message(STATUS "pthreads disabled explicitely.") @@ -65,8 +85,18 @@ if(MSVC) else() # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-g -Og") - # stuff to make the code smaller - #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + if(CMAKE_BUILD_TYPE EQUAL Debug) + set(CMAKE_CXX_FLAGS "-g -Og") + else() + set_property(TARGET PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO + set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + endif() + if(CMAKE_BUILD_TYPE EQUAL Debug) + target_compile_options(WaveSabrePlayerLib PUBLIC -g -Og) + else() + #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON) + target_compile_options(WaveSabrePlayerLib + PUBLIC -O2 -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics + PRIVATE -ffast-math -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections) + endif() endif() diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h index 1f177db0..52f7f3e6 100644 --- a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/AplayRenderThread.h @@ -10,7 +10,7 @@ #include #include -#if HAVE_PTHREADS +#if HAVE_PTHREAD #include #endif @@ -32,7 +32,9 @@ namespace WaveSabrePlayerLib private: static void AplayProc(int readend, int rate); +#if HAVE_PTHREAD static void* AplayWriterProc(void* ud); +#endif void GetBufferTick(bool block = false); diff --git a/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SDL2RenderThread.h b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SDL2RenderThread.h new file mode 100644 index 00000000..2ef69bf1 --- /dev/null +++ b/WaveSabrePlayerLib/include/WaveSabrePlayerLib/SDL2RenderThread.h @@ -0,0 +1,62 @@ +#ifndef __WAVESABREPLAYERLIB_SDL2RENDERTHREAD_H__ +#define __WAVESABREPLAYERLIB_SDL2RENDERTHREAD_H__ + +#include "SongRenderer.h" +#include "CriticalSection.h" +#include "IRenderThread.h" + +#include +#include + +#include + +#if HAVE_PTHREAD +#include +#include +#endif + +namespace WaveSabrePlayerLib +{ + class SDL2RenderThread : public IRenderThread + { + public: + SDL2RenderThread(RenderCallback callback, void *callbackData, + int sampleRate, int bufferSizeMs = 1000, bool pthread = false); + virtual ~SDL2RenderThread(); + + virtual int GetPlayPositionMs(); + + virtual void DoForegroundWork(); + + private: +#if HAVE_PTHREAD + static void* SDL2WriterProc(void* ud); +#endif + + void SDL2Callback2(uint8_t* buf, int len); + static void SDL2Callback(void* userdata, uint8_t* buf, int len); + + RenderCallback callback; + void *callbackData; + int sampleRate; + int bufferSizeMs; + int bufferSizeBytes; + int bytesWritten; + SongRenderer::Sample *sampleBuffer; +#if HAVE_PTHREAD + SongRenderer::Sample *backupBuffer; +#endif + const SongRenderer::Sample *bufferToWrite; + int bufferBytesLeft; + + SDL_AudioSpec spec; + SDL_AudioDeviceID dev; +#if HAVE_PTHREAD + sem_t needbuf_sem; + pthread_t writer; + bool writerStarted; +#endif + }; +} + +#endif diff --git a/WaveSabrePlayerLib/src/AplayRenderThread.cpp b/WaveSabrePlayerLib/src/AplayRenderThread.cpp index 3c99d71e..ee9e6b28 100644 --- a/WaveSabrePlayerLib/src/AplayRenderThread.cpp +++ b/WaveSabrePlayerLib/src/AplayRenderThread.cpp @@ -18,7 +18,7 @@ #include #endif -static unsigned is_le(void) +inline static unsigned is_le(void) { const union { unsigned u; unsigned char c[4]; } one = { 1 }; return one.c[0]; @@ -80,7 +80,6 @@ namespace WaveSabrePlayerLib #if HAVE_PTHREAD if (pthread) { - //printf("create pthread!\n"); rv = pthread_create(&writer, NULL, AplayWriterProc, this); assert(rv == 0 && "Couldn't create background writer thread"); @@ -98,13 +97,7 @@ namespace WaveSabrePlayerLib #if HAVE_PTHREAD if (writerStarted) { pthread_cancel(writer); - if (pthread_timedjoin_np(writer, NULL, &ts) != 0) { - pthread_kill(writer, SIGTERM); - if (pthread_timedjoin_np(writer, NULL, &ts) != 0) { - pthread_kill(writer, SIGKILL); - pthread_join(writer, NULL); - } - } + pthread_join(writer, NULL); writerStarted = false; } #endif @@ -127,11 +120,12 @@ namespace WaveSabrePlayerLib this->aupipe = -1; this->aplay = -1; } + + free(sampleBuffer); } void AplayRenderThread::DoForegroundWork() { - //printf("\nfgw\n"); #if HAVE_PTHREAD if (!writerStarted) #else @@ -241,7 +235,6 @@ namespace WaveSabrePlayerLib #if HAVE_PTHREAD void* AplayRenderThread::AplayWriterProc(void* ud) { - //printf("writer proc!\n"); auto self = (AplayRenderThread*)ud; while (self->aupipe >= 0) { diff --git a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp index 0a73189d..6b5fbf19 100644 --- a/WaveSabrePlayerLib/src/PreRenderPlayer.cpp +++ b/WaveSabrePlayerLib/src/PreRenderPlayer.cpp @@ -2,6 +2,8 @@ #if defined(WIN32) || defined(_WIN32) #include +#elif HAVE_SDL2 +#include #elif HAVE_APLAY #include #endif @@ -71,6 +73,8 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs); +#elif HAVE_SDL2 + renderThread = new SDL2RenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs, true); #elif HAVE_APLAY renderThread = new AplayRenderThread(renderCallback, this, sampleRate, playbackBufferSizeMs, true); #endif diff --git a/WaveSabrePlayerLib/src/RealtimePlayer.cpp b/WaveSabrePlayerLib/src/RealtimePlayer.cpp index a0e3b6bf..92d6d577 100644 --- a/WaveSabrePlayerLib/src/RealtimePlayer.cpp +++ b/WaveSabrePlayerLib/src/RealtimePlayer.cpp @@ -2,6 +2,8 @@ #if defined(WIN32) || defined(_WIN32) #include +#elif HAVE_SDL2 +#include #elif HAVE_APLAY #include #endif @@ -41,6 +43,8 @@ namespace WaveSabrePlayerLib songRenderer = new SongRenderer(song, numRenderThreads); #if defined(WIN32) || defined(_WIN32) renderThread = new DirectSoundRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs); +#elif HAVE_SDL2 + renderThread = new SDL2RenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs, true); #elif HAVE_APLAY renderThread = new AplayRenderThread(renderCallback, this, songRenderer->GetSampleRate(), bufferSizeMs, true); #endif diff --git a/WaveSabrePlayerLib/src/SDL2RenderThread.cpp b/WaveSabrePlayerLib/src/SDL2RenderThread.cpp new file mode 100644 index 00000000..07a2a68c --- /dev/null +++ b/WaveSabrePlayerLib/src/SDL2RenderThread.cpp @@ -0,0 +1,155 @@ +#include + +#include +#include +#include +#include + +namespace WaveSabrePlayerLib +{ + SDL2RenderThread::SDL2RenderThread(RenderCallback callback, + void *callbackData, int sampleRate, int bufferSizeMs, bool pthread) + : callback(callback) + , callbackData(callbackData) + , sampleRate(sampleRate) + , bufferSizeMs(bufferSizeMs) + , bytesWritten(0) + , bufferBytesLeft(0) +#if HAVE_PTHREAD + , writerStarted(false) +#endif + { +#if !defined(HAVE_PTHREAD) || !HAVE_PTHREAD + pthread = false; +#endif + + bufferSizeBytes = sampleRate * SongRenderer::BlockAlign * bufferSizeMs / 1000; + sampleBuffer = (SongRenderer::Sample*)malloc(bufferSizeBytes); +#if HAVE_PTHREAD + backupBuffer = (SongRenderer::Sample*)malloc(bufferSizeBytes); +#endif + + int rv = SDL_Init(SDL_INIT_AUDIO); + assert(rv >= 0 && "Can't init SDL2 audio"); + + spec.freq = sampleRate; + spec.format = AUDIO_S16SYS; + spec.channels = 2; + spec.samples = bufferSizeBytes / (sizeof(SongRenderer::Sample) * spec.channels); + spec.callback = SDL2Callback; + spec.userdata = this; + + dev = SDL_OpenAudioDevice(NULL, 0, &spec, NULL, 0); +#ifndef NDEBUG + if (dev <= 0) { + printf("SDL2 error: %s\n", SDL_GetError()); + } +#endif + assert(rv > 0 && "Can't open SDL2 audio"); + + // pre-buffer stuff a bit, SDL2 tends to be finnicky + callback(sampleBuffer, bufferSizeBytes/sizeof(SongRenderer::Sample), callbackData); + bufferBytesLeft = bufferSizeBytes; + bufferToWrite = sampleBuffer; + +#if HAVE_PTHREAD + if (pthread) { + sem_init(&needbuf_sem, 0, 0); + + rv = pthread_create(&writer, NULL, SDL2WriterProc, this); + assert(rv == 0 && "Couldn't create background writer thread"); + + sem_post(&needbuf_sem); // start filling in the backup buffer.. + + writerStarted = true; + } +#endif + + SDL_PauseAudioDevice(dev, 0); + } + + SDL2RenderThread::~SDL2RenderThread() + { + SDL_CloseAudioDevice(dev); + +#if HAVE_PTHREAD + if (writerStarted) { + pthread_cancel(writer); + pthread_join(writer, NULL); + sem_destroy(&needbuf_sem); + writerStarted = false; + } +#endif + + free(sampleBuffer); +#if HAVE_PTHREAD + free(backupBuffer); +#endif + } + + void SDL2RenderThread::DoForegroundWork() + { + // nop. + } + + int SDL2RenderThread::GetPlayPositionMs() + { + return (int)(bytesWritten / SongRenderer::BlockAlign * 1000 / sampleRate); + } + +#if HAVE_PTHREAD + void* SDL2RenderThread::SDL2WriterProc(void* ud) + { + auto self = (SDL2RenderThread*)ud; + + while (true) { + sem_wait(&self->needbuf_sem); + + self->callback(self->backupBuffer, + self->bufferSizeBytes/sizeof(SongRenderer::Sample), + self->callbackData); + } + + self->writerStarted = false; + return NULL; + } +#endif + + void SDL2RenderThread::SDL2Callback2(uint8_t* buf, int len) + { + if (bufferBytesLeft <= 0) { +#if HAVE_PTHREAD + if (writerStarted) { + // TODO: pingpong backupBuffer<->sampleBuffer? + // (needs an atomic exchange) + SDL_memcpy(sampleBuffer, backupBuffer, bufferSizeBytes); + sem_post(&needbuf_sem); + } else { +#else + callback(sampleBuffer, bufferSizeBytes/sizeof(SongRenderer::Sample), callbackData); +#endif + } + + bufferToWrite = sampleBuffer; + bufferBytesLeft = bufferSizeBytes; + } + + if (len == 0) + return; + + if (len > bufferBytesLeft) + len = bufferBytesLeft; + + SDL_memcpy(buf, bufferToWrite, len); + + bufferToWrite += len / sizeof(SongRenderer::Sample); + bufferBytesLeft -= len; + bytesWritten += len; + } + + void SDL2RenderThread::SDL2Callback(void* userdata, uint8_t* buf, int len) + { + auto self = (SDL2RenderThread*)userdata; + self->SDL2Callback2(buf, len); + } +} diff --git a/WaveSabrePlayerLib/src/SongRenderer.cpp b/WaveSabrePlayerLib/src/SongRenderer.cpp index 53758570..e98952de 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.cpp @@ -3,6 +3,8 @@ #include #include +#include + #if HAVE_PTHREAD #include #endif @@ -133,20 +135,10 @@ namespace WaveSabrePlayerLib to.tv_sec = 0; // .1sec milli micro to.tv_nsec = 100 * 1000 * 1000; - bool badAddThrds[numRenderThreads - 1] = { false }; for (int i = 0; i < numRenderThreads - 1; i++) { if (pthread_timedjoin_np(additionalRenderThreads[i], NULL, &to)) { - badAddThrds[i] = true; - } - } - - for (int i = 0; i < numRenderThreads - 1; i++) { - if (badAddThrds[i]) { - pthread_kill(additionalRenderThreads[i], SIGTERM); - if (pthread_timedjoin_np(additionalRenderThreads[i], NULL, &to)) { - pthread_kill(additionalRenderThreads[i], SIGKILL); - pthread_join(additionalRenderThreads[i], NULL); - } + pthread_cancel(additionalRenderThreads[i]); + pthread_join(additionalRenderThreads[i], NULL); } } } diff --git a/WaveSabrePlayerLib/src/WavWriter.cpp b/WaveSabrePlayerLib/src/WavWriter.cpp index 4b96eee1..def6f5ea 100644 --- a/WaveSabrePlayerLib/src/WavWriter.cpp +++ b/WaveSabrePlayerLib/src/WavWriter.cpp @@ -56,7 +56,6 @@ namespace WaveSabrePlayerLib SongRenderer::Sample buf[stepSize]; for (int i = 0; i < numSamples; i += stepSize) { - //asm volatile("int3\n"); songRenderer->RenderSamples(buf, stepSize); for (int j = 0; j < stepSize; j++) writeShort(buf[j], file); diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 2ed55929..894f7520 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -1,5 +1,5 @@ add_executable(WaveSabreStandAlonePlayer main.cpp) -target_link_libraries(WaveSabreStandAlonePlayer WaveSabrePlayerLib) +target_link_libraries(WaveSabreStandAlonePlayer PUBLIC WaveSabrePlayerLib) if(MSVC) target_compile_definitions(WaveSabreStandAlonePlayer PRIVATE _CRT_SECURE_NO_WARNINGS) @@ -13,10 +13,29 @@ if(MSVC) $<$:_NO_CRT_STDIO_INLINE>) endif() else() + #if(ENABLE_SDL2) + # message(STATUS "Enabling SDL2 backend") + # + # find_package(SDL2) + # if(SDL2_FOUND) + # message(STATUS "Found SDL2, enabling SDL2 backend") + # target_link_libraries(WaveSabreStandAlonePlayer PUBLIC SDL2::SDL2 SDL2::SDL2main) + # target_compile_definitions(WaveSabreStandAlonePlayer PUBLIC HAVE_SDL2=1) + # else() + # message(WARNING "SDL2 not found, disabling SDL2 backend") + # endif() + #else() + # message(STATUS "SDL2 backend disabled") + #endif() + # assuming GCC or clang for now - set(CMAKE_CXX_FLAGS "-g -Og") - # stuff to make the code smaller - #set_property(TARGET WaveSabrePlayerLib PROPERTY INTERPROCEDURAL_OPTIMIZATION True) # enable LTO - #set(CMAKE_CXX_FLAGS "-g -O2 -ffast-math -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections") + if(CMAKE_BUILD_TYPE EQUAL Debug) + target_compile_options(WaveSabreStandAlonePlayer PUBLIC -g -Og) + else() + #set_property(TARGET WaveSabreStandAlonePlayer PROPERTY INTERPROCEDURAL_OPTIMIZATION ON) + target_compile_options(WaveSabreStandAlonePlayer + PUBLIC -O2 -fno-exceptions -fno-rtti -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer -fno-threadsafe-statics + PRIVATE -ffast-math -march=nocona -ffunction-sections -fdata-sections -Wl,--gc-sections) + endif() endif() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index cf525117..8bfdbf0f 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -158,7 +158,11 @@ static void parse_args(struct player_args* args, int argc, char** argv) } } +/*#if HAVE_SDL2 +int SDLmain(int argc, char **argv) +#else*/ int main(int argc, char **argv) +//#endif { /*double minerr = 1.0/0, maxerr = -1.0/0; double stddev = 0, avg = 0; @@ -244,7 +248,6 @@ int main(int argc, char **argv) printf("WAV writer activated.\n"); printf("Rendering...\n"); - //asm volatile("int3\n"); wavWriter.Write(args.outfile, progressCallback, nullptr); printf("\n\nWAV file written to \"%s\". Enjoy.\n", args.outfile); From cd1a918a5e3ff377c62ad75626b68b664cbe4996 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 04:47:42 +0200 Subject: [PATCH 18/32] synchronous/non-threaded option --- CMakeLists.txt | 2 +- WaveSabrePlayerLib/CMakeLists.txt | 2 +- WaveSabrePlayerLib/src/SDL2RenderThread.cpp | 5 ++-- WaveSabrePlayerLib/src/SongRenderer.cpp | 27 ++++++++++++----- WaveSabreStandAlonePlayer/CMakeLists.txt | 32 +++++++++++---------- WaveSabreStandAlonePlayer/main.cpp | 8 ++++++ 6 files changed, 50 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 98d2155b..1eaa9994 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.11) set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(NOT WIN32) - option(USE_PTHREADS "Use Pthreads for threading" OFF) + option(ENABLE_PTHREADS "Use Pthreads for threading" OFF) option(ENABLE_APLAY "Enable aplay(1) backend for audio playback" ON) option(ENABLE_SDL2 "Enable SDL2_Audio backend for audio playback" ON) endif() diff --git a/WaveSabrePlayerLib/CMakeLists.txt b/WaveSabrePlayerLib/CMakeLists.txt index 3d010b89..f14a6eb9 100644 --- a/WaveSabrePlayerLib/CMakeLists.txt +++ b/WaveSabrePlayerLib/CMakeLists.txt @@ -56,7 +56,7 @@ else() message(STATUS "SDL2 backend disabled") endif() - if(USE_PTHREADS) + if(ENABLE_PTHREADS) set_property(TARGET WaveSabrePlayerLib PROPERTY CXX_STANDARD 11) # need a recent one for stdatomic.h message(STATUS "Trying to look for pthreads...") diff --git a/WaveSabrePlayerLib/src/SDL2RenderThread.cpp b/WaveSabrePlayerLib/src/SDL2RenderThread.cpp index 07a2a68c..08e63782 100644 --- a/WaveSabrePlayerLib/src/SDL2RenderThread.cpp +++ b/WaveSabrePlayerLib/src/SDL2RenderThread.cpp @@ -124,11 +124,12 @@ namespace WaveSabrePlayerLib // (needs an atomic exchange) SDL_memcpy(sampleBuffer, backupBuffer, bufferSizeBytes); sem_post(&needbuf_sem); - } else { + } else #else + { callback(sampleBuffer, bufferSizeBytes/sizeof(SongRenderer::Sample), callbackData); -#endif } +#endif bufferToWrite = sampleBuffer; bufferBytesLeft = bufferSizeBytes; diff --git a/WaveSabrePlayerLib/src/SongRenderer.cpp b/WaveSabrePlayerLib/src/SongRenderer.cpp index e98952de..9b34a6b9 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.cpp @@ -125,6 +125,8 @@ namespace WaveSabrePlayerLib WaitForMultipleObjects(numRenderThreads - 1, additionalRenderThreads, TRUE, INFINITE); for (int i = 0; i < numRenderThreads - 1; i++) CloseHandle(additionalRenderThreads[i]); + + delete [] additionalRenderThreads; #elif HAVE_PTHREAD for (int i = 0; i < numRenderThreads; i++) sem_post(&renderThreadStartEvents[i]); @@ -142,8 +144,9 @@ namespace WaveSabrePlayerLib } } } -#endif + delete [] additionalRenderThreads; +#endif } for (int i = 0; i < numDevices; i++) delete devices[i]; @@ -159,13 +162,16 @@ namespace WaveSabrePlayerLib #if defined(WIN32) || defined(_WIN32) for (int i = 0; i < numRenderThreads; i++) CloseHandle(renderThreadStartEvents[i]); + delete [] renderThreadStartEvents; + CloseHandle(renderDoneEvent); #elif HAVE_PTHREAD for (int i = 0; i < numRenderThreads; i++) sem_destroy(&renderThreadStartEvents[i]); + delete [] renderThreadStartEvents; + sem_destroy(&renderDoneEvent); #endif - delete [] renderThreadStartEvents; } void SongRenderer::RenderSamples(Sample *buffer, int numSamples) @@ -178,11 +184,12 @@ namespace WaveSabrePlayerLib renderThreadNumFloatSamples = numSamples / 2; // Dispatch work - renderThreadsRunning = numRenderThreads; #if defined(WIN32) || defined(_WIN32) + renderThreadsRunning = numRenderThreads; for (int i = 0; i < numRenderThreads; i++) SetEvent(renderThreadStartEvents[i]); #elif HAVE_PTHREAD + renderThreadsRunning = numRenderThreads; for (int i = 0; i < numRenderThreads; i++) sem_post(&renderThreadStartEvents[i]); #endif @@ -207,11 +214,12 @@ namespace WaveSabrePlayerLib } } -#if defined(WIN32) || defined(_WIN32) +#if defined(WIN32) || defined(_WIN32) || HAVE_PTHREAD + #if defined(WIN32) || defined(_WIN32) DWORD WINAPI SongRenderer::renderThreadProc(LPVOID lpParameter) -#elif HAVE_PTHREAD + #elif HAVE_PTHREAD void* SongRenderer::renderThreadProc(void* lpParameter) -#endif + #endif { auto renderThreadData = (RenderThreadData *)lpParameter; @@ -225,6 +233,7 @@ namespace WaveSabrePlayerLib return 0; } +#endif bool SongRenderer::renderThreadWork(int renderThreadIndex) { @@ -277,9 +286,13 @@ namespace WaveSabrePlayerLib &xv, (int)TrackRenderState::Rendering) ) #else -#error "AAAAAAAAAAAAAAAA" + if (trackRenderStates[i] == TrackRenderState::Idle) #endif { +#if !(defined(WIN32) || defined(_WIN32)) && !(defined(HAVE_PTHREAD) && HAVE_PTHREAD) + trackRenderStates[i] = TrackRenderState::Rendering; +#endif + // We marked it successfully, so now we'll do the work tracks[i]->Run(renderThreadNumFloatSamples); // And mark it as finished :) diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 894f7520..28b47a61 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -13,23 +13,25 @@ if(MSVC) $<$:_NO_CRT_STDIO_INLINE>) endif() else() - #if(ENABLE_SDL2) - # message(STATUS "Enabling SDL2 backend") - # - # find_package(SDL2) - # if(SDL2_FOUND) - # message(STATUS "Found SDL2, enabling SDL2 backend") - # target_link_libraries(WaveSabreStandAlonePlayer PUBLIC SDL2::SDL2 SDL2::SDL2main) - # target_compile_definitions(WaveSabreStandAlonePlayer PUBLIC HAVE_SDL2=1) - # else() - # message(WARNING "SDL2 not found, disabling SDL2 backend") - # endif() - #else() - # message(STATUS "SDL2 backend disabled") - #endif() - # assuming GCC or clang for now + if(ENABLE_PTHREADS) + set_property(TARGET WaveSabrePlayerLib PROPERTY CXX_STANDARD 11) # need a recent one for stdatomic.h + + message(STATUS "Trying to look for pthreads...") + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + message(STATUS "Found pthreads, enabling pthreads for background rendering threads.") + #target_link_libraries(WaveSabrePlayerLib PRIVATE Threads::Threads) # we don't actually need it here + target_compile_definitions(WaveSabrePlayerLib PRIVATE HAVE_PTHREAD=1) + else() + message(WARNING "pthreads not found, using synchronous fallback.") + endif() + else() + message(STATUS "pthreads disabled explicitely.") + endif() + if(CMAKE_BUILD_TYPE EQUAL Debug) target_compile_options(WaveSabreStandAlonePlayer PUBLIC -g -Og) else() diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 8bfdbf0f..baf23c45 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -9,6 +9,7 @@ using namespace WaveSabrePlayerLib; #if !defined(WIN32) && !defined(_WIN32) #include +#include #endif WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) @@ -295,7 +296,14 @@ int main(int argc, char **argv) Sleep(10); #else fflush(stdout); + #if HAVE_PTHREAD sleep( 1); + #else + struct timespec to; + to.tv_sec = 0; + to.tv_nsec = 50*1000*1000; // 50 ms + nanosleep(&to, NULL); + #endif #endif } printf("\n"); From b39a67a6c54746b0d7f99d5eeb5401b0267a83f8 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Tue, 1 Sep 2020 04:50:55 +0200 Subject: [PATCH 19/32] fix windows cmake once more --- WaveSabreStandAlonePlayer/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WaveSabreStandAlonePlayer/CMakeLists.txt b/WaveSabreStandAlonePlayer/CMakeLists.txt index 28b47a61..a59e4df7 100644 --- a/WaveSabreStandAlonePlayer/CMakeLists.txt +++ b/WaveSabreStandAlonePlayer/CMakeLists.txt @@ -4,7 +4,7 @@ target_link_libraries(WaveSabreStandAlonePlayer PUBLIC WaveSabrePlayerLib) if(MSVC) target_compile_definitions(WaveSabreStandAlonePlayer PRIVATE _CRT_SECURE_NO_WARNINGS) - target_link_libraries(WaveSabreStandAlonePlayer $<$:msvcrt>) + target_link_libraries(WaveSabreStandAlonePlayer PUBLIC $<$:msvcrt>) set_property(TARGET WaveSabreStandAlonePlayer APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /NODEFAULTLIB /SAFESEH:NO /MANIFEST:NO /LTCG /OPT:REF /OPT:ICF /DYNAMICBASE:NO") From a8061eedab43b749cd7642d90be05de308e3b7f9 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Wed, 2 Sep 2020 01:48:32 +0200 Subject: [PATCH 20/32] fix assembly maths functions --- WaveSabreCore/src/Helpers.cpp | 201 +++++++++++++++-------------- WaveSabreStandAlonePlayer/main.cpp | 36 +----- 2 files changed, 103 insertions(+), 134 deletions(-) diff --git a/WaveSabreCore/src/Helpers.cpp b/WaveSabreCore/src/Helpers.cpp index d82ce7d4..80009aa6 100644 --- a/WaveSabreCore/src/Helpers.cpp +++ b/WaveSabreCore/src/Helpers.cpp @@ -4,29 +4,19 @@ #define _USE_MATH_DEFINES #include -// TODO: make assembly equivalent for x64 (use intrinsic ?) -#if defined(_MSC_VER) && defined(_M_IX86) - #define ASM_MATH_AVAILABLE (1) - #define ASMNAKED __declspec(naked) - #define ASMVECTORCALL __vectorcall -#elif defined(__GNUC__) +// TODO: make assembly equivalent for Windows x64 (use intrinsic ?) +// ^--- you probably only need to change esp to rsp? -poro + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__GNUC__) #define ASM_MATH_AVAILABLE (1) - //#if defined(__x86_64) || defined(__i386__) - // #define ASMNAKED inline __attribute__((__always_inline__/*__no_inline__,__naked__*/)) - //#else - #define ASMNAKED inline - //#endif - #define ASMVECTORCALL #else /* nor MSVC nor GCC/clang */ #define ASM_MATH_AVAILABLE (0) #endif -// XXX: this pow() implementation seems to suck for y > 4, regardless of float/double - #if ASM_MATH_AVAILABLE == 1 -static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) -{ #if defined(_MSC_VER) && defined(_M_IX86) +static __declspec(naked) double __vectorcall fpuPow(double x, double y) +{ __asm { sub esp, 8 @@ -71,10 +61,22 @@ static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) ret } +} #elif defined(__GNUC__) - //#if defined(__x86_64__) || defined(__i386__) - /*asm volatile("push %%rax\n" // subq $8, %rsp - "xorpd %%xmm2, %%xmm2\n" + #if defined(__x86_64__) || defined(__i386__) +__attribute__((__naked__,__noinline__)) static double fpuPow(double x, double y) +{ + // i386 Linux ABI: pass thru the stack, return in st(0) + // x86_64 SysV ABI: pass/return thru xmm0/1 + asm volatile( +#ifdef __x86_64__ + "subq $8, %%rsp\n" +#else + "movsd 4(%%esp), %%xmm0\n" + "movsd 12(%%esp), %%xmm1\n" + "subl $8, %%esp\n" +#endif + "xorpd %%xmm2, %%xmm2\n" "comisd %%xmm2, %%xmm1\n" "jne 1f\n" @@ -89,15 +91,22 @@ static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) "jmp 3f\n" "2:\n" +#ifdef __x86_64__ "movsd %%xmm1, (%%rsp)\n" "fldl (%%rsp)\n" "movsd %%xmm0, (%%rsp)\n" "fldl (%%rsp)\n" +#else + "movsd %%xmm1, (%%esp)\n" + "fldl (%%esp)\n" + "movsd %%xmm0, (%%esp)\n" + "fldl (%%esp)\n" +#endif "fyl2x\n" "fld %%st(0)\n" "frndint\n" - "fsubr %%st(0), %%st(1)\n" + "fsub %%st(0), %%st(1)\n" "fxch %%st(1)\n" "fchs\n" "f2xm1\n" @@ -107,51 +116,35 @@ static ASMNAKED double ASMVECTORCALL fpuPow(double x, double y) "fstp %%st(1)\n" "3:\n" +#ifdef __x86_64__ "fstpl (%%rsp)\n" "movsd (%%rsp), %%xmm0\n" - "pop %%rax\n" // addq $8, %%rsp + "addq $8, %%rsp\n" +#else + "addl $8, %%esp\n" +#endif "ret\n" :// no output :// no input :"xmm2" // clobbered - );*/ - // not writing the *entire* function body in assembly actually helps - // gcc and clang with inlining and LTO - /*if (y == 0.0) return 1.0; - if (x == 0.0) return 0.0; - - if (y >= 1.9999 && y <= 2.0001) return x*x; - if (y >= 3.9999 && y <= 4.0001) { - x = x*x; - return x*x; - } - - //return pow(x,y); - asm volatile("fyl2x\n" - "fld %%st(0)\n" - "frndint\n" - "fsubr %%st(0), %%st(1)\n" - "fxch %%st(1)\n" - "fchs\n" - "f2xm1\n" - "fld1\n" - "faddp %%st(0), %%st(1)\n" - "fscale\n" - "fstp %%st(1)\n" - :"+t"(x) // output regs ('+': inout) - :"u"(y) // input regs - : // destroyed regs - ); - return x;*/ - //#else /* not x86 */ - return pow(x, y); // __builtin_pow only accepts an integer exponent :/ - //#endif /* GNUC, platform */ -#endif /* MSVC/GNUC */ + ); } + #else + // __builtin_pow only supports integer exponents... so if the exponent + // is an integer, use __builtin_pow, using some preprocessor magic + #define fpuPow(x, y) \ + ((__builtin_constant_p(y) && ((y) == (int)(y))) \ + ? __builtin_pow(x, y) \ + : pow(x, y)) \ + + #endif +#else +#error "Unsupported compiler." +#endif /* compiler */ -static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) -{ #if defined(_MSC_VER) && defined(_M_IX86) +static __declspec(naked) float __vectorcall fpuPowF(float x, float y) +{ __asm { sub esp, 8 @@ -196,10 +189,22 @@ static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) ret } +} #elif defined(__GNUC__) - //#if defined (__x86_64__) || defined(__i386__) - /*asm volatile("push %%rax\n" // subq $8, %rsp - "xorps %%xmm2, %%xmm2\n" + #if defined(__x86_64__) || defined(__i386__) +__attribute__((__naked__,__noinline__)) static float fpuPowF(float x, float y) +{ + // i386 Linux ABI: pass thru the stack, return in st(0) + // x86_64 SysV ABI: pass/return thru xmm0/1 + asm volatile( +#ifdef __x86_64__ + "subq $8, %%rsp\n" +#else + "movss 4(%%esp), %%xmm0\n" + "movss 8(%%esp), %%xmm1\n" + "subl $8, %%esp\n" +#endif + "xorps %%xmm2, %%xmm2\n" "comiss %%xmm2, %%xmm1\n" "jne 1f\n" @@ -214,15 +219,22 @@ static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) "jmp 3f\n" "2:\n" +#ifdef __x86_64__ "movss %%xmm1, (%%rsp)\n" "flds (%%rsp)\n" "movss %%xmm0, (%%rsp)\n" "flds (%%rsp)\n" +#else + "movss %%xmm1, (%%esp)\n" + "flds (%%esp)\n" + "movss %%xmm0, (%%esp)\n" + "flds (%%esp)\n" +#endif "fyl2x\n" "fld %%st(0)\n" "frndint\n" - "fsubr %%st(0), %%st(1)\n" + "fsub %%st(0), %%st(1)\n" "fxch %%st(1)\n" "fchs\n" "f2xm1\n" @@ -232,51 +244,35 @@ static ASMNAKED float ASMVECTORCALL fpuPowF(float x, float y) "fstp %%st(1)\n" "3:\n" +#ifdef __x86_64__ "fstps (%%rsp)\n" "movss (%%rsp), %%xmm0\n" - "pop %%rax\n" // addq $8, %%rsp + "addq $8, %%rsp\n" +#else + "addl $8, %%esp\n" +#endif "ret\n" :// no output :// no input :"xmm2" // clobbered - );*/ - // not writing the *entire* function body in assembly actually helps - // gcc and clang with inlining and LTO - /*if (y == 0.0f) return 1.0f; - if (x == 0.0f) return 0.0f; - - if (y >= 1.9999f && y <= 2.0001f) return x*x; - if (y >= 3.9999f && y <= 4.0001f) { - x = x*x; - return x*x; - } - - //return powf(x,y); - asm volatile("fyl2x\n" - "fld %%st(0)\n" - "frndint\n" - "fsubr %%st(0), %%st(1)\n" - "fxch %%st(1)\n" - "fchs\n" - "f2xm1\n" - "fld1\n" - "faddp %%st(0), %%st(1)\n" - "fscale\n" - "fstp %%st(1)\n" - :"+t"(x) // output regs ('+': inout) - :"u"(y) // input regs - : // destroyed regs - ); - return x;*/ - //#else /* not x86_64 */ - return powf(x, y); // __builtin_pow only accepts an integer exponent :/ - //#endif /* GNUC, platform */ -#endif /* MSVC/GNUC */ + ); } + #else + // __builtin_powf only supports integer exponents... so if the exponent + // is an integer, use __builtin_powf, using some preprocessor magic + #define fpuPowF(x, y) \ + ((__builtin_constant_p(y) && ((y) == (int)(y))) \ + ? __builtin_powf(x, y) \ + : powf(x, y)) \ + + #endif +#else +#error "Unsupported compiler." +#endif /* compiler */ -static ASMNAKED double ASMVECTORCALL fpuCos(double x) -{ #if defined(_MSC_VER) && defined(_M_IX86) +static __declspec(naked) double __vectorcall fpuCos(double x) +{ __asm { sub esp, 8 @@ -291,17 +287,24 @@ static ASMNAKED double ASMVECTORCALL fpuCos(double x) ret } +} #elif defined(__GNUC__) #if defined(__x86_64__) || defined(__i386__) +__attribute__((__always_inline__)) inline static double fpuCos(double x) +{ // not writing the *entire* function body in assembly actually helps // gcc and clang with inlining and LTO + // ... except trying this with fpuPow/F somehow got botched, so those I + // wrote as pure assembly asm volatile("fcos\n":"+t"(x)::); return x; +} #else /* x86_64 */ - return __builtin_cos(x); + #define fpuCos(x) __builtin_cos(x) #endif /* GNUC, platform */ -#endif /* MSVC/GNUC */ -} +#else +#error "Unsupported compiler." +#endif /* compiler */ #endif // ASM_MATH_AVAILABLE == 1 namespace WaveSabreCore diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index baf23c45..1078c818 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -4,8 +4,8 @@ using namespace WaveSabrePlayerLib; #include +#include #include -#include #if !defined(WIN32) && !defined(_WIN32) #include @@ -165,40 +165,6 @@ int SDLmain(int argc, char **argv) int main(int argc, char **argv) //#endif { - /*double minerr = 1.0/0, maxerr = -1.0/0; - double stddev = 0, avg = 0; - - const int N = 1000; - const double scaler = 0.001; - - for (int y = 1; y <= N; ++y) - //int y = 1.0/12; - for (int x = 1; x <= N; ++x) { - double xd = x * scaler, - yd = y * scaler; - - double pf = WaveSabreCore::Helpers::Pow(xd, yd), - pm = pow(xd, yd); // from libm - - double err = abs(pf - pm); - //err = err / pm; // fuck it, relative error - - if (err < minerr) minerr = err; - if (err > maxerr) maxerr = err; - - avg += err; - stddev += err*err; - printf("%f\n",err); - } - - avg /= N*N; - stddev /= N*N - 1; - stddev = sqrt(stddev); - - printf("#min=%f avg=%f max=%f stddev=%f\n", minerr, avg, maxerr, stddev); - - return 0;*/ - struct player_args args; parse_args(&args, argc, argv); From 27d5277767b4f5b9bbe9535a3077e72930acd040 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 03:41:09 +0200 Subject: [PATCH 21/32] factor out sample loading logic from Thunder and Specimen let's see if I messed up the Windows build --- CMakeLists.txt | 6 +- WaveSabreCore/CMakeLists.txt | 9 ++ WaveSabreCore/include/WaveSabreCore/Devices.h | 2 - .../include/WaveSabreCore/SampleLoader.h | 56 ++++++++ .../include/WaveSabreCore/Specimen.h | 21 --- WaveSabreCore/include/WaveSabreCore/Thunder.h | 21 --- WaveSabreCore/src/SampleLoader.cpp | 114 +++++++++++++++ WaveSabreCore/src/Specimen.cpp | 132 +++--------------- WaveSabreCore/src/Thunder.cpp | 124 ++-------------- WaveSabrePlayerLib/src/SongRenderer.cpp | 1 - WaveSabreStandAlonePlayer/main.cpp | 2 +- 11 files changed, 218 insertions(+), 270 deletions(-) create mode 100644 WaveSabreCore/include/WaveSabreCore/SampleLoader.h create mode 100644 WaveSabreCore/src/SampleLoader.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eaa9994..0c6c31fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,13 @@ cmake_minimum_required(VERSION 3.11) set_property(GLOBAL PROPERTY USE_FOLDERS ON) if(NOT WIN32) + # compo build defaults option(ENABLE_PTHREADS "Use Pthreads for threading" OFF) option(ENABLE_APLAY "Enable aplay(1) backend for audio playback" ON) - option(ENABLE_SDL2 "Enable SDL2_Audio backend for audio playback" ON) + option(ENABLE_SDL2 "Enable SDL2_Audio backend for audio playback" OFF) + option(ENABLE_FFMPEG_GSM "Enable GSM 6.10 sample decoding using FFmpeg. Required for Thunder and Specimen." OFF) + # for standalone playback, or playing back Windows-specific tracks, you + # probably want to enable all of the above options endif() if(MSVC) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index eb4f7689..1873f8be 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -17,6 +17,7 @@ add_library(WaveSabreCore include/WaveSabreCore/Leveller.h include/WaveSabreCore/MxcsrFlagGuard.h include/WaveSabreCore/ResampleBuffer.h + include/WaveSabreCore/SampleLoader.h include/WaveSabreCore/SamplePlayer.h include/WaveSabreCore/Scissor.h include/WaveSabreCore/Slaughter.h @@ -44,6 +45,7 @@ add_library(WaveSabreCore src/Leveller.cpp src/MxcsrFlagGuard.cpp src/ResampleBuffer.cpp + src/SampleLoader.cpp src/SamplePlayer.cpp src/Scissor.cpp src/Slaughter.cpp @@ -79,6 +81,13 @@ if(MSVC) else() # assuming GCC or clang for now + if(ENABLE_FFMPEG_GSM) + message(NOTICE "Enabling FFmpeg GSM 6.10 decoding") + target_compile_definitions(WaveSabrePlayerLib PRIVATE HAVE_FFMPEG_GSM=1) + else() + message(NOTICE "FFmpeg GSM 6.10 decoding disabled") + endif() + if(CMAKE_BUILD_TYPE EQUAL Debug) target_compile_options(WaveSabreCore PUBLIC -g -Og) else() diff --git a/WaveSabreCore/include/WaveSabreCore/Devices.h b/WaveSabreCore/include/WaveSabreCore/Devices.h index 30e829e8..06bc217b 100644 --- a/WaveSabreCore/include/WaveSabreCore/Devices.h +++ b/WaveSabreCore/include/WaveSabreCore/Devices.h @@ -3,11 +3,9 @@ #include "Falcon.h" #include "Slaughter.h" -#if defined(WIN32) || defined(_WIN32) #include "Thunder.h" #include "Adultery.h" #include "Specimen.h" -#endif #include "Scissor.h" #include "Leveller.h" diff --git a/WaveSabreCore/include/WaveSabreCore/SampleLoader.h b/WaveSabreCore/include/WaveSabreCore/SampleLoader.h new file mode 100644 index 00000000..f4b5200b --- /dev/null +++ b/WaveSabreCore/include/WaveSabreCore/SampleLoader.h @@ -0,0 +1,56 @@ +#ifndef __WAVESABRECORE_SAMPLELOADER_H__ +#define __WAVESABRECORE_SAMPLELOADER_H__ + +#if defined(WIN32) || defined(_WIN32) +#include +#include + +#ifdef UNICODE +#define _UNICODE +#endif +#include +#else /* not WIN32 */ +#include + +typedef struct __attribute__((__packed__)) tWAVEFORMATEX { + int16_t wFormatTag; + int16_t nChannels; + int32_t nSamplesPerSec; + int32_t nAvgBytesPerSec; + int16_t nBlockAlign; + int16_t wBitsPerSample; + int16_t cbSize; +} WAVEFORMATEX; +#endif /* WIN32 */ + +namespace WaveSabreCore +{ + class SampleLoader + { + public: + struct LoadedSample { + char *chunkData; + + char *waveFormatData; + int compressedSize, uncompressedSize; + + char *compressedData; + float *sampleData; + + int sampleLength; + }; + + static LoadedSample LoadSampleGSM(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat); + + private: +#if defined(WIN32) || defined(_WIN32) + static BOOL __stdcall driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport); + static BOOL __stdcall formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport); + + static HACMDRIVERID driverId; +#endif + + }; +} + +#endif diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h index 69b561ba..086340be 100644 --- a/WaveSabreCore/include/WaveSabreCore/Specimen.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -6,16 +6,6 @@ #include "StateVariableFilter.h" #include "SamplePlayer.h" -#if defined(WIN32) || defined(_WIN32) -#include -#include - -#ifdef UNICODE -#define _UNICODE -#endif -#include -#endif /* WIN32 */ - namespace WaveSabreCore { class Specimen : public SynthDevice @@ -72,10 +62,6 @@ namespace WaveSabreCore virtual void SetChunk(void *data, int size); virtual int GetChunk(void **data); -#if defined(WIN32) || defined(_WIN32) - void LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat); -#endif - private: class SpecimenVoice : public Voice { @@ -103,13 +89,6 @@ namespace WaveSabreCore float velocity; }; -#if defined(WIN32) || defined(_WIN32) - static BOOL __stdcall driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport); - static BOOL __stdcall formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport); - - static HACMDRIVERID driverId; -#endif - char *chunkData; char *waveFormatData; diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h index e163b83b..8f099d68 100644 --- a/WaveSabreCore/include/WaveSabreCore/Thunder.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -3,16 +3,6 @@ #include "SynthDevice.h" -#if defined(WIN32) || defined(_WIN32) -#include -#include - -#ifdef UNICODE -#define _UNICODE -#endif -#include -#endif /* WIN32 */ - namespace WaveSabreCore { class Thunder : public SynthDevice @@ -26,10 +16,6 @@ namespace WaveSabreCore virtual void SetChunk(void *data, int size); virtual int GetChunk(void **data); -#if defined(WIN32) || defined(_WIN32) - void LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat); -#endif - private: class ThunderVoice : public Voice { @@ -47,13 +33,6 @@ namespace WaveSabreCore int samplePos; }; -#if defined(WIN32) || defined(_WIN32) - static BOOL __stdcall driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport); - static BOOL __stdcall formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport); - - static HACMDRIVERID driverId; -#endif - char *chunkData; char *waveFormatData; diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp new file mode 100644 index 00000000..4195c352 --- /dev/null +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -0,0 +1,114 @@ +#include + +#include + +namespace WaveSabreCore +{ +#if defined(WIN32) || defined(_WIN32) + HACMDRIVERID SampleLoader::driverId = NULL; +#endif + + SampleLoader::LoadedSample SampleLoader::LoadSampleGSM(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat) + { + LoadedSample ret; + + ret.compressedSize = compressedSize; + ret.uncompressedSize = uncompressedSize; + + //if (waveFormatData) delete [] waveFormatData; + ret.waveFormatData = new char[sizeof(WAVEFORMATEX) + waveFormat->cbSize]; + memcpy(ret.waveFormatData, waveFormat, sizeof(WAVEFORMATEX) + waveFormat->cbSize); + //if (compressedData) delete [] compressedData; + ret.compressedData = new char[compressedSize]; + memcpy(ret.compressedData, data, compressedSize); + + //if (sampleData) delete [] sampleData; + +#if defined(WIN32) || defined(_WIN32) + acmDriverEnum(driverEnumCallback, NULL, NULL); + HACMDRIVER driver = NULL; + acmDriverOpen(&driver, driverId, 0); + + WAVEFORMATEX dstWaveFormat = + { + WAVE_FORMAT_PCM, + 1, + waveFormat->nSamplesPerSec, + waveFormat->nSamplesPerSec * 2, + sizeof(short), + sizeof(short) * 8, + 0 + }; + + HACMSTREAM stream = NULL; + acmStreamOpen(&stream, driver, waveFormat, &dstWaveFormat, NULL, NULL, NULL, ACM_STREAMOPENF_NONREALTIME); + + ACMSTREAMHEADER streamHeader; + memset(&streamHeader, 0, sizeof(ACMSTREAMHEADER)); + streamHeader.cbStruct = sizeof(ACMSTREAMHEADER); + streamHeader.pbSrc = (LPBYTE)ret.compressedData; + streamHeader.cbSrcLength = compressedSize; + auto uncompressedData = new short[uncompressedSize * 2]; + streamHeader.pbDst = (LPBYTE)uncompressedData; + streamHeader.cbDstLength = uncompressedSize * 2; + acmStreamPrepareHeader(stream, &streamHeader, 0); + + acmStreamConvert(stream, &streamHeader, 0); + + acmStreamClose(stream, 0); + acmDriverClose(driver, 0); + + ret.sampleLength = streamHeader.cbDstLengthUsed / sizeof(short); + ret.sampleData = new float[sampleLength]; + for (int i = 0; i < ret.sampleLength; i++) + ret.sampleData[i] = (float)((double)uncompressedData[i] / 32768.0); + + delete [] uncompressedData; +#else + ret.sampleLength = 1; + ret.sampleData = new float[1]; + ret.sampleData[0] = 0; +#endif + + return ret; + } + +#if defined(WIN32) || defined(_WIN32) + BOOL __stdcall SampleLoader::driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport) + { + if (SampleLoader::driverId) return 1; + + HACMDRIVER driver = NULL; + acmDriverOpen(&driver, driverId, 0); + + int waveFormatSize = 0; + acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &waveFormatSize); + auto waveFormat = (WAVEFORMATEX *)(new char[waveFormatSize]); + memset(waveFormat, 0, waveFormatSize); + ACMFORMATDETAILS formatDetails; + memset(&formatDetails, 0, sizeof(formatDetails)); + formatDetails.cbStruct = sizeof(formatDetails); + formatDetails.pwfx = waveFormat; + formatDetails.cbwfx = waveFormatSize; + formatDetails.dwFormatTag = WAVE_FORMAT_UNKNOWN; + acmFormatEnum(driver, &formatDetails, formatEnumCallback, NULL, NULL); + + delete [] (char *)waveFormat; + + acmDriverClose(driver, 0); + + return 1; + } + + BOOL __stdcall SampleLoader::formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport) + { + if (formatDetails->pwfx->wFormatTag == WAVE_FORMAT_GSM610 && + formatDetails->pwfx->nChannels == 1 && + formatDetails->pwfx->nSamplesPerSec == SampleRate) + { + SampleLoader::driverId = driverId; + } + return 1; + } +#endif +} diff --git a/WaveSabreCore/src/Specimen.cpp b/WaveSabreCore/src/Specimen.cpp index 2f9a7cc9..aa24e84e 100644 --- a/WaveSabreCore/src/Specimen.cpp +++ b/WaveSabreCore/src/Specimen.cpp @@ -1,14 +1,12 @@ #include #include +#include #include +#include namespace WaveSabreCore { -#if defined(WIN32) || defined(_WIN32) - HACMDRIVERID Specimen::driverId = NULL; -#endif - Specimen::Specimen() : SynthDevice(0) { @@ -154,7 +152,6 @@ namespace WaveSabreCore void Specimen::SetChunk(void *data, int size) { -#if defined(WIN32) || defined(_WIN32) if (!size) return; // Read header @@ -168,7 +165,22 @@ namespace WaveSabreCore // Read compressed data and load sample auto compressedDataPtr = (char *)waveFormatPtr + waveFormatSize; auto compressedDataSize = headerPtr->CompressedSize; - LoadSample(compressedDataPtr, headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); + auto sample = SampleLoader::LoadSampleGSM(compressedDataPtr, + headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); + + this->compressedSize = sample.compressedSize; + this->uncompressedSize = sample.uncompressedSize; + + if (waveFormatData) delete [] waveFormatData; + waveFormatData = sample.waveFormatData; + if (compressedData) delete [] compressedData; + compressedData = sample.compressedData; + if (sampleData) delete [] sampleData; + sampleData = sample.sampleData; + + sampleLength = sample.sampleLength; + sampleLoopStart = 0; + sampleLoopLength = sampleLength; // Read params // The rest of the data from the start of the params until the end of the chunk @@ -185,18 +197,10 @@ namespace WaveSabreCore auto numChunkParams = (int)((size - sizeof(int) - (paramDataPtr - (char *)data)) / sizeof(float)); for (int i = 0; i < numChunkParams; i++) SetParam(i, ((float *)paramDataPtr)[i]); -#else - sampleLength = 1; - sampleData = new float[1]; - sampleData[0] = 0.0f; - sampleLoopStart = 0; - sampleLoopLength = 1; -#endif } int Specimen::GetChunk(void **data) { -#if defined(WIN32) || defined(_WIN32) if (!compressedData) return 0; // Figure out size of chunk @@ -237,69 +241,8 @@ namespace WaveSabreCore *data = chunkData; return size; -#else - return 0; -#endif } -#if defined(WIN32) || defined(_WIN32) - void Specimen::LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat) - { - this->compressedSize = compressedSize; - this->uncompressedSize = uncompressedSize; - - if (waveFormatData) delete [] waveFormatData; - waveFormatData = new char[sizeof(WAVEFORMATEX) + waveFormat->cbSize]; - memcpy(waveFormatData, waveFormat, sizeof(WAVEFORMATEX) + waveFormat->cbSize); - if (compressedData) delete [] compressedData; - compressedData = new char[compressedSize]; - memcpy(compressedData, data, compressedSize); - - acmDriverEnum(driverEnumCallback, NULL, NULL); - HACMDRIVER driver = NULL; - acmDriverOpen(&driver, driverId, 0); - - WAVEFORMATEX dstWaveFormat = - { - WAVE_FORMAT_PCM, - 1, - waveFormat->nSamplesPerSec, - waveFormat->nSamplesPerSec * 2, - sizeof(short), - sizeof(short) * 8, - 0 - }; - - HACMSTREAM stream = NULL; - acmStreamOpen(&stream, driver, waveFormat, &dstWaveFormat, NULL, NULL, NULL, ACM_STREAMOPENF_NONREALTIME); - - ACMSTREAMHEADER streamHeader; - memset(&streamHeader, 0, sizeof(ACMSTREAMHEADER)); - streamHeader.cbStruct = sizeof(ACMSTREAMHEADER); - streamHeader.pbSrc = (LPBYTE)compressedData; - streamHeader.cbSrcLength = compressedSize; - auto uncompressedData = new short[uncompressedSize * 2]; - streamHeader.pbDst = (LPBYTE)uncompressedData; - streamHeader.cbDstLength = uncompressedSize * 2; - acmStreamPrepareHeader(stream, &streamHeader, 0); - - acmStreamConvert(stream, &streamHeader, 0); - - acmStreamClose(stream, 0); - acmDriverClose(driver, 0); - - sampleLength = streamHeader.cbDstLengthUsed / sizeof(short); - if (sampleData) delete [] sampleData; - sampleData = new float[sampleLength]; - for (int i = 0; i < sampleLength; i++) sampleData[i] = (float)((double)uncompressedData[i] / 32768.0); - - sampleLoopStart = 0; - sampleLoopLength = sampleLength; - - delete [] uncompressedData; - } -#endif - Specimen::SpecimenVoice::SpecimenVoice(Specimen *specimen) { this->specimen = specimen; @@ -406,43 +349,4 @@ namespace WaveSabreCore { samplePlayer.CalcPitch(GetNote() - 60 + Detune + specimen->fineTune * 2.0f - 1.0f + SpecimenVoice::coarseDetune(specimen->coarseTune)); } - -#if defined(WIN32) || defined(_WIN32) - BOOL __stdcall Specimen::driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport) - { - if (Specimen::driverId) return 1; - - HACMDRIVER driver = NULL; - acmDriverOpen(&driver, driverId, 0); - - int waveFormatSize = 0; - acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &waveFormatSize); - auto waveFormat = (WAVEFORMATEX *)(new char[waveFormatSize]); - memset(waveFormat, 0, waveFormatSize); - ACMFORMATDETAILS formatDetails; - memset(&formatDetails, 0, sizeof(formatDetails)); - formatDetails.cbStruct = sizeof(formatDetails); - formatDetails.pwfx = waveFormat; - formatDetails.cbwfx = waveFormatSize; - formatDetails.dwFormatTag = WAVE_FORMAT_UNKNOWN; - acmFormatEnum(driver, &formatDetails, formatEnumCallback, NULL, NULL); - - delete [] (char *)waveFormat; - - acmDriverClose(driver, 0); - - return 1; - } - - BOOL __stdcall Specimen::formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport) - { - if (formatDetails->pwfx->wFormatTag == WAVE_FORMAT_GSM610 && - formatDetails->pwfx->nChannels == 1 && - formatDetails->pwfx->nSamplesPerSec == SampleRate) - { - Specimen::driverId = driverId; - } - return 1; - } -#endif } diff --git a/WaveSabreCore/src/Thunder.cpp b/WaveSabreCore/src/Thunder.cpp index 79a77caa..56bb3756 100644 --- a/WaveSabreCore/src/Thunder.cpp +++ b/WaveSabreCore/src/Thunder.cpp @@ -1,14 +1,12 @@ #include #include +#include +#include #include namespace WaveSabreCore { -#if defined(WIN32) || defined(_WIN32) - HACMDRIVERID Thunder::driverId = NULL; -#endif - Thunder::Thunder() : SynthDevice(0) { @@ -40,22 +38,27 @@ namespace WaveSabreCore void Thunder::SetChunk(void *data, int size) { -#if defined(WIN32) || defined(_WIN32) if (!size) return; auto h = (ChunkHeader *)data; auto waveFormat = (WAVEFORMATEX *)((char *)data + sizeof(ChunkHeader)); auto compressedData = (char *)waveFormat + sizeof(WAVEFORMATEX) + waveFormat->cbSize; - LoadSample(compressedData, h->CompressedSize, h->UncompressedSize, waveFormat); -#else - sampleLength = 1; - sampleData = new float[1]; - sampleData[0] = 0.0f; -#endif + auto sample = SampleLoader::LoadSampleGSM(compressedData, h->CompressedSize, h->UncompressedSize, waveFormat); + + this->compressedSize = sample.compressedSize; + this->uncompressedSize = sample.uncompressedSize; + + if (waveFormatData) delete [] waveFormatData; + waveFormatData = sample.waveFormatData; + if (compressedData) delete [] compressedData; + compressedData = sample.compressedData; + if (sampleData) delete [] sampleData; + sampleData = sample.sampleData; + + sampleLength = sample.sampleLength; } int Thunder::GetChunk(void **data) { -#if defined(WIN32) || defined(_WIN32) if (!compressedData) return 0; ChunkHeader h; h.CompressedSize = compressedSize; @@ -69,66 +72,8 @@ namespace WaveSabreCore *(int *)(chunkData + chunkSize - sizeof(int)) = chunkSize; *data = chunkData; return chunkSize; -#else - return 0; -#endif } -#if defined(WIN32) || defined(_WIN32) - void Thunder::LoadSample(char *data, int compressedSize, int uncompressedSize, WAVEFORMATEX *waveFormat) - { - this->compressedSize = compressedSize; - this->uncompressedSize = uncompressedSize; - - if (waveFormatData) delete [] waveFormatData; - waveFormatData = new char[sizeof(WAVEFORMATEX) + waveFormat->cbSize]; - memcpy(waveFormatData, waveFormat, sizeof(WAVEFORMATEX) + waveFormat->cbSize); - if (compressedData) delete [] compressedData; - compressedData = new char[compressedSize]; - memcpy(compressedData, data, compressedSize); - - acmDriverEnum(driverEnumCallback, NULL, NULL); - HACMDRIVER driver = NULL; - acmDriverOpen(&driver, driverId, 0); - - WAVEFORMATEX dstWaveFormat = - { - WAVE_FORMAT_PCM, - 1, - waveFormat->nSamplesPerSec, - waveFormat->nSamplesPerSec * 2, - sizeof(short), - sizeof(short) * 8, - 0 - }; - - HACMSTREAM stream = NULL; - acmStreamOpen(&stream, driver, waveFormat, &dstWaveFormat, NULL, NULL, NULL, ACM_STREAMOPENF_NONREALTIME); - - ACMSTREAMHEADER streamHeader; - memset(&streamHeader, 0, sizeof(ACMSTREAMHEADER)); - streamHeader.cbStruct = sizeof(ACMSTREAMHEADER); - streamHeader.pbSrc = (LPBYTE)compressedData; - streamHeader.cbSrcLength = compressedSize; - auto uncompressedData = new short[uncompressedSize * 2]; - streamHeader.pbDst = (LPBYTE)uncompressedData; - streamHeader.cbDstLength = uncompressedSize * 2; - acmStreamPrepareHeader(stream, &streamHeader, 0); - - acmStreamConvert(stream, &streamHeader, 0); - - acmStreamClose(stream, 0); - acmDriverClose(driver, 0); - - sampleLength = streamHeader.cbDstLengthUsed / sizeof(short); - if (sampleData) delete [] sampleData; - sampleData = new float[sampleLength]; - for (int i = 0; i < sampleLength; i++) sampleData[i] = (float)((double)uncompressedData[i] / 32768.0); - - delete [] uncompressedData; - } -#endif - Thunder::ThunderVoice::ThunderVoice(Thunder *thunder) { this->thunder = thunder; @@ -160,43 +105,4 @@ namespace WaveSabreCore Voice::NoteOn(note, velocity, detune, pan); samplePos = 0; } - -#if defined(WIN32) || defined(_WIN32) - BOOL __stdcall Thunder::driverEnumCallback(HACMDRIVERID driverId, DWORD_PTR dwInstance, DWORD fdwSupport) - { - if (Thunder::driverId) return 1; - - HACMDRIVER driver = NULL; - acmDriverOpen(&driver, driverId, 0); - - int waveFormatSize = 0; - acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &waveFormatSize); - auto waveFormat = (WAVEFORMATEX *)(new char[waveFormatSize]); - memset(waveFormat, 0, waveFormatSize); - ACMFORMATDETAILS formatDetails; - memset(&formatDetails, 0, sizeof(formatDetails)); - formatDetails.cbStruct = sizeof(formatDetails); - formatDetails.pwfx = waveFormat; - formatDetails.cbwfx = waveFormatSize; - formatDetails.dwFormatTag = WAVE_FORMAT_UNKNOWN; - acmFormatEnum(driver, &formatDetails, formatEnumCallback, NULL, NULL); - - delete [] (char *)waveFormat; - - acmDriverClose(driver, 0); - - return 1; - } - - BOOL __stdcall Thunder::formatEnumCallback(HACMDRIVERID driverId, LPACMFORMATDETAILS formatDetails, DWORD_PTR dwInstance, DWORD fdwSupport) - { - if (formatDetails->pwfx->wFormatTag == WAVE_FORMAT_GSM610 && - formatDetails->pwfx->nChannels == 1 && - formatDetails->pwfx->nSamplesPerSec == SampleRate) - { - Thunder::driverId = driverId; - } - return 1; - } -#endif } diff --git a/WaveSabrePlayerLib/src/SongRenderer.cpp b/WaveSabrePlayerLib/src/SongRenderer.cpp index 9b34a6b9..d7a2a6ac 100644 --- a/WaveSabrePlayerLib/src/SongRenderer.cpp +++ b/WaveSabrePlayerLib/src/SongRenderer.cpp @@ -307,7 +307,6 @@ namespace WaveSabrePlayerLib #endif } - //asm volatile("int3\n"); #if defined(WIN32) || defined(_WIN32) if (!InterlockedDecrement(&renderThreadsRunning)) SetEvent(renderDoneEvent); diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 1078c818..941355f2 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -31,8 +31,8 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) case SongRenderer::DeviceId::Cathedral: return new WaveSabreCore::Cathedral(); #if defined(WIN32) || defined(_WIN32) case SongRenderer::DeviceId::Adultery: return new WaveSabreCore::Adultery(); - case SongRenderer::DeviceId::Specimen: return new WaveSabreCore::Specimen(); #endif + case SongRenderer::DeviceId::Specimen: return new WaveSabreCore::Specimen(); } printf("ack, unknown device %d!\n", id); return nullptr; From 38d1a95d0c959d4a947ee032f0ffd5b1c6ca99fc Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 03:44:58 +0200 Subject: [PATCH 22/32] it broke the Windows bulid because of course it did --- WaveSabreCore/include/WaveSabreCore/SampleLoader.h | 2 ++ WaveSabreCore/include/WaveSabreCore/Specimen.h | 2 -- WaveSabreCore/include/WaveSabreCore/Thunder.h | 2 -- WaveSabreCore/src/SampleLoader.cpp | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/WaveSabreCore/include/WaveSabreCore/SampleLoader.h b/WaveSabreCore/include/WaveSabreCore/SampleLoader.h index f4b5200b..cf0c97ea 100644 --- a/WaveSabreCore/include/WaveSabreCore/SampleLoader.h +++ b/WaveSabreCore/include/WaveSabreCore/SampleLoader.h @@ -28,6 +28,8 @@ namespace WaveSabreCore class SampleLoader { public: + static const int SampleRate = 44100; + struct LoadedSample { char *chunkData; diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h index 086340be..889a2360 100644 --- a/WaveSabreCore/include/WaveSabreCore/Specimen.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -51,8 +51,6 @@ namespace WaveSabreCore NumParams, }; - static const int SampleRate = 44100; - Specimen(); virtual ~Specimen(); diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h index 8f099d68..0d977fb9 100644 --- a/WaveSabreCore/include/WaveSabreCore/Thunder.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -8,8 +8,6 @@ namespace WaveSabreCore class Thunder : public SynthDevice { public: - static const int SampleRate = 44100; - Thunder(); virtual ~Thunder(); diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp index 4195c352..7e5eeba4 100644 --- a/WaveSabreCore/src/SampleLoader.cpp +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -59,7 +59,7 @@ namespace WaveSabreCore acmDriverClose(driver, 0); ret.sampleLength = streamHeader.cbDstLengthUsed / sizeof(short); - ret.sampleData = new float[sampleLength]; + ret.sampleData = new float[ret.sampleLength]; for (int i = 0; i < ret.sampleLength; i++) ret.sampleData[i] = (float)((double)uncompressedData[i] / 32768.0); From 56344f63422190480fcb2c8d54dd23afb7e04276 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 03:49:39 +0200 Subject: [PATCH 23/32] psh, vst sources relying on the core to include all the dependency headers, not exactly good practice --- WaveSabreCore/include/WaveSabreCore/Specimen.h | 1 + WaveSabreCore/include/WaveSabreCore/Thunder.h | 1 + 2 files changed, 2 insertions(+) diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h index 889a2360..c6ec186b 100644 --- a/WaveSabreCore/include/WaveSabreCore/Specimen.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -4,6 +4,7 @@ #include "SynthDevice.h" #include "Envelope.h" #include "StateVariableFilter.h" +#include "SampleLoader.h" #include "SamplePlayer.h" namespace WaveSabreCore diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h index 0d977fb9..84c2f452 100644 --- a/WaveSabreCore/include/WaveSabreCore/Thunder.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -2,6 +2,7 @@ #define __WAVESABRECORE_THUNDER_H__ #include "SynthDevice.h" +#include "SampleLoader.h" namespace WaveSabreCore { From bd872fb6e0babcbd78960fbdf6e8d363d9e2c312 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 03:52:50 +0200 Subject: [PATCH 24/32] more of the same --- WaveSabreCore/include/WaveSabreCore/Specimen.h | 2 ++ WaveSabreCore/include/WaveSabreCore/Thunder.h | 2 ++ WaveSabreCore/src/SampleLoader.cpp | 1 + 3 files changed, 5 insertions(+) diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h index c6ec186b..d18dca48 100644 --- a/WaveSabreCore/include/WaveSabreCore/Specimen.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -12,6 +12,8 @@ namespace WaveSabreCore class Specimen : public SynthDevice { public: + static const int SampleRate = SampleLoader::SampleRate; + enum class ParamIndices { AmpAttack, diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h index 84c2f452..26767c4d 100644 --- a/WaveSabreCore/include/WaveSabreCore/Thunder.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -9,6 +9,8 @@ namespace WaveSabreCore class Thunder : public SynthDevice { public: + static const int SampleRate = SampleLoader::SampleRate; + Thunder(); virtual ~Thunder(); diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp index 7e5eeba4..4ced8eb6 100644 --- a/WaveSabreCore/src/SampleLoader.cpp +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -64,6 +64,7 @@ namespace WaveSabreCore ret.sampleData[i] = (float)((double)uncompressedData[i] / 32768.0); delete [] uncompressedData; +#elif HAVE_FFMPEG_GSM #else ret.sampleLength = 1; ret.sampleData = new float[1]; From 586aea0acfb02b2ab82ae18f9b4c176cc3e64a1f Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 04:00:43 +0200 Subject: [PATCH 25/32] and move some stuff back to the headers I hope the VSTs are finally happy now --- .../include/WaveSabreCore/Specimen.h | 20 +++++++++++++++++++ WaveSabreCore/include/WaveSabreCore/Thunder.h | 18 +++++++++++++++++ WaveSabreCore/src/Specimen.cpp | 16 +-------------- WaveSabreCore/src/Thunder.cpp | 14 +------------ 4 files changed, 40 insertions(+), 28 deletions(-) diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h index d18dca48..56031e5e 100644 --- a/WaveSabreCore/include/WaveSabreCore/Specimen.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -63,6 +63,26 @@ namespace WaveSabreCore virtual void SetChunk(void *data, int size); virtual int GetChunk(void **data); + inline void LoadSample(char *compressedDataPtr, int compressedSize, + int uncompressedSize, WAVEFORMATEX *waveFormatPtr) + { + auto sample = SampleLoader::LoadSampleGSM(compressedDataPtr, + headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); + + this->compressedSize = sample.compressedSize; + this->uncompressedSize = sample.uncompressedSize; + + if (waveFormatData) delete [] waveFormatData; + waveFormatData = sample.waveFormatData; + if (compressedData) delete [] compressedData; + compressedData = sample.compressedData; + if (sampleData) delete [] sampleData; + sampleData = sample.sampleData; + + sampleLength = sample.sampleLength; + sampleLoopStart = 0; + sampleLoopLength = sampleLength; + } private: class SpecimenVoice : public Voice { diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h index 26767c4d..d0200649 100644 --- a/WaveSabreCore/include/WaveSabreCore/Thunder.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -17,6 +17,24 @@ namespace WaveSabreCore virtual void SetChunk(void *data, int size); virtual int GetChunk(void **data); + inline void LoadSample(char *compressedDataPtr, int compressedSize, + int uncompressedSize, WAVEFORMATEX *waveFormatPtr) + { + auto sample = SampleLoader::LoadSampleGSM(compressedDataPtr, + headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); + + this->compressedSize = sample.compressedSize; + this->uncompressedSize = sample.uncompressedSize; + + if (waveFormatData) delete [] waveFormatData; + waveFormatData = sample.waveFormatData; + if (compressedData) delete [] compressedData; + compressedData = sample.compressedData; + if (sampleData) delete [] sampleData; + sampleData = sample.sampleData; + + sampleLength = sample.sampleLength; + } private: class ThunderVoice : public Voice { diff --git a/WaveSabreCore/src/Specimen.cpp b/WaveSabreCore/src/Specimen.cpp index aa24e84e..aafe84ec 100644 --- a/WaveSabreCore/src/Specimen.cpp +++ b/WaveSabreCore/src/Specimen.cpp @@ -165,22 +165,8 @@ namespace WaveSabreCore // Read compressed data and load sample auto compressedDataPtr = (char *)waveFormatPtr + waveFormatSize; auto compressedDataSize = headerPtr->CompressedSize; - auto sample = SampleLoader::LoadSampleGSM(compressedDataPtr, - headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); - this->compressedSize = sample.compressedSize; - this->uncompressedSize = sample.uncompressedSize; - - if (waveFormatData) delete [] waveFormatData; - waveFormatData = sample.waveFormatData; - if (compressedData) delete [] compressedData; - compressedData = sample.compressedData; - if (sampleData) delete [] sampleData; - sampleData = sample.sampleData; - - sampleLength = sample.sampleLength; - sampleLoopStart = 0; - sampleLoopLength = sampleLength; + LoadSample(compressedDataPtr, headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); // Read params // The rest of the data from the start of the params until the end of the chunk diff --git a/WaveSabreCore/src/Thunder.cpp b/WaveSabreCore/src/Thunder.cpp index 56bb3756..9d922904 100644 --- a/WaveSabreCore/src/Thunder.cpp +++ b/WaveSabreCore/src/Thunder.cpp @@ -42,19 +42,7 @@ namespace WaveSabreCore auto h = (ChunkHeader *)data; auto waveFormat = (WAVEFORMATEX *)((char *)data + sizeof(ChunkHeader)); auto compressedData = (char *)waveFormat + sizeof(WAVEFORMATEX) + waveFormat->cbSize; - auto sample = SampleLoader::LoadSampleGSM(compressedData, h->CompressedSize, h->UncompressedSize, waveFormat); - - this->compressedSize = sample.compressedSize; - this->uncompressedSize = sample.uncompressedSize; - - if (waveFormatData) delete [] waveFormatData; - waveFormatData = sample.waveFormatData; - if (compressedData) delete [] compressedData; - compressedData = sample.compressedData; - if (sampleData) delete [] sampleData; - sampleData = sample.sampleData; - - sampleLength = sample.sampleLength; + LoadSample(compressedData, h->CompressedSize, h->UncompressedSize, waveFormat); } int Thunder::GetChunk(void **data) From 876f0cb7eda8e516dadb01df7ff099811ac02a78 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 04:03:14 +0200 Subject: [PATCH 26/32] oops please don't look at the previous commit --- WaveSabreCore/include/WaveSabreCore/Specimen.h | 2 +- WaveSabreCore/include/WaveSabreCore/Thunder.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WaveSabreCore/include/WaveSabreCore/Specimen.h b/WaveSabreCore/include/WaveSabreCore/Specimen.h index 56031e5e..ff3fd498 100644 --- a/WaveSabreCore/include/WaveSabreCore/Specimen.h +++ b/WaveSabreCore/include/WaveSabreCore/Specimen.h @@ -67,7 +67,7 @@ namespace WaveSabreCore int uncompressedSize, WAVEFORMATEX *waveFormatPtr) { auto sample = SampleLoader::LoadSampleGSM(compressedDataPtr, - headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); + compressedSize, uncompressedSize, waveFormatPtr); this->compressedSize = sample.compressedSize; this->uncompressedSize = sample.uncompressedSize; diff --git a/WaveSabreCore/include/WaveSabreCore/Thunder.h b/WaveSabreCore/include/WaveSabreCore/Thunder.h index d0200649..c083f450 100644 --- a/WaveSabreCore/include/WaveSabreCore/Thunder.h +++ b/WaveSabreCore/include/WaveSabreCore/Thunder.h @@ -21,7 +21,7 @@ namespace WaveSabreCore int uncompressedSize, WAVEFORMATEX *waveFormatPtr) { auto sample = SampleLoader::LoadSampleGSM(compressedDataPtr, - headerPtr->CompressedSize, headerPtr->UncompressedSize, waveFormatPtr); + compressedSize, uncompressedSize, waveFormatPtr); this->compressedSize = sample.compressedSize; this->uncompressedSize = sample.uncompressedSize; From fabbe20e2bdcb1d78d28822ae3c638281d6b8648 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 04:56:47 +0200 Subject: [PATCH 27/32] saving unfinished horror code for now --- WaveSabreCore/CMakeLists.txt | 2 +- WaveSabreCore/src/SampleLoader.cpp | 138 +++++++++++++++++++ WaveSabrePlayerLib/src/AplayRenderThread.cpp | 2 + 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index 1873f8be..efa29575 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -83,7 +83,7 @@ else() if(ENABLE_FFMPEG_GSM) message(NOTICE "Enabling FFmpeg GSM 6.10 decoding") - target_compile_definitions(WaveSabrePlayerLib PRIVATE HAVE_FFMPEG_GSM=1) + target_compile_definitions(WaveSabreCore PRIVATE HAVE_FFMPEG_GSM=1) else() message(NOTICE "FFmpeg GSM 6.10 decoding disabled") endif() diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp index 4ced8eb6..a2f70be2 100644 --- a/WaveSabreCore/src/SampleLoader.cpp +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -2,6 +2,18 @@ #include +#if !defined(WIN32) && !defined(_WIN32) && HAVE_FFMPEG_GSM +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + namespace WaveSabreCore { #if defined(WIN32) || defined(_WIN32) @@ -65,7 +77,133 @@ namespace WaveSabreCore delete [] uncompressedData; #elif HAVE_FFMPEG_GSM + // you're going to hate me for this and I'll absolutely deserve it + + // cat test.gsm \ + // | ffmpeg -i - -f f32le -acodec pcm_f32le - 2>/dev/null \ + // | aplay -traw -c1 -r44100 -fFLOAT - + + int gsmpipe[2], wavpipe[2]; + int rv; + rv = pipe(gsmpipe); + assert(rv == 0 && "Can't set up input pipe for ffmpeg"); + rv = pipe(wavpipe); + assert(rv == 0 && "Can't set up output pipe for ffmpeg"); + + int gsmread = gsmpipe[0], gsmwrite = gsmpipe[1], + wavread = wavpipe[0], wavwrite = wavpipe[1]; + + pid_t child = fork(); // dun dun duuuuun + assert(child >= 0 && "fork() failed."); + + if (child == 0) { + // child + close(gsmwrite); + close(wavread); + //close(STDERR_FILENO); + + dup2(gsmread, STDIN_FILENO); + dup2(wavwrite, STDOUT_FILENO); + + char *const args[] = {"/usr/bin/ffmpeg", "-i", "-", "-f", "f32le", + "-acodec", "pcm_f32le", "-", NULL}; + rv = execve("/usr/bin/ffmpeg", args, environ); + assert(rv >= 0 && "Failed to run FFmpeg!"); + // unreachable + } else { + // parent + close(gsmread); + close(wavwrite); + + // write fake(ish) WAV header + size_t M = (waveFormat->nAvgBytesPerSec * waveFormat->nSamplesPerSec) / waveFormat->nChannels; + char fileheader[4+4/*RIFF*/ + 4/*WAVE*/ + 4+4+sizeof(WAVEFORMATEX)/*fmt */ + 4+4+4/*fact*/ + 4+4/*data*/]; + + memcpy(&fileheader[0], "RIFF", 4); + *(uint32_t*)&fileheader[4] = 4 + 4+4+sizeof(WAVEFORMATEX) + 4+4+4 + 4+4+compressedSize; + + memcpy(&fileheader[8], "WAVEfmt ", 8); + *(uint32_t*)&fileheader[16] = sizeof(WAVEFORMATEX); + memcpy(&fileheader[20], waveFormat, sizeof(WAVEFORMATEX)); + + memcpy(&fileheader[20+sizeof(WAVEFORMATEX)], "FACT\x04\0\0\0", 8); + *(uint32_t*)&fileheader[28+sizeof(WAVEFORMATEX)] = M; + + memcpy(&fileheader[32+sizeof(WAVEFORMATEX)], "data", 4); + *(uint32_t*)&fileheader[36+sizeof(WAVEFORMATEX)] = compressedSize; + + bool wrote_hdr = false; + size_t uncompr_cap = PIPE_BUF, + uncompr_incr = PIPE_BUF >> 2; + uint8_t* uncompr = (uint8_t*)malloc(uncompr_cap); + size_t uncomprSize = 0; + + int rev; + while (true) { + struct pollfd watched[2]; + watched[0].fd = wavread; + watched[0].events = POLLIN; + watched[0].revents = 0; + watched[1].fd = gsmwrite; + watched[1].events = (compressedSize > 0) ? POLLOUT : 0; + watched[1].revents = 0; + + rv = poll(watched, (compressedSize > 0) ? 2 : 1, -1); + assert(rv >= 0 && "poll(2) failed?!"); + + if (watched[0].revents & POLLNVAL) + watched[0].revents |= POLLERR; + if (watched[1].revents & POLLNVAL) + watched[1].revents |= POLLERR; + rev = (watched[0].revents | watched[1].revents); + + if ((rev & POLLOUT) && !wrote_hdr && compressedSize > 0 && !(watched[1].revents & POLLERR)) { + if (wrote_hdr) { + wrote_hdr = false; + write(gsmwrite, fileheader, sizeof(fileheader)); + } else { + size_t towr = compressedSize; + if (towr > PIPE_BUF) + towr = PIPE_BUF; + rv = write(gsmwrite, data, towr); + assert(rv >= 0 && "Couldn't write GSM data to FFmpeg"); + compressedSize -= rv; + } + } + if ((rev & POLLIN) && !(watched[0].revents & POLLERR)) { + // TODO: make nonblocking, seek for smallest possible data size left + if (uncomprSize + uncompr_incr > uncompr_cap) { + uncompr_cap <<= 1; + uncompr = (uint8_t*)realloc(uncompr, uncompr_cap); + } + rv = read(wavread, uncompr + uncomprSize, uncompr_incr); + assert(rv >= 0 && "Couldn't read converted GSM data from FFmpeg"); + uncomprSize += rv; + } + + if (rev & (POLLHUP | POLLERR)) + break; + } + + if (rev & POLLERR) { + printf("I/O error to ffmpeg?\n"); + } + + waitpid(child, &rev, 0); + if (rev != 0) { + printf("FFmpeg exited with error %d!\n", rev); + } + + ret.sampleLength = uncomprSize / sizeof(float); + ret.sampleData = new float[ret.sampleLength]; + memcpy(ret.sampleData, uncompr, uncomprSize); + free(uncompr); + } #else + // sorry, not supported. +#ifndef NDEBUG + printf("WARNING: trying to load a GSM sample, while this is unsupported. Output will be silence.\n"); +#endif ret.sampleLength = 1; ret.sampleData = new float[1]; ret.sampleData[0] = 0; diff --git a/WaveSabrePlayerLib/src/AplayRenderThread.cpp b/WaveSabrePlayerLib/src/AplayRenderThread.cpp index ee9e6b28..83dab180 100644 --- a/WaveSabrePlayerLib/src/AplayRenderThread.cpp +++ b/WaveSabrePlayerLib/src/AplayRenderThread.cpp @@ -144,6 +144,7 @@ namespace WaveSabrePlayerLib // stdin becomes readend (properly sets up the pipe) // doing this instead of using /proc/$pid/fd is more portable dup2(readend, STDIN_FILENO); + //close(STDERR_FILENO); // format aplay args char arg2[strlen("-fS16_LE")+1]; @@ -159,6 +160,7 @@ namespace WaveSabrePlayerLib char *const args[] = {"/usr/bin/aplay", "-traw", "-c2", arg2, arg3, NULL}; int rv = execve("/usr/bin/aplay", args, environ); assert(rv >= 0 && "Failed to run aplay!"); + // unreachable } void AplayRenderThread::GetBufferTick(bool block) From e31e26956138925a36f671cfbf26c1e6cbc8b3de Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 05:12:34 +0200 Subject: [PATCH 28/32] oh no it's starting to work --- WaveSabreCore/src/SampleLoader.cpp | 43 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp index a2f70be2..5c0de626 100644 --- a/WaveSabreCore/src/SampleLoader.cpp +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -100,7 +100,7 @@ namespace WaveSabreCore // child close(gsmwrite); close(wavread); - //close(STDERR_FILENO); + close(STDERR_FILENO); dup2(gsmread, STDIN_FILENO); dup2(wavwrite, STDOUT_FILENO); @@ -133,19 +133,23 @@ namespace WaveSabreCore *(uint32_t*)&fileheader[36+sizeof(WAVEFORMATEX)] = compressedSize; bool wrote_hdr = false; - size_t uncompr_cap = PIPE_BUF, - uncompr_incr = PIPE_BUF >> 2; - uint8_t* uncompr = (uint8_t*)malloc(uncompr_cap); - size_t uncomprSize = 0; + uint8_t* uncompr = (uint8_t*)malloc(uncompressedSize); + size_t uncomprOff = 0; int rev; while (true) { + bool needDataOut = compressedSize > 0 || !wrote_hdr; + bool needDataIn = uncomprOff < uncompressedSize; + + if (!needDataOut && !needDataIn) + break; + struct pollfd watched[2]; watched[0].fd = wavread; watched[0].events = POLLIN; watched[0].revents = 0; watched[1].fd = gsmwrite; - watched[1].events = (compressedSize > 0) ? POLLOUT : 0; + watched[1].events = needDataOut ? POLLOUT : 0; watched[1].revents = 0; rv = poll(watched, (compressedSize > 0) ? 2 : 1, -1); @@ -157,9 +161,9 @@ namespace WaveSabreCore watched[1].revents |= POLLERR; rev = (watched[0].revents | watched[1].revents); - if ((rev & POLLOUT) && !wrote_hdr && compressedSize > 0 && !(watched[1].revents & POLLERR)) { - if (wrote_hdr) { - wrote_hdr = false; + if ((rev & POLLOUT) && needDataOut && !(watched[1].revents & POLLERR)) { + if (!wrote_hdr) { + wrote_hdr = true; write(gsmwrite, fileheader, sizeof(fileheader)); } else { size_t towr = compressedSize; @@ -168,17 +172,20 @@ namespace WaveSabreCore rv = write(gsmwrite, data, towr); assert(rv >= 0 && "Couldn't write GSM data to FFmpeg"); compressedSize -= rv; + + if (compressedSize <= 0) close(gsmwrite); } } - if ((rev & POLLIN) && !(watched[0].revents & POLLERR)) { + if ((rev & POLLIN) && uncomprOff < uncompressedSize && !(watched[0].revents & POLLERR)) { // TODO: make nonblocking, seek for smallest possible data size left - if (uncomprSize + uncompr_incr > uncompr_cap) { - uncompr_cap <<= 1; - uncompr = (uint8_t*)realloc(uncompr, uncompr_cap); - } - rv = read(wavread, uncompr + uncomprSize, uncompr_incr); + size_t toRead = 0x100; + if (toRead > (uncompressedSize - uncomprOff)) + toRead = (uncompressedSize - uncomprOff); + rv = read(wavread, uncompr + uncomprOff, toRead); assert(rv >= 0 && "Couldn't read converted GSM data from FFmpeg"); - uncomprSize += rv; + uncomprOff += rv; + + if (uncomprOff >= uncompressedSize) close(wavread); } if (rev & (POLLHUP | POLLERR)) @@ -194,9 +201,9 @@ namespace WaveSabreCore printf("FFmpeg exited with error %d!\n", rev); } - ret.sampleLength = uncomprSize / sizeof(float); + ret.sampleLength = uncompressedSize / sizeof(float); ret.sampleData = new float[ret.sampleLength]; - memcpy(ret.sampleData, uncompr, uncomprSize); + memcpy(ret.sampleData, uncompr, uncompressedSize); free(uncompr); } #else From f9a24b2e7c5b9bf13c33db49aa0cff4934cd6318 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 20:39:19 +0200 Subject: [PATCH 29/32] forgot the extra wavefmtex info --- WaveSabreCore/src/SampleLoader.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp index 5c0de626..c50fb17d 100644 --- a/WaveSabreCore/src/SampleLoader.cpp +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -117,20 +117,21 @@ namespace WaveSabreCore // write fake(ish) WAV header size_t M = (waveFormat->nAvgBytesPerSec * waveFormat->nSamplesPerSec) / waveFormat->nChannels; - char fileheader[4+4/*RIFF*/ + 4/*WAVE*/ + 4+4+sizeof(WAVEFORMATEX)/*fmt */ + 4+4+4/*fact*/ + 4+4/*data*/]; + size_t wvfmtsz = sizeof(WAVEFORMATEX) + waveFormat->cbSize; + char fileheader[4+4/*RIFF*/ + 4/*WAVE*/ + 4+4+wvfmtsz/*fmt */ + 4+4+4/*fact*/ + 4+4/*data*/]; memcpy(&fileheader[0], "RIFF", 4); - *(uint32_t*)&fileheader[4] = 4 + 4+4+sizeof(WAVEFORMATEX) + 4+4+4 + 4+4+compressedSize; + *(uint32_t*)&fileheader[4] = 4 + 4+4+wvfmtsz + 4+4+4 + 4+4+compressedSize; memcpy(&fileheader[8], "WAVEfmt ", 8); - *(uint32_t*)&fileheader[16] = sizeof(WAVEFORMATEX); - memcpy(&fileheader[20], waveFormat, sizeof(WAVEFORMATEX)); + *(uint32_t*)&fileheader[16] = wvfmtsz; + memcpy(&fileheader[20], waveFormat, wvfmtsz); - memcpy(&fileheader[20+sizeof(WAVEFORMATEX)], "FACT\x04\0\0\0", 8); - *(uint32_t*)&fileheader[28+sizeof(WAVEFORMATEX)] = M; + memcpy(&fileheader[20+wvfmtsz], "FACT\x04\0\0\0", 8); + *(uint32_t*)&fileheader[28+wvfmtsz] = M; - memcpy(&fileheader[32+sizeof(WAVEFORMATEX)], "data", 4); - *(uint32_t*)&fileheader[36+sizeof(WAVEFORMATEX)] = compressedSize; + memcpy(&fileheader[32+wvfmtsz], "data", 4); + *(uint32_t*)&fileheader[36+wvfmtsz] = compressedSize; bool wrote_hdr = false; uint8_t* uncompr = (uint8_t*)malloc(uncompressedSize); From cd412c1dbba48d1e4889464614573fb3a7d70366 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 21:29:26 +0200 Subject: [PATCH 30/32] extremely ugly gm.dls stuff --- CMakeLists.txt | 1 + WaveSabreCore/CMakeLists.txt | 7 +++ WaveSabreCore/include/WaveSabreCore.h | 2 - WaveSabreCore/src/GmDls.cpp | 88 ++++++++++++++++++++++++++- WaveSabreStandAlonePlayer/main.cpp | 4 -- 5 files changed, 95 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c6c31fb..44656a90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ if(NOT WIN32) option(ENABLE_APLAY "Enable aplay(1) backend for audio playback" ON) option(ENABLE_SDL2 "Enable SDL2_Audio backend for audio playback" OFF) option(ENABLE_FFMPEG_GSM "Enable GSM 6.10 sample decoding using FFmpeg. Required for Thunder and Specimen." OFF) + option(ENABLE_EXTERNAL_GMDLS "Enable using a gm.dls file provided at runtime" OFF) # for standalone playback, or playing back Windows-specific tracks, you # probably want to enable all of the above options endif() diff --git a/WaveSabreCore/CMakeLists.txt b/WaveSabreCore/CMakeLists.txt index efa29575..60a5505f 100644 --- a/WaveSabreCore/CMakeLists.txt +++ b/WaveSabreCore/CMakeLists.txt @@ -88,6 +88,13 @@ else() message(NOTICE "FFmpeg GSM 6.10 decoding disabled") endif() + if(ENABLE_EXTERNAL_GMDLS) + message(NOTICE "Enabling gm.dls support") + target_compile_definitions(WaveSabreCore PRIVATE HAVE_EXTERNAL_GMDLS=1) + else() + message(NOTICE "External gm.dls loading disabled") + endif() + if(CMAKE_BUILD_TYPE EQUAL Debug) target_compile_options(WaveSabreCore PUBLIC -g -Og) else() diff --git a/WaveSabreCore/include/WaveSabreCore.h b/WaveSabreCore/include/WaveSabreCore.h index 02d3df7c..4f179e1e 100644 --- a/WaveSabreCore/include/WaveSabreCore.h +++ b/WaveSabreCore/include/WaveSabreCore.h @@ -12,9 +12,7 @@ #include "WaveSabreCore/AllPassDelay.h" #include "WaveSabreCore/Comb.h" #include "WaveSabreCore/ResampleBuffer.h" -#if defined(WIN32) || defined(_WIN32) #include "WaveSabreCore/GmDls.h" -#endif #include "WaveSabreCore/MxcsrFlagGuard.h" #include "WaveSabreCore/Devices.h" diff --git a/WaveSabreCore/src/GmDls.cpp b/WaveSabreCore/src/GmDls.cpp index 44656a8b..fb13fa11 100644 --- a/WaveSabreCore/src/GmDls.cpp +++ b/WaveSabreCore/src/GmDls.cpp @@ -2,6 +2,10 @@ #if defined(WIN32) || defined(_WIN32) #include +#elif HAVE_EXTERNAL_GMDLS +#include +#include +#include #endif static char const *gmDlsPaths[2] = @@ -29,8 +33,90 @@ namespace WaveSabreCore CloseHandle(gmDlsFile); return gmDls; +#elif HAVE_EXTERNAL_GMDLS + unsigned char *rv = NULL; + + bool gmDlsPathAlloc = false; + bool dataHomeAlloc = false; + char *gmDlsPath, *dataHome; + char *home = getenv("HOME"); + FILE *filed; + long filesz; + + // first, check if there's a user-specified one available + gmDlsPath = getenv("WAVESABRE_GMDLS_PATH"); + + if (gmDlsPath != NULL && access(gmDlsPath, R_OK) == 0) + goto have_gmdls; + +try_xdgdat: + // try something XDG-conformant next + dataHome = getenv("XDG_DATA_HOME"); + if (dataHome == NULL) { + if (home == NULL) + goto try_winepfx; // fuck it + + dataHomeAlloc = true; + dataHome = (char*)malloc(0x100/*PATH_MAX*/); + snprintf(dataHome, 0x100, "%s/.local/share", home); + } + + gmDlsPathAlloc = true; + gmDlsPathAlloc = (char*)malloc(0x100); + snprintf(gmDlsPath, 0x100, "%s/WaveSabre/gm.dls", dataHome); + if (dataHomeAlloc) free(dataHome); + + if (access(gmDlsPath, R_OK) == 0) + goto have_gmdls; + + free(gmDlsPath); + gmDlsPathAlloc = false; + + // ... try grabbing it from the user's wineprefix, because no trick too dirty for us +try_winepfx: + if (home == NULL) + goto try_cwd; + + for (int i = 0; i < sizeof(gmDlsPaths)/sizeof(*gmDlsPaths); ++i) { + gmDlsPathAlloc = true; + gmDlsPath = (char*)malloc(0x100); + snprintf(gmDlsPath, 0x100, "%s/.wine/drive_c/windows/system32/%s", home, gmDlsPaths[i]); + + if (access(gmDlsPath, R_OK) == 0) + goto have_gmdls; + + free(gmDlsPath); + gmDlsPathAlloc = false; + } + +try_cwd: // fuck it, grab it from the current dir + gmDlsPath = "./gm.dls"; + + if (access(gmDlsPath, R_OK) == 0) + goto have_gmdls; + + return rv; + +have_gmdls: // actually read the file + filed = fopen(gmDlsPath, "rb"); + if (filed == NULL) + goto end; + + // get size of file + fseek(filed, 0, SEEK_END); + filesz = ftell(filed); + fseek(filed, 0, SEEK_SET); + + rv = new unsigned char[filesz]; + fread(rv, 1, filesz, filed); + fclose(filed); + +end: + if (gmDlsPathAlloc) free(gmDlsPath); + + return rv; #else - return nullptr; + return NULL; #endif } } diff --git a/WaveSabreStandAlonePlayer/main.cpp b/WaveSabreStandAlonePlayer/main.cpp index 941355f2..6ccb62d4 100644 --- a/WaveSabreStandAlonePlayer/main.cpp +++ b/WaveSabreStandAlonePlayer/main.cpp @@ -18,9 +18,7 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) { case SongRenderer::DeviceId::Falcon: return new WaveSabreCore::Falcon(); case SongRenderer::DeviceId::Slaughter: return new WaveSabreCore::Slaughter(); -#if defined(WIN32) || defined(_WIN32) case SongRenderer::DeviceId::Thunder: return new WaveSabreCore::Thunder(); -#endif case SongRenderer::DeviceId::Scissor: return new WaveSabreCore::Scissor(); case SongRenderer::DeviceId::Leveller: return new WaveSabreCore::Leveller(); case SongRenderer::DeviceId::Crusher: return new WaveSabreCore::Crusher(); @@ -29,9 +27,7 @@ WaveSabreCore::Device *SongFactory(SongRenderer::DeviceId id) case SongRenderer::DeviceId::Chamber: return new WaveSabreCore::Chamber(); case SongRenderer::DeviceId::Twister: return new WaveSabreCore::Twister(); case SongRenderer::DeviceId::Cathedral: return new WaveSabreCore::Cathedral(); -#if defined(WIN32) || defined(_WIN32) case SongRenderer::DeviceId::Adultery: return new WaveSabreCore::Adultery(); -#endif case SongRenderer::DeviceId::Specimen: return new WaveSabreCore::Specimen(); } printf("ack, unknown device %d!\n", id); From 0c17f7701618f8316e222066dae1d70b5e9ad405 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Thu, 3 Sep 2020 22:45:53 +0200 Subject: [PATCH 31/32] and fix the bugs --- WaveSabreCore/src/GmDls.cpp | 2 +- WaveSabrePlayerLib/src/SDL2RenderThread.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WaveSabreCore/src/GmDls.cpp b/WaveSabreCore/src/GmDls.cpp index fb13fa11..792d989c 100644 --- a/WaveSabreCore/src/GmDls.cpp +++ b/WaveSabreCore/src/GmDls.cpp @@ -62,7 +62,7 @@ namespace WaveSabreCore } gmDlsPathAlloc = true; - gmDlsPathAlloc = (char*)malloc(0x100); + gmDlsPath = (char*)malloc(0x100); snprintf(gmDlsPath, 0x100, "%s/WaveSabre/gm.dls", dataHome); if (dataHomeAlloc) free(dataHome); diff --git a/WaveSabrePlayerLib/src/SDL2RenderThread.cpp b/WaveSabrePlayerLib/src/SDL2RenderThread.cpp index 08e63782..750cb29a 100644 --- a/WaveSabrePlayerLib/src/SDL2RenderThread.cpp +++ b/WaveSabrePlayerLib/src/SDL2RenderThread.cpp @@ -45,7 +45,7 @@ namespace WaveSabrePlayerLib printf("SDL2 error: %s\n", SDL_GetError()); } #endif - assert(rv > 0 && "Can't open SDL2 audio"); + assert(dev > 0 && "Can't open SDL2 audio"); // pre-buffer stuff a bit, SDL2 tends to be finnicky callback(sampleBuffer, bufferSizeBytes/sizeof(SongRenderer::Sample), callbackData); From c23e94099176907f9a7a628b872a7e50ab1324e2 Mon Sep 17 00:00:00 2001 From: PoroCYon Date: Wed, 9 Sep 2020 16:53:59 +0200 Subject: [PATCH 32/32] stuff --- WaveSabreCore/src/GmDls.cpp | 1 + WaveSabreCore/src/SampleLoader.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/WaveSabreCore/src/GmDls.cpp b/WaveSabreCore/src/GmDls.cpp index 792d989c..59341872 100644 --- a/WaveSabreCore/src/GmDls.cpp +++ b/WaveSabreCore/src/GmDls.cpp @@ -80,6 +80,7 @@ namespace WaveSabreCore for (int i = 0; i < sizeof(gmDlsPaths)/sizeof(*gmDlsPaths); ++i) { gmDlsPathAlloc = true; gmDlsPath = (char*)malloc(0x100); + // TODO: $WINEPREFIX snprintf(gmDlsPath, 0x100, "%s/.wine/drive_c/windows/system32/%s", home, gmDlsPaths[i]); if (access(gmDlsPath, R_OK) == 0) diff --git a/WaveSabreCore/src/SampleLoader.cpp b/WaveSabreCore/src/SampleLoader.cpp index c50fb17d..3d07a384 100644 --- a/WaveSabreCore/src/SampleLoader.cpp +++ b/WaveSabreCore/src/SampleLoader.cpp @@ -118,6 +118,9 @@ namespace WaveSabreCore // write fake(ish) WAV header size_t M = (waveFormat->nAvgBytesPerSec * waveFormat->nSamplesPerSec) / waveFormat->nChannels; size_t wvfmtsz = sizeof(WAVEFORMATEX) + waveFormat->cbSize; +#ifndef NDEBUG + assert(waveFormat->cbSize <= 8 && "cbSize too high!"); +#endif char fileheader[4+4/*RIFF*/ + 4/*WAVE*/ + 4+4+wvfmtsz/*fmt */ + 4+4+4/*fact*/ + 4+4/*data*/]; memcpy(&fileheader[0], "RIFF", 4);