Support for Yamaha DX7 SysEx commands (#195)

SysEx handling for parameter changes

Co-authored-by: Holger Wirtz <wirtz@parasitstudio.de>
Co-authored-by: probonopd <probonopd@users.noreply.github.com>
pull/210/head^2
Holger 3 years ago committed by GitHub
parent 984e77f000
commit 3894cb3b21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CMSIS_5
  2. 2
      Synth_Dexed
  3. 158
      src/mididevice.cpp
  4. 2
      src/mididevice.h
  5. 178
      src/minidexed.cpp
  6. 19
      src/minidexed.h
  7. 46
      src/serialmididevice.cpp
  8. 6
      src/serialmididevice.h

@ -1 +1 @@
Subproject commit 18205c6c2b68e7e96f40dc941c47efdbdd9f7d01 Subproject commit 8a64562247485159a090ec4a01588f44f8d10311

@ -1 +1 @@
Subproject commit e40f29dc0ab6b70d57fc6a0c0d30130adfbc903d Subproject commit 8c677ceb4b3fb73f8643e30ff6cf4158dc8b9e53

@ -20,12 +20,16 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#include <circle/logger.h>
#include "mididevice.h" #include "mididevice.h"
#include "minidexed.h" #include "minidexed.h"
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
LOGMODULE ("mididevice");
#define MIDI_NOTE_OFF 0b1000 #define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001 #define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0b1010 // TODO #define MIDI_AFTERTOUCH 0b1010 // TODO
@ -45,6 +49,7 @@
#define MIDI_PROGRAM_CHANGE 0b1100 #define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110 #define MIDI_PITCH_BEND 0b1110
#define MIDI_SYSTEM_EXCLUSIVE 0xF0
#define MIDI_TIMING_CLOCK 0xF8 #define MIDI_TIMING_CLOCK 0xF8
#define MIDI_ACTIVE_SENSING 0xFE #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) void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable)
{ {
assert (m_pSynthesizer != 0);
// The packet contents are just normal MIDI data - see // The packet contents are just normal MIDI data - see
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message // 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[0], (unsigned) pMessage[1],
(unsigned) pMessage[2]); (unsigned) pMessage[2]);
break; 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,7 +152,21 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
u8 ucChannel = ucStatus & 0x0F; u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4; u8 ucType = ucStatus >> 4;
// 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
{
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
// MIDI SYSEX per MIDI channel
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE && m_ChannelMap[nTG] == pMessage[2] & 0x07)
HandleSystemExclusive(pMessage, nLength, nTG);
else
{ {
if ( m_ChannelMap[nTG] == ucChannel if ( m_ChannelMap[nTG] == ucChannel
|| m_ChannelMap[nTG] == OmniMode) || m_ChannelMap[nTG] == OmniMode)
@ -254,6 +290,8 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
} }
} }
}
}
void CMIDIDevice::AddDevice (const char *pDeviceName) void CMIDIDevice::AddDevice (const char *pDeviceName)
{ {
@ -265,3 +303,119 @@ void CMIDIDevice::AddDevice (const char *pDeviceName)
s_DeviceMap.insert (std::pair<std::string, CMIDIDevice *> (pDeviceName, this)); s_DeviceMap.insert (std::pair<std::string, CMIDIDevice *> (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;
}
}

@ -55,6 +55,8 @@ protected:
void AddDevice (const char *pDeviceName); void AddDevice (const char *pDeviceName);
void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const uint8_t nTG);
private: private:
CMiniDexed *m_pSynthesizer; CMiniDexed *m_pSynthesizer;
CConfig *m_pConfig; CConfig *m_pConfig;

@ -23,6 +23,7 @@
#include <circle/pwmsoundbasedevice.h> #include <circle/pwmsoundbasedevice.h>
#include <circle/i2ssoundbasedevice.h> #include <circle/i2ssoundbasedevice.h>
#include <circle/hdmisoundbasedevice.h> #include <circle/hdmisoundbasedevice.h>
#include <circle/gpiopin.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
@ -255,6 +256,7 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated)
if (m_bUseSerial) if (m_bUseSerial)
{ {
m_SerialMIDI.Process (); m_SerialMIDI.Process ();
m_SerialMIDI.Process ();
} }
m_UI.Process (); m_UI.Process ();
@ -940,3 +942,179 @@ bool CMiniDexed::SavePerformance (void)
return m_PerformanceConfig.Save (); 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));
}

