diff --git a/Synth_Dexed b/Synth_Dexed
index e40f29d..8c677ce 160000
--- a/Synth_Dexed
+++ b/Synth_Dexed
@@ -1 +1 @@
-Subproject commit e40f29dc0ab6b70d57fc6a0c0d30130adfbc903d
+Subproject commit 8c677ceb4b3fb73f8643e30ff6cf4158dc8b9e53
diff --git a/src/mididevice.cpp b/src/mididevice.cpp
index 47f3f11..7856a12 100644
--- a/src/mididevice.cpp
+++ b/src/mididevice.cpp
@@ -20,12 +20,16 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
+
+#include
#include "mididevice.h"
#include "minidexed.h"
#include "config.h"
#include
#include
+LOGMODULE ("mididevice");
+
#define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0b1010 // TODO
@@ -45,6 +49,7 @@
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
+#define MIDI_SYSTEM_EXCLUSIVE 0xF0
#define MIDI_TIMING_CLOCK 0xF8
#define MIDI_ACTIVE_SENSING 0xFE
@@ -77,10 +82,24 @@ u8 CMIDIDevice::GetChannel (unsigned nTG) const
return m_ChannelMap[nTG];
}
-void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable)
+int8_t CMIDIDevice::SearchChannel (uint8_t uMidiChannel) const
{
- assert (m_pSynthesizer != 0);
+ uint8_t nTG;
+ int8_t nResult=-1;
+ for (nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
+ {
+ if (m_ChannelMap[nTG] == uMidiChannel)
+ {
+ nResult=nTG;
+ break;
+ }
+ }
+
+ return(nResult);
+}
+void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable)
+{
// The packet contents are just normal MIDI data - see
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message
@@ -252,6 +271,8 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
}
+ else if(ucStatus == MIDI_SYSTEM_EXCLUSIVE) // No MIDI channel information in SYSEX
+ HandleSystemExclusive(pMessage, nLength, ucChannel);
}
}
@@ -265,3 +286,132 @@ void CMIDIDevice::AddDevice (const char *pDeviceName)
s_DeviceMap.insert (std::pair (pDeviceName, this));
}
+
+void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const uint8_t nMidiChannel)
+{
+ int16_t sysex_return;
+ int8_t nTG;
+
+ if ((pMessage[2] & 0x0f) != nMidiChannel) // for Yamaha: SysEx channel checking inside message
+ return;
+
+ nTG = SearchChannel(nMidiChannel);
+ if(nTG < 0)
+ return;
+
+ LOGDBG("SysEx data length: [%d]",nLength);
+ LOGDBG("SysEx data:");
+ for (uint16_t i = 0; i < nLength; i++)
+ LOGDBG("%04d : [0x%02h",i,pMessage[i]);
+
+ sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG);
+ LOGDBG("SYSEX handler return value: %d", sysex_return);
+
+ switch (sysex_return)
+ {
+ case -1:
+ LOGERR("SysEx end status byte not detected.");
+ break;
+ case -2:
+ LOGERR("E: SysEx vendor not Yamaha.");
+ break;
+ case -3:
+ LOGERR("E: Unknown SysEx parameter change.");
+ break;
+ case -4:
+ LOGERR(" Unknown SysEx voice or function.");
+ break;
+ case -5:
+ LOGERR("E: Not a SysEx voice bulk upload.");
+ break;
+ case -6:
+ LOGERR("E: Wrong length for SysEx voice bulk upload (not 155).");
+ break;
+ case -7:
+ LOGERR("E: Checksum error for one voice.");
+ break;
+ case -8:
+ LOGERR("E: Not a SysEx bank bulk upload.");
+ break;
+ case -9:
+ LOGERR("E: Wrong length for SysEx bank bulk upload (not 4096).");
+ case -10:
+ LOGERR("E: Checksum error for bank.");
+ break;
+ case -11:
+ LOGERR("E: Unknown SysEx message.");
+ break;
+ case 64:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setMonoMode(pMessage[5],nTG);
+ break;
+ case 65:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setPitchbendRange(pMessage[5],nTG);
+ break;
+ case 66:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setPitchbendStep(pMessage[5],nTG);
+ break;
+ case 67:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setPortamentoMode(pMessage[5],nTG);
+ break;
+ case 68:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setPortamentoGlissando(pMessage[5],nTG);
+ break;
+ case 69:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setPortamentoTime(pMessage[5],nTG);
+ break;
+ case 70:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setModWheelRange(pMessage[5],nTG);
+ break;
+ case 71:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setModWheelTarget(pMessage[5],nTG);
+ break;
+ case 72:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setFootControllerRange(pMessage[5],nTG);
+ break;
+ case 73:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setFootControllerTarget(pMessage[5],nTG);
+ break;
+ case 74:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setBreathControllerRange(pMessage[5],nTG);
+ break;
+ case 75:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setBreathControllerTarget(pMessage[5],nTG);
+ break;
+ case 76:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setAftertouchRange(pMessage[5],nTG);
+ break;
+ case 77:
+ LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]);
+ m_pSynthesizer->setAftertouchTarget(pMessage[5],nTG);
+ break;
+ case 100:
+ // load sysex-data into voice memory
+ LOGDBG("One Voice bulk upload");
+ m_pSynthesizer->loadVoiceParameters(pMessage,nTG);
+
+ break;
+ case 200:
+ LOGDBG("Bank bulk upload.");
+ //TODO: add code for storing a bank bulk upload
+ LOGNOTE("Currently code for storing a bulk bank upload is missing!");
+ break;
+ default:
+ LOGDBG("SysEx voice parameter change: %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]);
+ m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG);
+ break;
+ }
+}
+
diff --git a/src/mididevice.h b/src/mididevice.h
index d4e3079..1611446 100644
--- a/src/mididevice.h
+++ b/src/mididevice.h
@@ -47,6 +47,7 @@ public:
void SetChannel (u8 ucChannel, unsigned nTG);
u8 GetChannel (unsigned nTG) const;
+ int8_t SearchChannel (uint8_t uMidiChannel) const;
virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {}
@@ -55,6 +56,8 @@ protected:
void AddDevice (const char *pDeviceName);
+ void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const uint8_t nMidiChannel);
+
private:
CMiniDexed *m_pSynthesizer;
CConfig *m_pConfig;
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 68908bf..c827074 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -940,3 +941,179 @@ bool CMiniDexed::SavePerformance (void)
return m_PerformanceConfig.Save ();
}
+
+void CMiniDexed::setMonoMode(uint8_t mono, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setMonoMode(constrain(mono, 0, 1));
+ m_pTG[nTG]->doRefreshVoice();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setPitchbendRange(uint8_t range, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setPitchbendRange(constrain(range, 0, 12));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setPitchbendStep(uint8_t step, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setPitchbendStep(constrain(step, 0, 12));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setPortamentoMode(uint8_t mode, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setPortamentoMode(constrain(mode, 0, 1));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setPortamentoGlissando(uint8_t glissando, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setPortamentoGlissando(constrain(glissando, 0, 1));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setPortamentoTime(uint8_t time, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setPortamentoTime(constrain(time, 0, 99));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setModWheelRange(uint8_t range, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setModWheelRange(constrain(range, 0, 99));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setModWheelTarget(uint8_t target, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setModWheelTarget(constrain(target, 0, 7));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setFootControllerRange(uint8_t range, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setFootControllerRange(constrain(range, 0, 99));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setFootControllerTarget(uint8_t target, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setFootControllerTarget(constrain(target, 0, 7));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setBreathControllerRange(uint8_t range, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setBreathControllerRange(constrain(range, 0, 99));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setBreathControllerTarget(uint8_t target, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setBreathControllerTarget(constrain(target, 0, 7));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setAftertouchRange(uint8_t range, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setAftertouchRange(constrain(range, 0, 99));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setAftertouchTarget(uint8_t target, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setAftertouchTarget(constrain(target, 0, 7));
+ m_pTG[nTG]->ControllersRefresh();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::loadVoiceParameters(const uint8_t* data, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ uint8_t voice[161];
+
+ memcpy(voice, data, sizeof(uint8_t)*161);
+
+ // fix voice name
+ for (uint8_t i = 0; i < 10; i++)
+ {
+ if (voice[151 + i] > 126) // filter characters
+ voice[151 + i] = 32;
+ }
+
+ m_pTG[nTG]->loadVoiceParameters(&voice[6]);
+ m_pTG[nTG]->doRefreshVoice();
+ m_UI.ParameterChanged ();
+}
+
+void CMiniDexed::setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG)
+{
+ assert (nTG < CConfig::ToneGenerators);
+ assert (m_pTG[nTG]);
+
+ m_pTG[nTG]->setVoiceDataElement(constrain(data, 0, 155),constrain(number, 0, 99));
+ m_pTG[nTG]->doRefreshVoice();
+ m_UI.ParameterChanged ();
+}
+
+int16_t CMiniDexed::checkSystemExclusive(const uint8_t* pMessage,const uint16_t nLength, uint8_t nTG)
+{
+ return(m_pTG[nTG]->checkSystemExclusive(pMessage, nLength));
+}
diff --git a/src/minidexed.h b/src/minidexed.h
index 1292e53..4aeb42f 100644
--- a/src/minidexed.h
+++ b/src/minidexed.h
@@ -84,6 +84,25 @@ public:
void SetReverbSend (unsigned nReverbSend, unsigned nTG); // 0 .. 127
+ void setMonoMode(uint8_t mono, uint8_t nTG);
+ void setPitchbendRange(uint8_t range, uint8_t nTG);
+ void setPitchbendStep(uint8_t step, uint8_t nTG);
+ void setPortamentoMode(uint8_t mode, uint8_t nTG);
+ void setPortamentoGlissando(uint8_t glissando, uint8_t nTG);
+ void setPortamentoTime(uint8_t time, uint8_t nTG);
+ void setModWheelRange(uint8_t range, uint8_t nTG);
+ void setModWheelTarget(uint8_t target, uint8_t nTG);
+ void setFootControllerRange(uint8_t range, uint8_t nTG);
+ void setFootControllerTarget(uint8_t target, uint8_t nTG);
+ void setBreathControllerRange(uint8_t range, uint8_t nTG);
+ void setBreathControllerTarget(uint8_t target, uint8_t nTG);
+ void setAftertouchRange(uint8_t range, uint8_t nTG);
+ void setAftertouchTarget(uint8_t target, uint8_t nTG);
+ void loadVoiceParameters(const uint8_t* data, uint8_t nTG);
+ void setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG);
+
+ int16_t checkSystemExclusive(const uint8_t* pMessage, const uint16_t nLength, uint8_t nTG);
+
enum TParameter
{
ParameterCompressorEnable,