diff --git a/README.md b/README.md
index 66dbb150..aa277e40 100644
--- a/README.md
+++ b/README.md
@@ -158,7 +158,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 70293ae5..e414a871 160000
--- a/Synth_Dexed
+++ b/Synth_Dexed
@@ -1 +1 @@
-Subproject commit 70293ae5998643c706244b090504dde8b4097851
+Subproject commit e414a8718300815aefc3fe0acd8df5c12ad0b58a
diff --git a/src/Synth_Dexed.mk b/src/Synth_Dexed.mk
index b411b0b0..498038a7 100644
--- a/src/Synth_Dexed.mk
+++ b/src/Synth_Dexed.mk
@@ -30,6 +30,6 @@ INCLUDE += -I $(SYNTH_DEXED_DIR)
INCLUDE += -I $(CMSIS_CORE_INCLUDE_DIR)
INCLUDE += -I $(CMSIS_DSP_INCLUDE_DIR)
INCLUDE += -I $(CMSIS_DSP_PRIVATE_INCLUDE_DIR)
-CXXFLAGS += -DARM_MATH_NEON
+CXXFLAGS += -DARM_MATH_NEON -DHAVE_NEON
EXTRACLEAN = $(SYNTH_DEXED_DIR)/*.[od] $(CMSIS_DSP_SOURCE_DIR)/SupportFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/SupportFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/BasicMathFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/FastMathFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/FilteringFunctions/*.[od] $(CMSIS_DSP_SOURCE_DIR)/CommonTables/*.[od]
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 00000000..ef6a51f3
--- /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 7f2badc5..be04f42d 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
@@ -56,10 +57,10 @@ class CDexedAdapter : public Dexed
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 4d987e90..ce2af1e4 100644
--- a/src/effect_platervbstereo.cpp
+++ b/src/effect_platervbstereo.cpp
@@ -153,14 +153,13 @@ 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.0f;
}
// #define sat16(n, rshift) signed_saturate_rshift((n), 16, (rshift))
-void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
+void AudioEffectPlateReverb::doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR, uint16_t len)
{
- int i;
float32_t input, acc, temp1, temp2;
uint16_t temp16;
float32_t rv_time;
@@ -203,7 +202,7 @@ void AudioEffectPlateReverb::doReverb(uint16_t len, int16_t audioblock[][2])
rv_time = rv_time_k;
- for (i=0; i < len; i++)
+ for (uint16_t i=0; i < len; i++)
{
// do the LFOs
lfo1_phase_acc += lfo1_adder;
@@ -236,7 +235,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 = inblockL[i] * input_attn;
// chained input allpasses, channel L
acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k;
@@ -259,7 +258,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 = inblockR[i] * input_attn;
// chained input allpasses, channel R
acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k;
@@ -406,13 +405,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;
+ rvbblockL[i] = master_lowpass_l;
// Channel R
#ifdef TAP1_MODULATED
@@ -456,12 +449,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;
+ rvbblockR[i] = master_lowpass_r;
}
}
diff --git a/src/effect_platervbstereo.h b/src/effect_platervbstereo.h
index 3abc62df..b7af5313 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,34 +61,28 @@ class AudioEffectPlateReverb
{
public:
AudioEffectPlateReverb(float32_t samplerate);
- void doReverb(uint16_t len, int16_t audioblock[][2]);
+ void doReverb(const float32_t* inblockL, const float32_t* inblockR, float32_t* rvbblockL, float32_t* rvbblockR,uint16_t len);
void size(float n)
{
n = constrain(n, 0.0f, 1.0f);
n = mapfloat(n, 0.0f, 1.0f, 0.2f, rv_time_k_max);
float32_t attn = mapfloat(n, 0.0f, rv_time_k_max, 0.5f, 0.25f);
- //__disable_irq();
rv_time_k = n;
input_attn = attn;
- //__enable_irq();
}
void hidamp(float n)
{
n = constrain(n, 0.0f, 1.0f);
- //__disable_irq();
lp_hidamp_k = 1.0f - n;
- //__enable_irq();
}
void lodamp(float n)
{
n = constrain(n, 0.0f, 1.0f);
- //__disable_irq();
lp_lodamp_k = -n;
rv_time_scaler = 1.0f - n * 0.12f; // limit the max reverb time, otherwise it will clip
- //__enable_irq();
}
void lowpass(float n)
@@ -130,24 +96,23 @@ class AudioEffectPlateReverb
{
n = constrain(n, 0.0f, 1.0f);
n = mapfloat(n, 0.0f, 1.0f, 0.005f, 0.65f);
- //__disable_irq();
in_allp_k = n;
loop_allp_k = n;
- //__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;}
bool get_bypass(void) {return bypass;}
void set_bypass(bool state) {bypass = state;};
void tgl_bypass(void) {bypass ^=1;}
+ float32_t get_level(void) {return reverb_level;}
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 eb4ba129..f33a5cad 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;
+ m_fPan[i] = 0.5f;
m_nMasterTune[i] = 0;
m_nMIDIChannel[i] = CMIDIDevice::Disabled;
@@ -113,8 +114,6 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
}
#endif
- SetParameter (ParameterCompressorEnable, 1);
-
// BEGIN setup reverb
reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate());
SetParameter (ParameterReverbEnable, 1);
@@ -123,8 +122,10 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
SetParameter (ParameterReverbLowDamp, 50);
SetParameter (ParameterReverbLowPass, 30);
SetParameter (ParameterReverbDiffusion, 65);
- SetParameter (ParameterReverbSend, 80);
+ SetParameter (ParameterReverbLevel, 80);
// END setup reverb
+
+ SetParameter (ParameterCompressorEnable, 1);
};
bool CMiniDexed::Initialize (void)
@@ -183,7 +184,7 @@ bool CMiniDexed::Initialize (void)
SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
- SetParameter (ParameterReverbSend, m_PerformanceConfig.GetReverbSend ());
+ SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
}
else
{
@@ -298,7 +299,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);
}
}
}
@@ -368,7 +369,8 @@ void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
assert (nTG < CConfig::ToneGenerators);
m_nPan[nTG] = nPan;
-
+ m_fPan[nTG]=mapfloat(nPan,0,127,0.0,1.0);
+
m_UI.ParameterChanged ();
}
@@ -550,9 +552,9 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
m_ReverbSpinLock.Release ();
break;
- case ParameterReverbSend:
+ case ParameterReverbLevel:
m_ReverbSpinLock.Acquire ();
- reverb->send (nValue / 99.0);
+ reverb->level (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
@@ -672,11 +674,20 @@ void CMiniDexed::ProcessSound (void)
m_GetChunkTimer.Start ();
}
- int16_t SampleBuffer[nFrames];
- m_pTG[0]->getSamples (nFrames, SampleBuffer);
+ float32_t SampleBuffer[nFrames];
+ m_pTG[0]->getSamples (SampleBuffer, nFrames);
- if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
- != (int) sizeof SampleBuffer)
+ // Convert dual float array (left, right) to single int16 array (left/right)
+ 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");
}
@@ -716,7 +727,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
@@ -728,54 +739,77 @@ 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;
+
+ if (m_bChannelsSwapped)
+ {
+ indexL=1;
+ indexR=0;
+ }
+ // init left sum output
+ assert (SampleBuffer[0]!=NULL);
+ arm_fill_f32(0.0, SampleBuffer[0], nFrames);
+ // init right sum output
+ assert (SampleBuffer[1]!=NULL);
+ arm_fill_f32(0.0, SampleBuffer[1], nFrames);
+
assert (CConfig::ToneGenerators == 8);
- for (unsigned i = 0; i < nFrames; i++)
+
+ // BEGIN stereo panorama
+ for (uint8_t i = 0; i < CConfig::ToneGenerators; 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
- {
- SampleBuffer[i][0] = (int16_t) nRight;
- SampleBuffer[i][1] = (int16_t) nLeft;
- }
+ float32_t tmpBuffer[nFrames];
+
+ m_PanoramaSpinLock.Acquire ();
+ // calculate left panorama of this TG
+ arm_scale_f32(m_OutputLevel[i], 1.0f-m_fPan[i], tmpBuffer, nFrames);
+ // add left panorama output of this TG to sum output
+ arm_add_f32(SampleBuffer[indexL], tmpBuffer, SampleBuffer[indexL], nFrames);
+
+ // calculate right panorama of this TG
+ arm_scale_f32(m_OutputLevel[i], m_fPan[i], tmpBuffer, nFrames);
+ // add right panaorama output of this TG to sum output
+ arm_add_f32(SampleBuffer[indexR], tmpBuffer, SampleBuffer[indexR], nFrames);
+ m_PanoramaSpinLock.Release ();
}
+ // END stereo panorama
// BEGIN adding reverb
if (m_nParameter[ParameterReverbEnable])
{
+ float32_t ReverbBuffer[2][nFrames];
+
m_ReverbSpinLock.Acquire ();
- reverb->doReverb(nFrames,SampleBuffer);
+ reverb->doReverb(SampleBuffer[indexL],SampleBuffer[indexR],ReverbBuffer[0], ReverbBuffer[1],nFrames);
m_ReverbSpinLock.Release ();
+
+ // scale down and add left reverb buffer by reverb level
+ arm_scale_f32(ReverbBuffer[0], reverb->get_level(), ReverbBuffer[0], nFrames);
+ arm_add_f32(SampleBuffer[indexL], ReverbBuffer[0], SampleBuffer[indexL], nFrames);
+ // scale down and add right reverb buffer by reverb level
+ arm_scale_f32(ReverbBuffer[1], reverb->get_level(), ReverbBuffer[1], nFrames);
+ arm_add_f32(SampleBuffer[indexR], ReverbBuffer[1], SampleBuffer[indexR], nFrames);
}
// END adding reverb
- if (m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer) != (int) sizeof SampleBuffer)
+ // Convert dual float array (left, right) to single int16 array (left/right)
+ 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");
}
@@ -812,7 +846,7 @@ bool CMiniDexed::SavePerformance (void)
m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]);
m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]);
m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
- m_PerformanceConfig.SetReverbSend (m_nParameter[ParameterReverbSend]);
+ m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]);
return m_PerformanceConfig.Save ();
}
diff --git a/src/minidexed.cpp.O b/src/minidexed.cpp.O
new file mode 100644
index 00000000..17a9c774
--- /dev/null
+++ b/src/minidexed.cpp.O
@@ -0,0 +1,853 @@
+//
+// minidexed.cpp
+//
+// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
+// Copyright (C) 2022 The MiniDexed Team
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+#include "minidexed.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+LOGMODULE ("minidexed");
+
+CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
+ CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, FATFS *pFileSystem)
+:
+#ifdef ARM_ALLOW_MULTI_CORE
+ CMultiCoreSupport (CMemorySystem::Get ()),
+#endif
+ m_pConfig (pConfig),
+ m_UI (this, pGPIOManager, pConfig),
+ m_PerformanceConfig (pFileSystem),
+ m_PCKeyboard (this, pConfig),
+ m_SerialMIDI (this, pInterrupt, pConfig),
+ m_bUseSerial (false),
+ m_pSoundDevice (0),
+ m_bChannelsSwapped (pConfig->GetChannelsSwapped ()),
+#ifdef ARM_ALLOW_MULTI_CORE
+ m_nActiveTGsLog2 (0),
+#endif
+ m_GetChunkTimer ("GetChunk",
+ 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()),
+ m_bProfileEnabled (m_pConfig->GetProfileEnabled ())
+{
+ assert (m_pConfig);
+
+ for (unsigned i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ m_nVoiceBankID[i] = 0;
+ 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;
+
+ m_nNoteLimitLow[i] = 0;
+ m_nNoteLimitHigh[i] = 127;
+ m_nNoteShift[i] = 0;
+
+ m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ());
+ assert (m_pTG[i]);
+
+ m_pTG[i]->activate ();
+ }
+
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i);
+ assert (m_pMIDIKeyboard[i]);
+ }
+
+ // select the sound device
+ const char *pDeviceName = pConfig->GetSoundDevice ();
+ if (strcmp (pDeviceName, "i2s") == 0)
+ {
+ LOGNOTE ("I2S mode");
+
+ m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize (), false,
+ pI2CMaster, pConfig->GetDACI2CAddress ());
+ }
+ else if (strcmp (pDeviceName, "hdmi") == 0)
+ {
+ LOGNOTE ("HDMI mode");
+
+ 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;
+ }
+ else
+ {
+ LOGNOTE ("PWM mode");
+
+ m_pSoundDevice = new CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize ());
+ }
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ for (unsigned nCore = 0; nCore < CORES; nCore++)
+ {
+ m_CoreStatus[nCore] = CoreStatusInit;
+ }
+#endif
+
+ // BEGIN setup tg_mixer
+ //tg_mixer = new AudioStereoMixer<8>();
+ // END setup tg_mixer
+
+ SetParameter (ParameterCompressorEnable, 1);
+
+ // BEGIN setup reverb
+ reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate());
+ SetParameter (ParameterReverbEnable, 1);
+ SetParameter (ParameterReverbSize, 70);
+ SetParameter (ParameterReverbHighDamp, 50);
+ SetParameter (ParameterReverbLowDamp, 50);
+ SetParameter (ParameterReverbLowPass, 30);
+ SetParameter (ParameterReverbDiffusion, 20);
+ SetParameter (ParameterReverbLevel, 80);
+ // END setup reverb
+};
+
+bool CMiniDexed::Initialize (void)
+{
+ assert (m_pConfig);
+ assert (m_pSoundDevice);
+
+ if (!m_UI.Initialize ())
+ {
+ return false;
+ }
+
+ m_SysExFileLoader.Load ();
+
+ if (m_SerialMIDI.Initialize ())
+ {
+ LOGNOTE ("Serial MIDI interface enabled");
+
+ m_bUseSerial = true;
+ }
+
+ for (unsigned i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ assert (m_pTG[i]);
+
+ SetVolume (100, i);
+ ProgramChange (0, i);
+
+ m_pTG[i]->setTranspose (24);
+
+ m_pTG[i]->setPBController (12, 1);
+ m_pTG[i]->setMWController (99, 7, 0);
+ }
+
+ if (m_PerformanceConfig.Load ())
+ {
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ BankSelectLSB (m_PerformanceConfig.GetBankNumber (nTG), nTG);
+ ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG);
+ SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG);
+ SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG);
+ SetPan (m_PerformanceConfig.GetPan (nTG), nTG);
+ SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG);
+
+ m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG);
+ m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
+ m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
+ }
+
+ // Effects
+ SetParameter (ParameterCompressorEnable, m_PerformanceConfig.GetCompressorEnable () ? 1 : 0);
+ SetParameter (ParameterReverbEnable, m_PerformanceConfig.GetReverbEnable () ? 1 : 0);
+ SetParameter (ParameterReverbSize, m_PerformanceConfig.GetReverbSize ());
+ SetParameter (ParameterReverbHighDamp, m_PerformanceConfig.GetReverbHighDamp ());
+ SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
+ SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
+ SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
+ SetParameter (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ());
+ }
+ else
+ {
+ SetMIDIChannel (CMIDIDevice::OmniMode, 0);
+ }
+
+ // setup and start the sound device
+ if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ()))
+ {
+ LOGERR ("Cannot allocate sound queue");
+
+ return false;
+ }
+
+#ifndef ARM_ALLOW_MULTI_CORE
+ m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono
+#else
+ m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 2); // 16-bit Stereo
+#endif
+
+ m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames ();
+
+ m_pSoundDevice->Start ();
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ // start secondary cores
+ if (!CMultiCoreSupport::Initialize ())
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void CMiniDexed::Process (bool bPlugAndPlayUpdated)
+{
+#ifndef ARM_ALLOW_MULTI_CORE
+ ProcessSound ();
+#endif
+
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ assert (m_pMIDIKeyboard[i]);
+ m_pMIDIKeyboard[i]->Process (bPlugAndPlayUpdated);
+ }
+
+ m_PCKeyboard.Process (bPlugAndPlayUpdated);
+
+ if (m_bUseSerial)
+ {
+ m_SerialMIDI.Process ();
+ }
+
+ m_UI.Process ();
+
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Dump ();
+ }
+}
+
+#ifdef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::Run (unsigned nCore)
+{
+ assert (1 <= nCore && nCore < CORES);
+
+ if (nCore == 1)
+ {
+ m_CoreStatus[nCore] = CoreStatusIdle; // core 1 ready
+
+ // wait for cores 2 and 3 to be ready
+ for (unsigned nCore = 2; nCore < CORES; nCore++)
+ {
+ while (m_CoreStatus[nCore] != CoreStatusIdle)
+ {
+ // just wait
+ }
+ }
+
+ while (m_CoreStatus[nCore] != CoreStatusExit)
+ {
+ ProcessSound ();
+ }
+ }
+ else // core 2 and 3
+ {
+ while (1)
+ {
+ m_CoreStatus[nCore] = CoreStatusIdle; // ready to be kicked
+ while (m_CoreStatus[nCore] == CoreStatusIdle)
+ {
+ // just wait
+ }
+
+ // now kicked from core 1
+
+ if (m_CoreStatus[nCore] == CoreStatusExit)
+ {
+ m_CoreStatus[nCore] = CoreStatusUnknown;
+
+ break;
+ }
+
+ assert (m_CoreStatus[nCore] == CoreStatusBusy);
+
+ // process the TGs, assigned to this core (2 or 3)
+
+ assert (m_nFramesToProcess <= CConfig::MaxChunkSize);
+ unsigned nTG = CConfig::TGsCore1 + (nCore-2)*CConfig::TGsCore23;
+ for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
+ {
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->getSamples (m_OutputLevel[nTG], m_nFramesToProcess);
+ }
+ }
+ }
+}
+
+#endif
+
+CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void)
+{
+ return &m_SysExFileLoader;
+}
+
+void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
+{
+ if (nBankLSB > 127)
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nVoiceBankID[nTG] = nBankLSB;
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
+{
+ if (nProgram > 31)
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nProgram[nTG] = nProgram;
+
+ uint8_t Buffer[156];
+ m_SysExFileLoader.GetVoice (m_nVoiceBankID[nTG], nProgram, Buffer);
+
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->loadVoiceParameters (Buffer);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetVolume (unsigned nVolume, unsigned nTG)
+{
+ if (nVolume > 127)
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nVolume[nTG] = nVolume;
+
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setGain (nVolume / 127.0);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetPan (unsigned nPan, unsigned nTG)
+{
+ 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 ();
+}
+
+void CMiniDexed::SetMasterTune (int nMasterTune, unsigned nTG)
+{
+ if (!(-99 <= nMasterTune && nMasterTune <= 99))
+ {
+ return;
+ }
+
+ assert (nTG < CConfig::ToneGenerators);
+ m_nMasterTune[nTG] = nMasterTune;
+
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setMasterTune ((int8_t) nMasterTune);
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ m_nMIDIChannel[nTG] = uchChannel;
+
+ for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
+ {
+ assert (m_pMIDIKeyboard[i]);
+ m_pMIDIKeyboard[i]->SetChannel (uchChannel, nTG);
+ }
+
+ m_PCKeyboard.SetChannel (uchChannel, nTG);
+
+ if (m_bUseSerial)
+ {
+ m_SerialMIDI.SetChannel (uchChannel, nTG);
+ }
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ unsigned nActiveTGs = 0;
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ if (m_nMIDIChannel[nTG] != CMIDIDevice::Disabled)
+ {
+ nActiveTGs++;
+ }
+ }
+
+ assert (nActiveTGs <= 8);
+ static const unsigned Log2[] = {0, 0, 1, 2, 2, 3, 3, 3, 3};
+ m_nActiveTGsLog2 = Log2[nActiveTGs];
+#endif
+
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ pitch = ApplyNoteLimits (pitch, nTG);
+ if (pitch >= 0)
+ {
+ m_pTG[nTG]->keyup (pitch);
+ }
+}
+
+void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ pitch = ApplyNoteLimits (pitch, nTG);
+ if (pitch >= 0)
+ {
+ m_pTG[nTG]->keydown (pitch, velocity);
+ }
+}
+
+int16_t CMiniDexed::ApplyNoteLimits (int16_t pitch, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+
+ if ( pitch < (int16_t) m_nNoteLimitLow[nTG]
+ || pitch > (int16_t) m_nNoteLimitHigh[nTG])
+ {
+ return -1;
+ }
+
+ pitch += m_nNoteShift[nTG];
+
+ if ( pitch < 0
+ || pitch > 127)
+ {
+ return -1;
+ }
+
+ return pitch;
+}
+
+void CMiniDexed::setSustain(bool sustain, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setSustain (sustain);
+}
+
+void CMiniDexed::setModWheel (uint8_t value, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setModWheel (value);
+}
+
+void CMiniDexed::setPitchbend (int16_t value, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setPitchbend (value);
+}
+
+void CMiniDexed::ControllersRefresh (unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->ControllersRefresh ();
+}
+
+void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
+{
+ assert (reverb);
+
+ assert (Parameter < ParameterUnknown);
+ m_nParameter[Parameter] = nValue;
+
+ switch (Parameter)
+ {
+<<<<<<< HEAD
+ case ParameterReverbSize: reverb->size (fValue); break;
+ case ParameterReverbHighDamp: reverb->hidamp (fValue); break;
+ case ParameterReverbLowDamp: reverb->lodamp (fValue); break;
+ case ParameterReverbLowPass: reverb->lowpass (fValue); break;
+ case ParameterReverbDiffusion: reverb->diffusion (fValue); break;
+ case ParameterReverbLevel: reverb->level (fValue); break;
+=======
+ case ParameterCompressorEnable:
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setCompressor (!!nValue);
+ }
+ break;
+
+ case ParameterReverbEnable:
+ m_ReverbSpinLock.Acquire ();
+ reverb->set_bypass (!nValue);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbSize:
+ m_ReverbSpinLock.Acquire ();
+ reverb->size (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbHighDamp:
+ m_ReverbSpinLock.Acquire ();
+ reverb->hidamp (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbLowDamp:
+ m_ReverbSpinLock.Acquire ();
+ reverb->lodamp (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbLowPass:
+ m_ReverbSpinLock.Acquire ();
+ reverb->lowpass (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbDiffusion:
+ m_ReverbSpinLock.Acquire ();
+ reverb->diffusion (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ case ParameterReverbLevel:
+ m_ReverbSpinLock.Acquire ();
+ reverb->level (nValue / 99.0);
+ m_ReverbSpinLock.Release ();
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+int CMiniDexed::GetParameter (TParameter Parameter)
+{
+ assert (Parameter < ParameterUnknown);
+ return m_nParameter[Parameter];
+}
+
+void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+
+ switch (Parameter)
+ {
+ case TGParameterVoiceBank: BankSelectLSB (nValue, nTG); break;
+ case TGParameterProgram: ProgramChange (nValue, nTG); break;
+ case TGParameterVolume: SetVolume (nValue, nTG); break;
+ case TGParameterPan: SetPan (nValue, nTG); break;
+ case TGParameterMasterTune: SetMasterTune (nValue, nTG); break;
+
+ case TGParameterMIDIChannel:
+ assert (0 <= nValue && nValue <= 255);
+ SetMIDIChannel ((uint8_t) nValue, nTG);
+ break;
+
+ default:
+ assert (0);
+ break;
+ }
+}
+
+int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+
+ switch (Parameter)
+ {
+ case TGParameterVoiceBank: return m_nVoiceBankID[nTG];
+ case TGParameterProgram: return m_nProgram[nTG];
+ case TGParameterVolume: return m_nVolume[nTG];
+ case TGParameterPan: return m_nPan[nTG];
+ case TGParameterMasterTune: return m_nMasterTune[nTG];
+ case TGParameterMIDIChannel: return m_nMIDIChannel[nTG];
+
+ default:
+ assert (0);
+ return 0;
+ }
+}
+
+void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigned nOP, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ assert (nOP <= 6);
+
+ if (nOP < 6)
+ {
+ nOP = 5 - nOP; // OPs are in reverse order
+ }
+
+ uchOffset += nOP * 21;
+ assert (uchOffset < 156);
+
+ m_pTG[nTG]->setVoiceDataElement (uchOffset, uchValue);
+}
+
+uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ assert (nOP <= 6);
+
+ if (nOP < 6)
+ {
+ nOP = 5 - nOP; // OPs are in reverse order
+ }
+
+ uchOffset += nOP * 21;
+ assert (uchOffset < 156);
+
+ return m_pTG[nTG]->getVoiceDataElement (uchOffset);
+}
+
+std::string CMiniDexed::GetVoiceName (unsigned nTG)
+{
+ char VoiceName[11];
+ memset (VoiceName, 0, sizeof VoiceName);
+
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+ m_pTG[nTG]->setName (VoiceName);
+
+ std::string Result (VoiceName);
+
+ return Result;
+}
+
+#ifndef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::ProcessSound (void)
+{
+ assert (m_pSoundDevice);
+
+ unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail ();
+ if (nFrames >= m_nQueueSizeFrames/2)
+ {
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Start ();
+ }
+
+ //int16_t SampleBuffer[nFrames]; // TODO float->int
+ m_pTG[0]->getSamples (SampleBuffer, nFrames);
+
+ if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
+ != (int) sizeof SampleBuffer)
+ {
+ LOGERR ("Sound data dropped");
+ }
+
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Stop ();
+ }
+ }
+}
+
+#else // #ifdef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::ProcessSound (void)
+{
+ assert (m_pSoundDevice);
+
+ unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail ();
+ if (nFrames >= m_nQueueSizeFrames/2)
+ {
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Start ();
+ }
+
+ m_nFramesToProcess = nFrames;
+
+ // kick secondary cores
+ for (unsigned nCore = 2; nCore < CORES; nCore++)
+ {
+ assert (m_CoreStatus[nCore] == CoreStatusIdle);
+ m_CoreStatus[nCore] = CoreStatusBusy;
+ }
+
+ // process the TGs assigned to core 1
+ assert (nFrames <= CConfig::MaxChunkSize);
+ for (unsigned i = 0; i < CConfig::TGsCore1; i++)
+ {
+ assert (m_pTG[i]);
+ m_pTG[i]->getSamples (m_OutputLevel[i], nFrames);
+ }
+
+ // wait for cores 2 and 3 to complete their work
+ for (unsigned nCore = 2; nCore < CORES; nCore++)
+ {
+ while (m_CoreStatus[nCore] != CoreStatusIdle)
+ {
+ // just wait
+ }
+ }
+
+ //
+ // Audio signal path after tone generators starts here
+ //
+
+ // now mix the output of all TGs
+ float32_t SampleBuffer[2][nFrames];
+ uint8_t indexL=0, indexR=1;
+
+ if (m_bChannelsSwapped)
+ {
+ indexL=1;
+ indexR=0;
+ }
+
+ // init left sum output
+ assert (SampleBuffer[0]!=NULL);
+ arm_fill_f32(0.0, SampleBuffer[0], nFrames);
+ // init right sum output
+ assert (SampleBuffer[1]!=NULL);
+ arm_fill_f32(0.0, SampleBuffer[1], nFrames);
+
+ assert (CConfig::ToneGenerators == 8);
+
+ // BEGIN stereo panorama
+ for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
+ {
+ float32_t tmpBuffer[nFrames];
+
+ m_PanoramaSpinLock.Acquire ();
+ // calculate left panorama of this TG
+ arm_scale_f32(m_OutputLevel[i], 1.0f-pan_float[i], tmpBuffer, nFrames);
+ // add left panorama output of this TG to sum output
+ arm_add_f32(SampleBuffer[indexL], tmpBuffer, SampleBuffer[indexL], nFrames);
+
+ // calculate right panorama of this TG
+ arm_scale_f32(m_OutputLevel[i], pan_float[i], tmpBuffer, nFrames);
+ // add right panaorama output of this TG to sum output
+ arm_add_f32(SampleBuffer[indexR], tmpBuffer, SampleBuffer[indexR], nFrames);
+
+ m_PanoramaSpinLock.Release ();
+ }
+ // END stereo panorama
+
+ // BEGIN adding reverb
+ if (m_nParameter[ParameterReverbEnable])
+ {
+ float32_t ReverbBuffer[2][nFrames];
+
+ m_ReverbSpinLock.Acquire ();
+ reverb->doReverb(SampleBuffer[indexL],SampleBuffer[indexR],ReverbBuffer[0], ReverbBuffer[1],nFrames);
+ m_ReverbSpinLock.Release ();
+
+ // scale down and add left reverb buffer by reverb level
+ arm_scale_f32(ReverbBuffer[0], reverb->get_level(), ReverbBuffer[0], nFrames);
+ arm_add_f32(SampleBuffer[indexL], ReverbBuffer[0], SampleBuffer[indexL], nFrames);
+ // scale down and add right reverb buffer by reverb level
+ arm_scale_f32(ReverbBuffer[1], reverb->get_level(), ReverbBuffer[1], nFrames);
+ arm_add_f32(SampleBuffer[indexR], ReverbBuffer[1], SampleBuffer[indexR], nFrames);
+ }
+ // END adding reverb
+
+ // Convert dual float array (left, right) to single int16 array (left/right)
+ 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");
+ }
+
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Stop ();
+ }
+ }
+}
+
+#endif
+
+bool CMiniDexed::SavePerformance (void)
+{
+ for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ m_PerformanceConfig.SetBankNumber (m_nVoiceBankID[nTG], nTG);
+ m_PerformanceConfig.SetVoiceNumber (m_nProgram[nTG], nTG);
+ m_PerformanceConfig.SetMIDIChannel (m_nMIDIChannel[nTG], nTG);
+ m_PerformanceConfig.SetVolume (m_nVolume[nTG], nTG);
+ m_PerformanceConfig.SetPan (m_nPan[nTG], nTG);
+ m_PerformanceConfig.SetDetune (m_nMasterTune[nTG], nTG);
+
+ m_PerformanceConfig.SetNoteLimitLow (m_nNoteLimitLow[nTG], nTG);
+ m_PerformanceConfig.SetNoteLimitHigh (m_nNoteLimitHigh[nTG], nTG);
+ m_PerformanceConfig.SetNoteShift (m_nNoteShift[nTG], nTG);
+ }
+
+ m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[ParameterCompressorEnable]);
+ m_PerformanceConfig.SetReverbEnable (!!m_nParameter[ParameterReverbEnable]);
+ m_PerformanceConfig.SetReverbSize (m_nParameter[ParameterReverbSize]);
+ m_PerformanceConfig.SetReverbHighDamp (m_nParameter[ParameterReverbHighDamp]);
+ m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]);
+ m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]);
+ m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
+ m_PerformanceConfig.SetReverbLevel (m_nParameter[ParameterReverbLevel]);
+
+ return m_PerformanceConfig.Save ();
+}
diff --git a/src/minidexed.h b/src/minidexed.h
index 2104091f..1aed730c 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
@@ -84,7 +86,7 @@ class CMiniDexed
ParameterReverbLowDamp,
ParameterReverbLowPass,
ParameterReverbDiffusion,
- ParameterReverbSend,
+ ParameterReverbLevel,
ParameterUnknown
};
@@ -141,6 +143,7 @@ class CMiniDexed
unsigned m_nProgram[CConfig::ToneGenerators];
unsigned m_nVolume[CConfig::ToneGenerators];
unsigned m_nPan[CConfig::ToneGenerators];
+ float32_t m_fPan[CConfig::ToneGenerators];
int m_nMasterTune[CConfig::ToneGenerators];
unsigned m_nMIDIChannel[CConfig::ToneGenerators];
@@ -165,13 +168,16 @@ class CMiniDexed
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_PanoramaSpinLock;
CSpinLock m_ReverbSpinLock;
};
diff --git a/src/mixer.cpp b/src/mixer.cpp
new file mode 100644
index 00000000..16f4b511
--- /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