Skip to content

MIDI messages for MiniDexed Performance parameters #915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ jobs:
mkdir -p ./sdcard/hardware/
cp -r ./hwconfig/minidexed_* ./sdcard/minidexed.ini ./sdcard/hardware/
# WLAN firmware
mkdir -p sdcard/firmware
cp circle-stdlib/libs/circle/addon/wlan/sample/hello_wlan/wpa_supplicant.conf sdcard/
cd sdcard/firmware
make -f ../../circle-stdlib/libs/circle/addon/wlan/firmware/Makefile
# mkdir -p sdcard/firmware
# cp circle-stdlib/libs/circle/addon/wlan/sample/hello_wlan/wpa_supplicant.conf sdcard/
# cd sdcard/firmware
# make -f ../../circle-stdlib/libs/circle/addon/wlan/firmware/Makefile
cd -

- name: Upload 64-bit artifacts
Expand Down
5 changes: 3 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \
sysexfileloader.o performanceconfig.o perftimer.o \
effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o \
arm_float_to_q23.o \
net/ftpdaemon.o net/ftpworker.o net/applemidi.o net/udpmidi.o net/mdnspublisher.o udpmididevice.o
net/ftpdaemon.o net/ftpworker.o net/applemidi.o net/udpmidi.o net/mdnspublisher.o udpmididevice.o \
performance_sysex.o

OPTIMIZE = -O3

Expand All @@ -23,4 +24,4 @@ include ./Rules.mk

clean:
@echo "Cleaning up..."
rm -f $(OBJS) *.o *.d *~ core
rm -f $(OBJS) *.o *.d *~ core
53 changes: 41 additions & 12 deletions src/mididevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
#include <assert.h>
#include "midi.h"
#include "userinterface.h"
#include "performanceconfig.h"
#include "performance_sysex.h"

// RAII guard for MIDI spinlock
class MIDISpinLockGuard {
public:
MIDISpinLockGuard(CSpinLock& lock) : m_lock(lock) { m_lock.Acquire(); }
~MIDISpinLockGuard() { m_lock.Release(); }
private:
CSpinLock& m_lock;
};

LOGMODULE ("mididevice");

Expand Down Expand Up @@ -128,6 +139,8 @@ u8 CMIDIDevice::GetChannel (unsigned nTG) const

void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable)
{
MIDISpinLockGuard guard(m_MIDISpinLock);

// The packet contents are just normal MIDI data - see
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message

Expand Down Expand Up @@ -207,8 +220,6 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
return;
}

m_MIDISpinLock.Acquire ();

u8 ucStatus = pMessage[0];
u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4;
Expand All @@ -222,14 +233,19 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
LOGNOTE("MIDI-SYSEX: Set TG%d to MIDI Channel %d", mTG + 1, val & 0x0F);
m_pSynthesizer->SetMIDIChannel(val & 0x0F, mTG);
// Do not process this message further for any TGs
m_MIDISpinLock.Release();
return;
}

// Handle MiniDexed performance SysEx messages
if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[1] == 0x7D ) {
handle_performance_sysex(pMessage, this, m_pSynthesizer, nCable);
return;
}

// Master Volume is set using a MIDI SysEx message as follows:
// F0 Start of SysEx
// 7F System Realtime SysEx
// 7F SysEx "channel" - 7F = all devices
// 7F SysEx "channel" - 7F = all devices
// 04 Device Control
// 01 Master Volume Device Control
// LL Low 7-bits of 14-bit volume
Expand Down Expand Up @@ -449,11 +465,8 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
break;
}
}
else
{
HandleSystemExclusive(pMessage, nLength, nCable, nTG);
}
}

}
} else {
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++) {
Expand Down Expand Up @@ -671,7 +684,6 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
}
}
m_MIDISpinLock.Release ();
}

void CMIDIDevice::AddDevice (const char *pDeviceName)
Expand Down Expand Up @@ -700,7 +712,7 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval)
}

