From 3894cb3b21fadaa11c8a72a16a4e89f614867afb Mon Sep 17 00:00:00 2001 From: Holger Date: Mon, 9 May 2022 19:04:40 +0200 Subject: [PATCH] Support for Yamaha DX7 SysEx commands (#195) SysEx handling for parameter changes Co-authored-by: Holger Wirtz Co-authored-by: probonopd --- CMSIS_5 | 2 +- Synth_Dexed | 2 +- src/mididevice.cpp | 384 +++++++++++++++++++++++++++------------ src/mididevice.h | 2 + src/minidexed.cpp | 178 ++++++++++++++++++ src/minidexed.h | 19 ++ src/serialmididevice.cpp | 118 +++++++----- src/serialmididevice.h | 6 +- 8 files changed, 551 insertions(+), 160 deletions(-) diff --git a/CMSIS_5 b/CMSIS_5 index 18205c6..8a64562 160000 --- a/CMSIS_5 +++ b/CMSIS_5 @@ -1 +1 @@ -Subproject commit 18205c6c2b68e7e96f40dc941c47efdbdd9f7d01 +Subproject commit 8a64562247485159a090ec4a01588f44f8d10311 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..a6f5375 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 @@ -79,8 +84,6 @@ u8 CMIDIDevice::GetChannel (unsigned nTG) const void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable) { - assert (m_pSynthesizer != 0); - // The packet contents are just normal MIDI data - see // https://www.midi.org/specifications/item/table-1-summary-of-midi-message @@ -106,6 +109,25 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign (unsigned) pMessage[0], (unsigned) pMessage[1], (unsigned) pMessage[2]); break; + default: + switch(pMessage[0]) + { + case MIDI_SYSTEM_EXCLUSIVE: + printf("SysEx data length: [%d]\n",uint16_t(nLength)); + printf("SysEx data:\n"); + for (uint16_t i = 0; i < nLength; i++) + { + if((i % 8) == 0) + printf("%04d:",i); + printf(" 0x%02x",pMessage[i]); + if((i % 8) == 0) + printf("\n"); + } + break; + default: + printf("Unhandled MIDI event type %0x02x\n",pMessage[0]); + } + break; } } @@ -130,126 +152,142 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign u8 ucChannel = ucStatus & 0x0F; u8 ucType = ucStatus >> 4; - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) + // GLOBAL MIDI SYSEX + if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength-1] == 0xF7) // MASTER VOLUME + { + float32_t nMasterVolume=(pMessage[5] & (pMessage[6]<<7))/(1<<14); + LOGNOTE("Master volume: %f",nMasterVolume); + // TODO: Handle global master volume + } + else { - if ( m_ChannelMap[nTG] == ucChannel - || m_ChannelMap[nTG] == OmniMode) + for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) { - switch (ucType) + // MIDI SYSEX per MIDI channel + if (ucStatus == MIDI_SYSTEM_EXCLUSIVE && m_ChannelMap[nTG] == pMessage[2] & 0x07) + HandleSystemExclusive(pMessage, nLength, nTG); + else { - case MIDI_NOTE_ON: - if (nLength < 3) - { - break; - } - - if (pMessage[2] > 0) + if ( m_ChannelMap[nTG] == ucChannel + || m_ChannelMap[nTG] == OmniMode) { - if (pMessage[2] <= 127) - { - m_pSynthesizer->keydown (pMessage[1], - pMessage[2], nTG); - } - } - else - { - m_pSynthesizer->keyup (pMessage[1], nTG); - } - break; - - case MIDI_NOTE_OFF: - if (nLength < 3) - { - break; - } - - m_pSynthesizer->keyup (pMessage[1], nTG); - break; - - case MIDI_CONTROL_CHANGE: - if (nLength < 3) - { - break; - } - - switch (pMessage[1]) - { - case MIDI_CC_MODULATION: - m_pSynthesizer->setModWheel (pMessage[2], nTG); - m_pSynthesizer->ControllersRefresh (nTG); - break; - - case MIDI_CC_VOLUME: - m_pSynthesizer->SetVolume (pMessage[2], nTG); - break; - - case MIDI_CC_PAN_POSITION: - m_pSynthesizer->SetPan (pMessage[2], nTG); - break; - - case MIDI_CC_BANK_SELECT_LSB: - m_pSynthesizer->BankSelectLSB (pMessage[2], nTG); - break; - - case MIDI_CC_BANK_SUSTAIN: - m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG); - break; - - case MIDI_CC_RESONANCE: - m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG); - break; - - case MIDI_CC_FREQUENCY_CUTOFF: - m_pSynthesizer->SetCutoff (maplong (pMessage[2], 0, 127, 0, 99), nTG); - break; - - case MIDI_CC_REVERB_LEVEL: - m_pSynthesizer->SetReverbSend (maplong (pMessage[2], 0, 127, 0, 99), nTG); - break; - - case MIDI_CC_DETUNE_LEVEL: - if (pMessage[2] == 0) - { - // "0 to 127, with 0 being no celeste (detune) effect applied at all." - m_pSynthesizer->SetMasterTune (0, nTG); - } - else + switch (ucType) { - m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG); + case MIDI_NOTE_ON: + if (nLength < 3) + { + break; + } + + if (pMessage[2] > 0) + { + if (pMessage[2] <= 127) + { + m_pSynthesizer->keydown (pMessage[1], + pMessage[2], nTG); + } + } + else + { + m_pSynthesizer->keyup (pMessage[1], nTG); + } + break; + + case MIDI_NOTE_OFF: + if (nLength < 3) + { + break; + } + + m_pSynthesizer->keyup (pMessage[1], nTG); + break; + + case MIDI_CONTROL_CHANGE: + if (nLength < 3) + { + break; + } + + switch (pMessage[1]) + { + case MIDI_CC_MODULATION: + m_pSynthesizer->setModWheel (pMessage[2], nTG); + m_pSynthesizer->ControllersRefresh (nTG); + break; + + case MIDI_CC_VOLUME: + m_pSynthesizer->SetVolume (pMessage[2], nTG); + break; + + case MIDI_CC_PAN_POSITION: + m_pSynthesizer->SetPan (pMessage[2], nTG); + break; + + case MIDI_CC_BANK_SELECT_LSB: + m_pSynthesizer->BankSelectLSB (pMessage[2], nTG); + break; + + case MIDI_CC_BANK_SUSTAIN: + m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG); + break; + + case MIDI_CC_RESONANCE: + m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG); + break; + + case MIDI_CC_FREQUENCY_CUTOFF: + m_pSynthesizer->SetCutoff (maplong (pMessage[2], 0, 127, 0, 99), nTG); + break; + + case MIDI_CC_REVERB_LEVEL: + m_pSynthesizer->SetReverbSend (maplong (pMessage[2], 0, 127, 0, 99), nTG); + break; + + case MIDI_CC_DETUNE_LEVEL: + if (pMessage[2] == 0) + { + // "0 to 127, with 0 being no celeste (detune) effect applied at all." + m_pSynthesizer->SetMasterTune (0, nTG); + } + else + { + m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG); + } + break; + + case MIDI_CC_ALL_SOUND_OFF: + m_pSynthesizer->panic (pMessage[2], nTG); + break; + + case MIDI_CC_ALL_NOTES_OFF: + m_pSynthesizer->notesOff (pMessage[2], nTG); + break; + } + break; + + case MIDI_PROGRAM_CHANGE: + // do program change only if enabled in config + if( m_pConfig->GetMIDIRXProgramChange() ) + m_pSynthesizer->ProgramChange (pMessage[1], nTG); + break; + + case MIDI_PITCH_BEND: { + if (nLength < 3) + { + break; + } + + s16 nValue = pMessage[1]; + nValue |= (s16) pMessage[2] << 7; + nValue -= 0x2000; + + m_pSynthesizer->setPitchbend (nValue, nTG); + } break; + + default: + break; } - break; - - case MIDI_CC_ALL_SOUND_OFF: - m_pSynthesizer->panic (pMessage[2], nTG); - break; - - case MIDI_CC_ALL_NOTES_OFF: - m_pSynthesizer->notesOff (pMessage[2], nTG); - break; - } - break; - - case MIDI_PROGRAM_CHANGE: - // do program change only if enabled in config - if( m_pConfig->GetMIDIRXProgramChange() ) - m_pSynthesizer->ProgramChange (pMessage[1], nTG); - break; - - case MIDI_PITCH_BEND: { - if (nLength < 3) - { - break; } - - s16 nValue = pMessage[1]; - nValue |= (s16) pMessage[2] << 7; - nValue -= 0x2000; - - m_pSynthesizer->setPitchbend (nValue, nTG); - } break; - - default: - break; } } } @@ -265,3 +303,119 @@ 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 nTG) +{ + int16_t sysex_return; + + 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..0b116d1 100644 --- a/src/mididevice.h +++ b/src/mididevice.h @@ -55,6 +55,8 @@ protected: void AddDevice (const char *pDeviceName); + void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const uint8_t nTG); + private: CMiniDexed *m_pSynthesizer; CConfig *m_pConfig; diff --git a/src/minidexed.cpp b/src/minidexed.cpp index 68908bf..45baf96 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,7 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated) if (m_bUseSerial) { m_SerialMIDI.Process (); + m_SerialMIDI.Process (); } m_UI.Process (); @@ -940,3 +942,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, diff --git a/src/serialmididevice.cpp b/src/serialmididevice.cpp index b03dfad..6c484cf 100644 --- a/src/serialmididevice.cpp +++ b/src/serialmididevice.cpp @@ -20,6 +20,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // +#include #include "serialmididevice.h" #include @@ -29,12 +30,14 @@ CSerialMIDIDevice::CSerialMIDIDevice (CMiniDexed *pSynthesizer, CInterruptSystem m_pConfig (pConfig), m_Serial (pInterrupt, TRUE), m_nSerialState (0), + m_nSysEx (0), m_SendBuffer (&m_Serial) { AddDevice ("ttyS1"); } CSerialMIDIDevice::~CSerialMIDIDevice (void) + { m_nSerialState = 255; } @@ -57,6 +60,21 @@ void CSerialMIDIDevice::Process (void) return; } + if (m_pConfig->GetMIDIDumpEnabled ()) + { + printf("Incoming MIDI data:\n"); + for (uint16_t i = 0; i < nResult; i++) + { + if((i % 8) == 0) + printf("%04d:",i); + printf(" 0x%02x",Buffer[i]); + if((i > 1 ) && (i % 8) == 0) + printf("\n"); + } + if((nResult % 8) != 0) + printf("\n"); + } + // Process MIDI messages // See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message // "Running status" see: https://www.lim.di.unimi.it/IEEE/MIDI/SOT5.HTM#Running- @@ -65,54 +83,70 @@ void CSerialMIDIDevice::Process (void) { u8 uchData = Buffer[i]; - switch (m_nSerialState) + if(uchData == 0xF0) { - case 0: - MIDIRestart: - if ( (uchData & 0x80) == 0x80 // status byte, all channels - && (uchData & 0xF0) != 0xF0) // ignore system messages - { - m_SerialMessage[m_nSerialState++] = uchData; - } - break; - - case 1: - case 2: - DATABytes: - if (uchData & 0x80) // got status when parameter expected - { - m_nSerialState = 0; - - goto MIDIRestart; - } - - m_SerialMessage[m_nSerialState++] = uchData; - - if ( (m_SerialMessage[0] & 0xE0) == 0xC0 - || m_nSerialState == 3) // message is complete - { - MIDIMessageHandler (m_SerialMessage, m_nSerialState); + // SYSEX found + m_SerialMessage[m_nSysEx++]=uchData; + continue; + } - m_nSerialState = 4; // State 4 for test if 4th byte is a status byte or a data byte - } - break; - case 4: - - if ((uchData & 0x80) == 0) // true data byte, false status byte + if(m_nSysEx > 0) + { + m_SerialMessage[m_nSysEx++]=uchData; + if ((uchData & 0x80) == 0x80 || m_nSysEx >= MAX_MIDI_MESSAGE) { - m_nSerialState = 1; - goto DATABytes; + if(uchData == 0xF7) + MIDIMessageHandler (m_SerialMessage, m_nSysEx); + m_nSysEx = 0; } - else + continue; + } + else + { + switch (m_nSerialState) { - m_nSerialState = 0; - goto MIDIRestart; + case 0: + MIDIRestart: + if ( (uchData & 0x80) == 0x80 // status byte, all channels + && (uchData & 0xF0) != 0xF0) // ignore system messages + { + m_SerialMessage[m_nSerialState++] = uchData; + } + break; + + case 1: + case 2: + DATABytes: + if (uchData & 0x80) // got status when parameter expected + { + m_nSerialState = 0; + + goto MIDIRestart; + } + + m_SerialMessage[m_nSerialState++] = uchData; + + if ( (m_SerialMessage[0] & 0xE0) == 0xC0 + || m_nSerialState == 3) // message is complete + { + MIDIMessageHandler (m_SerialMessage, m_nSerialState); + + m_nSerialState = 4; // State 4 for test if 4th byte is a status byte or a data byte + } + + break; + case 4: + + if ((uchData & 0x80) == 0) // true data byte, false status byte + { + m_nSerialState = 1; + goto DATABytes; + } + break; + default: + assert (0); + break; } - break; - - default: - assert (0); - break; } } } diff --git a/src/serialmididevice.h b/src/serialmididevice.h index 0e6d9f5..1f3619e 100644 --- a/src/serialmididevice.h +++ b/src/serialmididevice.h @@ -30,6 +30,9 @@ #include #include +#define MAX_DX7_SYSEX_LENGTH 4104 +#define MAX_MIDI_MESSAGE MAX_DX7_SYSEX_LENGTH + class CMiniDexed; class CSerialMIDIDevice : public CMIDIDevice @@ -49,7 +52,8 @@ private: CSerialDevice m_Serial; unsigned m_nSerialState; - u8 m_SerialMessage[3]; + unsigned m_nSysEx; + u8 m_SerialMessage[MAX_MIDI_MESSAGE]; CWriteBufferDevice m_SendBuffer; };