|
|
|
@ -20,12 +20,16 @@ |
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include <circle/logger.h> |
|
|
|
|
#include "mididevice.h" |
|
|
|
|
#include "minidexed.h" |
|
|
|
|
#include "config.h" |
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <assert.h> |
|
|
|
|
|
|
|
|
|
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<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; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|