probonopd 5 days ago committed by GitHub
commit 1040214375
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/build.yml
  2. 95
      src/mididevice.cpp
  3. 1
      src/mididevice.h
  4. 28
      src/minidexed.cpp
  5. 4
      src/minidexed.h

@ -11,7 +11,7 @@ on:
jobs: jobs:
Build: Build:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

@ -2,7 +2,7 @@
// mididevice.cpp // mididevice.cpp
// //
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi // 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: // Original author of this class:
// R. Stange <rsta2@o2online.de> // R. Stange <rsta2@o2online.de>
@ -51,6 +51,10 @@ LOGMODULE ("mididevice");
#define MIDI_CC_DETUNE_LEVEL 94 #define MIDI_CC_DETUNE_LEVEL 94
#define MIDI_CC_ALL_SOUND_OFF 120 #define MIDI_CC_ALL_SOUND_OFF 120
#define MIDI_CC_ALL_NOTES_OFF 123 #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_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110 #define MIDI_PITCH_BEND 0b1110
@ -84,6 +88,7 @@ CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInter
for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++) for (unsigned nTG = 0; nTG < CConfig::AllToneGenerators; nTG++)
{ {
m_ChannelMap[nTG] = Disabled; m_ChannelMap[nTG] = Disabled;
m_PreviousChannelMap[nTG] = Disabled; // Initialize previous channel map
} }
m_nMIDISystemCCVol = m_pConfig->GetMIDISystemCCVol(); m_nMIDISystemCCVol = m_pConfig->GetMIDISystemCCVol();
@ -132,6 +137,12 @@ CMIDIDevice::~CMIDIDevice (void)
void CMIDIDevice::SetChannel (u8 ucChannel, unsigned nTG) void CMIDIDevice::SetChannel (u8 ucChannel, unsigned nTG)
{ {
assert (nTG < CConfig::AllToneGenerators); assert (nTG < CConfig::AllToneGenerators);
// When changing to OMNI mode, store the previous channel
if (ucChannel == OmniMode && m_ChannelMap[nTG] != OmniMode) {
m_PreviousChannelMap[nTG] = m_ChannelMap[nTG];
}
m_ChannelMap[nTG] = ucChannel; m_ChannelMap[nTG] = ucChannel;
} }
@ -265,6 +276,59 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
else 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 // Perform any MiniDexed level MIDI handling before specific Tone Generators
unsigned nPerfCh = m_pSynthesizer->GetPerformanceSelectChannel(); unsigned nPerfCh = m_pSynthesizer->GetPerformanceSelectChannel();
switch (ucType) switch (ucType)
@ -476,6 +540,35 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
break; break;
case MIDI_CC_OMNI_MODE_OFF:
// Sets to "Omni Off" mode
if (m_ChannelMap[nTG] == OmniMode) {
// Restore the previous channel if available, otherwise use current channel
u8 channelToRestore = (m_PreviousChannelMap[nTG] != Disabled) ?
m_PreviousChannelMap[nTG] : ucChannel;
m_pSynthesizer->SetMIDIChannel(channelToRestore, nTG);
LOGDBG("Omni Mode Off: TG %d restored to MIDI channel %d", nTG, channelToRestore+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: default:
// Check for system-level, cross-TG MIDI Controls, but only do it once. // Check for system-level, cross-TG MIDI Controls, but only do it once.
// Also, if successfully handled, then no need to process other TGs, // Also, if successfully handled, then no need to process other TGs,

@ -70,6 +70,7 @@ private:
CUserInterface *m_pUI; CUserInterface *m_pUI;
u8 m_ChannelMap[CConfig::AllToneGenerators]; u8 m_ChannelMap[CConfig::AllToneGenerators];
u8 m_PreviousChannelMap[CConfig::AllToneGenerators]; // Store previous channels for OMNI OFF restore
unsigned m_nMIDISystemCCVol; unsigned m_nMIDISystemCCVol;
unsigned m_nMIDISystemCCPan; unsigned m_nMIDISystemCCPan;

@ -2,7 +2,7 @@
// minidexed.cpp // minidexed.cpp
// //
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi // 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 // 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 // it under the terms of the GNU General Public License as published by
@ -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.h
// //
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi // 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 // 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 // it under the terms of the GNU General Public License as published by
@ -229,6 +229,8 @@ public:
void setMasterVolume (float32_t vol); void setMasterVolume (float32_t vol);
void setOperatorMask(uint8_t operatorMask, unsigned nTG); // Set operators enabled/disabled from bitmask
private: private:
int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note
uint8_t m_uchOPMask[CConfig::AllToneGenerators]; uint8_t m_uchOPMask[CConfig::AllToneGenerators];

Loading…
Cancel
Save