@ -84,6 +84,25 @@ public:
void SetReverbSend (unsigned nReverbSend, unsigned nTG); // 0 .. 127 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 enum TParameter
{ {
ParameterCompressorEnable, ParameterCompressorEnable,

@ -20,6 +20,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
#include <cstring>
#include "serialmididevice.h" #include "serialmididevice.h"
#include <assert.h> #include <assert.h>
@ -29,12 +30,14 @@ CSerialMIDIDevice::CSerialMIDIDevice (CMiniDexed *pSynthesizer, CInterruptSystem
m_pConfig (pConfig), m_pConfig (pConfig),
m_Serial (pInterrupt, TRUE), m_Serial (pInterrupt, TRUE),
m_nSerialState (0), m_nSerialState (0),
m_nSysEx (0),
m_SendBuffer (&m_Serial) m_SendBuffer (&m_Serial)
{ {
AddDevice ("ttyS1"); AddDevice ("ttyS1");
} }
CSerialMIDIDevice::~CSerialMIDIDevice (void) CSerialMIDIDevice::~CSerialMIDIDevice (void)
{ {
m_nSerialState = 255; m_nSerialState = 255;
} }
@ -57,6 +60,21 @@ void CSerialMIDIDevice::Process (void)
return; 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 // Process MIDI messages
// See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message // 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- // "Running status" see: https://www.lim.di.unimi.it/IEEE/MIDI/SOT5.HTM#Running-
@ -65,6 +83,26 @@ void CSerialMIDIDevice::Process (void)
{ {
u8 uchData = Buffer[i]; u8 uchData = Buffer[i];
if(uchData == 0xF0)
{
// SYSEX found
m_SerialMessage[m_nSysEx++]=uchData;
continue;
}
if(m_nSysEx > 0)
{
m_SerialMessage[m_nSysEx++]=uchData;
if ((uchData & 0x80) == 0x80 || m_nSysEx >= MAX_MIDI_MESSAGE)
{
if(uchData == 0xF7)
MIDIMessageHandler (m_SerialMessage, m_nSysEx);
m_nSysEx = 0;
}
continue;
}
else
{
switch (m_nSerialState) switch (m_nSerialState)
{ {
case 0: case 0:
@ -95,6 +133,7 @@ void CSerialMIDIDevice::Process (void)
m_nSerialState = 4; // State 4 for test if 4th byte is a status byte or a data byte m_nSerialState = 4; // State 4 for test if 4th byte is a status byte or a data byte
} }
break; break;
case 4: case 4:
@ -103,19 +142,14 @@ void CSerialMIDIDevice::Process (void)
m_nSerialState = 1; m_nSerialState = 1;
goto DATABytes; goto DATABytes;
} }
else
{
m_nSerialState = 0;
goto MIDIRestart;
}
break; break;
default: default:
assert (0); assert (0);
break; break;
} }
} }
} }
}
void CSerialMIDIDevice::Send (const u8 *pMessage, size_t nLength, unsigned nCable) void CSerialMIDIDevice::Send (const u8 *pMessage, size_t nLength, unsigned nCable)
{ {

@ -30,6 +30,9 @@
#include <circle/writebuffer.h> #include <circle/writebuffer.h>
#include <circle/types.h> #include <circle/types.h>
#define MAX_DX7_SYSEX_LENGTH 4104
#define MAX_MIDI_MESSAGE MAX_DX7_SYSEX_LENGTH
class CMiniDexed; class CMiniDexed;
class CSerialMIDIDevice : public CMIDIDevice class CSerialMIDIDevice : public CMIDIDevice
@ -49,7 +52,8 @@ private:
CSerialDevice m_Serial; CSerialDevice m_Serial;
unsigned m_nSerialState; unsigned m_nSerialState;
u8 m_SerialMessage[3]; unsigned m_nSysEx;
u8 m_SerialMessage[MAX_MIDI_MESSAGE];
CWriteBufferDevice m_SendBuffer; CWriteBufferDevice m_SendBuffer;
}; };

Loading…
Cancel
Save