// Not looking for duplicate CCs so return once handled
for (unsigned tg=0; tg<8; tg++) {
for (unsigned tg=0; tg<8; tg++) {
if (m_nMIDISystemCCVol != 0) {
if (ucCC == MIDISystemCCMap[m_nMIDISystemCCVol][tg]) {
m_pSynthesizer->SetVolume (ucCCval, tg);
Expand Down Expand Up @@ -736,7 +748,17 @@ bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval)
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{

LOGDBG("HandleSystemExclusive: TG %d, length %zu", nTG, nLength);
LOGDBG("HandleSystemExclusive: TG %d, length %u", nTG, nLength);

// Only handle Yamaha (0x43) or MiniDexed (0x7D) SysEx
if (nLength < 2) {
LOGDBG("SysEx too short, ignoring");
return;
}
if (pMessage[1] != 0x43 && pMessage[1] != 0x7D) {
LOGDBG("Ignoring SysEx with manufacturer 0x%02X (not Yamaha or MiniDexed)", pMessage[1]);
return;
}

// Check if it is a dump request; these have the format F0 43 2n ff F7
// with n = the MIDI channel and ff = 00 for voice or 09 for bank
Expand Down Expand Up @@ -794,9 +816,16 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL
LOGERR("Unknown SysEx message.");
break;
case 100:
// load sysex-data into voice memory
// Load sysex-data into voice memory
LOGDBG("One Voice bulk upload");
m_pSynthesizer->loadVoiceParameters(pMessage,nTG);
// Defer performance config update to main loop because it would be too slow to do it here
if (m_pSynthesizer && m_pSynthesizer->GetPerformanceConfig()) {
CMiniDexed* pMiniDexed = static_cast<CMiniDexed*>(m_pSynthesizer);
if (pMiniDexed) {
pMiniDexed->SetPendingVoicePerformanceUpdate(nTG);
}
}
break;
case 200:
LOGDBG("Bank bulk upload.");
Expand Down
36 changes: 21 additions & 15 deletions src/midikeyboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,52 +95,58 @@ void CMIDIKeyboard::USBMIDIMessageHandler (u8 *pPacket, unsigned nLength, unsign
if ((pPacket[0] == 0xF0) && (m_nSysExIdx == 0))
{
// Start of SysEx message
//printf("SysEx Start Idx=%d, (%d)\n", m_nSysExIdx, nLength);
for (unsigned i=0; i<USB_SYSEX_BUFFER_SIZE; i++) {
m_SysEx[i] = 0;
}
for (unsigned i=0; i<nLength; i++) {
m_SysEx[m_nSysExIdx++] = pPacket[i];
}
// Early check for manufacturer ID after at least 2 bytes
if (m_nSysExIdx > 2 && m_SysEx[1] != 0x43 && m_SysEx[1] != 0x7D) {
// LOGNOTE("Aborting SysEx assembly: manufacturer 0x%02X not Yamaha or MiniDexed", m_SysEx[1]);
m_nSysExIdx = 0;
// Do not process remaining bytes as MIDI events
}
}
else if (m_nSysExIdx != 0)
{
// Continue processing SysEx message
//printf("SysEx Packet Idx=%d, (%d)\n", m_nSysExIdx, nLength);
for (unsigned i=0; i<nLength; i++) {
if (pPacket[i] == 0xF8 || pPacket[i] == 0xFA || pPacket[i] == 0xFB || pPacket[i] == 0xFC || pPacket[i] == 0xFE || pPacket[i] == 0xFF) {
// Singe-byte System Realtime Messages can happen at any time!
MIDIMessageHandler (&pPacket[i], 1, nCable);
}
else if (m_nSysExIdx >= USB_SYSEX_BUFFER_SIZE) {
// Run out of space, so reset and ignore rest of the message
// LOGERR("SysEx buffer overflow, resetting SysEx assembly");
m_nSysExIdx = 0;
break;
}
else if (pPacket[i] == 0xF7) {
// End of SysEx message
m_SysEx[m_nSysExIdx++] = pPacket[i];
//printf ("SysEx End Idx=%d\n", m_nSysExIdx);
MIDIMessageHandler (m_SysEx, m_nSysExIdx, nCable);
// Reset ready for next time
// Check manufacturer ID before passing to handler
if (m_SysEx[1] == 0x43 || m_SysEx[1] == 0x7D) {
MIDIMessageHandler (m_SysEx, m_nSysExIdx, nCable);
} else {
// LOGNOTE("Ignoring completed SysEx: manufacturer 0x%02X not Yamaha or MiniDexed", m_SysEx[1]);
}
m_nSysExIdx = 0;
}
else if ((pPacket[i] & 0x80) != 0) {
// Received another command, so reset processing as something has gone wrong
//printf ("SysEx Reset\n");
// LOGERR("Unexpected status byte 0x%02X in SysEx, resetting", pPacket[i]);
m_nSysExIdx = 0;
break;
}
else
{
// Store the byte
else {
m_SysEx[m_nSysExIdx++] = pPacket[i];
// Early check for manufacturer ID after at least 2 bytes
if (m_nSysExIdx == 2 && m_SysEx[1] != 0x43 && m_SysEx[1] != 0x7D) {
// LOGNOTE("Aborting SysEx assembly: manufacturer 0x%02X not Yamaha or MiniDexed", m_SysEx[1]);
m_nSysExIdx = 0;
// Do not process remaining bytes as MIDI events
}
}
}
}
else
{
// Assume it is a standard message
MIDIMessageHandler (pPacket, nLength, nCable);
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/minidexed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2498,3 +2498,24 @@ bool CMiniDexed::InitNetwork()
return false;
}
}

void CMiniDexed::GetCurrentVoiceData(uint8_t* dest, unsigned nTG) {
if (nTG < m_nToneGenerators && m_pTG[nTG]) {
m_pTG[nTG]->getVoiceData(dest);
}
}

void CMiniDexed::SetPendingVoicePerformanceUpdate(unsigned nTG) {
if (nTG < m_nToneGenerators && m_pTG[nTG]) {
// Get the current voice data from the synthesizer
uint8_t currentVoiceData[155];
m_pTG[nTG]->getVoiceData(currentVoiceData);
// We need to set the voice data in the synthesizer's performance config
// to ensure that the performance is not changed back to the previous one
// when a parameter is changed.
// This is a workaround for the fact that the performance config
// is not updated when the voice data is changed because it would be
// too costly to do in the thread.
this->GetPerformanceConfig()->SetVoiceDataToTxt(currentVoiceData, nTG);
}
}
15 changes: 14 additions & 1 deletion src/minidexed.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,16 @@ class CMiniDexed
bool InitNetwork();
void UpdateNetwork();

void LoadPerformanceParameters(void);

void GetCurrentVoiceData(uint8_t* dest, unsigned nTG);

public:
void SetPendingVoicePerformanceUpdate(unsigned nTG);

private:
int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note
uint8_t m_uchOPMask[CConfig::AllToneGenerators];
void LoadPerformanceParameters(void);
void ProcessSound (void);
const char* GetNetworkDeviceShortName() const;

Expand Down Expand Up @@ -364,6 +370,13 @@ class CMiniDexed
bool m_bLoadPerformanceBusy;
bool m_bLoadPerformanceBankBusy;
bool m_bSaveAsDeault;

// Add for deferred performance update after SysEx voice load
struct PendingVoicePerformanceUpdate {
bool pending = false;
uint8_t voiceData[156];
uint8_t tg = 0;
} m_PendingVoicePerformanceUpdate;
};

#endif
2 changes: 1 addition & 1 deletion src/net/applemidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,6 @@ bool CAppleMIDIParticipant::SendMIDIToHost(const u8* pData, size_t nSize)
return false;
}

LOGDBG("Successfully sent %zu bytes of MIDI data", nSize);
LOGDBG("Successfully sent %u bytes of MIDI data", nSize);
return true;
}
Loading
Loading