diff --git a/src/config.cpp b/src/config.cpp index 15a37adc..52147132 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -108,6 +108,10 @@ void CConfig::Load (void) m_bMIDIAutoVoiceDumpOnPC = m_Properties.GetNumber ("MIDIAutoVoiceDumpOnPC", 0) != 0; m_bHeaderlessSysExVoices = m_Properties.GetNumber ("HeaderlessSysExVoices", 0) != 0; m_bExpandPCAcrossBanks = m_Properties.GetNumber ("ExpandPCAcrossBanks", 1) != 0; + + m_nMIDISystemCCVol = m_Properties.GetNumber ("MIDISystemCCVol", 0); + m_nMIDISystemCCPan = m_Properties.GetNumber ("MIDISystemCCPan", 0); + m_nMIDISystemCCDetune = m_Properties.GetNumber ("MIDISystemCCDetune", 0); m_bLCDEnabled = m_Properties.GetNumber ("LCDEnabled", 0) != 0; m_nLCDPinEnable = m_Properties.GetNumber ("LCDPinEnable", 4); @@ -283,6 +287,11 @@ unsigned CConfig::GetEngineType (void) const return m_EngineType; } +bool CConfig::GetQuadDAC8Chan (void) const +{ + return m_bQuadDAC8Chan; +} + unsigned CConfig::GetMIDIBaudRate (void) const { return m_nMIDIBaudRate; @@ -323,9 +332,19 @@ bool CConfig::GetExpandPCAcrossBanks (void) const return m_bExpandPCAcrossBanks; } -bool CConfig::GetQuadDAC8Chan (void) const +unsigned CConfig::GetMIDISystemCCVol (void) const { - return m_bQuadDAC8Chan; + return m_nMIDISystemCCVol; +} + +unsigned CConfig::GetMIDISystemCCPan (void) const +{ + return m_nMIDISystemCCPan; +} + +unsigned CConfig::GetMIDISystemCCDetune (void) const +{ + return m_nMIDISystemCCDetune; } bool CConfig::GetLCDEnabled (void) const diff --git a/src/config.h b/src/config.h index 166c4bbb..a454d907 100644 --- a/src/config.h +++ b/src/config.h @@ -117,6 +117,7 @@ class CConfig // Configuration for MiniDexed unsigned GetDACI2CAddress (void) const; // 0 for auto probing bool GetChannelsSwapped (void) const; unsigned GetEngineType (void) const; + bool GetQuadDAC8Chan (void) const; // false if not specified // MIDI unsigned GetMIDIBaudRate (void) const; @@ -127,7 +128,9 @@ class CConfig // Configuration for MiniDexed bool GetMIDIAutoVoiceDumpOnPC (void) const; // false if not specified bool GetHeaderlessSysExVoices (void) const; // false if not specified bool GetExpandPCAcrossBanks (void) const; // true if not specified - bool GetQuadDAC8Chan (void) const; // false if not specified + unsigned GetMIDISystemCCVol (void) const; + unsigned GetMIDISystemCCPan (void) const; + unsigned GetMIDISystemCCDetune (void) const; // HD44780 LCD // GPIO pin numbers are chip numbers, not header positions @@ -245,6 +248,7 @@ class CConfig // Configuration for MiniDexed unsigned m_nDACI2CAddress; bool m_bChannelsSwapped; unsigned m_EngineType; + bool m_bQuadDAC8Chan; unsigned m_nMIDIBaudRate; std::string m_MIDIThruIn; @@ -254,7 +258,9 @@ class CConfig // Configuration for MiniDexed bool m_bMIDIAutoVoiceDumpOnPC; bool m_bHeaderlessSysExVoices; bool m_bExpandPCAcrossBanks; - bool m_bQuadDAC8Chan; + unsigned m_nMIDISystemCCVol; + unsigned m_nMIDISystemCCPan; + unsigned m_nMIDISystemCCDetune; bool m_bLCDEnabled; unsigned m_nLCDPinEnable; diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 504423e6..f2b51def 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -53,6 +53,21 @@ LOGMODULE ("mididevice"); #define MIDI_PROGRAM_CHANGE 0b1100 #define MIDI_PITCH_BEND 0b1110 +// MIDI "System" level (i.e. all TG) custom CC maps +// Note: Even if number of TGs is not 8, there are only 8 +// available to be used in the mappings here. +#define NUM_MIDI_CC_MAPS 8 +const unsigned MIDISystemCCMap[NUM_MIDI_CC_MAPS][8] = { + {0,0,0,0,0,0,0,0}, // 0 = disabled + {16,17,18,19,80,81,82,83}, // 1 = General Purpose Controllers 1-8 + {20,21,22,23,24,25,26,27}, + {52,53,54,55,56,57,58,59}, + {102,103,104,105,106,107,108,109}, + {110,111,112,113,114,115,116,117}, + {3,9,14,15,28,29,30,31}, + {35,41,46,47,60,61,62,63} +}; + #define MIDI_SYSTEM_EXCLUSIVE_BEGIN 0xF0 #define MIDI_SYSTEM_EXCLUSIVE_END 0xF7 #define MIDI_TIMING_CLOCK 0xF8 @@ -69,6 +84,34 @@ CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInter { m_ChannelMap[nTG] = Disabled; } + + m_nMIDISystemCCVol = m_pConfig->GetMIDISystemCCVol(); + m_nMIDISystemCCPan = m_pConfig->GetMIDISystemCCPan(); + m_nMIDISystemCCDetune = m_pConfig->GetMIDISystemCCDetune(); + + m_MIDISystemCCBitmap[0] = 0; + m_MIDISystemCCBitmap[1] = 0; + m_MIDISystemCCBitmap[2] = 0; + m_MIDISystemCCBitmap[3] = 0; + + for (int tg=0; tg<8; tg++) + { + if (m_nMIDISystemCCVol != 0) { + u8 cc = MIDISystemCCMap[m_nMIDISystemCCVol][tg]; + m_MIDISystemCCBitmap[cc>>5] |= (1<<(cc%32)); + } + if (m_nMIDISystemCCPan != 0) { + u8 cc = MIDISystemCCMap[m_nMIDISystemCCPan][tg]; + m_MIDISystemCCBitmap[cc>>5] |= (1<<(cc%32)); + } + if (m_nMIDISystemCCDetune != 0) { + u8 cc = MIDISystemCCMap[m_nMIDISystemCCDetune][tg]; + m_MIDISystemCCBitmap[cc>>5] |= (1<<(cc%32)); + } + } + if (m_pConfig->GetMIDIDumpEnabled ()) { + LOGNOTE("MIDI System CC Map: %08X %08X %08X %08X", m_MIDISystemCCBitmap[3],m_MIDISystemCCBitmap[2],m_MIDISystemCCBitmap[1],m_MIDISystemCCBitmap[0]); + } } CMIDIDevice::~CMIDIDevice (void) @@ -239,7 +282,9 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign } // Process MIDI for each active Tone Generator - for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators(); nTG++) + bool bSystemCCHandled = false; + bool bSystemCCChecked = false; + for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++) { if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN) { @@ -373,6 +418,17 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign m_pSynthesizer->notesOff (pMessage[2], nTG); } break; + + default: + // Check for system-level, cross-TG MIDI Controls, but only do it once. + // Also, if successfully handled, then no need to process other TGs, + // so it is possible to break out of the main TG loop too. + // Note: We handle this here so we get the TG MIDI channel checking. + if (!bSystemCCChecked) { + bSystemCCHandled = HandleMIDISystemCC(pMessage[1], pMessage[2]); + bSystemCCChecked = true; + } + break; } break; @@ -418,6 +474,52 @@ void CMIDIDevice::AddDevice (const char *pDeviceName) s_DeviceMap.insert (std::pair (pDeviceName, this)); } +bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval) +{ + // This only makes sense when there are at least 8 TGs. + // Note: If more than 8 TGs then only 8 TGs are controllable this way. + if (m_pConfig->GetToneGenerators() < 8) { + return false; + } + + // Quickly reject any CCs not in the configured maps + if ((m_MIDISystemCCBitmap[ucCC>>5] & (1<<(ucCC%32))) == 0) { + // Not in the map + return false; + } + + // Not looking for duplicate CCs so return once handled + for (unsigned tg=0; tg<8; tg++) { + if (m_nMIDISystemCCVol != 0) { + if (ucCC == MIDISystemCCMap[m_nMIDISystemCCVol][tg]) { + m_pSynthesizer->SetVolume (ucCCval, tg); + return true; + } + } + if (m_nMIDISystemCCPan != 0) { + if (ucCC == MIDISystemCCMap[m_nMIDISystemCCPan][tg]) { + m_pSynthesizer->SetPan (ucCCval, tg); + return true; + } + } + if (m_nMIDISystemCCDetune != 0) { + if (ucCC == MIDISystemCCMap[m_nMIDISystemCCDetune][tg]) { + if (ucCCval == 0) + { + m_pSynthesizer->SetMasterTune (0, tg); + } + else + { + m_pSynthesizer->SetMasterTune (maplong (ucCCval, 1, 127, -99, 99), tg); + } + return true; + } + } + } + + return false; +} + void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG) { int16_t sysex_return; diff --git a/src/mididevice.h b/src/mididevice.h index b7e7fe75..44f16916 100644 --- a/src/mididevice.h +++ b/src/mididevice.h @@ -60,12 +60,21 @@ class CMIDIDevice void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0); void AddDevice (const char *pDeviceName); void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG); + +private: + bool HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval); + private: CMiniDexed *m_pSynthesizer; CConfig *m_pConfig; CUserInterface *m_pUI; u8 m_ChannelMap[CConfig::AllToneGenerators]; + + unsigned m_nMIDISystemCCVol; + unsigned m_nMIDISystemCCPan; + unsigned m_nMIDISystemCCDetune; + u32 m_MIDISystemCCBitmap[4]; // to allow for 128 bit entries std::string m_DeviceName;