diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0e91f21..cc87341 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,11 @@ jobs: wget -q https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz tar xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz + # Patch Synth_Dexed PluginFX + - name: Patch Synth_Dexed + run: | + ( cd Synth_Dexed ; patch -p1 < ../src/patches/0001-PluginFX-change-gain-at-zero-crossing.patch ) + - name: Build for Raspberry Pi 5 (64-bit) run: | set -ex @@ -124,6 +129,11 @@ jobs: wget -q https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz tar xf gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz + # Patch Synth_Dexed PluginFX + - name: Patch Synth_Dexed + run: | + ( cd Synth_Dexed ; patch -p1 < ../src/patches/0001-PluginFX-change-gain-at-zero-crossing.patch ) + - name: Build for Raspberry Pi 2 (32-bit) run: | set -ex diff --git a/src/effect_mixer.hpp b/src/effect_mixer.hpp index 44184ab..e16b6f5 100644 --- a/src/effect_mixer.hpp +++ b/src/effect_mixer.hpp @@ -22,7 +22,10 @@ public: { buffer_length=len; for (uint8_t i=0; i 0U) + { + if (*pIn >= 0 && *(pIn - 1) < 0) + return blockSize - blkCnt; + + pIn++; + blkCnt--; + } + + return blockSize; + } + void pan(uint8_t channel, float32_t pan) { if (channel >= NN) return; @@ -113,43 +139,38 @@ public: pan = MIN_PANORAMA; // From: https://stackoverflow.com/questions/67062207/how-to-pan-audio-sample-data-naturally - panorama[channel][0]=arm_sin_f32(mapfloat(pan, MIN_PANORAMA, MAX_PANORAMA, 0.0, M_PI/2.0)); - panorama[channel][1]=arm_cos_f32(mapfloat(pan, MIN_PANORAMA, MAX_PANORAMA, 0.0, M_PI/2.0)); + panorama_w[channel][0]=arm_cos_f32(mapfloat(pan, MIN_PANORAMA, MAX_PANORAMA, 0.0, M_PI/2.0)); + panorama_w[channel][1]=arm_sin_f32(mapfloat(pan, MIN_PANORAMA, MAX_PANORAMA, 0.0, M_PI/2.0)); } void doAddMix(uint8_t channel, float32_t* in) { float32_t tmp[buffer_length]; - + uint32_t zc; assert(in); - // left - arm_scale_f32(in, panorama[channel][0], tmp, buffer_length); - if(multiplier[channel]!=UNITY_GAIN) - arm_scale_f32(tmp,multiplier[channel],tmp,buffer_length); - arm_add_f32(sumbufL, tmp, sumbufL, buffer_length); - // right - arm_scale_f32(in, panorama[channel][1], tmp, buffer_length); - if(multiplier[channel]!=UNITY_GAIN) - arm_scale_f32(tmp,multiplier[channel],tmp,buffer_length); - arm_add_f32(sumbufR, tmp, sumbufR, buffer_length); - } - - void doAddMix(uint8_t channel, float32_t* inL, float32_t* inR) - { - float32_t tmp[buffer_length]; + if ((panorama[channel][0] != panorama_w[channel][0] || multiplier[channel] != multiplier_w[channel]) && + (zc = find_zc_f32(in, buffer_length)) != buffer_length) + { + arm_scale_f32(in, panorama[channel][0] * multiplier[channel], tmp, zc); + arm_scale_f32(in + zc, panorama_w[channel][0] * multiplier_w[channel], tmp + zc, buffer_length - zc); + arm_add_f32(sumbufL, tmp, sumbufL, buffer_length); + panorama[channel][0] = panorama_w[channel][0]; - assert(inL); - assert(inR); + arm_scale_f32(in, panorama[channel][1] * multiplier[channel], tmp, zc); + arm_scale_f32(in + zc, panorama_w[channel][1] * multiplier_w[channel], tmp + zc, buffer_length - zc); + arm_add_f32(sumbufR, tmp, sumbufR, buffer_length); + panorama[channel][1] = panorama_w[channel][1]; - // left - if(multiplier[channel]!=UNITY_GAIN) - arm_scale_f32(inL,multiplier[channel],tmp,buffer_length); - arm_add_f32(sumbufL, tmp, sumbufL, buffer_length); - // right - if(multiplier[channel]!=UNITY_GAIN) - arm_scale_f32(inR,multiplier[channel],tmp,buffer_length); - arm_add_f32(sumbufR, tmp, sumbufR, buffer_length); + multiplier[channel] = multiplier_w[channel]; + } + else + { + arm_scale_f32(in, panorama[channel][0] * multiplier[channel], tmp, buffer_length); + arm_add_f32(sumbufL, tmp, sumbufL, buffer_length); + arm_scale_f32(in, panorama[channel][1] * multiplier[channel], tmp, buffer_length); + arm_add_f32(sumbufR, tmp, sumbufR, buffer_length); + } } void getMix(float32_t* bufferL, float32_t* bufferR) @@ -168,11 +189,27 @@ public: arm_fill_f32(0.0f, sumbufR, buffer_length); } + void getBuffers(float32_t (*buffers[2])) + { + buffers[0] = sumbufL; + buffers[1] = sumbufR; + } + + void zeroFill() + { + if(sumbufL) + arm_fill_f32(0.0f, sumbufL, buffer_length); + if(sumbufR) + arm_fill_f32(0.0f, sumbufR, buffer_length); + } + protected: using AudioMixer::sumbufL; using AudioMixer::multiplier; + using AudioMixer::multiplier_w; using AudioMixer::buffer_length; float32_t panorama[NN][2]; + float32_t panorama_w[NN][2]; float32_t* sumbufR; }; diff --git a/src/minidexed.cpp b/src/minidexed.cpp index 51e6444..75efdfb 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -213,10 +213,6 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, m_pSoundDevice = new CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize ()); - - // The channels are swapped by default in the HDMI sound driver. - // TODO: Remove this line, when this has been fixed in the driver. - m_bChannelsSwapped = !m_bChannelsSwapped; #endif } else @@ -1406,34 +1402,35 @@ void CMiniDexed::ProcessSound (void) if(nMasterVolume > 0.0) { + // get the mix buffer of all TGs + float32_t (*SampleBuffer[2]); + tg_mixer->getBuffers(SampleBuffer); + + tg_mixer->zeroFill(); + for (uint8_t i = 0; i < m_nToneGenerators; i++) { tg_mixer->doAddMix(i,m_OutputLevel[i]); - reverb_send_mixer->doAddMix(i,m_OutputLevel[i]); } // END TG mixing - // BEGIN create SampleBuffer for holding audio data - float32_t SampleBuffer[2][nFrames]; - // END create SampleBuffer for holding audio data - - // get the mix of all TGs - tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]); - // BEGIN adding reverb if (m_nParameter[ParameterReverbEnable]) { float32_t ReverbBuffer[2][nFrames]; - float32_t ReverbSendBuffer[2][nFrames]; - arm_fill_f32(0.0f, ReverbBuffer[indexL], nFrames); - arm_fill_f32(0.0f, ReverbBuffer[indexR], nFrames); - arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames); - arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames); + float32_t (*ReverbSendBuffer[2]); + reverb_send_mixer->getBuffers(ReverbSendBuffer); + + reverb_send_mixer->zeroFill(); + + for (uint8_t i = 0; i < m_nToneGenerators; i++) + { + reverb_send_mixer->doAddMix(i,m_OutputLevel[i]); + } m_ReverbSpinLock.Acquire (); - reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]); reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames); // scale down and add left reverb buffer by reverb level diff --git a/src/patches/0001-PluginFX-change-gain-at-zero-crossing.patch b/src/patches/0001-PluginFX-change-gain-at-zero-crossing.patch new file mode 100644 index 0000000..ec72f63 --- /dev/null +++ b/src/patches/0001-PluginFX-change-gain-at-zero-crossing.patch @@ -0,0 +1,73 @@ +From 7daa07bd662d1894bc312b81c6b9e79260e7a66a Mon Sep 17 00:00:00 2001 +From: Gergo Koteles +Date: Fri, 11 Jul 2025 20:28:52 +0200 +Subject: [PATCH] PluginFX change gain at zero crossing + +--- + src/PluginFx.cpp | 26 +++++++++++++++++++------- + src/PluginFx.h | 2 ++ + 2 files changed, 21 insertions(+), 7 deletions(-) + +diff --git a/src/PluginFx.cpp b/src/PluginFx.cpp +index 7c3a1ab..2b7bb9b 100644 +--- a/src/PluginFx.cpp ++++ b/src/PluginFx.cpp +@@ -56,6 +56,7 @@ PluginFx::PluginFx() { + Cutoff = 1.0; + Reso = 0.0; + Gain = 1.0; ++ aGain = 1.0; + } + + void PluginFx::init(FRAC_NUM sr) { +@@ -117,16 +118,27 @@ void PluginFx::process(float *work, uint16_t sampleSize) { + + dc_od = work[sampleSize - 1]; + +- // Gain +- if (Gain == 0.0) +- { ++ if (Gain != aGain) { + for (uint16_t i = 0; i < sampleSize; i++ ) +- work[i] = 0.0; ++ { ++ if (i != 0 && work[i] >= 0 && work[i-1] <= 0) ++ aGain = Gain; ++ work[i] *= aGain; ++ } + } +- else if ( Gain != 1.0) ++ else + { +- for (uint16_t i = 0; i < sampleSize; i++ ) +- work[i] *= Gain; ++ // Gain ++ if (aGain == 0.0) ++ { ++ for (uint16_t i = 0; i < sampleSize; i++ ) ++ work[i] = 0.0; ++ } ++ else if ( aGain != 1.0) ++ { ++ for (uint16_t i = 0; i < sampleSize; i++ ) ++ work[i] *= aGain; ++ } + } + + // don't apply the LPF if the cutoff is to maximum +diff --git a/src/PluginFx.h b/src/PluginFx.h +index 090e241..c27ccc5 100644 +--- a/src/PluginFx.h ++++ b/src/PluginFx.h +@@ -67,6 +67,8 @@ class PluginFx { + float Reso; + float Gain; + ++ float aGain; ++ + void init(FRAC_NUM sampleRate); + void process(float *work, uint16_t sampleSize); + float getGain(void); +-- +2.50.1 +