probonopd 3 days ago committed by GitHub
commit a3837752d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 87
      src/mididevice.cpp
  2. 30
      src/minidexed.cpp
  3. 4
      src/minidexed.h

@ -2,7 +2,7 @@
// mididevice.cpp
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
// Copyright (C) 2022-25 The MiniDexed Team
//
// Original author of this class:
// R. Stange <rsta2@o2online.de>
@ -51,6 +51,10 @@ LOGMODULE ("mididevice");
#define MIDI_CC_DETUNE_LEVEL 94
#define MIDI_CC_ALL_SOUND_OFF 120
#define MIDI_CC_ALL_NOTES_OFF 123
#define MIDI_CC_OMNI_MODE_OFF 124
#define MIDI_CC_OMNI_MODE_ON 125
#define MIDI_CC_MONO_MODE_ON 126
#define MIDI_CC_POLY_MODE_ON 127
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
@ -265,6 +269,59 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
else
{
// Handle Voice Dump Request SysEx (Format 0)
if (nLength == 5 &&
pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN &&
pMessage[1] == 0x43 && // Yamaha manufacturer ID
(pMessage[2] & 0xF0) == 0x20 && // Status byte with channel (0x2n)
pMessage[3] == 0x00 && // Format 0 (single voice)
pMessage[4] == MIDI_SYSTEM_EXCLUSIVE_END) {
uint8_t channel = pMessage[2] & 0x0F;
LOGDBG("Voice Dump Request (Format 0) received for channel %d", channel);
// Find TG matching this channel
bool found = false;
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators(); nTG++) {
if (m_ChannelMap[nTG] == channel || m_ChannelMap[nTG] == OmniMode) {
SendSystemExclusiveVoice(0, nCable, nTG);
found = true;
// Don't break here to allow multiple TGs on same channel to respond
}
}
if (!found) {
// If no specific TG is found for this channel, respond with TG 0
SendSystemExclusiveVoice(0, nCable, 0);
}
return;
}
// Operator enable/disable SysEx handling - Format F0 43 11 g=0 h=1 1B p F7
// Where 1B (27) is parameter for operator on/off
// And p is a bitmask for operators (bit 0=OP6, bit 1=OP5, bit 2=OP4, bit 3=OP3, bit 4=OP2, bit 5=OP1)
if (nLength == 7 &&
pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN &&
pMessage[1] == 0x43 && // Yamaha manufacturer ID
pMessage[2] == 0x11 && // Sub-status byte (parameter change)
pMessage[3] == 0x01 && // Format byte (h=1, g=0)
pMessage[4] == 0x1B && // Parameter 27 (0x1B) - operator on/off
pMessage[5] <= 0x3F && // Value (6-bit mask, 0x00-0x3F valid range)
pMessage[6] == MIDI_SYSTEM_EXCLUSIVE_END) {
uint8_t operatorMask = pMessage[5];
LOGDBG("Operator On/Off SysEx received: Mask 0x%02X", operatorMask);
// Apply to MIDI channel-specific TGs
uint8_t channel = 0; // Default to first channel if not specified
// Find TGs to apply this to (apply to all TGs if from UI)
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators(); nTG++) {
// Set the operator mask directly (no toggling)
m_pSynthesizer->setOperatorMask(operatorMask, nTG);
}
return;
}
// Perform any MiniDexed level MIDI handling before specific Tone Generators
unsigned nPerfCh = m_pSynthesizer->GetPerformanceSelectChannel();
switch (ucType)
@ -476,6 +533,32 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
break;
case MIDI_CC_OMNI_MODE_OFF:
// Sets to "Omni Off" mode
if (m_ChannelMap[nTG] == OmniMode) {
m_pSynthesizer->SetMIDIChannel(ucChannel, nTG);
LOGDBG("Omni Mode Off: TG %d set to MIDI channel %d", nTG, ucChannel+1);
}
break;
case MIDI_CC_OMNI_MODE_ON:
// Sets to "Omni On" mode
m_pSynthesizer->SetMIDIChannel(OmniMode, nTG);
LOGDBG("Omni Mode On: TG %d set to OMNI", nTG);
break;
case MIDI_CC_MONO_MODE_ON:
// Sets monophonic mode
m_pSynthesizer->setMonoMode(1, nTG);
LOGDBG("Mono Mode On: TG %d set to MONO", nTG);
break;
case MIDI_CC_POLY_MODE_ON:
// Sets polyphonic mode
m_pSynthesizer->setMonoMode(0, nTG);
LOGDBG("Poly Mode On: TG %d set to POLY", 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,
@ -720,4 +803,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());
}
}
}

@ -2,7 +2,7 @@
// minidexed.cpp
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
// Copyright (C) 2022-25 The MiniDexed Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -1672,7 +1672,7 @@ void CMiniDexed::setBreathControllerTarget(uint8_t target, uint8_t nTG)
assert (m_pTG[nTG]);
m_nBreathControlTarget[nTG]=target;
m_nBreathControlTarget[nTG] = target;
m_pTG[nTG]->setBreathControllerTarget(constrain(target, 0, 7));
m_pTG[nTG]->ControllersRefresh();
@ -2184,3 +2184,29 @@ unsigned CMiniDexed::getModController (unsigned controller, unsigned parameter,
}
}
void CMiniDexed::setOperatorMask(uint8_t operatorMask, unsigned nTG) {
if (nTG >= CConfig::AllToneGenerators) {
LOGERR("Invalid tone generator: TG=%u", nTG);
return;
}
// According to Yamaha DX7/TX SysEx format:
// Bit 0 = OP6, Bit 1 = OP5, Bit 2 = OP4, Bit 3 = OP3, Bit 4 = OP2, Bit 5 = OP1
// For MiniDexed: Bit 0 = OP1, Bit 1 = OP2, etc. - need to reverse the bit order to match
uint8_t reversedMask = 0;
for (int i = 0; i < 6; i++) {
if (operatorMask & (1 << i)) {
reversedMask |= (1 << (5 - i));
}
}
m_uchOPMask[nTG] = reversedMask;
LOGDBG("Set operator mask for TG %u: Yamaha=0x%02X, Reversed=0x%02X", nTG, operatorMask, reversedMask);
// Apply the updated operator mask to the tone generator
if (nTG < m_nToneGenerators) {
m_pTG[nTG]->setOPAll(m_uchOPMask[nTG]);
}
}

@ -2,7 +2,7 @@
// minidexed.h
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
// Copyright (C) 2022-25 The MiniDexed Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@ -229,6 +229,8 @@ public:
void setMasterVolume (float32_t vol);
void setOperatorMask(uint8_t operatorMask, unsigned nTG); // Set operators enabled/disabled from bitmask
private:
int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note
uint8_t m_uchOPMask[CConfig::AllToneGenerators];

Loading…
Cancel
Save