diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ea39178b..2a1c28130 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -86,8 +86,10 @@ jobs: uses: actions/checkout@v1 - name: Checkout submodules run: git submodule update --init --recursive + - name: brew update + run: brew update - name: Install packages - run: brew install cmake freetype libvorbis sdl2 libpng jpeg libarchive + run: brew install freetype libvorbis sdl2 libpng jpeg libarchive - name: cmake run: cmake . -DLibArchive_LIBRARY=/usr/local/opt/libarchive/lib/libarchive.dylib -DLibArchive_INCLUDE_DIR=/usr/local/opt/libarchive/include -DCMAKE_BUILD_TYPE=Release - name: make diff --git a/.gitignore b/.gitignore index cb4bc67ed..25114877b 100755 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ bin/skins/* !bin/skins/Default bin/skins/Default/skin.cfg bin/replays +bin/profiles # Intermediate Files x64 diff --git a/Audio/CMakeLists.txt b/Audio/CMakeLists.txt index b427b1d5e..bc739b152 100644 --- a/Audio/CMakeLists.txt +++ b/Audio/CMakeLists.txt @@ -59,6 +59,9 @@ target_link_libraries(Audio ${SDL2_LIBRARY}) target_link_libraries(Audio ${OGG_LIBRARIES}) target_link_libraries(Audio ${Vorbis_LIBRARIES}) -if(WIN32) - target_compile_options(Graphics PRIVATE /Zi) -endif() +target_link_libraries(Audio cc-common) + +# Enable multiprocess compiling +if(MSVC) + target_compile_options(Audio PRIVATE /MP) +endif(MSVC) \ No newline at end of file diff --git a/Audio/include/Audio/AudioBase.hpp b/Audio/include/Audio/AudioBase.hpp index 60be9650b..293525637 100644 --- a/Audio/include/Audio/AudioBase.hpp +++ b/Audio/include/Audio/AudioBase.hpp @@ -7,9 +7,8 @@ class DSP { protected: DSP() = default; // Abstract - DSP(const DSP&) = delete; + DSP(const DSP &) = delete; - inline void SetSampleRate(uint32 sampleRate) { m_sampleRate = sampleRate; } uint32 GetStartSample() const; uint32 GetCurrentSample() const; @@ -17,23 +16,24 @@ class DSP // Only use this for initializing parameters uint32 m_sampleRate = 0; - class AudioBase* m_audioBase = nullptr; - class Audio_Impl* m_audio = nullptr; + class AudioBase *m_audioBase = nullptr; public: virtual ~DSP(); + static bool Sorter(DSP *&a, DSP *&b); - void SetAudio(class Audio_Impl* audio); - void SetAudioBase(class AudioBase* audioBase); + void SetAudioBase(class AudioBase *audioBase); inline void RemoveAudioBase() { m_audioBase = nullptr; } + inline void SetSampleRate(uint32 sampleRate) { m_sampleRate = sampleRate; } // Process amount of samples in stereo float format - virtual void Process(float* out, uint32 numSamples) = 0; - virtual const char* GetName() const = 0; + virtual void Process(float *out, uint32 numSamples) = 0; + virtual const char *GetName() const = 0; float mix = 1.0f; uint32 priority = 0; uint32 startTime = 0; + uint32 endTime = 0; int32 chartOffset = 0; int32 lastTimingPoint = 0; }; @@ -46,27 +46,31 @@ class AudioBase public: virtual ~AudioBase(); // Process amount of samples in stereo float format - virtual void Process(float* out, uint32 numSamples) = 0; - + virtual void Process(float *out, uint32 numSamples) = 0; + // Gets the playback position in millisecond virtual int32 GetPosition() const = 0; // Get the sample rate of this audio stream virtual uint32 GetSampleRate() const = 0; + // Get the exact playback position in samples + virtual uint64 GetSamplePos() const = 0; + // Get the sample rate of the audio connected to this uint32 GetAudioSampleRate() const; // Gets pcm data from a decoded stream, nullptr if not available - virtual float* GetPCM() = 0; + virtual float *GetPCM() = 0; + // Gets pcm sample count virtual uint64 GetPCMCount() const = 0; + virtual void PreRenderDSPs(Vector &DSPs) = 0; - void ProcessDSPs(float* out, uint32 numSamples); + void ProcessDSPs(float *out, uint32 numSamples); // Adds a signal processor to the audio - void AddDSP(DSP* dsp); + void AddDSP(DSP *dsp); // Removes a signal processor from the audio - void RemoveDSP(DSP* dsp); - + void RemoveDSP(DSP *dsp); void Deregister(); @@ -80,9 +84,10 @@ class AudioBase return m_volume; } - Vector DSPs; + Vector DSPs; float PlaybackSpeed = 1.0; - class Audio_Impl* audio = nullptr; + class Audio_Impl *audio = nullptr; + private: float m_volume = 1.0f; }; \ No newline at end of file diff --git a/Audio/include/Audio/AudioStream.hpp b/Audio/include/Audio/AudioStream.hpp index 9bbd953e0..0b758030e 100644 --- a/Audio/include/Audio/AudioStream.hpp +++ b/Audio/include/Audio/AudioStream.hpp @@ -10,7 +10,8 @@ class Audio; class AudioStream : public AudioBase { public: - static Ref Create(Audio* audio, const String& path, bool preload); + static Ref Create(Audio *audio, const String &path, bool preload); + static Ref Clone(Audio *audio, Ref source); virtual ~AudioStream() = default; // Starts playback of the stream or continues a paused stream virtual void Play() = 0; diff --git a/Audio/include/Audio/DSP.hpp b/Audio/include/Audio/DSP.hpp index 3b5792f34..262ac4fe9 100644 --- a/Audio/include/Audio/DSP.hpp +++ b/Audio/include/Audio/DSP.hpp @@ -10,8 +10,8 @@ class PanDSP : public DSP public: // -1 to 1 LR pan value float panning = 0.0f; - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "PanDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "PanDSP"; } }; // Biquad Filter @@ -28,8 +28,8 @@ class BQFDSP : public DSP float a1 = 0.0f; float a2 = 0.0f; - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "BQFDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "BQFDSP"; } // Sets the filter parameters void SetPeaking(float q, float freq, float gain); @@ -39,13 +39,14 @@ class BQFDSP : public DSP void SetPeaking(float q, float freq, float gain, float sampleRate); void SetLowPass(float q, float freq, float sampleRate); void SetHighPass(float q, float freq, float sampleRate); + private: // Delayed samples static const uint32 order = 2; // FIR Delay buffers - float zb[2][order]; + float zb[2][order]{}; // IIR Delay buffers - float za[2][order]; + float za[2][order]{}; }; // Combinded Low/High-pass and Peaking filter @@ -55,9 +56,10 @@ class CombinedFilterDSP : public DSP CombinedFilterDSP(uint32 sampleRate); void SetLowPass(float q, float freq, float peakQ, float peakGain); void SetHighPass(float q, float freq, float peakQ, float peakGain); - virtual const char* GetName() const { return "CombinedFilterDSP"; } + virtual const char *GetName() const { return "CombinedFilterDSP"; } + + virtual void Process(float *out, uint32 numSamples); - virtual void Process(float* out, uint32 numSamples); private: BQFDSP a; BQFDSP peak; @@ -70,8 +72,9 @@ class LimiterDSP : public DSP LimiterDSP(uint32 sampleRate); float releaseTime = 0.1f; - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "LimiterDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "LimiterDSP"; } + private: float m_currentMaxVolume = 1.0f; float m_currentReleaseTimer = releaseTime; @@ -84,12 +87,13 @@ class BitCrusherDSP : public DSP // Duration of samples, <1 = disable void SetPeriod(float period = 0); - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "BitCrusherDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "BitCrusherDSP"; } + private: uint32 m_period = 1; uint32 m_increment = 0; - float m_sampleBuffer[2] = { 0.0f }; + float m_sampleBuffer[2] = {0.0f}; uint32 m_currentDuration = 0; }; @@ -105,14 +109,15 @@ class GateDSP : public DSP // Low volume float low = 0.1f; - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "GateDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "GateDSP"; } + private: float m_gating = 0.5f; uint32 m_length = 0; - uint32 m_fadeIn = 0; // Fade In mark + uint32 m_fadeIn = 0; // Fade In mark uint32 m_fadeOut = 0; // Fade Out mark - uint32 m_halfway; // Halfway mark + uint32 m_halfway{}; // Halfway mark uint32 m_currentSample = 0; }; @@ -123,8 +128,9 @@ class TapeStopDSP : public DSP void SetLength(double length); - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "TapeStopDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "TapeStopDSP"; } + private: uint32 m_length = 0; Vector m_sampleBuffer; @@ -142,8 +148,9 @@ class RetriggerDSP : public DSP void SetGating(float gating); void SetMaxLength(uint32 length); - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "RetriggerDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "RetriggerDSP"; } + private: float m_gating = 0.75f; uint32 m_length = 0; @@ -166,16 +173,17 @@ class WobbleDSP : public BQFDSP float fmax = 20000.0f; float q = 1.414f; - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "WobbleDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "WobbleDSP"; } + private: - uint32 m_length; + uint32 m_length{}; uint32 m_currentSample = 0; }; // Referenced http://www.musicdsp.org/files/phaser.cpp class PhaserDSP : public DSP -{ +{ public: PhaserDSP(uint32 sampleRate); @@ -184,13 +192,13 @@ class PhaserDSP : public DSP // Frequency range float dmin = 1000.0f; float dmax = 4000.0f; - float fb = 0.2f; //feedback + float fb = 0.2f; //feedback float lmix = 0.33f; //local mix void SetLength(double length); - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "PhaserDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "PhaserDSP"; } private: uint32 m_length = 0; @@ -204,7 +212,7 @@ class PhaserDSP : public DSP }; APF filters[2][6]; - float za[2] = { 0.0f }; + float za[2] = {0.0f}; }; class FlangerDSP : public DSP @@ -215,8 +223,9 @@ class FlangerDSP : public DSP void SetLength(double length); void SetDelayRange(uint32 min, uint32 max); - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "FlangerDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "FlangerDSP"; } + private: uint32 m_length = 0; @@ -239,8 +248,9 @@ class EchoDSP : public DSP float feedback = 0.6f; - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "EchoDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "EchoDSP"; } + private: uint32 m_bufferLength = 0; size_t m_bufferOffset = 0; @@ -248,7 +258,6 @@ class EchoDSP : public DSP Vector m_sampleBuffer; }; - class SidechainDSP : public DSP { public: @@ -260,10 +269,11 @@ class SidechainDSP : public DSP // Volume multiplier for the sidechaing float amount = 0.25f; - Interpolation::CubicBezier curve; + Interpolation::CubicBezier curve{}; + + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "SidechainDSP"; } - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "SidechainDSP"; } private: uint32 m_length = 0; size_t m_time = 0; @@ -279,8 +289,9 @@ class PitchShiftDSP : public DSP ~PitchShiftDSP(); - virtual void Process(float* out, uint32 numSamples); - virtual const char* GetName() const { return "PitchShiftDSP"; } + virtual void Process(float *out, uint32 numSamples); + virtual const char *GetName() const { return "PitchShiftDSP"; } + private: - class PitchShiftDSP_Impl* m_impl; + class PitchShiftDSP_Impl *m_impl; }; \ No newline at end of file diff --git a/Audio/src/Audio.cpp b/Audio/src/Audio.cpp index 5bb084963..4f8a169b1 100644 --- a/Audio/src/Audio.cpp +++ b/Audio/src/Audio.cpp @@ -5,7 +5,7 @@ #include "AudioOutput.hpp" #include "DSP.hpp" -Audio* g_audio = nullptr; +Audio *g_audio = nullptr; static Audio_Impl g_impl; Audio_Impl::Audio_Impl() @@ -15,7 +15,7 @@ Audio_Impl::Audio_Impl() #endif } -void Audio_Impl::Mix(void* data, uint32& numSamples) +void Audio_Impl::Mix(void *data, uint32 &numSamples) { double adv = GetSecondsPerSample(); @@ -30,17 +30,17 @@ void Audio_Impl::Mix(void* data, uint32& numSamples) } uint32 currentNumberOfSamples = 0; - while(currentNumberOfSamples < numSamples) + while (currentNumberOfSamples < numSamples) { // Generate new sample - if(m_remainingSamples <= 0) + if (m_remainingSamples <= 0) { // Clear sample buffer storing a fixed amount of samples m_sampleBuffer.fill(0); // Render items lock.lock(); - for(auto& item : itemsToRender) + for (auto &item : itemsToRender) { // Clear per-channel data m_itemBuffer.fill(0); @@ -54,7 +54,7 @@ void Audio_Impl::Mix(void* data, uint32& numSamples) #endif // Mix into buffer and apply volume scaling - for(uint32 i = 0; i < m_sampleBufferLength; i++) + for (uint32 i = 0; i < m_sampleBufferLength; i++) { m_sampleBuffer[i * 2 + 0] += m_itemBuffer[i * 2] * item->GetVolume(); m_sampleBuffer[i * 2 + 1] += m_itemBuffer[i * 2 + 1] * item->GetVolume(); @@ -62,14 +62,14 @@ void Audio_Impl::Mix(void* data, uint32& numSamples) } // Process global DSPs - for(auto dsp : globalDSPs) + for (auto dsp : globalDSPs) { dsp->Process(m_sampleBuffer.data(), m_sampleBufferLength); } lock.unlock(); // Apply volume levels - for(uint32 i = 0; i < m_sampleBufferLength; i++) + for (uint32 i = 0; i < m_sampleBufferLength; i++) { m_sampleBuffer[i * 2 + 0] *= globalVolume; m_sampleBuffer[i * 2 + 1] *= globalVolume; @@ -86,19 +86,19 @@ void Audio_Impl::Mix(void* data, uint32& numSamples) // Copy samples from sample buffer uint32 sampleOffset = m_sampleBufferLength - m_remainingSamples; uint32 maxSamples = Math::Min(numSamples - currentNumberOfSamples, m_remainingSamples); - for(uint32 c = 0; c < outputChannels; c++) + for (uint32 c = 0; c < outputChannels; c++) { - if(c < 2) + if (c < 2) { - for(uint32 i = 0; i < maxSamples; i++) + for (uint32 i = 0; i < maxSamples; i++) { if (output->IsIntegerFormat()) { - ((int16*)data)[(currentNumberOfSamples + i) * outputChannels + c] = (int16)(0x7FFF * Math::Clamp(m_sampleBuffer[(sampleOffset + i) * 2 + c],-1.f,1.f)); + ((int16 *)data)[(currentNumberOfSamples + i) * outputChannels + c] = (int16)(0x7FFF * Math::Clamp(m_sampleBuffer[(sampleOffset + i) * 2 + c], -1.f, 1.f)); } else { - ((float*)data)[(currentNumberOfSamples + i) * outputChannels + c] = m_sampleBuffer[(sampleOffset + i) * 2 + c]; + ((float *)data)[(currentNumberOfSamples + i) * outputChannels + c] = m_sampleBuffer[(sampleOffset + i) * 2 + c]; } } } @@ -111,7 +111,6 @@ void Audio_Impl::Mix(void* data, uint32& numSamples) void Audio_Impl::Start() { limiter = new LimiterDSP(GetSampleRate()); - limiter->SetAudio(this); limiter->releaseTime = 0.2f; globalDSPs.Add(limiter); @@ -125,7 +124,7 @@ void Audio_Impl::Stop() delete limiter; limiter = nullptr; } -void Audio_Impl::Register(AudioBase* audio) +void Audio_Impl::Register(AudioBase *audio) { if (audio) { @@ -135,7 +134,7 @@ void Audio_Impl::Register(AudioBase* audio) lock.unlock(); } } -void Audio_Impl::Deregister(AudioBase* audio) +void Audio_Impl::Deregister(AudioBase *audio) { lock.lock(); itemsToRender.Remove(audio); @@ -159,7 +158,7 @@ Audio::Audio() } Audio::~Audio() { - if(m_initialized) + if (m_initialized) { g_impl.Stop(); delete g_impl.output; @@ -174,7 +173,7 @@ bool Audio::Init(bool exclusive) audioLatency = 0; g_impl.output = new AudioOutput(); - if(!g_impl.output->Init(exclusive)) + if (!g_impl.output->Init(exclusive)) { delete g_impl.output; g_impl.output = nullptr; @@ -193,16 +192,16 @@ uint32 Audio::GetSampleRate() const { return g_impl.output->GetSampleRate(); } -class Audio_Impl* Audio::GetImpl() +class Audio_Impl *Audio::GetImpl() { return &g_impl; } -Ref Audio::CreateStream(const String& path, bool preload) +Ref Audio::CreateStream(const String &path, bool preload) { return AudioStream::Create(this, path, preload); } -Sample Audio::CreateSample(const String& path) +Sample Audio::CreateSample(const String &path) { return SampleRes::Create(this, path); } @@ -214,6 +213,7 @@ void Audio_Impl::InitMemoryGuard() } void Audio_Impl::CheckMemoryGuard() { - for (auto x : m_guard) assert(x == 0); + for (auto x : m_guard) + assert(x == 0); } #endif \ No newline at end of file diff --git a/Audio/src/AudioBase.cpp b/Audio/src/AudioBase.cpp index ba8e93deb..efa6730c8 100644 --- a/Audio/src/AudioBase.cpp +++ b/Audio/src/AudioBase.cpp @@ -5,12 +5,12 @@ uint32 DSP::GetStartSample() const { - return static_cast(startTime * static_cast(m_audio->GetSampleRate()) / 1000.0); + return static_cast(startTime * static_cast(m_audioBase->GetSampleRate()) / 1000.0); } uint32 DSP::GetCurrentSample() const { - return static_cast(m_audioBase->GetPosition() * static_cast(m_audio->GetSampleRate()) / 1000.0); + return static_cast(m_audioBase->GetSamplePos()); } DSP::~DSP() @@ -19,30 +19,25 @@ DSP::~DSP() assert(!m_audioBase); } -void DSP::SetAudio(Audio_Impl* audio) +bool DSP::Sorter(DSP *&a, DSP *&b) { - assert(!m_audio && !m_audioBase); - - m_audio = audio; - m_audioBase = nullptr; + if (a->priority == b->priority) + { + return a->startTime < b->startTime; + } + return a->priority < b->priority; } -void DSP::SetAudioBase(class AudioBase* audioBase) +void DSP::SetAudioBase(class AudioBase *audioBase) { if (!audioBase) { m_audioBase = nullptr; - m_audio = nullptr; return; } - assert(!m_audio && !m_audioBase); - m_audioBase = audioBase; - m_audio = audioBase->audio; - - assert(m_sampleRate == m_audio->GetSampleRate()); } AudioBase::~AudioBase() @@ -55,28 +50,27 @@ uint32 AudioBase::GetAudioSampleRate() const { return audio->GetSampleRate(); } -void AudioBase::ProcessDSPs(float* out, uint32 numSamples) +void AudioBase::ProcessDSPs(float *out, uint32 numSamples) { - for(DSP* dsp : DSPs) + for (DSP *dsp : DSPs) { dsp->Process(out, numSamples); } } -void AudioBase::AddDSP(DSP* dsp) +void AudioBase::AddDSP(DSP *dsp) { audio->lock.lock(); DSPs.AddUnique(dsp); // Sort by priority - DSPs.Sort([](DSP* l, DSP* r) - { - if(l->priority == r->priority) + DSPs.Sort([](DSP *l, DSP *r) { + if (l->priority == r->priority) return l < r; return l->priority < r->priority; }); dsp->SetAudioBase(this); audio->lock.unlock(); } -void AudioBase::RemoveDSP(DSP* dsp) +void AudioBase::RemoveDSP(DSP *dsp) { assert(DSPs.Contains(dsp)); @@ -89,14 +83,14 @@ void AudioBase::RemoveDSP(DSP* dsp) void AudioBase::Deregister() { // Remove from audio manager - if(audio) + if (audio) { audio->Deregister(this); } // Unbind DSP's // It is safe to do here since the audio won't be rendered again after a call to deregister - for(DSP* dsp : DSPs) + for (DSP *dsp : DSPs) { dsp->RemoveAudioBase(); } diff --git a/Audio/src/AudioStream.cpp b/Audio/src/AudioStream.cpp index a1ca4421d..5ec5efca1 100644 --- a/Audio/src/AudioStream.cpp +++ b/Audio/src/AudioStream.cpp @@ -4,6 +4,7 @@ #include "AudioStreamMp3.hpp" #include "AudioStreamOgg.hpp" #include "AudioStreamWav.hpp" +#include "AudioStreamPcm.hpp" #include using CreateFunc = Ref(Audio *, const String &, bool); @@ -15,33 +16,45 @@ static std::unordered_map decoders = { //{"wav", AudioStreamWav::Create}, }; -static Ref FindImplementation(Audio* audio, const String& path, bool preload) +static Ref FindImplementation(Audio *audio, const String &path, bool preload) { Ref impl; // Try decoder based on extension auto fav = decoders.find(Path::GetExtension(path)); - if (fav != decoders.end()) { + if (fav != decoders.end()) + { impl = fav->second(audio, path, preload); - if (impl) { + if (impl) + { return impl; } } // Fallback on trying each other method - for (auto it = decoders.begin(); it != decoders.end(); it++) { + for (auto it = decoders.begin(); it != decoders.end(); it++) + { if (fav == it) continue; impl = it->second(audio, path, preload); - if (impl) { + if (impl) + { return impl; } } return impl; } -Ref AudioStream::Create(Audio* audio, const String& path, bool preload) +Ref AudioStream::Create(Audio *audio, const String &path, bool preload) { Ref impl = FindImplementation(audio, path, preload); - if(impl) + if (impl) audio->GetImpl()->Register(impl.get()); return impl; +} + +Ref AudioStream::Clone(Audio *audio, Ref source) +{ + auto clone = AudioStreamPcm::Create(audio, source); + if (clone) + audio->GetImpl()->Register(clone.get()); + return clone; } \ No newline at end of file diff --git a/Audio/src/AudioStreamBase.cpp b/Audio/src/AudioStreamBase.cpp index 507a10dc1..5b35842c7 100644 --- a/Audio/src/AudioStreamBase.cpp +++ b/Audio/src/AudioStreamBase.cpp @@ -5,18 +5,18 @@ // Fixed point format for resampling const uint64 AudioStreamBase::fp_sampleStep = 1ull << 48; -BinaryStream& AudioStreamBase::m_reader() +BinaryStream &AudioStreamBase::m_reader() { - return m_preloaded ? (BinaryStream&)m_memoryReader : (BinaryStream&)m_fileReader; + return m_preloaded ? (BinaryStream &)m_memoryReader : (BinaryStream &)m_fileReader; } -bool AudioStreamBase::Init(Audio* audio, const String& path, bool preload) +bool AudioStreamBase::Init(Audio *audio, const String &path, bool preload) { m_audio = audio; - if(!m_file.OpenRead(path)) + if (!m_file.OpenRead(path)) return false; - if(preload) + if (preload) { m_data.resize(m_file.GetSize()); m_file.Read(m_data.data(), m_data.size()); @@ -39,8 +39,8 @@ void AudioStreamBase::m_initSampling(uint32 sampleRate) double stepCheck = (double)m_sampleStepIncrement / (double)fp_sampleStep; // TODO: for practice mode: m_sampleStepIncrement *= playback_speed; m_numChannels = 2; - m_readBuffer = new float*[m_numChannels]; - for(uint32 c = 0; c < m_numChannels; c++) + m_readBuffer = new float *[m_numChannels]; + for (uint32 c = 0; c < m_numChannels; c++) { m_readBuffer[c] = new float[m_bufferSize]; } @@ -48,11 +48,11 @@ void AudioStreamBase::m_initSampling(uint32 sampleRate) void AudioStreamBase::Play() { - if(!m_playing) + if (!m_playing) { m_playing = true; } - if(m_paused) + if (m_paused) { m_paused = false; m_restartTiming(); @@ -60,7 +60,7 @@ void AudioStreamBase::Play() } void AudioStreamBase::Pause() { - if(!m_paused) + if (!m_paused) { // Store time the stream was paused m_streamTimeOffset = m_streamTimer.SecondsAsDouble(); @@ -78,25 +78,30 @@ bool AudioStreamBase::HasEnded() const } uint64 AudioStreamBase::m_secondsToSamples(double s) const { - return (uint64)(s * (double)const_cast(this)->GetStreamRate_Internal()); + return (uint64)(s * (double)const_cast(this)->GetStreamRate_Internal()); } double AudioStreamBase::SamplesToSeconds(int64 s) const { - return (double)s / (double)const_cast(this)->GetStreamRate_Internal(); + return (double)s / (double)const_cast(this)->GetStreamRate_Internal(); } double AudioStreamBase::m_getPositionSeconds(bool allowFreezeSkip /*= true*/) const { double samplePosTime = SamplesToSeconds(m_samplePos); - if(m_paused || m_samplePos < 0) + if (m_paused || m_samplePos < 0) return samplePosTime; else { double ret = m_streamTimeOffset + m_streamTimer.SecondsAsDouble() - m_offsetCorrection; - if(allowFreezeSkip && (ret - samplePosTime) > 0.2f) // Prevent time from running of when the application freezes + if (allowFreezeSkip && (ret - samplePosTime) > 0.2f) // Prevent time from running of when the application freezes return samplePosTime; return ret; } } +uint64 AudioStreamBase::GetSamplePos() const +{ + return m_samplePos; +} + int32 AudioStreamBase::GetPosition() const { return (int32)(m_getPositionSeconds() * 1000.0); @@ -110,13 +115,13 @@ void AudioStreamBase::SetPosition(int32 pos) m_ended = false; m_lock.unlock(); } -float* AudioStreamBase::GetPCM() +float *AudioStreamBase::GetPCM() { return GetPCM_Internal(); } uint64 AudioStreamBase::GetPCMCount() const { - return m_samplesTotal; + return GetSampleCount_Internal(); } uint32 AudioStreamBase::GetSampleRate() const { @@ -131,23 +136,27 @@ void AudioStreamBase::m_restartTiming() m_deltaSum = 0; m_deltaSamples = 0; } -void AudioStreamBase::Process(float* out, uint32 numSamples) +void AudioStreamBase::PreRenderDSPs(Vector &DSPs) { - if(!m_playing || m_paused) + PreRenderDSPs_Internal(DSPs); +} +void AudioStreamBase::Process(float *out, uint32 numSamples) +{ + if (!m_playing || m_paused) return; m_lock.lock(); uint32 outCount = 0; - while(outCount < numSamples) + while (outCount < numSamples) { - if(m_remainingBufferData > 0) + if (m_remainingBufferData > 0) { uint32 idxStart = (m_currentBufferSize - m_remainingBufferData); uint32 readOffset = 0; // Offset from the start to read from - for(uint32 i = 0; outCount < numSamples && readOffset < m_remainingBufferData; i++) + for (uint32 i = 0; outCount < numSamples && readOffset < m_remainingBufferData; i++) { - if(m_samplePos < 0) + if (m_samplePos < 0) { out[outCount * 2] = 0.0f; out[outCount * 2 + 1] = 0.0f; @@ -161,10 +170,10 @@ void AudioStreamBase::Process(float* out, uint32 numSamples) // Increment source sample with resampling m_sampleStep += static_cast(m_sampleStepIncrement * PlaybackSpeed); - while(m_sampleStep >= fp_sampleStep) + while (m_sampleStep >= fp_sampleStep) { m_sampleStep -= fp_sampleStep; - if(m_samplePos >= 0) + if (m_samplePos >= 0) readOffset++; m_samplePos++; } @@ -172,11 +181,11 @@ void AudioStreamBase::Process(float* out, uint32 numSamples) m_remainingBufferData -= readOffset; } - if(outCount >= numSamples) + if (outCount >= numSamples) break; // Read more data - if(DecodeData_Internal() <= 0) + if (DecodeData_Internal() <= 0) { // Ended Log("Audio stream ended", Logger::Severity::Info); @@ -192,11 +201,11 @@ void AudioStreamBase::Process(float* out, uint32 numSamples) m_samplePos = GetStreamPosition_Internal() - (int64)m_remainingBufferData; } - if(m_samplePos > 0) + if (m_samplePos > 0) { - if((uint64)m_samplePos >= m_samplesTotal) + if ((uint64)m_samplePos >= m_samplesTotal) { - if(!m_ended) + if (!m_ended) { // Ended Log("Audio stream ended", Logger::Severity::Info); @@ -209,14 +218,14 @@ void AudioStreamBase::Process(float* out, uint32 numSamples) m_deltaSamples += 1; double avgDelta = m_deltaSum / (double)m_deltaSamples; - if(abs(timingDelta - avgDelta) > 0.2) + if (abs(timingDelta - avgDelta) > 0.2) { Logf("Timing restart, delta = %f", Logger::Severity::Info, avgDelta); m_restartTiming(); } else { - if(fabs(avgDelta) > 0.001f) + if (fabs(avgDelta) > 0.001f) { // Fine tune timing double step = abs(avgDelta) * 0.1f; diff --git a/Audio/src/AudioStreamBase.hpp b/Audio/src/AudioStreamBase.hpp index fed80619a..0877e8545 100644 --- a/Audio/src/AudioStreamBase.hpp +++ b/Audio/src/AudioStreamBase.hpp @@ -9,17 +9,17 @@ class AudioStreamBase : public AudioStream // Fixed point format for sample positions (used in resampling) static const uint64 fp_sampleStep; - Audio* m_audio; + Audio *m_audio; File m_file; Buffer m_data; MemoryReader m_memoryReader; FileReader m_fileReader; bool m_preloaded = false; - BinaryStream& m_reader(); + BinaryStream &m_reader(); mutex m_lock; - float** m_readBuffer = nullptr; + float **m_readBuffer = nullptr; uint32 m_bufferSize = 4096; uint32 m_numChannels = 0; uint32 m_currentBufferSize = 0; @@ -53,23 +53,28 @@ class AudioStreamBase : public AudioStream // Implementation specific set position virtual void SetPosition_Internal(int32 pos) = 0; virtual int32 GetStreamPosition_Internal() = 0; - virtual float* GetPCM_Internal() = 0; + virtual float *GetPCM_Internal() = 0; virtual uint32 GetSampleRate_Internal() const = 0; + virtual uint64 GetSampleCount_Internal() const = 0; + virtual void PreRenderDSPs_Internal(Vector &DSPs){}; // Internal sample rate virtual int32 GetStreamRate_Internal() = 0; // Implementation specific decode // return negative for end of stream or failure virtual int32 DecodeData_Internal() = 0; - virtual bool Init(Audio* audio, const String& path, bool preload); + virtual bool Init(Audio *audio, const String &path, bool preload); + public: virtual void Play() override; virtual void Pause() override; virtual bool HasEnded() const override; double SamplesToSeconds(int64 s) const; virtual int32 GetPosition() const override; + virtual uint64 GetSamplePos() const override; virtual void SetPosition(int32 pos) override; - virtual float* GetPCM() override; + virtual float *GetPCM() override; virtual uint64 GetPCMCount() const override; virtual uint32 GetSampleRate() const override; - virtual void Process(float* out, uint32 numSamples) override; + virtual void PreRenderDSPs(Vector &DSPs) override; + virtual void Process(float *out, uint32 numSamples) override; }; \ No newline at end of file diff --git a/Audio/src/AudioStreamMa.cpp b/Audio/src/AudioStreamMa.cpp index 88dd283b9..0dc38bb32 100644 --- a/Audio/src/AudioStreamMa.cpp +++ b/Audio/src/AudioStreamMa.cpp @@ -2,9 +2,9 @@ #include "AudioStreamMa.hpp" #define DR_WAV_IMPLEMENTATION -#include "extras/dr_wav.h" // Enables WAV decoding. +#include "extras/dr_wav.h" // Enables WAV decoding. #define DR_FLAC_IMPLEMENTATION -#include "extras/dr_flac.h" // Enables FLAC decoding. +#include "extras/dr_flac.h" // Enables FLAC decoding. #define DR_MP3_IMPLEMENTATION #include "extras/dr_mp3.h" // Enables MP3 decoding. #define STB_VORBIS_HEADER_ONLY @@ -36,7 +36,7 @@ AudioStreamMa::~AudioStreamMa() delete[] m_readBuffer; } -bool AudioStreamMa::Init(Audio* audio, const String& path, bool preload) +bool AudioStreamMa::Init(Audio *audio, const String &path, bool preload) { if (!AudioStreamBase::Init(audio, path, preload)) // Always preload for now return false; @@ -46,10 +46,10 @@ bool AudioStreamMa::Init(Audio* audio, const String& path, bool preload) if (m_preloaded) { - result = ma_decode_memory((void*)m_data.data(), m_file.GetSize(), &config, &m_samplesTotal, (void**)&m_pcm); + result = ma_decode_memory((void *)m_data.data(), m_file.GetSize(), &config, &m_samplesTotal, (void **)&m_pcm); } else - { + { result = ma_decoder_init_file(*path, &config, &m_decoder); } @@ -76,7 +76,6 @@ void AudioStreamMa::SetPosition_Internal(int32 pos) else m_playbackPointer = pos; - if (!m_preloaded) { //seek_to_pcm_frame is very slow @@ -127,7 +126,7 @@ int32 AudioStreamMa::DecodeData_Internal() } return 0; } -float* AudioStreamMa::GetPCM_Internal() +float *AudioStreamMa::GetPCM_Internal() { return m_pcm; } @@ -135,10 +134,18 @@ uint32 AudioStreamMa::GetSampleRate_Internal() const { return sample_rate; } +uint64 AudioStreamMa::GetSampleCount_Internal() const +{ + if (m_preloaded) + { + return m_samplesTotal; + } + return 0; +} -Ref AudioStreamMa::Create(class Audio* audio, const String& path, bool preload) +Ref AudioStreamMa::Create(class Audio *audio, const String &path, bool preload) { - AudioStreamMa* impl = new AudioStreamMa(); + AudioStreamMa *impl = new AudioStreamMa(); if (!impl->Init(audio, path, preload)) { delete impl; diff --git a/Audio/src/AudioStreamMa.hpp b/Audio/src/AudioStreamMa.hpp index e0ddfe7ab..25f6a7831 100644 --- a/Audio/src/AudioStreamMa.hpp +++ b/Audio/src/AudioStreamMa.hpp @@ -6,20 +6,23 @@ class AudioStreamMa : public AudioStreamBase { private: Buffer m_Internaldata; - float* m_pcm = nullptr; + float *m_pcm = nullptr; int64 m_playbackPointer = 0; const int sample_rate = 48000; - ma_decoder m_decoder = { }; + ma_decoder m_decoder = {}; + protected: - bool Init(Audio* audio, const String& path, bool preload) override; + bool Init(Audio *audio, const String &path, bool preload) override; int32 GetStreamPosition_Internal() override; int32 GetStreamRate_Internal() override; void SetPosition_Internal(int32 pos) override; int32 DecodeData_Internal() override; - float* GetPCM_Internal() override; + float *GetPCM_Internal() override; + uint64 GetSampleCount_Internal() const override; uint32 GetSampleRate_Internal() const override; + public: AudioStreamMa() = default; ~AudioStreamMa(); - static Ref Create(class Audio* audio, const String& path, bool preload); + static Ref Create(class Audio *audio, const String &path, bool preload); }; diff --git a/Audio/src/AudioStreamMp3.cpp b/Audio/src/AudioStreamMp3.cpp index ddd8f2c92..25670bfbc 100644 --- a/Audio/src/AudioStreamMp3.cpp +++ b/Audio/src/AudioStreamMp3.cpp @@ -6,7 +6,8 @@ int AudioStreamMp3::m_unsynchsafe(int in) { int out = 0, mask = 0x7F000000; - while (mask) { + while (mask) + { out >>= 1; out |= in & mask; mask >>= 8; @@ -17,11 +18,10 @@ int AudioStreamMp3::m_unsynchsafe(int in) int AudioStreamMp3::m_toLittleEndian(int num) { - return ((num >> 24) & 0xff) | // move byte 3 to byte 0 - ((num << 8) & 0xff0000) | // move byte 1 to byte 2 - ((num >> 8) & 0xff00) | // move byte 2 to byte 1 - ((num << 24) & 0xff000000); // byte 0 to byte 3 - + return ((num >> 24) & 0xff) | // move byte 3 to byte 0 + ((num << 8) & 0xff0000) | // move byte 1 to byte 2 + ((num >> 8) & 0xff00) | // move byte 2 to byte 1 + ((num << 24) & 0xff000000); // byte 0 to byte 3 } AudioStreamMp3::~AudioStreamMp3() { @@ -34,12 +34,11 @@ AudioStreamMp3::~AudioStreamMp3() } delete[] m_readBuffer; } -bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) +bool AudioStreamMp3::Init(Audio *audio, const String &path, bool preload) { ///TODO: Write non-preload functions - if(!AudioStreamBase::Init(audio, path, true)) // Always preload for now + if (!AudioStreamBase::Init(audio, path, true)) // Always preload for now return false; - // Always use preloaded data m_mp3dataLength = m_reader().GetSize(); @@ -53,7 +52,7 @@ bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) } while (tag == "ID3") { - tagSize += m_unsynchsafe(m_toLittleEndian(*(int32*)(m_dataSource + 6 + tagSize))) + 10; + tagSize += m_unsynchsafe(m_toLittleEndian(*(int32 *)(m_dataSource + 6 + tagSize))) + 10; for (size_t i = 0; i < 3; i++) { tag[i] = m_dataSource[i + tagSize]; @@ -69,22 +68,22 @@ bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) } // Scan MP3 frame offsets uint32 sampleOffset = 0; - for(size_t i = tagSize; i < m_mp3dataLength;) + for (size_t i = tagSize; i < m_mp3dataLength;) { - if(m_dataSource[i] == 0xFF) + if (m_dataSource[i] == 0xFF) { - if(i + 1 > m_mp3dataLength) + if (i + 1 > m_mp3dataLength) continue; - if((m_dataSource[i + 1] & 0xE0) == 0xE0) // Frame Sync + if ((m_dataSource[i + 1] & 0xE0) == 0xE0) // Frame Sync { - uint8 version = (m_dataSource[i+1] & 0x18) >> 3; + uint8 version = (m_dataSource[i + 1] & 0x18) >> 3; uint8 layer = (m_dataSource[i + 1] & 0x06) >> 1; bool crc = (m_dataSource[i + 1] & 0x01) != 0; uint8 bitrateIndex = (m_dataSource[i + 2] & 0xF0) >> 4; uint8 rateIndex = (m_dataSource[i + 2] & 0x0C) >> 2; bool paddingEnabled = ((m_dataSource[i + 2] & 0x02) >> 1) != 0; uint8 channelFlags = ((m_dataSource[i + 3] & 0xC0) >> 6); - if(bitrateIndex == 0xF || rateIndex > 2) // bad + if (bitrateIndex == 0xF || rateIndex > 2) // bad { return false; i++; @@ -99,13 +98,13 @@ bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) uint32 padding = paddingEnabled ? 1 : 0; uint32 frameLength = 144 * bitrate / sampleRate + padding; - if(frameLength == 0) + if (frameLength == 0) { return false; i++; continue; } - + i += frameLength; uint32 frameSamples = (linearVersion == 0) ? 1152 : 576; m_frameIndices.Add((int32)sampleOffset, i); @@ -117,21 +116,20 @@ bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) } // No mp3 frames found - if(m_frameIndices.empty()) + if (m_frameIndices.empty()) { Logf("No valid mp3 frames found in file \"%s\"", Logger::Severity::Warning, path); return false; } - // Total sample m_samplesTotal = sampleOffset; - m_decoder = (mp3_decoder_t*)mp3_create(); + m_decoder = (mp3_decoder_t *)mp3_create(); m_preloaded = false; SetPosition_Internal(-400000); int32 r = DecodeData_Internal(); - if(r <= 0) + if (r <= 0) return false; if (preload) @@ -142,7 +140,7 @@ bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) for (int32 i = 0; i < r; i++) { m_pcm.Add(m_readBuffer[0][i]); - m_pcm.Add(m_readBuffer[1][i]); + m_pcm.Add(m_readBuffer[1][i]); } totalSamples += r; r = DecodeData_Internal(); @@ -154,7 +152,6 @@ bool AudioStreamMp3::Init(Audio* audio, const String& path, bool preload) m_preloaded = preload; m_playPos = 0; - return true; } void AudioStreamMp3::SetPosition_Internal(int32 pos) @@ -166,16 +163,15 @@ void AudioStreamMp3::SetPosition_Internal(int32 pos) else m_playPos = pos; - return; } auto it = m_frameIndices.lower_bound(pos); - if(it == m_frameIndices.end()) + if (it == m_frameIndices.end()) { --it; // Take the last frame } - if(it != m_frameIndices.end()) + if (it != m_frameIndices.end()) { m_mp3samplePosition = it->first; m_mp3dataOffset = it->second; @@ -196,7 +192,16 @@ uint32 AudioStreamMp3::GetSampleRate_Internal() const { return m_samplingRate; } -float* AudioStreamMp3::GetPCM_Internal() +uint64 AudioStreamMp3::GetSampleCount_Internal() const +{ + if (m_preloaded) + { + return m_samplesTotal; + } + return 0; +} + +float *AudioStreamMp3::GetPCM_Internal() { if (m_preloaded) return &m_pcm.front(); @@ -237,22 +242,22 @@ int32 AudioStreamMp3::DecodeData_Internal() int16 buffer[MP3_MAX_SAMPLES_PER_FRAME]; mp3_info_t info; int32 readData = 0; - while(true) + while (true) { - readData = mp3_decode(m_decoder, (uint8*)m_dataSource + m_mp3dataOffset, (int)(m_mp3dataLength - m_mp3dataOffset), buffer, &info); + readData = mp3_decode(m_decoder, (uint8 *)m_dataSource + m_mp3dataOffset, (int)(m_mp3dataLength - m_mp3dataOffset), buffer, &info); m_mp3dataOffset += readData; - if(m_mp3dataOffset >= m_mp3dataLength) // EOF + if (m_mp3dataOffset >= m_mp3dataLength) // EOF return -1; - if(readData <= 0) + if (readData <= 0) return -1; - if(info.audio_bytes >= 0) + if (info.audio_bytes >= 0) break; } int32 samplesGotten = info.audio_bytes / (info.channels * sizeof(short)); m_mp3samplePosition += samplesGotten; - if(m_firstFrame) + if (m_firstFrame) { m_bufferSize = MP3_MAX_SAMPLES_PER_FRAME / 2; m_initSampling(m_samplingRate = info.sample_rate); @@ -260,14 +265,14 @@ int32 AudioStreamMp3::DecodeData_Internal() } // Copy data to read buffer - for(int32 i = 0; i < samplesGotten; i++) + for (int32 i = 0; i < samplesGotten; i++) { - if(info.channels == 1) + if (info.channels == 1) { m_readBuffer[0][i] = (float)buffer[i] / (float)0x7FFF; m_readBuffer[1][i] = m_readBuffer[0][i]; } - else if(info.channels == 2) + else if (info.channels == 2) { m_readBuffer[0][i] = (float)buffer[i * 2 + 0] / (float)0x7FFF; m_readBuffer[1][i] = (float)buffer[i * 2 + 1] / (float)0x7FFF; @@ -278,15 +283,14 @@ int32 AudioStreamMp3::DecodeData_Internal() return samplesGotten; } -Ref AudioStreamMp3::Create(Audio* audio, const String& path, bool preload) +Ref AudioStreamMp3::Create(Audio *audio, const String &path, bool preload) { - AudioStreamMp3* impl = new AudioStreamMp3(); + AudioStreamMp3 *impl = new AudioStreamMp3(); if (!impl->Init(audio, path, preload)) { delete impl; impl = nullptr; return Ref(); - } return Ref(impl); } diff --git a/Audio/src/AudioStreamMp3.hpp b/Audio/src/AudioStreamMp3.hpp index dcecdc5d4..4f8f55238 100644 --- a/Audio/src/AudioStreamMp3.hpp +++ b/Audio/src/AudioStreamMp3.hpp @@ -2,17 +2,17 @@ #include "AudioStreamBase.hpp" extern "C" { - #include "minimp3.h" +#include "minimp3.h" } class AudioStreamMp3 : public AudioStreamBase { - mp3_decoder_t* m_decoder = nullptr; + mp3_decoder_t *m_decoder = nullptr; size_t m_mp3dataOffset = 0; size_t m_mp3dataLength = 0; int32 m_mp3samplePosition = 0; int32 m_samplingRate = 0; - uint8* m_dataSource = 0; + uint8 *m_dataSource = 0; Map m_frameIndices; Vector m_pcm; @@ -21,17 +21,19 @@ class AudioStreamMp3 : public AudioStreamBase bool m_firstFrame = true; int m_unsynchsafe(int in); int m_toLittleEndian(int num); -protected: - bool Init(Audio* audio, const String& path, bool preload) override; +protected: + bool Init(Audio *audio, const String &path, bool preload) override; void SetPosition_Internal(int32 pos) override; int32 GetStreamPosition_Internal() override; int32 GetStreamRate_Internal() override; uint32 GetSampleRate_Internal() const override; - float* GetPCM_Internal() override; + uint64 GetSampleCount_Internal() const override; + float *GetPCM_Internal() override; int32 DecodeData_Internal() override; + public: AudioStreamMp3() = default; ~AudioStreamMp3(); - static Ref Create(Audio* audio, const String& path, bool preload); + static Ref Create(Audio *audio, const String &path, bool preload); }; diff --git a/Audio/src/AudioStreamOgg.cpp b/Audio/src/AudioStreamOgg.cpp index a79520212..88de06f31 100644 --- a/Audio/src/AudioStreamOgg.cpp +++ b/Audio/src/AudioStreamOgg.cpp @@ -2,7 +2,6 @@ #include "AudioStreamOgg.hpp" #include - AudioStreamOgg::~AudioStreamOgg() { Deregister(); @@ -13,39 +12,39 @@ AudioStreamOgg::~AudioStreamOgg() } delete[] m_readBuffer; - if(!m_preloaded) + if (!m_preloaded) { ov_clear(&m_ovf); } } -bool AudioStreamOgg::Init(Audio* audio, const String& path, bool preload) +bool AudioStreamOgg::Init(Audio *audio, const String &path, bool preload) { - if(!AudioStreamBase::Init(audio, path, preload)) + if (!AudioStreamBase::Init(audio, path, preload)) return false; int32 r = ov_open_callbacks(this, &m_ovf, 0, 0, { - (decltype(ov_callbacks::read_func))&AudioStreamOgg::m_Read, - (decltype(ov_callbacks::seek_func))&AudioStreamOgg::m_Seek, - nullptr, // Close - (decltype(ov_callbacks::tell_func))&AudioStreamOgg::m_Tell, - }); - if(r != 0) + (decltype(ov_callbacks::read_func)) & AudioStreamOgg::m_Read, + (decltype(ov_callbacks::seek_func)) & AudioStreamOgg::m_Seek, + nullptr, // Close + (decltype(ov_callbacks::tell_func)) & AudioStreamOgg::m_Tell, + }); + if (r != 0) { Logf("ov_open_callbacks failed with code %d", Logger::Severity::Error, r); return false; } - vorbis_comment* comments = ov_comment(&m_ovf, 0); - vorbis_info* infoPtr = ov_info(&m_ovf, 0); - if(!infoPtr) + vorbis_comment *comments = ov_comment(&m_ovf, 0); + vorbis_info *infoPtr = ov_info(&m_ovf, 0); + if (!infoPtr) return false; m_info = *infoPtr; m_samplesTotal = ov_pcm_total(&m_ovf, 0); if (preload) { - float** readBuffer; + float **readBuffer; int r; int totalRead = 0; while (true) @@ -88,7 +87,7 @@ void AudioStreamOgg::SetPosition_Internal(int32 pos) { if (m_preloaded) { - if(pos < 0) + if (pos < 0) m_playPos = 0; else m_playPos = pos; @@ -111,13 +110,22 @@ int32 AudioStreamOgg::GetStreamRate_Internal() return (int32)m_info.rate; } -float* AudioStreamOgg::GetPCM_Internal() +float *AudioStreamOgg::GetPCM_Internal() { if (m_preloaded) return &m_pcm.front(); return nullptr; } +uint64 AudioStreamOgg::GetSampleCount_Internal() const +{ + if (m_preloaded) + { + return m_samplesTotal; + } + return 0; +} + uint32 AudioStreamOgg::GetSampleRate_Internal() const { return m_info.rate; @@ -145,7 +153,8 @@ int32 AudioStreamOgg::DecodeData_Internal() m_remainingBufferData = m_bufferSize; m_readBuffer[0][i] = 0; m_readBuffer[1][i] = 0; - if (!earlyOut) { + if (!earlyOut) + { retVal = i; earlyOut = true; } @@ -160,14 +169,14 @@ int32 AudioStreamOgg::DecodeData_Internal() return retVal; } - float** readBuffer; + float **readBuffer; int32 r = ov_read_float(&m_ovf, &readBuffer, m_bufferSize, 0); - if(r > 0) + if (r > 0) { - if(m_info.channels == 1) + if (m_info.channels == 1) { // Copy mono to read buffer - for(int32 i = 0; i < r; i++) + for (int32 i = 0; i < r; i++) { m_readBuffer[0][i] = readBuffer[0][i]; m_readBuffer[1][i] = readBuffer[0][i]; @@ -176,7 +185,7 @@ int32 AudioStreamOgg::DecodeData_Internal() else { // Copy data to read buffer - for(int32 i = 0; i < r; i++) + for (int32 i = 0; i < r; i++) { m_readBuffer[0][i] = readBuffer[0][i]; m_readBuffer[1][i] = readBuffer[1][i]; @@ -186,7 +195,7 @@ int32 AudioStreamOgg::DecodeData_Internal() m_remainingBufferData = r; return r; } - else if(r == 0) + else if (r == 0) { // EOF m_playing = false; @@ -201,33 +210,33 @@ int32 AudioStreamOgg::DecodeData_Internal() } } -size_t AudioStreamOgg::m_Read(void* ptr, size_t size, size_t nmemb, AudioStreamOgg* self) +size_t AudioStreamOgg::m_Read(void *ptr, size_t size, size_t nmemb, AudioStreamOgg *self) { - return self->m_reader().Serialize(ptr, nmemb*size); + return self->m_reader().Serialize(ptr, nmemb * size); } -int AudioStreamOgg::m_Seek(AudioStreamOgg* self, int64 offset, int whence) +int AudioStreamOgg::m_Seek(AudioStreamOgg *self, int64 offset, int whence) { - if(whence == SEEK_SET) + if (whence == SEEK_SET) self->m_reader().Seek((size_t)offset); - else if(whence == SEEK_CUR) + else if (whence == SEEK_CUR) self->m_reader().Skip((size_t)offset); - else if(whence == SEEK_END) + else if (whence == SEEK_END) self->m_reader().SeekReverse((size_t)offset); else assert(false); return 0; } -long AudioStreamOgg::m_Tell(AudioStreamOgg* self) +long AudioStreamOgg::m_Tell(AudioStreamOgg *self) { return (long)self->m_reader().Tell(); } -Ref AudioStreamOgg::Create(class Audio* audio, const String& path, bool preload) +Ref AudioStreamOgg::Create(class Audio *audio, const String &path, bool preload) { - AudioStreamOgg* impl = new AudioStreamOgg(); - if(!impl->Init(audio, path, preload)) + AudioStreamOgg *impl = new AudioStreamOgg(); + if (!impl->Init(audio, path, preload)) { delete impl; impl = nullptr; diff --git a/Audio/src/AudioStreamOgg.hpp b/Audio/src/AudioStreamOgg.hpp index 2ebac4ca7..3c5396b73 100644 --- a/Audio/src/AudioStreamOgg.hpp +++ b/Audio/src/AudioStreamOgg.hpp @@ -11,20 +11,22 @@ class AudioStreamOgg : public AudioStreamBase Vector m_pcm; int64 m_playPos; - - bool Init(Audio* audio, const String& path, bool preload) override; + bool Init(Audio *audio, const String &path, bool preload) override; void SetPosition_Internal(int32 pos) override; int32 GetStreamPosition_Internal() override; int32 GetStreamRate_Internal() override; - float* GetPCM_Internal() override; + float *GetPCM_Internal() override; uint32 GetSampleRate_Internal() const override; + uint64 GetSampleCount_Internal() const override; int32 DecodeData_Internal() override; + private: - static size_t m_Read(void* ptr, size_t size, size_t nmemb, AudioStreamOgg* self); - static int m_Seek(AudioStreamOgg* self, int64 offset, int whence); - static long m_Tell(AudioStreamOgg* self); + static size_t m_Read(void *ptr, size_t size, size_t nmemb, AudioStreamOgg *self); + static int m_Seek(AudioStreamOgg *self, int64 offset, int whence); + static long m_Tell(AudioStreamOgg *self); + public: AudioStreamOgg() = default; ~AudioStreamOgg(); - static Ref Create(class Audio* audio, const String& path, bool preload); + static Ref Create(class Audio *audio, const String &path, bool preload); }; diff --git a/Audio/src/AudioStreamPcm.cpp b/Audio/src/AudioStreamPcm.cpp new file mode 100644 index 000000000..cacc867b1 --- /dev/null +++ b/Audio/src/AudioStreamPcm.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "AudioStreamPcm.hpp" + +bool AudioStreamPcm::Init(Audio *audio, const String &path, bool preload) +{ + AudioStreamBase::Init(audio, path, preload); + m_initSampling(m_sampleRate); + return true; +} +void AudioStreamPcm::SetPosition_Internal(int32 pos) +{ + //negative pos is causing issues somewhere + //TODO: Investigate more + m_playPos = Math::Max(0, pos); +} +int32 AudioStreamPcm::GetStreamPosition_Internal() +{ + return m_playPos; +} +int32 AudioStreamPcm::GetStreamRate_Internal() +{ + return m_sampleRate; +} +float *AudioStreamPcm::GetPCM_Internal() +{ + return m_pcm; +} +uint32 AudioStreamPcm::GetSampleRate_Internal() const +{ + return m_sampleRate; +} +int32 AudioStreamPcm::DecodeData_Internal() +{ + uint32 samplesPerRead = 128; + int32 retVal = samplesPerRead; + bool earlyOut = false; + for (size_t i = 0; i < samplesPerRead; i++) + { + if (m_playPos < 0) + { + m_readBuffer[0][i] = 0; + m_readBuffer[1][i] = 0; + m_playPos++; + continue; + } + else if ((uint64)m_playPos >= m_samplesTotal) + { + m_currentBufferSize = m_bufferSize; + m_remainingBufferData = m_bufferSize; + m_readBuffer[0][i] = 0; + m_readBuffer[1][i] = 0; + if (!earlyOut) + { + retVal = i; + earlyOut = true; + } + continue; + } + m_readBuffer[0][i] = m_pcm[m_playPos * 2]; + m_readBuffer[1][i] = m_pcm[m_playPos * 2 + 1]; + m_playPos++; + } + m_currentBufferSize = samplesPerRead; + m_remainingBufferData = samplesPerRead; + return retVal; +} + +uint64 AudioStreamPcm::GetSampleCount_Internal() const +{ + return m_samplesTotal; +} +void AudioStreamPcm::PreRenderDSPs_Internal(Vector &DSPs) +{ + auto originalPlayPos = m_playPos; + for (auto &&dsp : DSPs) + { + m_playPos = ((uint64)dsp->startTime * (uint64)m_sampleRate) / 1000; + m_samplePos = m_playPos; + int64 endSamplePos = ((uint64)dsp->endTime * (uint64)m_sampleRate) / 1000; + endSamplePos = Math::Min(endSamplePos, (int64)m_samplesTotal); + if (m_playPos >= endSamplePos) + { + Logf("Effect %s at %dms not rendered", Logger::Severity::Debug, dsp->GetName(), dsp->startTime); + continue; + } + + uint32 numSamples = endSamplePos - m_playPos; + float *buffer = new float[numSamples * 2]; + memcpy(buffer, m_pcm + m_playPos * 2, numSamples * 2 * sizeof(float)); + dsp->Process(buffer, numSamples); + memcpy(m_pcm + m_playPos * 2, buffer, numSamples * 2 * sizeof(float)); + Logf("Rendered %s at %dms with %d samples", Logger::Severity::Debug, dsp->GetName(), dsp->startTime, numSamples); + delete[] buffer; + } + m_playPos = originalPlayPos; + m_samplePos = m_playPos; +} + +AudioStreamPcm::~AudioStreamPcm() +{ + Deregister(); + if (m_pcm) + { + delete m_pcm; + } +} + +Ref AudioStreamPcm::Create(class Audio *audio, const Ref &other) +{ + AudioStreamPcm *impl = new AudioStreamPcm(); + impl->m_pcm = nullptr; + float *source = other->GetPCM(); + uint64 sampleCount = other->GetPCMCount(); + if (source == nullptr || sampleCount == 0) + { + delete impl; + impl = nullptr; + } + else + { + impl->m_playPos = 0; + impl->m_pcm = new float[sampleCount * 2]; + memcpy(impl->m_pcm, source, sampleCount * 2 * sizeof(float)); + impl->m_sampleRate = other->GetSampleRate(); + impl->m_samplesTotal = sampleCount; + impl->Init(audio, "", false); + } + return Ref(impl); +} \ No newline at end of file diff --git a/Audio/src/AudioStreamPcm.hpp b/Audio/src/AudioStreamPcm.hpp new file mode 100644 index 000000000..c47b7d312 --- /dev/null +++ b/Audio/src/AudioStreamPcm.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "stdafx.h" +#include "AudioStreamBase.hpp" + +class AudioStreamPcm : public AudioStreamBase +{ +protected: + float *m_pcm; + uint32 m_sampleRate; + int64 m_playPos; + + bool Init(Audio *audio, const String &path, bool preload) override; + void SetPosition_Internal(int32 pos) override; + int32 GetStreamPosition_Internal() override; + int32 GetStreamRate_Internal() override; + float *GetPCM_Internal() override; + void PreRenderDSPs_Internal(Vector &DSPs) override; + uint32 GetSampleRate_Internal() const override; + uint64 GetSampleCount_Internal() const override; + int32 DecodeData_Internal() override; + +public: + AudioStreamPcm() = default; + ~AudioStreamPcm(); + static Ref Create(class Audio *audio, const Ref &other); +}; \ No newline at end of file diff --git a/Audio/src/AudioStreamWav.cpp b/Audio/src/AudioStreamWav.cpp index 90b46ae6e..bdd494d88 100644 --- a/Audio/src/AudioStreamWav.cpp +++ b/Audio/src/AudioStreamWav.cpp @@ -1,22 +1,22 @@ #include "stdafx.h" #include "AudioStreamWav.hpp" -uint32 AudioStreamWav::m_decode_ms_adpcm(const Buffer& encoded, Buffer* decoded, uint64 pos) +uint32 AudioStreamWav::m_decode_ms_adpcm(const Buffer &encoded, Buffer *decoded, uint64 pos) { - int16* pcm = (int16*)decoded->data(); + int16 *pcm = (int16 *)decoded->data(); - int8* src = ((int8*)encoded.data()) + pos; - int8 blockPredictors[] = { 0, 0 }; - int32 ideltas[] = { 0, 0 }; - int32 sample1[] = { 0, 0 }; - int32 sample2[] = { 0, 0 }; + int8 *src = ((int8 *)encoded.data()) + pos; + int8 blockPredictors[] = {0, 0}; + int32 ideltas[] = {0, 0}; + int32 sample1[] = {0, 0}; + int32 sample2[] = {0, 0}; blockPredictors[0] = *src++; blockPredictors[1] = *src++; assert(blockPredictors[0] >= 0 && blockPredictors[0] < 7); assert(blockPredictors[1] >= 0 && blockPredictors[0] < 7); - int16* src_16 = (int16*)src; + int16 *src_16 = (int16 *)src; ideltas[0] = *src_16++; ideltas[1] = *src_16++; @@ -31,16 +31,14 @@ uint32 AudioStreamWav::m_decode_ms_adpcm(const Buffer& encoded, Buffer* decoded, *pcm++ = sample1[0]; *pcm++ = sample1[1]; - - src = (int8*)src_16; + src = (int8 *)src_16; uint32 decodedCount = 2; int AdaptationTable[] = { 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 - }; - int AdaptCoeff1[] = { 256, 512, 0, 192, 240, 460, 392 }; - int AdaptCoeff2[] = { 0, -256, 0, 64, 0, -208, -232 }; + 768, 614, 512, 409, 307, 230, 230, 230}; + int AdaptCoeff1[] = {256, 512, 0, 192, 240, 460, 392}; + int AdaptCoeff2[] = {0, -256, 0, 64, 0, -208, -232}; // Decode the rest of the data in the block int remainingInBlock = m_format.nBlockAlign - 14; @@ -48,16 +46,15 @@ uint32 AudioStreamWav::m_decode_ms_adpcm(const Buffer& encoded, Buffer* decoded, { int8 nibbleData = *src++; - int8 nibbles[] = { 0, 0 }; + int8 nibbles[] = {0, 0}; nibbles[0] = nibbleData >> 4; nibbles[0] &= 0x0F; nibbles[1] = nibbleData & 0x0F; - int16 predictors[] = { 0, 0 }; + int16 predictors[] = {0, 0}; for (size_t i = 0; i < 2; i++) { - predictors[i] = ((sample1[i] * AdaptCoeff1[blockPredictors[i]]) + (sample2[i] * AdaptCoeff2[blockPredictors[i]])) / 256; if (nibbles[i] & 0x08) predictors[i] += (nibbles[i] - 0x10) * ideltas[i]; @@ -86,7 +83,7 @@ AudioStreamWav::~AudioStreamWav() delete[] m_readBuffer; } -bool AudioStreamWav::Init(Audio* audio, const String& path, bool preload) +bool AudioStreamWav::Init(Audio *audio, const String &path, bool preload) { if (!AudioStreamBase::Init(audio, path, preload)) // Always preload for now return false; @@ -157,13 +154,12 @@ bool AudioStreamWav::Init(Audio* audio, const String& path, bool preload) m_memoryReader.Serialize(m_Internaldata.data(), chunkHdr.nLength); } - //Decode to float uint64 pos = 0; m_pcm.resize(2 * m_samplesTotal * sizeof(float)); if (m_format.nFormat == 1) { - int16* src = ((int16*)m_Internaldata.data()); + int16 *src = ((int16 *)m_Internaldata.data()); if (m_format.nChannels == 2) { while (pos < m_samplesTotal) @@ -188,12 +184,12 @@ bool AudioStreamWav::Init(Audio* audio, const String& path, bool preload) int decoded = 0; Buffer decodedBuffer; decodedBuffer.resize(m_format.nBlockAlign * m_format.nChannels * sizeof(short)); - + while (pos < m_samplesTotal) { uint32 newDecoded = m_decode_ms_adpcm(m_Internaldata, &decodedBuffer, pos); pos += m_format.nBlockAlign; - int16* src = ((int16*)decodedBuffer.data()); + int16 *src = ((int16 *)decodedBuffer.data()); for (uint32 i = 0; i < newDecoded; i++) { m_pcm[(2 * decoded) + (i * 2)] = (float)src[i * 2] / (float)0x7FFF; @@ -303,7 +299,6 @@ void AudioStreamWav::SetPosition_Internal(int32 pos) else m_playbackPointer = pos; - if (!m_preloaded) { int filePos = 0; @@ -330,7 +325,7 @@ int32 AudioStreamWav::DecodeData_Internal() Buffer readData; readData.resize(samplesPerRead * m_format.nChannels * sizeof(short)); m_fileReader.Serialize(readData.data(), samplesPerRead * m_format.nChannels * sizeof(short)); - int16* src = ((int16*)readData.data()); + int16 *src = ((int16 *)readData.data()); if (m_format.nChannels == 2) { for (uint32 i = 0; i < samplesPerRead; i++) @@ -384,7 +379,7 @@ int32 AudioStreamWav::DecodeData_Internal() uint64 bufferOffset = 0; for (uint32 i = 0; i < decodedCount; i++) { - int16* src = ((int16*)decoded.data()) + (i * 2); + int16 *src = ((int16 *)decoded.data()) + (i * 2); m_readBuffer[0][i] = (float)src[0] / (float)0x7FFF; m_readBuffer[1][i] = (float)src[1] / (float)0x7FFF; } @@ -394,7 +389,6 @@ int32 AudioStreamWav::DecodeData_Internal() m_currentBufferSize = decodedCount; m_remainingBufferData = decodedCount; return decodedCount; - } } else @@ -426,21 +420,29 @@ int32 AudioStreamWav::DecodeData_Internal() } return 0; } -float* AudioStreamWav::GetPCM_Internal() +float *AudioStreamWav::GetPCM_Internal() { if (m_preloaded) return &m_pcm.front(); - + return nullptr; } +uint64 AudioStreamWav::GetSampleCount_Internal() const +{ + if (m_preloaded) + { + return m_samplesTotal; + } + return 0; +} uint32 AudioStreamWav::GetSampleRate_Internal() const { return m_format.nSampleRate; } -Ref AudioStreamWav::Create(class Audio* audio, const String& path, bool preload) +Ref AudioStreamWav::Create(class Audio *audio, const String &path, bool preload) { - AudioStreamWav* impl = new AudioStreamWav(); + AudioStreamWav *impl = new AudioStreamWav(); if (!impl->Init(audio, path, preload)) { delete impl; diff --git a/Audio/src/AudioStreamWav.hpp b/Audio/src/AudioStreamWav.hpp index b2068700d..a0ee24f5b 100644 --- a/Audio/src/AudioStreamWav.hpp +++ b/Audio/src/AudioStreamWav.hpp @@ -9,17 +9,16 @@ class AudioStreamWav : public AudioStreamBase char id[4]; uint32 nLength; - bool operator ==(const char* rhs) const + bool operator==(const char *rhs) const { return strncmp(id, rhs, 4) == 0; } - bool operator !=(const char* rhs) const + bool operator!=(const char *rhs) const { return !(*this == rhs); } }; - struct WavFormat { uint16 nFormat; @@ -34,19 +33,20 @@ class AudioStreamWav : public AudioStreamBase Vector m_pcm; int64 m_playbackPointer = 0; uint64 m_dataPosition = 0; - uint32 m_decode_ms_adpcm(const Buffer& encoded, Buffer* decoded, uint64 pos); + uint32 m_decode_ms_adpcm(const Buffer &encoded, Buffer *decoded, uint64 pos); protected: - - bool Init(Audio* audio, const String& path, bool preload) override; + bool Init(Audio *audio, const String &path, bool preload) override; int32 GetStreamPosition_Internal() override; int32 GetStreamRate_Internal() override; void SetPosition_Internal(int32 pos) override; int32 DecodeData_Internal() override; - float* GetPCM_Internal() override; + float *GetPCM_Internal() override; uint32 GetSampleRate_Internal() const override; + uint64 GetSampleCount_Internal() const override; + public: AudioStreamWav() = default; ~AudioStreamWav(); - static Ref Create(class Audio* audio, const String& path, bool preload); + static Ref Create(class Audio *audio, const String &path, bool preload); }; diff --git a/Audio/src/DSP.cpp b/Audio/src/DSP.cpp index 908590eeb..0254085ab 100644 --- a/Audio/src/DSP.cpp +++ b/Audio/src/DSP.cpp @@ -2,15 +2,14 @@ #include "DSP.hpp" #include "AudioOutput.hpp" #include "Audio_Impl.hpp" -#include -void PanDSP::Process(float* out, uint32 numSamples) +void PanDSP::Process(float *out, uint32 numSamples) { - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(panning > 0) + if (panning > 0) out[i * 2 + 0] = (out[i * 2 + 0] * (1.0f - panning)) * mix + out[i * 2 + 0] * (1 - mix); - if(panning < 0) + if (panning < 0) out[i * 2 + 1] = (out[i * 2 + 1] * (1.0f + panning)) * mix + out[i * 2 + 1] * (1 - mix); } } @@ -26,24 +25,22 @@ BQFDSP::BQFDSP(uint32 sampleRate) : DSP() za[i][j] = 0.f; zb[i][j] = 0.f; } - } - } -void BQFDSP::Process(float* out, uint32 numSamples) +void BQFDSP::Process(float *out, uint32 numSamples) { - for(uint32 c = 0; c < 2; c++) + for (uint32 c = 0; c < 2; c++) { - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - float& sample = out[i * 2 + c]; + float &sample = out[i * 2 + c]; float src = sample; - float filtered = - (b0 / a0) * src + - (b1 / a0) * zb[c][0] + - (b2 / a0) * zb[c][1] - - (a1 / a0) * za[c][0] - + float filtered = + (b0 / a0) * src + + (b1 / a0) * zb[c][0] + + (b2 / a0) * zb[c][1] - + (a1 / a0) * za[c][0] - (a2 / a0) * za[c][1]; // Shift delay buffers @@ -77,7 +74,7 @@ void BQFDSP::SetLowPass(float q, float freq, float sampleRate) } void BQFDSP::SetLowPass(float q, float freq) { - SetLowPass(q, freq, (float) m_sampleRate); + SetLowPass(q, freq, (float)m_sampleRate); } void BQFDSP::SetHighPass(float q, float freq, float sampleRate) { @@ -98,7 +95,7 @@ void BQFDSP::SetHighPass(float q, float freq, float sampleRate) } void BQFDSP::SetHighPass(float q, float freq) { - SetHighPass(q, freq, (float) m_sampleRate); + SetHighPass(q, freq, (float)m_sampleRate); } void BQFDSP::SetPeaking(float q, float freq, float gain, float sampleRate) { @@ -112,14 +109,14 @@ void BQFDSP::SetPeaking(float q, float freq, float gain, float sampleRate) b0 = 1 + (float)(alpha * A); b1 = -2 * (float)cw0; - b2 = 1 - (float)(alpha*A); + b2 = 1 - (float)(alpha * A); a0 = 1 + (float)(alpha / A); a1 = -2 * (float)cw0; a2 = 1 - (float)(alpha / A); } void BQFDSP::SetPeaking(float q, float freq, float gain) { - SetPeaking(q, freq, gain, (float) m_sampleRate); + SetPeaking(q, freq, gain, (float)m_sampleRate); } CombinedFilterDSP::CombinedFilterDSP(uint32 sampleRate) : DSP(), a(sampleRate), peak(sampleRate) @@ -136,7 +133,7 @@ void CombinedFilterDSP::SetHighPass(float q, float freq, float peakQ, float peak a.SetHighPass(q, freq); peak.SetPeaking(peakQ, freq, peakGain); } -void CombinedFilterDSP::Process(float* out, uint32 numSamples) +void CombinedFilterDSP::Process(float *out, uint32 numSamples) { a.mix = mix; peak.mix = mix; @@ -148,24 +145,24 @@ LimiterDSP::LimiterDSP(uint32 sampleRate) : DSP() { SetSampleRate(sampleRate); } -void LimiterDSP::Process(float* out, uint32 numSamples) +void LimiterDSP::Process(float *out, uint32 numSamples) { - const float secondsPerSample = (float) m_audio->GetSecondsPerSample(); - for(uint32 i = 0; i < numSamples; i++) + const float secondsPerSample = 1.0f / (float)m_sampleRate; + for (uint32 i = 0; i < numSamples; i++) { float currentGain = 1.0f; - if(m_currentReleaseTimer < releaseTime) + if (m_currentReleaseTimer < releaseTime) { float t = (1.0f - m_currentReleaseTimer / releaseTime); currentGain = (1.0f / m_currentMaxVolume) * t + (1.0f - t); } - - float maxVolume = Math::Max(abs(out[i*2]), abs(out[i * 2 + 1])); + + float maxVolume = Math::Max(abs(out[i * 2]), abs(out[i * 2 + 1])); out[i * 2] *= currentGain * 0.9f; out[i * 2 + 1] *= currentGain * 0.9f; - + float currentMax = 1.0f / currentGain; - if(maxVolume > currentMax) + if (maxVolume > currentMax) { m_currentMaxVolume = maxVolume; m_currentReleaseTimer = 0.0f; @@ -190,20 +187,20 @@ void BitCrusherDSP::SetPeriod(float period /*= 0*/) m_increment = (uint32)((double)(1 << 16)); m_period = (uint32)(f * period * (double)(1 << 16)); } -void BitCrusherDSP::Process(float* out, uint32 numSamples) +void BitCrusherDSP::Process(float *out, uint32 numSamples) { - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { m_currentDuration += m_increment; - if(m_currentDuration > m_period) + if (m_currentDuration > m_period) { m_sampleBuffer[0] = out[i * 2]; - m_sampleBuffer[1] = out[i*2+1]; + m_sampleBuffer[1] = out[i * 2 + 1]; m_currentDuration -= m_period; } out[i * 2] = m_sampleBuffer[0] * mix + out[i * 2] * (1.0f - mix); - out[i * 2 + 1] = m_sampleBuffer[1] * mix + out[i * 2+1] * (1.0f - mix); + out[i * 2 + 1] = m_sampleBuffer[1] * mix + out[i * 2 + 1] * (1.0f - mix); } } @@ -219,6 +216,7 @@ void GateDSP::SetLength(double length) } void GateDSP::SetGating(float gating) { + gating = Math::Clamp(gating, 0.f, 1.f); float flength = (float)m_length; m_gating = gating; m_halfway = (uint32)(flength * gating); @@ -227,32 +225,32 @@ void GateDSP::SetGating(float gating) m_fadeOut = (uint32)((float)m_halfway * (1.0f - fadeDuration)); m_currentSample = 0; } -void GateDSP::Process(float* out, uint32 numSamples) +void GateDSP::Process(float *out, uint32 numSamples) { - if(m_length < 2) + if (m_length < 2) return; const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(currentSample + i < startSample) + if (currentSample + i < startSample) { continue; } float c = 1.0f; - if(m_currentSample < m_halfway) + if (m_currentSample < m_halfway) { // Fade out before silence - if(m_currentSample > m_fadeOut) - c = 1-(float)(m_currentSample - m_fadeOut) / (float)m_fadeIn; + if (m_currentSample > m_fadeOut) + c = 1 - (float)(m_currentSample - m_fadeOut) / (float)m_fadeIn; } else { uint32 t = m_currentSample - m_halfway; // Fade in again - if(t > m_fadeOut) + if (t > m_fadeOut) c = (float)(t - m_fadeOut) / (float)m_fadeIn; else c = 0.0f; @@ -260,7 +258,7 @@ void GateDSP::Process(float* out, uint32 numSamples) // Multiply volume c = (c * (1 - low) + low); // Range [low, 1] - c = c * mix + (1.0f-mix); + c = c * mix + (1.0f - mix); out[i * 2] *= c; out[i * 2 + 1] *= c; @@ -281,33 +279,33 @@ void TapeStopDSP::SetLength(double length) m_sampleBuffer.clear(); m_sampleBuffer.reserve(2 * m_length + 100); } -void TapeStopDSP::Process(float* out, uint32 numSamples) +void TapeStopDSP::Process(float *out, uint32 numSamples) { const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(currentSample + i < startSample) + if (currentSample + i < startSample) { continue; } float sampleRate = 1.0f - (float)m_currentSample / (float)m_length; - if(sampleRate == 0.0f) + if (sampleRate == 0.0f) { // Mute out[i * 2] = 0.0f; - out[i * 2+1] = 0.0f; + out[i * 2 + 1] = 0.0f; continue; } // Store samples for later - m_sampleBuffer.Add(out[i*2]); - m_sampleBuffer.Add(out[i*2+1]); + m_sampleBuffer.Add(out[i * 2]); + m_sampleBuffer.Add(out[i * 2 + 1]); // The sample index into the buffer uint32 i2 = (uint32)floor(m_sampleIdx); out[i * 2] = m_sampleBuffer[i2 * 2] * mix + out[i * 2] * (1 - mix); - out[i * 2 + 1] = m_sampleBuffer[i2 * 2 + 1] * mix + out[i * 2+1] * (1 - mix); + out[i * 2 + 1] = m_sampleBuffer[i2 * 2 + 1] * mix + out[i * 2 + 1] * (1 - mix); // Increase index m_sampleIdx += sampleRate; @@ -332,39 +330,41 @@ void RetriggerDSP::SetLength(double length) } void RetriggerDSP::SetResetDuration(uint32 resetDuration) { - float flength = (float)resetDuration / 1000.0f * (float) m_sampleRate; + float flength = (float)resetDuration / 1000.0f * (float)m_sampleRate; m_resetDuration = (uint32)flength; } void RetriggerDSP::SetGating(float gating) { + gating = Math::Clamp(gating, 0.f, 1.f); m_gating = gating; m_gateLength = (uint32)((float)m_length * gating); } void RetriggerDSP::SetMaxLength(uint32 length) { - float flength = (float)length / 1000.0f * (float) m_sampleRate; + float flength = (float)length / 1000.0f * (float)m_sampleRate; if (!m_bufferReserved) { m_sampleBuffer.reserve(2 * (uint32_t)flength + 100); m_bufferReserved = true; } } -void RetriggerDSP::Process(float* out, uint32 numSamples) +void RetriggerDSP::Process(float *out, uint32 numSamples) { if (m_length == 0) return; const uint32 startSample = GetStartSample(); const uint32 nowSample = GetCurrentSample(); + const auto maxSample = m_audioBase->GetPCMCount(); - float* pcmSource = m_audioBase->GetPCM(); - double rateMult = (double)m_audioBase->GetSampleRate() / m_audio->GetSampleRate(); + float *pcmSource = m_audioBase->GetPCM(); + double rateMult = (double)m_audioBase->GetSampleRate() / m_sampleRate; uint32 pcmStartSample = static_cast(lastTimingPoint * ((double)m_audioBase->GetSampleRate() / 1000.0)); - uint32 baseStartRepeat = static_cast(lastTimingPoint * ((double)m_audio->GetSampleRate() / 1000.0)); + uint32 baseStartRepeat = static_cast(lastTimingPoint * ((double)m_audioBase->GetSampleRate() / 1000.0)); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(nowSample + i < startSample) + if (nowSample + i < startSample) { continue; } @@ -372,12 +372,12 @@ void RetriggerDSP::Process(float* out, uint32 numSamples) int startOffset = 0; if (m_resetDuration > 0) { - startOffset = (nowSample + i - baseStartRepeat) / (int)m_resetDuration; + startOffset = (nowSample + i - baseStartRepeat) / (int)(m_resetDuration * rateMult); startOffset = static_cast(startOffset * m_resetDuration * rateMult); } else { - startOffset = static_cast((startSample - baseStartRepeat) * rateMult); + startOffset = static_cast((startSample - baseStartRepeat)); } int pcmSample = static_cast(pcmStartSample) + startOffset + static_cast(m_currentSample * rateMult); @@ -385,12 +385,15 @@ void RetriggerDSP::Process(float* out, uint32 numSamples) if (m_currentSample > m_gateLength) gating = 0; // Sample from buffer - out[i * 2] = gating * pcmSource[pcmSample * 2] * mix + out[i * 2] * (1 - mix); - out[i * 2 + 1] = gating * pcmSource[pcmSample * 2 + 1] * mix + out[i * 2 + 1] * (1 - mix); - + assert(static_cast(pcmSample) < maxSample); + if (static_cast(pcmSample) < maxSample) //TODO: Improve whatever is necessary to make sure this never happens + { + out[i * 2] = gating * pcmSource[pcmSample * 2] * mix + out[i * 2] * (1 - mix); + out[i * 2 + 1] = gating * pcmSource[pcmSample * 2 + 1] * mix + out[i * 2 + 1] * (1 - mix); + } + // Increase index m_currentSample = (m_currentSample + 1) % m_length; - } } @@ -402,7 +405,7 @@ void WobbleDSP::SetLength(double length) double flength = length / 1000.0 * m_sampleRate; m_length = (uint32)flength; } -void WobbleDSP::Process(float* out, uint32 numSamples) +void WobbleDSP::Process(float *out, uint32 numSamples) { if (m_length == 0) return; @@ -411,9 +414,9 @@ void WobbleDSP::Process(float* out, uint32 numSamples) const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(currentSample + i < startSample) + if (currentSample + i < startSample) { continue; } @@ -422,7 +425,7 @@ void WobbleDSP::Process(float* out, uint32 numSamples) float freq = fmin + (fmax - fmin) * f; SetLowPass(q, freq); - float s[2] = { out[i * 2], out[i * 2 + 1] }; + float s[2] = {out[i * 2], out[i * 2 + 1]}; BQFDSP::Process(&out[i * 2], 1); @@ -445,7 +448,7 @@ void PhaserDSP::SetLength(double length) double flength = length / 1000.0 * m_sampleRate; m_length = (uint32)flength; } -void PhaserDSP::Process(float* out, uint32 numSamples) +void PhaserDSP::Process(float *out, uint32 numSamples) { if (m_length == 0) return; @@ -453,9 +456,9 @@ void PhaserDSP::Process(float* out, uint32 numSamples) const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(currentSample + i < startSample) + if (currentSample + i < startSample) { continue; } @@ -465,16 +468,16 @@ void PhaserDSP::Process(float* out, uint32 numSamples) //calculate and update phaser sweep lfo... //float d = dmin + (dmax - dmin) * ((sin(f) + 1.0f) / 2.0f); float d = dmin + (dmax - dmin) * f; - d /= (float) m_audio->GetSampleRate(); + d /= (float)m_sampleRate; //calculate output per channel - for(uint32 c = 0; c < 2; c++) + for (uint32 c = 0; c < 2; c++) { - APF* filters1 = filters[c]; + APF *filters1 = filters[c]; //update filter coeffs float a1 = (1.f - d) / (1.f + d); - for(int i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) filters1[i].a1 = a1; float filtered = filters1[0].Update( @@ -505,7 +508,8 @@ FlangerDSP::FlangerDSP(uint32 sampleRate) : DSP() { SetSampleRate(sampleRate); } -void FlangerDSP::SetLength(double length) { +void FlangerDSP::SetLength(double length) +{ double flength = length / 1000.0 * m_sampleRate; m_length = (uint32)flength; } @@ -515,24 +519,24 @@ void FlangerDSP::SetDelayRange(uint32 offset, uint32 depth) const uint32 max = offset + depth; const uint32 min = offset; - const float mult = (float) m_sampleRate / 44100.f; + const float mult = (float)m_sampleRate / 44100.f; m_min = static_cast(min * mult); m_max = static_cast(max * mult); m_bufferLength = m_max * 2; m_sampleBuffer.resize(m_bufferLength); } -void FlangerDSP::Process(float* out, uint32 numSamples) +void FlangerDSP::Process(float *out, uint32 numSamples) { if (m_bufferLength <= 0) return; - float* data = m_sampleBuffer.data(); + float *data = m_sampleBuffer.data(); if (data == nullptr) return; const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { if (currentSample + i < startSample) { @@ -554,9 +558,9 @@ void FlangerDSP::Process(float* out, uint32 numSamples) // Apply delay out[i * 2] = (data[samplePos] + out[i * 2]) * 0.5f * mix + - out[i * 2] * (1 - mix); + out[i * 2] * (1 - mix); out[i * 2 + 1] = (data[samplePos + 1] + out[i * 2 + 1]) * 0.5f * mix + - out[i * 2 + 1] * (1 - mix); + out[i * 2 + 1] * (1 - mix); m_bufferOffset += 2; if (m_bufferOffset >= m_bufferLength) @@ -578,18 +582,18 @@ void EchoDSP::SetLength(double length) memset(m_sampleBuffer.data(), 0, sizeof(float) * m_bufferLength); m_numLoops = 0; } -void EchoDSP::Process(float* out, uint32 numSamples) +void EchoDSP::Process(float *out, uint32 numSamples) { - float* data = m_sampleBuffer.data(); + float *data = m_sampleBuffer.data(); if (!data) return; const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(currentSample + i < startSample) + if (currentSample + i < startSample) { continue; } @@ -597,7 +601,7 @@ void EchoDSP::Process(float* out, uint32 numSamples) float l0 = data[m_bufferOffset + 0]; float l1 = data[m_bufferOffset + 1]; - if(m_numLoops > 0) + if (m_numLoops > 0) { // Send echo to output out[i * 2] = l0 * mix; @@ -609,7 +613,7 @@ void EchoDSP::Process(float* out, uint32 numSamples) data[m_bufferOffset + 1] = out[i * 2 + 1] * feedback; m_bufferOffset += 2; - if(m_bufferOffset >= m_bufferLength) + if (m_bufferOffset >= m_bufferLength) { m_bufferOffset = 0; m_numLoops++; @@ -627,31 +631,31 @@ void SidechainDSP::SetLength(double length) m_length = (uint32)flength; m_time = 0; } -void SidechainDSP::Process(float* out, uint32 numSamples) +void SidechainDSP::Process(float *out, uint32 numSamples) { - if(m_length == 0) + if (m_length == 0) return; const uint32 startSample = GetStartSample(); const uint32 currentSample = GetCurrentSample(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(currentSample + i < startSample) + if (currentSample + i < startSample) { continue; } float r = (float)m_time / (float)m_length; // FadeIn const float fadeIn = 0.08f; - if(r < fadeIn) + if (r < fadeIn) r = 1.0f - r / fadeIn; else - r = curve((r- fadeIn) / (1.0f- fadeIn)); - float sampleGain = 1.0f - amount * (1.0f- r); + r = curve((r - fadeIn) / (1.0f - fadeIn)); + float sampleGain = 1.0f - amount * (1.0f - r); out[i * 2 + 0] *= sampleGain; out[i * 2 + 1] *= sampleGain; - if(++m_time > m_length) + if (++m_time > m_length) { m_time = 0; } @@ -672,28 +676,24 @@ class PitchShiftDSP_Impl Vector m_receiveBuffer; public: - PitchShiftDSP_Impl() - { - } - ~PitchShiftDSP_Impl() - { - } - void Init(Audio_Impl* audio) + PitchShiftDSP_Impl() = default; + ~PitchShiftDSP_Impl() = default; + void Init(uint32 sampleRate) { m_soundtouch.setChannels(2); - m_soundtouch.setSampleRate(audio->GetSampleRate()); + m_soundtouch.setSampleRate(sampleRate); m_soundtouch.setSetting(SETTING_USE_AA_FILTER, 0); m_soundtouch.setSetting(SETTING_SEQUENCE_MS, 5); //m_soundtouch.setSetting(SETTING_SEEKWINDOW_MS, 10); //m_soundtouch.setSetting(SETTING_OVERLAP_MS, 10); } - void Process(float* out, uint32 numSamples) + void Process(float *out, uint32 numSamples) { - m_receiveBuffer.resize(numSamples*2); + m_receiveBuffer.resize(numSamples * 2); m_soundtouch.setPitchSemiTones(pitch); m_soundtouch.putSamples(out, numSamples); uint32 receivedSamples = m_soundtouch.receiveSamples(m_receiveBuffer.data(), numSamples); - if(receivedSamples > 0) + if (receivedSamples > 0) { memcpy(out, m_receiveBuffer.data(), receivedSamples * sizeof(float) * 2); } @@ -709,10 +709,10 @@ PitchShiftDSP::~PitchShiftDSP() { delete m_impl; } -void PitchShiftDSP::Process(float* out, uint32 numSamples) +void PitchShiftDSP::Process(float *out, uint32 numSamples) { m_impl->pitch = Math::Clamp(amount, -12.0f, 12.0f); - if(!m_impl->init) - m_impl->Init(m_audio); + if (!m_impl->init) + m_impl->Init(m_sampleRate); m_impl->Process(out, numSamples); } diff --git a/Audio/src/Sample.cpp b/Audio/src/Sample.cpp index c16bae9af..f1dfa1e0e 100644 --- a/Audio/src/Sample.cpp +++ b/Audio/src/Sample.cpp @@ -11,13 +11,12 @@ #include "miniaudio.h" - class Sample_Impl : public SampleRes { public: Buffer m_data; - Audio* m_audio; - float* m_pcm = nullptr; + Audio *m_audio; + float *m_pcm = nullptr; mutex m_lock; @@ -41,7 +40,7 @@ class Sample_Impl : public SampleRes m_playing = true; m_playbackPointer = 0; m_looping = looping; - m_lock.unlock(); + m_lock.unlock(); } virtual void Stop() override { @@ -50,30 +49,30 @@ class Sample_Impl : public SampleRes m_looping = false; m_lock.unlock(); } - bool Init(const String& path) + bool Init(const String &path) { ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, g_audio->GetSampleRate()); ma_result result; - result = ma_decode_file( *path, &config, &m_length, (void**)&m_pcm); + result = ma_decode_file(*path, &config, &m_length, (void **)&m_pcm); if (result != MA_SUCCESS) return false; return true; } - virtual void Process(float* out, uint32 numSamples) override + virtual void Process(float *out, uint32 numSamples) override { - if(!m_playing) + if (!m_playing) return; m_lock.lock(); - for(uint32 i = 0; i < numSamples; i++) + for (uint32 i = 0; i < numSamples; i++) { - if(m_playbackPointer >= m_length) + if (m_playbackPointer >= m_length) { - if(m_looping) + if (m_looping) { m_playbackPointer = 0; } @@ -91,7 +90,7 @@ class Sample_Impl : public SampleRes } m_lock.unlock(); } - const Buffer& GetData() const override + const Buffer &GetData() const override { return m_data; } @@ -107,7 +106,7 @@ class Sample_Impl : public SampleRes { return m_playbackPointer; } - float* GetPCM() override + float *GetPCM() override { return nullptr; } @@ -123,15 +122,19 @@ class Sample_Impl : public SampleRes { return m_playing; } - + void PreRenderDSPs(Vector &DSPs) override {} + uint64 GetSamplePos() const override + { + return m_playbackPointer; + } }; -Sample SampleRes::Create(Audio* audio, const String& path) +Sample SampleRes::Create(Audio *audio, const String &path) { - Sample_Impl* res = new Sample_Impl(); + Sample_Impl *res = new Sample_Impl(); res->m_audio = audio; - if(!res->Init(path)) + if (!res->Init(path)) { delete res; return Sample(); diff --git a/Beatmap/CMakeLists.txt b/Beatmap/CMakeLists.txt index f967ac147..ce8423754 100644 --- a/Beatmap/CMakeLists.txt +++ b/Beatmap/CMakeLists.txt @@ -1,5 +1,7 @@ # Beatmap library +set(ENV{ASAN_OPTIONS}, "new_delete_type_mismatch=0") + set(INCROOT ${CMAKE_CURRENT_SOURCE_DIR}/include/Beatmap/) set(SRCROOT ${CMAKE_CURRENT_SOURCE_DIR}/src/) set(PCHROOT ${CMAKE_CURRENT_SOURCE_DIR}/) @@ -22,7 +24,7 @@ source_group("" FILES ${PCH_FILES}) enable_precompiled_headers("${BEATMAP_SRC}" ${PCH_SRC}) add_library(Beatmap ${BEATMAP_SRC} ${PCH_FILES}) -target_compile_features(Beatmap PUBLIC cxx_std_14) +target_compile_features(Beatmap PUBLIC cxx_std_17) target_include_directories(Beatmap PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(Beatmap PRIVATE ${INCROOT} @@ -39,7 +41,9 @@ target_link_libraries(Beatmap Shared) target_link_libraries(Beatmap sqlite3) target_link_libraries(Beatmap nlohmann_json) -if(WIN32) - target_compile_options(Beatmap PRIVATE /Zi) -endif() +target_link_libraries(Beatmap cc-common) +# Enable multiprocess compiling +if(MSVC) + target_compile_options(Beatmap PRIVATE /MP) +endif(MSVC) \ No newline at end of file diff --git a/Beatmap/include/Beatmap/AudioEffects.hpp b/Beatmap/include/Beatmap/AudioEffects.hpp index 49cc328f1..ea8c9458d 100644 --- a/Beatmap/include/Beatmap/AudioEffects.hpp +++ b/Beatmap/include/Beatmap/AudioEffects.hpp @@ -7,38 +7,38 @@ // The types of effects that can be used on the effect buttons and on lasers DefineEnum(EffectType, - None = 0, - Retrigger, - Flanger, - Phaser, - Gate, - TapeStop, - Bitcrush, - Wobble, - SideChain, - Echo, - Panning, - PitchShift, - LowPassFilter, - HighPassFilter, - PeakingFilter, - SwitchAudio, // Not a real effect - UserDefined0 = 0x40, // This ID or higher is user for user defined effects inside map objects - UserDefined1, // Keep this ID at least a few ID's away from the normal effect so more native effects can be added later - UserDefined2, - UserDefined3, - UserDefined4, - UserDefined5, - UserDefined6, - UserDefined7, - UserDefined8, - UserDefined9 // etc... - ) - -/* + None = 0, + Retrigger, + Flanger, + Phaser, + Gate, + TapeStop, + Bitcrush, + Wobble, + SideChain, + Echo, + Panning, + PitchShift, + LowPassFilter, + HighPassFilter, + PeakingFilter, + SwitchAudio, // Not a real effect + UserDefined0 = 0x40, // This ID or higher is user for user defined effects inside map objects + UserDefined1, // Keep this ID at least a few ID's away from the normal effect so more native effects can be added later + UserDefined2, + UserDefined3, + UserDefined4, + UserDefined5, + UserDefined6, + UserDefined7, + UserDefined8, + UserDefined9 // etc... + ) + + /* Effect parameter that is used to define a certain time range/period/speed */ -class EffectDuration + class EffectDuration { public: EffectDuration() = default; @@ -47,7 +47,7 @@ class EffectDuration // Duration relative to whole note duration EffectDuration(float rate); - static EffectDuration Lerp(const EffectDuration& lhs, const EffectDuration& rhs, float time); + static EffectDuration Lerp(const EffectDuration &lhs, const EffectDuration &rhs, float time); // Convert to ms duration // pass in the whole note duration @@ -69,7 +69,8 @@ class EffectDuration Type type; }; -template T InterpolateEffectParamValue(T a, T b, float t) +template +T InterpolateEffectParamValue(T a, T b, float t) { return T(a + (b - a) * t); } @@ -78,7 +79,7 @@ EffectDuration InterpolateEffectParamValue(EffectDuration a, EffectDuration b, f /* Effect parameter that allows all the values which can be set for effects */ -template +template class EffectParam { public: @@ -99,7 +100,7 @@ class EffectParam } // Sample based on laser input, or without parameters for just the actual value - T Sample(float t = 0.0f) const + T Sample(float t = 0.0f) const { t = Math::Clamp(timeFunction(t), 0.0f, 1.0f); return isRange ? InterpolateEffectParamValue(values[0], values[1], t) : values[0]; @@ -117,10 +118,10 @@ class EffectParam struct AudioEffect { // Use this to get default effect settings - static const AudioEffect& GetDefault(EffectType type); + static const AudioEffect &GetDefault(EffectType type); + static int GetDefaultEffectPriority(EffectType type); - - void SetDefaultEffectParams(int16* params); + void SetDefaultEffectParams(int16 *params); // The effect type EffectType type = EffectType::None; @@ -191,12 +192,12 @@ struct AudioEffect // Ammount of echo (0-1) EffectParam feedback; } echo; - struct + struct { // Panning position, 0 is center (-1-1) EffectParam panning; } panning; - struct + struct { // Pitch shift amount, in semitones EffectParam amount; diff --git a/Beatmap/include/Beatmap/Beatmap.hpp b/Beatmap/include/Beatmap/Beatmap.hpp index 5ab041243..c63286886 100644 --- a/Beatmap/include/Beatmap/Beatmap.hpp +++ b/Beatmap/include/Beatmap/Beatmap.hpp @@ -44,6 +44,10 @@ struct BeatmapSettings float slamVolume = 1.0f; float laserEffectMix = 1.0f; float musicVolume = 1.0f; + + // BPM Override for mmod calculation + float speedBpm = -1.0f; + EffectType laserEffectType = EffectType::PeakingFilter; }; diff --git a/Beatmap/include/Beatmap/BeatmapPlayback.hpp b/Beatmap/include/Beatmap/BeatmapPlayback.hpp index 1a36e8cb1..d11346273 100644 --- a/Beatmap/include/Beatmap/BeatmapPlayback.hpp +++ b/Beatmap/include/Beatmap/BeatmapPlayback.hpp @@ -95,7 +95,7 @@ class BeatmapPlayback Delegate OnFXBegin; // Called when an FX button with effect leaves Delegate OnFXEnd; - + // Called when a new timing point becomes active Delegate OnTimingPointChanged; diff --git a/Beatmap/include/Beatmap/MapDatabase.hpp b/Beatmap/include/Beatmap/MapDatabase.hpp index de2300a4c..9d20bd4ec 100644 --- a/Beatmap/include/Beatmap/MapDatabase.hpp +++ b/Beatmap/include/Beatmap/MapDatabase.hpp @@ -1,6 +1,7 @@ #pragma once #include "Beatmap.hpp" #include "json.hpp" +#include "PlaybackOptions.hpp" struct SimpleHitStat { @@ -24,7 +25,11 @@ struct ScoreIndex int32 almost; int32 miss; float gauge; - uint32 gameflags; + GaugeType gaugeType; + uint32 gaugeOption; + AutoFlags autoFlags; + bool random; + bool mirror; String replayPath; String chartHash; uint64 timestamp; @@ -36,6 +41,7 @@ struct ScoreIndex int32 hitWindowGood; int32 hitWindowHold; int32 hitWindowMiss; + int32 hitWindowSlam; }; diff --git a/Beatmap/include/Beatmap/PlaybackOptions.hpp b/Beatmap/include/Beatmap/PlaybackOptions.hpp new file mode 100644 index 000000000..a4d846f8b --- /dev/null +++ b/Beatmap/include/Beatmap/PlaybackOptions.hpp @@ -0,0 +1,32 @@ +#pragma once +#include + +enum class GaugeType : uint16 { + Normal = 0, + Hard, +}; + +enum class AutoFlags : uint8 { + None = 0, + AutoBT = 0x1, + AutoFX = 0x2, + AutoLaser = 0x4, +}; + +typedef struct PlaybackOptions +{ + static PlaybackOptions FromFlags(uint32 flags); + static uint32 ToLegacyFlags(const PlaybackOptions& options); + + GaugeType gaugeType = GaugeType::Normal; + + // Per gauge type defined option, i.e. star rating + uint32 gaugeOption = 0; + bool mirror = false; + bool random = false; + + // If true and gaugeType != normal then insert a normal type gauge as a fallback + bool backupGauge = false; + + AutoFlags autoFlags = AutoFlags::None; +} PlaybackOptions; \ No newline at end of file diff --git a/Beatmap/src/AudioEffects.cpp b/Beatmap/src/AudioEffects.cpp index 77a021ef6..baf4a765b 100644 --- a/Beatmap/src/AudioEffects.cpp +++ b/Beatmap/src/AudioEffects.cpp @@ -12,17 +12,17 @@ EffectDuration::EffectDuration(float rate) type = Rate; } -EffectDuration EffectDuration::Lerp(const EffectDuration& lhs, const EffectDuration& rhs, float time) +EffectDuration EffectDuration::Lerp(const EffectDuration &lhs, const EffectDuration &rhs, float time) { assert(rhs.type == lhs.type); - if(lhs.type == Type::Rate) + if (lhs.type == Type::Rate) return lhs.rate + (lhs.rate - rhs.rate) * time; else return (int32)(lhs.duration + (float)(lhs.duration - rhs.duration) * time); } uint32 EffectDuration::Absolute(double noteDuration) { - if(type == Time) + if (type == Time) return duration; else return (uint32)(rate * noteDuration); @@ -49,7 +49,7 @@ static AudioEffect CreateDefault(EffectType type) Interpolation::CubicBezier lpfEasingCurve = Interpolation::EaseOutCubic; // Set defaults based on effect type - switch(type) + switch (type) { // These are assumed to mostly be laser effect types (at least when used with the defaults) case EffectType::PeakingFilter: @@ -122,35 +122,52 @@ class DefaultEffectSettings { // Preload all default effect settings effects.resize((size_t)EffectType::_Length); - for(auto e : Enum_EffectType::GetMap()) + for (auto e : Enum_EffectType::GetMap()) { effects[(size_t)e.first] = CreateDefault(e.first); } } }; -const AudioEffect& AudioEffect::GetDefault(EffectType type) +const AudioEffect &AudioEffect::GetDefault(EffectType type) { static DefaultEffectSettings defaults; assert((size_t)type < defaults.effects.size()); return defaults.effects[(size_t)type]; } -void AudioEffect::SetDefaultEffectParams(int16* params) +int AudioEffect::GetDefaultEffectPriority(EffectType type) +{ + const Map priorityMap = { + {EffectType::Retrigger, 0}, + {EffectType::Echo, 1}, + {EffectType::Gate, 2}, + {EffectType::TapeStop, 3}, + {EffectType::SideChain, 4}, + {EffectType::Bitcrush, 5}, + }; + + if (priorityMap.Contains(type)) + return priorityMap.at(type); + + return 99; +} + +void AudioEffect::SetDefaultEffectParams(int16 *params) { // Set defaults based on effect type switch (type) { - case EffectType::Bitcrush: - params[0] = bitcrusher.reduction.Sample(1); - break; - case EffectType::Wobble: - break; - case EffectType::Flanger: - params[0] = flanger.depth.Sample(1); - params[1] = flanger.offset.Sample(1); - break; - default: - break; + case EffectType::Bitcrush: + params[0] = bitcrusher.reduction.Sample(1); + break; + case EffectType::Wobble: + break; + case EffectType::Flanger: + params[0] = flanger.depth.Sample(1); + params[1] = flanger.offset.Sample(1); + break; + default: + break; } } diff --git a/Beatmap/src/BeatmapFromKSH.cpp b/Beatmap/src/BeatmapFromKSH.cpp index 8e58dca83..58d50e09d 100755 --- a/Beatmap/src/BeatmapFromKSH.cpp +++ b/Beatmap/src/BeatmapFromKSH.cpp @@ -198,12 +198,7 @@ struct MultiParamRange static MultiParam ParseParam(const String &in) { MultiParam ret; - if (in.find('.') != -1) - { - ret.type = MultiParam::Float; - sscanf(*in, "%f", &ret.fval); - } - else if (in.find('/') != -1) + if (in.find('/') != -1) { ret.type = MultiParam::Float; String a, b; @@ -222,6 +217,11 @@ static MultiParam ParseParam(const String &in) sscanf(*in, "%i", &percentage); ret.fval = percentage / 100.0; } + else if (in.find('.') != -1) + { + ret.type = MultiParam::Float; + sscanf(*in, "%f", &ret.fval); + } else { ret.type = MultiParam::Int; @@ -236,7 +236,7 @@ AudioEffect ParseCustomEffect(const KShootEffectDefinition &def, Vector bool typeSet = false; Map params; - for (auto s : def.parameters) + for (const auto& s : def.parameters) { // This one is easy if (s.first == "type") @@ -547,6 +547,10 @@ bool Beatmap::m_ProcessKShootMap(BinaryStream &input, bool metadataOnly) { m_settings.musicVolume = (float)atoi(*s.second) / 100.0f; } + else if (s.first == "to") + { + m_settings.speedBpm = atof(*s.second); + } } // Temporary map for timing points diff --git a/Beatmap/src/BeatmapPlayback.cpp b/Beatmap/src/BeatmapPlayback.cpp index 51446ab48..37f0352cf 100644 --- a/Beatmap/src/BeatmapPlayback.cpp +++ b/Beatmap/src/BeatmapPlayback.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "BeatmapPlayback.hpp" -#include "Shared/Profiling.hpp" BeatmapPlayback::BeatmapPlayback(Beatmap& beatmap) : m_beatmap(&beatmap) { diff --git a/Beatmap/src/MapDatabase.cpp b/Beatmap/src/MapDatabase.cpp index 08802399f..1ae5de90b 100644 --- a/Beatmap/src/MapDatabase.cpp +++ b/Beatmap/src/MapDatabase.cpp @@ -90,7 +90,7 @@ class MapDatabase_Impl List m_pendingChanges; mutex m_pendingChangesLock; - static const int32 m_version = 17; + static const int32 m_version = 19; public: MapDatabase_Impl(MapDatabase& outer, bool transferScores) : m_outer(outer) @@ -207,7 +207,6 @@ class MapDatabase_Impl continue; } - String hash; File diffFile; if (diffFile.OpenRead(diffpath)) @@ -245,7 +244,7 @@ class MapDatabase_Impl score.almost = scoreScan.IntColumn(3); score.miss = scoreScan.IntColumn(4); score.gauge = (float) scoreScan.DoubleColumn(5); - score.gameflags = scoreScan.IntColumn(6); + score.gaugeOption = scoreScan.IntColumn(6); Buffer hitstats = scoreScan.BlobColumn(7); score.timestamp = scoreScan.Int64Column(8); auto timestamp = Shared::Time(score.timestamp); @@ -283,7 +282,7 @@ class MapDatabase_Impl addScore.BindInt(3, score.almost); addScore.BindInt(4, score.miss); addScore.BindDouble(5, score.gauge); - addScore.BindInt(6, score.gameflags); + addScore.BindInt(6, score.gaugeOption); addScore.BindString(7, score.replayPath); addScore.BindInt64(8, score.timestamp); addScore.BindString(9, score.chartHash); @@ -345,9 +344,9 @@ class MapDatabase_Impl m_database.Exec("ALTER TABLE Scores ADD COLUMN window_hold INTEGER"); m_database.Exec("ALTER TABLE Scores ADD COLUMN window_miss INTEGER"); m_database.Exec("UPDATE Scores SET window_perfect=46"); - m_database.Exec("UPDATE Scores SET window_good=92"); - m_database.Exec("UPDATE Scores SET window_hold=138"); - m_database.Exec("UPDATE Scores SET window_miss=250"); + m_database.Exec("UPDATE Scores SET window_good=150"); + m_database.Exec("UPDATE Scores SET window_hold=150"); + m_database.Exec("UPDATE Scores SET window_miss=300"); gotVersion = 16; } if (gotVersion == 16) @@ -367,6 +366,56 @@ class MapDatabase_Impl ")"); gotVersion = 17; } + if (gotVersion == 17) + { + Map optionMap; + int totalScoreCount = 0; + DBStatement scoreScan = m_database.Query("SELECT rowid,gameflags FROM Scores"); + while (scoreScan.StepRow()) + { + optionMap.Add(scoreScan.IntColumn(0), PlaybackOptions::FromFlags(scoreScan.IntColumn(1))); + totalScoreCount++; + } + + m_outer.OnDatabaseUpdateProgress.Call(0, totalScoreCount); + int progress = 0; + + //alter table. + // if we were on a newer sqlite version the gameflags column could easily be renamed but it will + // instead still exist in the db after this update but will be unused. + m_database.Exec("BEGIN"); + m_database.Exec("ALTER TABLE Scores ADD COLUMN gauge_type INTEGER"); + m_database.Exec("ALTER TABLE Scores ADD COLUMN auto_flags INTEGER"); + m_database.Exec("ALTER TABLE Scores ADD COLUMN gauge_opt INTEGER"); + m_database.Exec("ALTER TABLE Scores ADD COLUMN mirror INTEGER"); + m_database.Exec("ALTER TABLE Scores ADD COLUMN random INTEGER"); + + DBStatement setScoreOpt = m_database.Query("UPDATE Scores set gauge_type=?, gauge_opt=?, mirror=?, random=?, auto_flags=? WHERE rowid=?"); + for (auto& o : optionMap) + { + setScoreOpt.BindInt(1, (int32)o.second.gaugeType); + setScoreOpt.BindInt(2, o.second.gaugeOption); + setScoreOpt.BindInt(3, o.second.mirror ? 1 : 0); + setScoreOpt.BindInt(4, o.second.random ? 1 : 0); + setScoreOpt.BindInt(5, (int32)o.second.autoFlags); + setScoreOpt.BindInt(6, o.first); + + setScoreOpt.StepRow(); + setScoreOpt.Rewind(); + + progress++; + m_outer.OnDatabaseUpdateProgress.Call(progress, totalScoreCount); + } + m_database.Exec("END"); + gotVersion = 18; + + } + if (gotVersion == 18) + { + m_database.Exec("ALTER TABLE Scores ADD COLUMN window_slam INTEGER"); + m_database.Exec("UPDATE Scores SET window_slam=84"); + gotVersion = 19; + } m_database.Exec(Utility::Sprintf("UPDATE Database SET `version`=%d WHERE `rowid`=1", m_version)); m_outer.OnDatabaseUpdateDone.Call(); @@ -783,7 +832,9 @@ class MapDatabase_Impl DBStatement removeChart = m_database.Query("DELETE FROM Charts WHERE rowid=?"); DBStatement removeChallenge = m_database.Query("DELETE FROM Challenges WHERE rowid=?"); DBStatement removeFolder = m_database.Query("DELETE FROM Folders WHERE rowid=?"); - DBStatement scoreScan = m_database.Query("SELECT rowid,score,crit,near,miss,gauge,gameflags,replay,timestamp,user_name,user_id,local_score,window_perfect,window_good,window_hold,window_miss FROM Scores WHERE chart_hash=?"); + DBStatement scoreScan = m_database.Query("SELECT " + "rowid,score,crit,near,miss,gauge,auto_flags,replay,timestamp,chart_hash,user_name,user_id,local_score,window_perfect,window_good,window_hold,window_miss,window_slam,gauge_type,gauge_opt,mirror,random " + "FROM Scores WHERE chart_hash=?"); DBStatement moveScores = m_database.Query("UPDATE Scores set chart_hash=? where chart_hash=?"); Set addedChartEvents; @@ -959,21 +1010,26 @@ class MapDatabase_Impl score->crit = scoreScan.IntColumn(2); score->almost = scoreScan.IntColumn(3); score->miss = scoreScan.IntColumn(4); - score->gauge = (float) scoreScan.DoubleColumn(5); - score->gameflags = scoreScan.IntColumn(6); + score->gauge = (float)scoreScan.DoubleColumn(5); + score->autoFlags = (AutoFlags)scoreScan.IntColumn(6); score->replayPath = scoreScan.StringColumn(7); score->timestamp = scoreScan.Int64Column(8); - score->userName = scoreScan.StringColumn(9); - score->userId = scoreScan.StringColumn(10); - score->localScore = scoreScan.IntColumn(11); - - score->hitWindowPerfect = scoreScan.IntColumn(12); - score->hitWindowGood = scoreScan.IntColumn(13); - score->hitWindowHold = scoreScan.IntColumn(14); - score->hitWindowMiss = scoreScan.IntColumn(15); - - score->chartHash = chart->hash; + score->chartHash = scoreScan.StringColumn(9); + score->userName = scoreScan.StringColumn(10); + score->userId = scoreScan.StringColumn(11); + score->localScore = scoreScan.IntColumn(12); + + score->hitWindowPerfect = scoreScan.IntColumn(13); + score->hitWindowGood = scoreScan.IntColumn(14); + score->hitWindowHold = scoreScan.IntColumn(15); + score->hitWindowMiss = scoreScan.IntColumn(16); + score->hitWindowSlam = scoreScan.IntColumn(17); + + score->gaugeType = (GaugeType)scoreScan.IntColumn(18); + score->gaugeOption = scoreScan.IntColumn(19); + score->mirror = scoreScan.IntColumn(20) == 1; + score->random = scoreScan.IntColumn(21) == 1; chart->scores.Add(score); } scoreScan.Rewind(); @@ -1192,7 +1248,9 @@ class MapDatabase_Impl void AddScore(ScoreIndex* score) { - DBStatement addScore = m_database.Query("INSERT INTO Scores(score,crit,near,miss,gauge,gameflags,replay,timestamp,chart_hash,user_name,user_id,local_score,window_perfect,window_good,window_hold,window_miss) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); + DBStatement addScore = m_database.Query("INSERT INTO " + "Scores(score,crit,near,miss,gauge,auto_flags,replay,timestamp,chart_hash,user_name,user_id,local_score,window_perfect,window_good,window_hold,window_miss,window_slam,gauge_type,gauge_opt,mirror,random) " + "VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); m_database.Exec("BEGIN"); addScore.BindInt(1, score->score); @@ -1200,7 +1258,7 @@ class MapDatabase_Impl addScore.BindInt(3, score->almost); addScore.BindInt(4, score->miss); addScore.BindDouble(5, score->gauge); - addScore.BindInt(6, score->gameflags); + addScore.BindInt(6, (int32)score->autoFlags); addScore.BindString(7, score->replayPath); addScore.BindInt64(8, score->timestamp); addScore.BindString(9, score->chartHash); @@ -1211,6 +1269,11 @@ class MapDatabase_Impl addScore.BindInt(14, score->hitWindowGood); addScore.BindInt(15, score->hitWindowHold); addScore.BindInt(16, score->hitWindowMiss); + addScore.BindInt(17, score->hitWindowSlam); + addScore.BindInt(18, (int32)score->gaugeType); + addScore.BindInt(19, score->gaugeOption); + addScore.BindInt(20, score->mirror ? 1 : 0); + addScore.BindInt(21, score->random ? 1 : 0); addScore.Step(); addScore.Rewind(); @@ -1451,7 +1514,11 @@ class MapDatabase_Impl "near INTEGER," "miss INTEGER," "gauge REAL," - "gameflags INTEGER," + "gauge_type INTEGER," + "gauge_opt INTEGER," + "auto_flags INTEGER," + "mirror INTEGER," + "random INTEGER," "timestamp INTEGER," "replay TEXT," "user_name TEXT," @@ -1461,6 +1528,7 @@ class MapDatabase_Impl "window_good INTEGER," "window_hold INTEGER," "window_miss INTEGER," + "window_slam INTEGER," "chart_hash TEXT)"); m_database.Exec("CREATE TABLE Collections" @@ -1599,7 +1667,9 @@ class MapDatabase_Impl } // Select Scores - DBStatement scoreScan = m_database.Query("SELECT rowid,score,crit,near,miss,gauge,gameflags,replay,timestamp,chart_hash,user_name,user_id,local_score,window_perfect,window_good,window_hold,window_miss FROM Scores"); + DBStatement scoreScan = m_database.Query("SELECT " + "rowid,score,crit,near,miss,gauge,auto_flags,replay,timestamp,chart_hash,user_name,user_id,local_score,window_perfect,window_good,window_hold,window_miss,window_slam,gauge_type,gauge_opt,mirror,random " + "FROM Scores"); while (scoreScan.StepRow()) { @@ -1610,7 +1680,7 @@ class MapDatabase_Impl score->almost = scoreScan.IntColumn(3); score->miss = scoreScan.IntColumn(4); score->gauge = (float) scoreScan.DoubleColumn(5); - score->gameflags = scoreScan.IntColumn(6); + score->autoFlags = (AutoFlags)scoreScan.IntColumn(6); score->replayPath = scoreScan.StringColumn(7); score->timestamp = scoreScan.Int64Column(8); @@ -1623,6 +1693,12 @@ class MapDatabase_Impl score->hitWindowGood = scoreScan.IntColumn(14); score->hitWindowHold = scoreScan.IntColumn(15); score->hitWindowMiss = scoreScan.IntColumn(16); + score->hitWindowSlam = scoreScan.IntColumn(17); + + score->gaugeType = (GaugeType)scoreScan.IntColumn(18); + score->gaugeOption = scoreScan.IntColumn(19); + score->mirror = scoreScan.IntColumn(20) == 1; + score->random = scoreScan.IntColumn(21) == 1; // Add difficulty to map and resort difficulties auto diffIt = m_chartsByHash.find(score->chartHash); diff --git a/Beatmap/src/PlaybackOptions.cpp b/Beatmap/src/PlaybackOptions.cpp new file mode 100644 index 000000000..adc6f28e8 --- /dev/null +++ b/Beatmap/src/PlaybackOptions.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "PlaybackOptions.hpp" + +enum class LegacyGameFlags : uint32 +{ + None = 0, + + Hard = 0b1, + + Mirror = 0b10, + + Random = 0b100, + + AutoBT = 0b1000, + + AutoFX = 0b10000, + + AutoLaser = 0b100000, + End +}; + + +PlaybackOptions PlaybackOptions::FromFlags(uint32 flags) +{ + PlaybackOptions res; + if ((flags & (uint32)LegacyGameFlags::Hard) != 0) { + res.gaugeType = GaugeType::Hard; + } + + res.random = (flags & (uint32)LegacyGameFlags::Random); + res.mirror = (flags & (uint32)LegacyGameFlags::Mirror); + res.autoFlags = (AutoFlags)((flags >> 3) & 0b111); + + return res; +} + +uint32 PlaybackOptions::ToLegacyFlags(const PlaybackOptions& options) +{ + uint32 flags = 0; + flags |= options.gaugeType != GaugeType::Normal ? (uint32)LegacyGameFlags::Hard : 0; + flags |= options.mirror ? (uint32)LegacyGameFlags::Mirror : 0; + flags |= options.random ? (uint32)LegacyGameFlags::Random : 0; + flags |= (uint32)options.autoFlags << 3; + return flags; +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 745598aca..13c9e2fc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,13 @@ find_package(Iconv REQUIRED) # this is mainly for windows functions either being defined to call A or W prefixed functions add_definitions(-DUNICODE -D_UNICODE) +#https://stackoverflow.com/questions/60041896/reuse-target-compile-options-from-variable-for-multiple-targets-cmake/60047012#60047012 +add_library(cc-common INTERFACE) + +if(WIN32) + target_compile_options(cc-common INTERFACE /Zi) +endif() + OPTION(EMBEDDED "Enable embedded build" OFF) if(EMBEDDED) @@ -71,6 +78,14 @@ if(CRASHDUMP) add_definitions(-DCRASHDUMP) endif() +OPTION(ASAN "Build With ASAN" OFF) +if(ASAN) + target_compile_options(cc-common INTERFACE + -fsanitize=address + -fno-omit-frame-pointer + ) +endif() + # Include macros include(${PROJECT_SOURCE_DIR}/cmake/Macros.cmake) diff --git a/GUI/CMakeLists.txt b/GUI/CMakeLists.txt index 9bd9a36b3..eefc4c33d 100644 --- a/GUI/CMakeLists.txt +++ b/GUI/CMakeLists.txt @@ -22,7 +22,7 @@ source_group("" FILES ${PCH_FILES}) enable_precompiled_headers("${GUI_SRC}" ${PCH_SRC}) add_library(GUI ${GUI_SRC} ${PCH_FILES}) -target_compile_features(GUI PUBLIC cxx_std_14) +target_compile_features(GUI PUBLIC cxx_std_17) target_include_directories(GUI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(GUI PRIVATE ${SRCROOT} @@ -36,6 +36,9 @@ target_link_libraries(GUI nanovg) target_link_libraries(GUI Graphics) target_link_libraries(GUI lua) -if(WIN32) - target_compile_options(Graphics PRIVATE /Zi) -endif() +target_link_libraries(GUI cc-common) + +# Enable multiprocess compiling +if(MSVC) + target_compile_options(GUI PRIVATE /MP) +endif(MSVC) \ No newline at end of file diff --git a/Graphics/CMakeLists.txt b/Graphics/CMakeLists.txt index 0639e647c..15ffc968f 100644 --- a/Graphics/CMakeLists.txt +++ b/Graphics/CMakeLists.txt @@ -22,7 +22,7 @@ source_group("" FILES ${PCH_FILES}) enable_precompiled_headers("${GRAPHICS_SRC}" ${PCH_SRC}) add_library(Graphics ${GRAPHICS_SRC} ${PCH_FILES}) -target_compile_features(Graphics PUBLIC cxx_std_14) +target_compile_features(Graphics PUBLIC cxx_std_17) target_include_directories(Graphics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(Graphics PRIVATE ${INCROOT} @@ -57,6 +57,9 @@ if(UNIX) endif(APPLE) endif(UNIX) -if(WIN32) - target_compile_options(Graphics PRIVATE /Zi) -endif() +target_link_libraries(Graphics cc-common) + +# Enable multiprocess compiling +if(MSVC) + target_compile_options(Graphics PRIVATE /MP) +endif(MSVC) \ No newline at end of file diff --git a/Graphics/include/Graphics/Window.hpp b/Graphics/include/Graphics/Window.hpp index eb47bf9f6..e21a57de0 100644 --- a/Graphics/include/Graphics/Window.hpp +++ b/Graphics/include/Graphics/Window.hpp @@ -5,13 +5,13 @@ namespace Graphics { - // Windowed or bordered window style + /// Windowed or bordered window style enum class WindowStyle { Windowed, Borderless }; - // Text input data + /// Text input data struct TextComposition { String composition; @@ -26,6 +26,39 @@ namespace Graphics class Window : Unique { public: + /// Window position and size parameter + struct PosAndShape + { + enum class Mode { Windowed, Fullscreen, WindowedFullscreen }; + Mode mode; + + PosAndShape(bool fullscreen, bool windowedFullscreen, const Vector2i& pos, const Vector2i& size, int32 monitorId, const Vector2i& fullscreenSize) + : PosAndShape(FlagsToMode(fullscreen, windowedFullscreen), pos, size, monitorId, fullscreenSize) {} + + PosAndShape(Mode mode, const Vector2i& pos, const Vector2i& size, int32 monitorId, const Vector2i& fullscreenSize) + : mode(mode), windowPos(pos), windowSize(size), monitorId(monitorId), fullscreenSize(fullscreenSize){} + + inline static Mode FlagsToMode(bool fullscreen, bool windowedFullscreen) + { + return fullscreen ? windowedFullscreen ? Mode::WindowedFullscreen : Mode::Fullscreen : Mode::Windowed; + } + + inline void SetMode(bool fullscreen, bool windowedFullscreen) + { + mode = FlagsToMode(fullscreen, windowedFullscreen); + } + + /// Position of the window; ignored when in fullscreen mode + Vector2i windowPos; + /// Size of the window; ignored when in fullscreen mode + Vector2i windowSize; + + /// Monitor to use when in fullscreen; ignored when in windowed mode + int32 monitorId; + /// Only used for windowed fullscreen mode + Vector2i fullscreenSize; + }; + Window(Vector2i size = Vector2i(800, 600), uint8 samplecount = 0); ~Window(); // Show the window @@ -56,8 +89,6 @@ namespace Graphics // Get full window position Vector2i GetWindowPos() const; - // Set full window position - void SetWindowPos(const Vector2i& pos); // Window Client area size Vector2i GetWindowSize() const; @@ -67,9 +98,9 @@ namespace Graphics // Window is active bool IsActive() const; + // Set window client area size - void SetWindowSize(const Vector2i& size); - void SwitchFullscreen(int w, int h, int fsw = -1, int fsh = -1, uint32 monitorID = -1, bool windowedFullscreen = false); + void SetPosAndShape(const PosAndShape& posAndShape, bool ensureInBound); bool IsFullscreen() const; int GetDisplayIndex() const; @@ -118,6 +149,7 @@ namespace Graphics Delegate OnTextInput; Delegate OnTextComposition; Delegate OnResized; + Delegate OnMoved; Delegate OnFocusChanged; private: diff --git a/Graphics/src/Window.cpp b/Graphics/src/Window.cpp index e292d6147..a9eb5451c 100644 --- a/Graphics/src/Window.cpp +++ b/Graphics/src/Window.cpp @@ -3,8 +3,24 @@ #include "KeyMap.hpp" #include "Image.hpp" #include "Gamepad_Impl.hpp" + +#include #include +static void GetDisplayBounds(Vector& bounds) +{ + const int displayNum = SDL_GetNumVideoDisplays(); + if (displayNum <= 0) return; + + for (int monitorId = 0; monitorId < displayNum; ++monitorId) + { + SDL_Rect rect; + if (SDL_GetDisplayBounds(monitorId, &rect) < 0) break; + + bounds.emplace_back(Shared::Recti{ {rect.x, rect.y}, {rect.w, rect.h} }); + } +} + namespace Graphics { /* SDL Instance singleton */ @@ -103,6 +119,12 @@ namespace Graphics { SDL_SetWindowPosition(m_window, pos.x, pos.y); } + + void SetWindowPosToCenter(int32 monitorId) + { + SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED_DISPLAY(monitorId), SDL_WINDOWPOS_CENTERED_DISPLAY(monitorId)); + } + void ShowMessageBox(String title, String message, int severity) { uint32 flags = 0; @@ -119,6 +141,7 @@ namespace Graphics } SDL_ShowSimpleMessageBox(flags, title.c_str(), message.c_str(), m_window); } + bool ShowYesNoMessage(String title, String message) { const SDL_MessageBoxButtonData buttons[] = @@ -143,6 +166,7 @@ namespace Graphics } return buttonid == 1; } + Vector2i GetWindowPos() const { Vector2i res; @@ -154,6 +178,7 @@ namespace Graphics { SDL_SetWindowSize(m_window, size.x, size.y); } + Vector2i GetWindowSize() const { Vector2i res; @@ -351,6 +376,11 @@ namespace Graphics { outer.OnFocusChanged.Call(false); } + else if (evt.window.event == SDL_WindowEventID::SDL_WINDOWEVENT_MOVED) + { + Vector2i newPos(evt.window.data1, evt.window.data2); + outer.OnMoved.Call(newPos); + } } } else if (evt.type == SDL_EventType::SDL_TEXTINPUT) @@ -374,61 +404,182 @@ namespace Graphics return !m_closed; } - void SwitchFullscreen(int w, int h, int fsw, int fsh, uint32 monitorID, bool windowedFullscreen) + void SetWindowed(const Vector2i& pos, const Vector2i& size) + { + SDL_SetWindowFullscreen(m_window, 0); + SDL_RestoreWindow(m_window); + + SetWindowSize(size); + + SDL_SetWindowResizable(m_window, SDL_TRUE); + SDL_SetWindowBordered(m_window, SDL_TRUE); + + SetWindowPos(pos); + + m_fullscreen = false; + } + + void SetWindowedFullscreen(int32 monitorId) { - if (monitorID == (uint32)-1) + if (monitorId == -1) { - monitorID = SDL_GetWindowDisplayIndex(m_window); + monitorId = SDL_GetWindowDisplayIndex(m_window); } - if (m_fullscreen) + SDL_DisplayMode dm; + SDL_GetDesktopDisplayMode(monitorId, &dm); + + SDL_Rect bounds; + SDL_GetDisplayBounds(monitorId, &bounds); + + SDL_RestoreWindow(m_window); + SDL_SetWindowSize(m_window, dm.w, dm.h); + SDL_SetWindowPosition(m_window, bounds.x, bounds.y); + SDL_SetWindowResizable(m_window, SDL_FALSE); + + m_fullscreen = true; + } + + void SetFullscreen(int32 monitorId, const Vector2i& res) + { + if (monitorId == -1) + { + monitorId = SDL_GetWindowDisplayIndex(m_window); + } + + SDL_DisplayMode dm; + SDL_GetDesktopDisplayMode(monitorId, &dm); + + if (res.x != -1) { - SDL_SetWindowFullscreen(m_window, 0); - SDL_RestoreWindow(m_window); - SDL_SetWindowSize(m_window, w, h); - SDL_SetWindowResizable(m_window, SDL_TRUE); - SDL_SetWindowBordered(m_window, SDL_TRUE); - SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED_DISPLAY(monitorID), SDL_WINDOWPOS_CENTERED_DISPLAY(monitorID)); - m_fullscreen = false; + dm.w = res.x; } - else if (windowedFullscreen) + + if (res.y != -1) { - SDL_DisplayMode dm; - SDL_GetDesktopDisplayMode(monitorID, &dm); - SDL_Rect bounds; - SDL_GetDisplayBounds(monitorID, &bounds); - - SDL_RestoreWindow(m_window); - SDL_SetWindowSize(m_window, dm.w, dm.h); - SDL_SetWindowPosition(m_window, bounds.x, bounds.y); - SDL_SetWindowResizable(m_window, SDL_FALSE); - m_fullscreen = true; + dm.h = res.y; } - else + + // move to correct display + SetWindowPosToCenter(monitorId); + + SDL_SetWindowDisplayMode(m_window, &dm); + SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN); + + m_fullscreen = true; + } + + void SetPosAndShape(const Window::PosAndShape& posAndShape, bool ensureInBound) + { + int32 monitorId = posAndShape.monitorId; + if (monitorId == -1) { + monitorId = SDL_GetWindowDisplayIndex(m_window); + } + + if (ensureInBound) + { + // Adjust the monitor to use, if the monitor does not exist. SDL_DisplayMode dm; - SDL_GetDesktopDisplayMode(monitorID, &dm); - if (fsw != -1) + if (SDL_GetDesktopDisplayMode(monitorId, &dm) < 0) { - dm.w = fsw; + Logf("Monitor %d is not available; using 0 instead", Logger::Severity::Warning, monitorId); + monitorId = 0; } - if (fsh != -1) + } + + switch (posAndShape.mode) + { + case Window::PosAndShape::Mode::Windowed: + { + Vector2i windowPos = posAndShape.windowPos; + Vector2i windowSize = posAndShape.windowSize; + + SetWindowed(windowPos, windowSize); + + // Adjust window position and size, if the window can't be fit into the display region. + if (ensureInBound) { - dm.h = fsh; - } + int borderTop = 0, borderLeft = 0, borderBottom = 0, borderRight = 0; + SDL_GetWindowBordersSize(m_window, &borderTop, &borderLeft, &borderBottom, &borderRight); - // move to correct display - SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED_DISPLAY(monitorID), SDL_WINDOWPOS_CENTERED_DISPLAY(monitorID)); + Shared::Recti windowRect(windowPos - Vector2i{ borderLeft, borderTop }, windowSize + Vector2i{ borderLeft+borderRight, borderTop+borderBottom }); + + Vector bounds; + GetDisplayBounds(bounds); + + if (bounds.empty()) + { + break; + } + + Logf("Adjusting window size for %u windows...", Logger::Severity::Info, bounds.size()); + + if (monitorId >= static_cast(bounds.size())) + { + monitorId = 0; + } - SDL_SetWindowDisplayMode(m_window, &dm); - SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN); - m_fullscreen = true; + bool foundContained = bounds[monitorId].Contains(windowRect); + if (foundContained) + { + break; + } + + bool foundLargeEnough = bounds[monitorId].NotSmallerThan(windowRect.size); + if (!foundLargeEnough) + { + + for (int i = 0; i < static_cast(bounds.size()); ++i) + { + if (bounds[i].Contains(windowRect)) + { + foundContained = true; + monitorId = i; + break; + } + + if (bounds[i].NotSmallerThan(windowRect.size)) + { + foundLargeEnough = true; + monitorId = i; + break; + } + } + } + + if (foundContained) + { + break; + } + + // Optimally, this should be set to the largest rectangle inside `bounds intersect windowRect`. + Shared::Recti adjustedRect = bounds[monitorId]; + if (foundLargeEnough) + { + adjustedRect.size = windowRect.size; + } + + adjustedRect.pos = bounds[monitorId].pos + (bounds[monitorId].size - adjustedRect.size) / 2; + + windowPos = adjustedRect.pos + Vector2i{ borderLeft, borderTop }; + windowSize = adjustedRect.size - Vector2i{ borderLeft+borderRight, borderTop+borderBottom }; + + SetWindowSize(windowSize); + SetWindowPosToCenter(monitorId); + } + } + break; + case Window::PosAndShape::Mode::WindowedFullscreen: + SetWindowedFullscreen(monitorId); + break; + case Window::PosAndShape::Mode::Fullscreen: + SetFullscreen(monitorId, posAndShape.fullscreenSize); + break; } } - bool IsFullscreen() const - { - return m_fullscreen; - } + + inline bool IsFullscreen() const { return m_fullscreen; } SDL_Window *m_window; @@ -449,7 +600,10 @@ namespace Graphics // Various window state bool m_active = true; bool m_closed = false; + bool m_fullscreen = false; + bool m_windowedFullscreen = false; + uint32 m_style; Vector2i m_clntSize; WString m_caption; @@ -507,31 +661,27 @@ namespace Graphics { m_impl->SetWindowStyle(style); } + Vector2i Window::GetWindowPos() const { return m_impl->GetWindowPos(); } - void Window::SetWindowPos(const Vector2i &pos) - { - m_impl->SetWindowPos(pos); - } Vector2i Window::GetWindowSize() const { return m_impl->GetWindowSize(); } + void Window::SetVSync(int8 setting) { m_impl->SetVSync(setting); } - void Window::SetWindowSize(const Vector2i &size) - { - m_impl->SetWindowSize(size); - } - void Window::SwitchFullscreen(int w, int h, int fsw, int fsh, uint32 monitorID, bool windowedFullscreen) + + void Window::SetPosAndShape(const PosAndShape& posAndShape, bool ensureInBound) { - m_impl->SwitchFullscreen(w, h, fsw, fsh, monitorID, windowedFullscreen); + m_impl->SetPosAndShape(posAndShape, ensureInBound); } + bool Window::IsFullscreen() const { return m_impl->IsFullscreen(); diff --git a/LICENSE.md b/LICENSE.md index db70b9d29..aa6a9d51d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ Copyright 2016-2018 Guus Waals (guus_waals@live.nl) -Copyright 2016-2019 Emil Draws (https://github.com/Drewol) +Copyright 2016-2021 Emil Draws (https://github.com/Drewol) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index 20bc5cd91..a6fc7df97 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -71,11 +71,13 @@ endif(GIT_FOUND) set_output_postfixes(usc-game) -# Target subsystem on windows, set debugging folder if(MSVC) - set_target_properties(usc-game PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS /DEBUG") + # Set debugging folder set_target_properties(usc-game PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin") - target_compile_options(usc-game PRIVATE /EHsc) + target_compile_options(usc-game PRIVATE /EHsc /MP) + # Target subsystem on windows, enable multiprocess build and faster PDB gen + set_target_properties(usc-game PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS /DEBUG:FASTLINK") + set_target_properties(usc-game PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS /DEBUG:FULL") elseif(NOT APPLE) target_compile_options(usc-game PUBLIC -Wall -Wextra -Werror -Wno-unused-variable -Wno-unused-parameter -Wno-unused-function) endif(MSVC) @@ -99,15 +101,15 @@ target_include_directories(usc-game SYSTEM PRIVATE ${LibArchive_INCLUDE_DIRS}) if(WIN32) target_link_libraries(usc-game optimized ${PROJECT_SOURCE_DIR}/third_party/breakpad/exception_handler_Release.lib - optimized ${PROJECT_SOURCE_DIR}/third_party/breakpad/crash_generation_client_Release.lib - optimized ${PROJECT_SOURCE_DIR}/third_party/breakpad/common_Release.lib + optimized ${PROJECT_SOURCE_DIR}/third_party/breakpad/crash_generation_client_Release.lib + optimized ${PROJECT_SOURCE_DIR}/third_party/breakpad/common_Release.lib ) target_link_libraries(usc-game debug ${PROJECT_SOURCE_DIR}/third_party/breakpad/exception_handler_Debug.lib - debug ${PROJECT_SOURCE_DIR}/third_party/breakpad/crash_generation_client_Debug.lib - debug ${PROJECT_SOURCE_DIR}/third_party/breakpad/common_Debug.lib + debug ${PROJECT_SOURCE_DIR}/third_party/breakpad/crash_generation_client_Debug.lib + debug ${PROJECT_SOURCE_DIR}/third_party/breakpad/common_Debug.lib ) target_include_directories(usc-game PUBLIC ${PROJECT_SOURCE_DIR}/third_party/breakpad/include) - # Always generate pdb - target_compile_options(usc-game PRIVATE /Zi) endif() + +target_link_libraries(usc-game cc-common) diff --git a/Main/include/Application.hpp b/Main/include/Application.hpp index 5438e981d..8f49e78f2 100644 --- a/Main/include/Application.hpp +++ b/Main/include/Application.hpp @@ -1,8 +1,7 @@ #pragma once -#include