diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 576821f..f387dbc 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -329,7 +329,127 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode) { LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG); - HandleSystemExclusive(pMessage, nLength, nCable, nTG); + + // Check for TX216/TX816 style performance sysex messages + + if (pMessage[3] == 0x04) + { + // TX816/TX216 Performance SysEx message + uint8_t mTG = pMessage[2] & 0x0F; // mTG = module/tone generator number (0-7) + uint8_t par = pMessage[4]; + uint8_t val = pMessage[5]; + + // For parameter 1 (Set MIDI Channel), only process for the TG with the number in pMessage[2] + if (par == 1) { + if (nTG != mTG) continue; + } else { + // For all other parameters, process for all TGs listening on the MIDI channel mTG or OmniMode + if (!(m_ChannelMap[nTG] == mTG || m_ChannelMap[nTG] == OmniMode)) continue; + } + LOGNOTE("MIDI-SYSEX: Assuming TX216/TX816 style performance sysex message because 4th byte is 0x04"); + + switch (par) + { + case 1: // MIDI Channel + LOGNOTE("MIDI-SYSEX: Set TG%d to MIDI Channel %d", mTG, val & 0x0F); + m_pSynthesizer->SetMIDIChannel(val & 0x0F, mTG); + break; + case 2: // Poly/Mono + LOGNOTE("MIDI-SYSEX: Set Poly/Mono %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setMonoMode(val ? true : false, nTG); + break; + case 3: // Pitch Bend Range + LOGNOTE("MIDI-SYSEX: Set Pitch Bend Range %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setPitchbendRange(val, nTG); + break; + case 4: // Pitch Bend Step + LOGNOTE("MIDI-SYSEX: Set Pitch Bend Step %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setPitchbendStep(val, nTG); + break; + case 5: // Portamento Time + LOGNOTE("MIDI-SYSEX: Set Portamento Time %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setPortamentoTime(val, nTG); + break; + case 6: // Portamento/Glissando + LOGNOTE("MIDI-SYSEX: Set Portamento/Glissando %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setPortamentoGlissando(val, nTG); + break; + case 7: // Portamento Mode + LOGNOTE("MIDI-SYSEX: Set Portamento Mode %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setPortamentoMode(val, nTG); + break; + case 9: // Mod Wheel Sensitivity + { + int scaled = (val * 99) / 15; + LOGNOTE("MIDI-SYSEX: Set Mod Wheel Sensitivity %d to %d (scaled %d)", nTG, val & 0x0F, scaled); + m_pSynthesizer->setModWheelRange(scaled, nTG); + } + break; + case 10: // Mod Wheel Assign + LOGNOTE("MIDI-SYSEX: Set Mod Wheel Assign %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setModWheelTarget(val, nTG); + break; + case 11: // Foot Controller Sensitivity + { + int scaled = (val * 99) / 15; + LOGNOTE("MIDI-SYSEX: Set Foot Controller Sensitivity %d to %d (scaled %d)", nTG, val & 0x0F, scaled); + m_pSynthesizer->setFootControllerRange(scaled, nTG); + } + break; + case 12: // Foot Controller Assign + LOGNOTE("MIDI-SYSEX: Set Foot Controller Assign %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setFootControllerTarget(val, nTG); + break; + case 13: // Aftertouch Sensitivity + { + int scaled = (val * 99) / 15; + LOGNOTE("MIDI-SYSEX: Set Aftertouch Sensitivity %d to %d (scaled %d)", nTG, val & 0x0F, scaled); + m_pSynthesizer->setAftertouchRange(scaled, nTG); + } + break; + case 14: // Aftertouch Assign + LOGNOTE("MIDI-SYSEX: Set Aftertouch Assign %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setAftertouchTarget(val, nTG); + break; + case 15: // Breath Controller Sensitivity + { + int scaled = (val * 99) / 15; + LOGNOTE("MIDI-SYSEX: Set Breath Controller Sensitivity %d to %d (scaled %d)", nTG, val & 0x0F, scaled); + m_pSynthesizer->setBreathControllerRange(scaled, nTG); + } + break; + case 16: // Breath Controller Assign + LOGNOTE("MIDI-SYSEX: Set Breath Controller Assign %d to %d", nTG, val & 0x0F); + m_pSynthesizer->setBreathControllerTarget(val, nTG); + break; + case 26: // Audio Output Level Attenuator + { + LOGNOTE("MIDI-SYSEX: Set Audio Output Level Attenuator %d to %d", nTG, val & 0x0F); + // Example: F0 43 10 04 1A 00 F7 to F0 43 10 04 1A 07 F7 + unsigned attenVal = val & 0x07; + // unsigned newVolume = (unsigned)(127.0 * pow(attenVal / 7.0, 2.0) + 0.5); // Logarithmic mapping + // But on the T816, there is an exponential (not logarithmic!) mapping, and 0 results in the same volume as 1: + // 7=127, 6=63, 5=31, 4=15, 3=7, 2=3, 1=1, 0=1 + unsigned newVolume = (attenVal == 0) ? 0 : (127 >> (7 - attenVal)); + if (newVolume == 0) newVolume = 1; // 0 is like 1 to avoid silence + m_pSynthesizer->SetVolume(newVolume, nTG); + } + break; + case 64: // Master Tuning + LOGNOTE("MIDI-SYSEX: Set Master Tuning"); + // TX812 scales from -75 to +75 cents. + m_pSynthesizer->SetMasterTune(maplong(val, 1, 127, -37, 37), nTG); // Would need 37.5 here, due to wrong constrain on dexed_synth module? + break; + default: + // Unknown or unsupported parameter + LOGNOTE("MIDI-SYSEX: Unknown parameter %d for TG %d", par, nTG); + break; + } + } + else + { + HandleSystemExclusive(pMessage, nLength, nCable, nTG); + } } } else @@ -431,7 +551,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign case MIDI_CC_SOSTENUTO: m_pSynthesizer->setSostenuto (pMessage[2] >= 64, nTG); break; - + case MIDI_CC_PORTAMENTO: m_pSynthesizer->setPortamentoMode (pMessage[2] >= 64, nTG); break; @@ -439,7 +559,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign case MIDI_CC_HOLD2: m_pSynthesizer->setHoldMode (pMessage[2] >= 64, nTG); break; - + case MIDI_CC_RESONANCE: m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG); break; @@ -455,11 +575,12 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign case MIDI_CC_DETUNE_LEVEL: if (pMessage[2] == 0) { - // "0 to 127, with 0 being no celeste (detune) effect applied at all." + // 0 to 127, with 0 being no detune effect applied at all m_pSynthesizer->SetMasterTune (0, nTG); } else { + // Scale to -99 to +99 cents m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG); } break; @@ -594,10 +715,12 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval) if (ucCC == MIDISystemCCMap[m_nMIDISystemCCDetune][tg]) { if (ucCCval == 0) { + // 0 to 127, with 0 being no detune effect applied at all m_pSynthesizer->SetMasterTune (0, tg); } else { + // Scale to -99 to +99 cents m_pSynthesizer->SetMasterTune (maplong (ucCCval, 1, 127, -99, 99), tg); } return true; @@ -649,62 +772,6 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL case -11: LOGERR("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"); @@ -756,4 +823,4 @@ void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable Iterator->second->Send (voicedump, sizeof(voicedump)*sizeof(uint8_t)); // LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str()); } -} +} diff --git a/src/uimenu.cpp b/src/uimenu.cpp index 1475342..730f320 100644 --- a/src/uimenu.cpp +++ b/src/uimenu.cpp @@ -1098,12 +1098,14 @@ string CUIMenu::GetOPValueString (unsigned nOPParameter, int nValue) string CUIMenu::ToVolume (int nValue) { - static const size_t MaxChars = CConfig::LCDColumns-2; - char VolumeBar[MaxChars+1]; - memset (VolumeBar, 0xFF, sizeof VolumeBar); // 0xFF is the block character - VolumeBar[nValue * MaxChars / 127] = '\0'; - - return VolumeBar; + constexpr size_t NumSquares = 14; + char VolumeBar[NumSquares + 1]; + size_t filled = (nValue * NumSquares + 63) / 127; + for (size_t i = 0; i < NumSquares; ++i) { + VolumeBar[i] = (i < filled) ? (char)0xFF : '.'; + } + VolumeBar[NumSquares] = '\0'; + return VolumeBar; } string CUIMenu::ToPan (int nValue) @@ -1394,11 +1396,11 @@ void CUIMenu::PgmUpDownHandler (TMenuEvent Event) || voiceName == "----------" || voiceName == "~~~~~~~~~~" ) { - if (Event == MenuEventPgmUp) { - PgmUpDownHandler (MenuEventPgmUp); + if (Event == MenuEventStepUp) { + PgmUpDownHandler (MenuEventStepUp); } - if (Event == MenuEventPgmDown) { - PgmUpDownHandler (MenuEventPgmDown); + if (Event == MenuEventStepDown) { + PgmUpDownHandler (MenuEventStepDown); } } } @@ -2043,6 +2045,15 @@ void CUIMenu::EditMasterVolume(CUIMenu *pUIMenu, TMenuEvent Event) default: return; } - std::string valueStr = ToVolume(pUIMenu->m_pMiniDexed->GetMasterVolume127()); - pUIMenu->m_pUI->DisplayWrite("Master Volume", "", valueStr.c_str(), nValue > rParam.Minimum, nValue < rParam.Maximum); + unsigned lcdCols = pUIMenu->m_pConfig->GetLCDColumns(); + unsigned barLen = (lcdCols > 2) ? lcdCols - 2 : 0; + std::string valueStr(barLen, '.'); + if (barLen > 0) { + size_t filled = (nValue * barLen + 63) / 127; + for (unsigned i = 0; i < barLen; ++i) { + if (i < filled) valueStr[i] = (char)0xFF; + } + } + // Do NOT add < or > here; let DisplayWrite handle it + pUIMenu->m_pUI->DisplayWrite("Master Volume", "", valueStr.c_str(), true, true); }