diff --git a/README.md b/README.md index 358b181..57b4f61 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ sudo losetup -d "${DEV}" rm -r boot # Write to SD card -sudo dd if="${IMG}" of=/dev/mmcblk0 bs=512k status=progress +sudo dd if="${IMG}" of=/dev/mmcblk0 bs=512k status=progress && sync ``` ## Acknowledgements diff --git a/Synth_Dexed b/Synth_Dexed index 70293ae..e414a87 160000 --- a/Synth_Dexed +++ b/Synth_Dexed @@ -1 +1 @@ -Subproject commit 70293ae5998643c706244b090504dde8b4097851 +Subproject commit e414a8718300815aefc3fe0acd8df5c12ad0b58a diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..ef6a51f --- /dev/null +++ b/src/common.h @@ -0,0 +1,21 @@ + +#ifndef _common_h +#define _common_h + +inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +inline float32_t mapfloat(float32_t val, float32_t in_min, float32_t in_max, float32_t out_min, float32_t out_max) +{ + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +#define constrain(amt, low, high) ({ \ + __typeof__(amt) _amt = (amt); \ + __typeof__(low) _low = (low); \ + __typeof__(high) _high = (high); \ + (_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \ +}) + +#endif diff --git a/src/dexedadapter.h b/src/dexedadapter.h index a7c77cc..71862bb 100644 --- a/src/dexedadapter.h +++ b/src/dexedadapter.h @@ -17,6 +17,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // + #ifndef _dexedadapter_h #define _dexedadapter_h @@ -34,10 +35,6 @@ public: : Dexed (maxnotes, rate) { Dexed::setCompressor(true); - if(Dexed::getCompressor()==true) - printf("Dexed-Compressor: enabled\n"); - else - printf("Dexed-Compressor: disabled\n"); } void loadVoiceParameters (uint8_t* data) @@ -61,10 +58,10 @@ public: m_SpinLock.Release (); } - void getSamples (uint16_t n_samples, int16_t* buffer) + void getSamples (float32_t* buffer, uint16_t n_samples) { m_SpinLock.Acquire (); - Dexed::getSamples (n_samples, buffer); + Dexed::getSamples (buffer, n_samples); m_SpinLock.Release (); } diff --git a/src/effect_platervbstereo.cpp b/src/effect_platervbstereo.cpp index 4d987e9..5f4ec46 100644 --- a/src/effect_platervbstereo.cpp +++ b/src/effect_platervbstereo.cpp @@ -153,12 +153,12 @@ AudioEffectPlateReverb::AudioEffectPlateReverb(float32_t samplerate) lfo2_phase_acc = 0; lfo2_adder = (UINT32_MAX + 1)/(samplerate * LFO2_FREQ_HZ); - send_level = 0.0; + reverb_level = 0.0; } // #define sat16(n, rshift) signed_saturate_rshift((n), 16, (rshift)) -void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2]) +void AudioEffectPlateReverb::doReverb(float32_t* audioblockL, float32_t* audioblockR, uint16_t len) { int i; float32_t input, acc, temp1, temp2; @@ -236,7 +236,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2]) y += (int64_t)y1 * idx; lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output - input = (float32_t(audioblock[i][0])/32767.0f) * input_attn; + input = audioblockL[i] * input_attn; // chained input allpasses, channel L acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k; @@ -259,7 +259,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2]) in_allp_out_L = acc; if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0; - input = (float32_t(audioblock[i][1])/32767.0f) * input_attn; + input = audioblockR[i] * input_attn; // chained input allpasses, channel R acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k; @@ -406,13 +406,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2]) temp1 = acc - master_lowpass_l; master_lowpass_l += temp1 * master_lowpass_f; - int32_t out = audioblock[i][0] + int16_t(master_lowpass_l * 32767.0f * send_level); - if(out > INT16_MAX) - audioblock[i][0] = INT16_MAX; - else if(out < INT16_MIN) - audioblock[i][0] = INT16_MIN; - else - audioblock[i][0] = out; + audioblockL[i]+=master_lowpass_l * reverb_level; // Channel R #ifdef TAP1_MODULATED @@ -456,12 +450,6 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2]) temp1 = acc - master_lowpass_r; master_lowpass_r += temp1 * master_lowpass_f; - out = audioblock[i][1] + int16_t(master_lowpass_l * 32767.0f * send_level); - if(out > INT16_MAX) - audioblock[i][1] = INT16_MAX; - else if(out < INT16_MIN) - audioblock[i][1] = INT16_MIN; - else - audioblock[i][1] = out; + audioblockR[i]+=master_lowpass_r * reverb_level; } } diff --git a/src/effect_platervbstereo.h b/src/effect_platervbstereo.h index 3abc62d..8db1bca 100644 --- a/src/effect_platervbstereo.h +++ b/src/effect_platervbstereo.h @@ -45,37 +45,9 @@ #ifndef _EFFECT_PLATERVBSTEREO_H #define _EFFECT_PLATERVBSTEREO_H -#include "arm_math.h" #include - -#define constrain(amt, low, high) ({ \ - __typeof__(amt) _amt = (amt); \ - __typeof__(low) _low = (low); \ - __typeof__(high) _high = (high); \ - (_amt < _low) ? _low : ((_amt > _high) ? _high : _amt); \ -}) - -/* -template -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -template -inline static T max(const T& a, const T& b) { - return a > b ? a : b; -} - -inline long maplong(long x, long in_min, long in_max, long out_min, long out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} -*/ - -inline float32_t mapfloat(float32_t val, float32_t in_min, float32_t in_max, float32_t out_min, float32_t out_max) -{ - return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - +#include +#include "common.h" /*** * Loop delay modulation: comment/uncomment to switch sin/cos @@ -89,7 +61,7 @@ class AudioEffectPlateReverb { public: AudioEffectPlateReverb(float32_t samplerate); - void doReverb(uint16_t len, int16_t audioblock[][2]); + void doReverb(float32_t* audioblockL, float32_t* audioblockR, uint16_t len); void size(float n) { @@ -136,9 +108,9 @@ public: //__enable_irq(); } - void send(float n) + void level(float n) { - send_level = constrain(n, 0.0f, 1.0f); + reverb_level = constrain(n, 0.0f, 1.0f); } float32_t get_size(void) {return rv_time_k;} @@ -147,7 +119,7 @@ public: void tgl_bypass(void) {bypass ^=1;} private: bool bypass = false; - float32_t send_level; + float32_t reverb_level; float32_t input_attn; float32_t in_allp_k; // input allpass coeff diff --git a/src/minidexed.cpp b/src/minidexed.cpp index b462b69..eef5fa4 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -58,6 +58,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, m_nProgram[i] = 0; m_nVolume[i] = 100; m_nPan[i] = 64; + pan_float[i]=0.0f; m_nMasterTune[i] = 0; m_nMIDIChannel[i] = CMIDIDevice::Disabled; @@ -113,6 +114,10 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, } #endif + // BEGIN setup tg_mixer + tg_mixer = new AudioStereoMixer<8>(); + // END setup tg_mixer + // BEGIN setup reverb reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate()); SetParameter (ParameterReverbSize, 70); @@ -120,7 +125,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, SetParameter (ParameterReverbLowDamp, 50); SetParameter (ParameterReverbLowPass, 30); SetParameter (ParameterReverbDiffusion, 65); - SetParameter (ParameterReverbSend, 80); + SetParameter (ParameterReverbLevel, 80); // END setup reverb }; @@ -285,7 +290,7 @@ void CMiniDexed::Run (unsigned nCore) for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++) { assert (m_pTG[nTG]); - m_pTG[nTG]->getSamples (m_nFramesToProcess, m_OutputLevel[nTG]); + m_pTG[nTG]->getSamples (m_OutputLevel[nTG], m_nFramesToProcess); } } } @@ -348,13 +353,11 @@ void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG) void CMiniDexed::SetPan (unsigned nPan, unsigned nTG) { - if (nPan > 127) - { - return; - } + constrain(nPan,-1.0f,1.0f); assert (nTG < CConfig::ToneGenerators); m_nPan[nTG] = nPan; + pan_float[nTG]=mapfloat(nPan,0,127,-1.0,1.0); m_UI.ParameterChanged (); } @@ -502,7 +505,7 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue) case ParameterReverbLowDamp: reverb->lodamp (fValue); break; case ParameterReverbLowPass: reverb->lowpass (fValue); break; case ParameterReverbDiffusion: reverb->diffusion (fValue); break; - case ParameterReverbSend: reverb->send (fValue); break; + case ParameterReverbLevel: reverb->level (fValue); break; default: assert (0); @@ -622,8 +625,8 @@ void CMiniDexed::ProcessSound (void) m_GetChunkTimer.Start (); } - int16_t SampleBuffer[nFrames]; - m_pTG[0]->getSamples (nFrames, SampleBuffer); + //int16_t SampleBuffer[nFrames]; // TODO float->int + m_pTG[0]->getSamples (SampleBuffer, nFrames); if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer) != (int) sizeof SampleBuffer) @@ -666,7 +669,7 @@ void CMiniDexed::ProcessSound (void) for (unsigned i = 0; i < CConfig::TGsCore1; i++) { assert (m_pTG[i]); - m_pTG[i]->getSamples (nFrames, m_OutputLevel[i]); + m_pTG[i]->getSamples (m_OutputLevel[i], nFrames); } // wait for cores 2 and 3 to complete their work @@ -678,51 +681,53 @@ void CMiniDexed::ProcessSound (void) } } + // + // Audio signal path after tone generators starts here + // + // now mix the output of all TGs - int16_t SampleBuffer[nFrames][2]; + float32_t SampleBuffer[2][nFrames]; + uint8_t indexL=0, indexR=1; + + assert (SampleBuffer[0]!=NULL); + arm_fill_f32(0.0, SampleBuffer[0], nFrames); + assert (SampleBuffer[1]!=NULL); + arm_fill_f32(0.0, SampleBuffer[1], nFrames); + + if (m_bChannelsSwapped) + { + indexL=1; + indexR=0; + } assert (CConfig::ToneGenerators == 8); - for (unsigned i = 0; i < nFrames; i++) + + for (uint16_t i = 0; i < nFrames; i++) { - int32_t nLeft = m_OutputLevel[0][i] * (127-m_nPan[0]) - + m_OutputLevel[1][i] * (127-m_nPan[1]) - + m_OutputLevel[2][i] * (127-m_nPan[2]) - + m_OutputLevel[3][i] * (127-m_nPan[3]) - + m_OutputLevel[4][i] * (127-m_nPan[4]) - + m_OutputLevel[5][i] * (127-m_nPan[5]) - + m_OutputLevel[6][i] * (127-m_nPan[6]) - + m_OutputLevel[7][i] * (127-m_nPan[7]); - nLeft >>= m_nActiveTGsLog2 + 7; - - int32_t nRight = m_OutputLevel[0][i] * m_nPan[0] - + m_OutputLevel[1][i] * m_nPan[1] - + m_OutputLevel[2][i] * m_nPan[2] - + m_OutputLevel[3][i] * m_nPan[3] - + m_OutputLevel[4][i] * m_nPan[4] - + m_OutputLevel[5][i] * m_nPan[5] - + m_OutputLevel[6][i] * m_nPan[6] - + m_OutputLevel[7][i] * m_nPan[7]; - nRight >>= m_nActiveTGsLog2 + 7; - - if (!m_bChannelsSwapped) - { - SampleBuffer[i][0] = (int16_t) nLeft; - SampleBuffer[i][1] = (int16_t) nRight; - } - else + for(uint8_t n=0; ndoReverb(nFrames,SampleBuffer); + reverb->doReverb(SampleBuffer[0],SampleBuffer[1],nFrames); m_ReverbSpinLock.Release (); // END adding reverb - if (m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer) != (int) sizeof SampleBuffer) + // Convert float to int16 array + float32_t tmp_float[nFrames*2]; + int16_t tmp_int[nFrames*2]; + for(uint16_t i=0; iWrite (tmp_int, sizeof tmp_int) != (int) sizeof tmp_int) { LOGERR ("Sound data dropped"); } diff --git a/src/minidexed.h b/src/minidexed.h index 2f991f0..987b27a 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -39,7 +39,9 @@ #include #include #include +#include "common.h" #include "effect_platervbstereo.h" +#include "mixer.h" class CMiniDexed #ifdef ARM_ALLOW_MULTI_CORE @@ -82,7 +84,7 @@ public: ParameterReverbLowDamp, ParameterReverbLowPass, ParameterReverbDiffusion, - ParameterReverbSend, + ParameterReverbLevel, ParameterUnknown }; @@ -137,6 +139,7 @@ private: unsigned m_nProgram[CConfig::ToneGenerators]; unsigned m_nVolume[CConfig::ToneGenerators]; unsigned m_nPan[CConfig::ToneGenerators]; + unsigned pan_float[CConfig::ToneGenerators]; int m_nMasterTune[CConfig::ToneGenerators]; unsigned m_nMIDIChannel[CConfig::ToneGenerators]; @@ -161,13 +164,15 @@ private: unsigned m_nActiveTGsLog2; volatile TCoreStatus m_CoreStatus[CORES]; volatile unsigned m_nFramesToProcess; - int16_t m_OutputLevel[CConfig::ToneGenerators][CConfig::MaxChunkSize]; + float32_t m_OutputLevel[CConfig::ToneGenerators][CConfig::MaxChunkSize]; #endif CPerformanceTimer m_GetChunkTimer; bool m_bProfileEnabled; AudioEffectPlateReverb* reverb; + AudioStereoMixer<8>* tg_mixer; + CSpinLock m_ReverbSpinLock; }; diff --git a/src/mixer.cpp b/src/mixer.cpp new file mode 100644 index 0000000..16f4b51 --- /dev/null +++ b/src/mixer.cpp @@ -0,0 +1,87 @@ +// Taken from https://github.com/manicken/Audio/tree/templateMixer +// Adapted for MiniDexed by Holger Wirtz + +/* Audio Library for Teensy 3.X + * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * 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: + * + * The above copyright notice, development funding notice, and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include "arm_math.h" +#include "mixer.h" + +template void AudioMixer::gain(uint8_t channel, float32_t gain) +{ + if (channel >= NN) return; + + if (gain > MAX_GAIN) + gain = MAX_GAIN; + else if (gain < MIN_GAIN) + gain = MIN_GAIN; + multiplier[channel] = gain; +} + +template void AudioMixer::gain(float32_t gain) +{ + for (uint8_t i = 0; i < NN; i++) + { + if (gain > MAX_GAIN) + gain = MAX_GAIN; + else if (gain < MIN_GAIN) + gain = MIN_GAIN; + multiplier[i] = gain; + } +} + +template void AudioMixer::doAddMix(uint8_t channel, float32_t* in, float32_t* out, uint16_t len) +{ + float32_t* tmp=malloc(sizeof(float32_t)*len); + + assert(tmp!=NULL); + + arm_scale_f32(in,multiplier[channel],tmp,len); + arm_add_f32(out, tmp, out, len); + + free(tmp); +} + +template void AudioStereoMixer::doAddMix(uint8_t channel, float32_t* in[], float32_t* out[], uint16_t len) +{ + float32_t* tmp=malloc(sizeof(float32_t)*len); + + assert(tmp!=NULL); + + // panorama + for(uint16_t i=0;i + +/* Audio Library for Teensy 3.X + * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * 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: + * + * The above copyright notice, development funding notice, and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef template_mixer_h_ +#define template_mixer_h_ + +#include "arm_math.h" +#include + +#define UNITYGAIN 1.0f +#define MAX_GAIN 1.0f +#define MIN_GAIN 0.0f + +template class AudioMixer +{ +public: + AudioMixer(void) + { + for (uint8_t i=0; i class AudioStereoMixer : public AudioMixer +{ +public: + AudioStereoMixer(void) + { + AudioMixer(); + for (uint8_t i=0; i