pull/899/merge
probonopd 4 days ago committed by GitHub
commit c9289840a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 68
      src/mididevice.cpp
  2. 4
      src/mididevice.h
  3. 2
      src/minidexed.cpp
  4. 45
      src/net/applemidi.cpp
  5. 3
      src/net/applemidi.h
  6. 26
      src/udpmididevice.cpp
  7. 7
      src/udpmididevice.h

@ -320,23 +320,21 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
// Process MIDI for each active Tone Generator // Process MIDI for each active Tone Generator
bool bSystemCCHandled = false; bool bSystemCCHandled = false;
bool bSystemCCChecked = false; bool bSystemCCChecked = false;
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++) if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN) {
{ uint8_t ucSysExChannel = (pMessage[2] & 0x0F);
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN) for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators(); nTG++) {
{ if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode) {
// MIDI SYSEX per MIDI channel
uint8_t ucSysExChannel = (pMessage[2] & 0x0F);
if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode)
{
LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG); LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG);
HandleSystemExclusive(pMessage, nLength, nCable, nTG); HandleSystemExclusive(pMessage, nLength, nCable, nTG);
if (nLength == 5) {
break; // Send dump request only to the first TG that matches the MIDI channel requested via the SysEx message device ID
}
} }
} }
else } else {
{ for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++) {
if ( m_ChannelMap[nTG] == ucChannel if ( m_ChannelMap[nTG] == ucChannel
|| m_ChannelMap[nTG] == OmniMode) || m_ChannelMap[nTG] == OmniMode) {
{
switch (ucType) switch (ucType)
{ {
case MIDI_NOTE_ON: case MIDI_NOTE_ON:
@ -610,6 +608,23 @@ 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) void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{ {
// 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
// It was confirmed that on the TX816, the device number is interpreted as the MIDI channel;
if (nLength == 5 && pMessage[3] == 0x00)
{
LOGDBG("SysEx voice dump request: device %d", nTG);
SendSystemExclusiveVoice(nTG, m_DeviceName, nCable, nTG);
return;
}
else if (nLength == 5 && pMessage[3] == 0x09)
{
LOGDBG("SysEx bank dump request: device %d", nTG);
LOGDBG("Still to be implemented");
return;
}
int16_t sysex_return; int16_t sysex_return;
sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG); sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG);
@ -735,25 +750,22 @@ void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nL
else if(sysex_return >= 500 && sysex_return < 600) else if(sysex_return >= 500 && sysex_return < 600)
{ {
LOGDBG("SysEx send voice %u request",sysex_return-500); LOGDBG("SysEx send voice %u request",sysex_return-500);
SendSystemExclusiveVoice(sysex_return-500, nCable, nTG); SendSystemExclusiveVoice(sysex_return-500, m_DeviceName, nCable, nTG);
} }
break; break;
} }
} }
void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG) void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const std::string& deviceName, unsigned nCable, uint8_t nTG)
{ {
uint8_t voicedump[163]; // Example: F0 43 20 00 F7
uint8_t voicedump[163];
// Get voice sysex dump from TG m_pSynthesizer->getSysExVoiceDump(voicedump, nTG);
m_pSynthesizer->getSysExVoiceDump(voicedump, nTG); TDeviceMap::const_iterator Iterator = s_DeviceMap.find(deviceName);
if (Iterator != s_DeviceMap.end()) {
TDeviceMap::const_iterator Iterator; Iterator->second->Send(voicedump, sizeof(voicedump), nCable);
LOGDBG("Send SYSEX voice dump %u to \"%s\"", nVoice, deviceName.c_str());
// send voice dump to all MIDI interfaces } else {
for(Iterator = s_DeviceMap.begin(); Iterator != s_DeviceMap.end(); ++Iterator) LOGWARN("No device found in s_DeviceMap for name: %s", deviceName.c_str());
{ }
Iterator->second->Send (voicedump, sizeof(voicedump)*sizeof(uint8_t)); }
// LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str());
}
}

@ -54,7 +54,9 @@ public:
u8 GetChannel (unsigned nTG) const; u8 GetChannel (unsigned nTG) const;
virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {} virtual void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) {}
virtual void SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG); // Change signature to specify device name
void SendSystemExclusiveVoice(uint8_t nVoice, const std::string& deviceName, unsigned nCable, uint8_t nTG);
const std::string& GetDeviceName() const { return m_DeviceName; }
protected: protected:
void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0); void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0);

@ -666,7 +666,7 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
// MIDI channel configured for this TG // MIDI channel configured for this TG
if (m_nMIDIChannel[nTG] < CMIDIDevice::Channels) if (m_nMIDIChannel[nTG] < CMIDIDevice::Channels)
{ {
m_SerialMIDI.SendSystemExclusiveVoice(nProgram,0,nTG); m_SerialMIDI.SendSystemExclusiveVoice(nProgram, m_SerialMIDI.GetDeviceName(), 0, nTG);
} }
} }

@ -31,6 +31,9 @@
#include "applemidi.h" #include "applemidi.h"
#include "byteorder.h" #include "byteorder.h"
#define MAX_DX7_SYSEX_LENGTH 4104
#define MAX_MIDI_MESSAGE MAX_DX7_SYSEX_LENGTH
// #define APPLEMIDI_DEBUG // #define APPLEMIDI_DEBUG
LOGMODULE("applemidi"); LOGMODULE("applemidi");
@ -876,4 +879,46 @@ bool CAppleMIDIParticipant::SendFeedbackPacket()
#endif #endif
return SendPacket(m_pControlSocket, &m_InitiatorIPAddress, m_nInitiatorControlPort, &FeedbackPacket, sizeof(FeedbackPacket)); return SendPacket(m_pControlSocket, &m_InitiatorIPAddress, m_nInitiatorControlPort, &FeedbackPacket, sizeof(FeedbackPacket));
}
bool CAppleMIDIParticipant::SendMIDIToHost(const u8* pData, size_t nSize)
{
if (m_State != TState::Connected)
return false;
// Build RTP-MIDI packet
TRTPMIDI packet;
packet.nFlags = htons((RTPMIDIVersion << 14) | RTPMIDIPayloadType);
packet.nSequence = htons(++m_nSequence);
packet.nTimestamp = htonl(0); // No timestamping for now
packet.nSSRC = htonl(m_nSSRC);
// RTP-MIDI command section: header + MIDI data
// Header: 0x80 | length (if length < 0x0F)
u8 midiHeader = 0x00;
size_t midiLen = nSize;
if (midiLen < 0x0F) {
midiHeader = midiLen & 0x0F;
} else {
midiHeader = 0x80 | ((midiLen >> 8) & 0x0F);
}
u8 buffer[sizeof(TRTPMIDI) + 2 + MAX_MIDI_MESSAGE];
size_t offset = 0;
memcpy(buffer + offset, &packet, sizeof(TRTPMIDI));
offset += sizeof(TRTPMIDI);
buffer[offset++] = midiHeader;
if (midiLen >= 0x0F) {
buffer[offset++] = midiLen & 0xFF;
}
memcpy(buffer + offset, pData, midiLen);
offset += midiLen;
if (SendPacket(m_pMIDISocket, &m_InitiatorIPAddress, m_nInitiatorMIDIPort, buffer, offset) <= 0) {
LOGNOTE("Failed to send MIDI data to host");
return false;
}
LOGDBG("Successfully sent %zu bytes of MIDI data", nSize);
return true;
} }

@ -46,6 +46,9 @@ public:
virtual void Run() override; virtual void Run() override;
public:
bool SendMIDIToHost(const u8* pData, size_t nSize);
private: private:
void ControlInvitationState(); void ControlInvitationState();
void MIDIInvitationState(); void MIDIInvitationState();

@ -25,6 +25,8 @@
#include <cstring> #include <cstring>
#include "udpmididevice.h" #include "udpmididevice.h"
#include <assert.h> #include <assert.h>
#include <circle/net/netsubsystem.h>
#include <circle/net/in.h>
#define VIRTUALCABLE 24 #define VIRTUALCABLE 24
@ -64,6 +66,13 @@ boolean CUDPMIDIDevice::Initialize (void)
} }
else else
LOGNOTE("UDP MIDI receiver initialized"); LOGNOTE("UDP MIDI receiver initialized");
// UDP MIDI send socket setup (default: broadcast 255.255.255.255:1999)
CNetSubSystem* pNet = CNetSubSystem::Get();
m_pUDPSendSocket = new CSocket(pNet, IPPROTO_UDP);
m_UDPDestAddress.Set(0xFFFFFFFF); // Broadcast by default
m_UDPDestPort = 1999;
return true; return true;
} }
@ -87,4 +96,21 @@ void CUDPMIDIDevice::OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const c
void CUDPMIDIDevice::OnUDPMIDIDataReceived(const u8* pData, size_t nSize) void CUDPMIDIDevice::OnUDPMIDIDataReceived(const u8* pData, size_t nSize)
{ {
MIDIMessageHandler(pData, nSize, VIRTUALCABLE); MIDIMessageHandler(pData, nSize, VIRTUALCABLE);
}
void CUDPMIDIDevice::Send(const u8 *pMessage, size_t nLength, unsigned nCable)
{
bool sentRTP = false;
if (m_pAppleMIDIParticipant && m_pAppleMIDIParticipant->SendMIDIToHost(pMessage, nLength)) {
sentRTP = true;
LOGNOTE("Sent %zu bytes to RTP-MIDI host", nLength);
}
if (!sentRTP && m_pUDPSendSocket) {
int res = m_pUDPSendSocket->SendTo(pMessage, nLength, 0, m_UDPDestAddress, m_UDPDestPort);
if (res < 0) {
LOGERR("Failed to send %zu bytes to UDP MIDI host", nLength);
} else {
LOGNOTE("Sent %zu bytes to UDP MIDI host (broadcast)", nLength);
}
}
} }

@ -29,6 +29,7 @@
#include "config.h" #include "config.h"
#include "net/applemidi.h" #include "net/applemidi.h"
#include "net/udpmidi.h" #include "net/udpmidi.h"
#include "midi.h"
class CMiniDexed; class CMiniDexed;
@ -43,6 +44,7 @@ public:
virtual void OnAppleMIDIConnect(const CIPAddress* pIPAddress, const char* pName) override; virtual void OnAppleMIDIConnect(const CIPAddress* pIPAddress, const char* pName) override;
virtual void OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pName) override; virtual void OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pName) override;
virtual void OnUDPMIDIDataReceived(const u8* pData, size_t nSize) override; virtual void OnUDPMIDIDataReceived(const u8* pData, size_t nSize) override;
virtual void Send(const u8 *pMessage, size_t nLength, unsigned nCable = 0) override;
private: private:
CMiniDexed *m_pSynthesizer; CMiniDexed *m_pSynthesizer;
@ -50,6 +52,11 @@ private:
CBcmRandomNumberGenerator m_Random; CBcmRandomNumberGenerator m_Random;
CAppleMIDIParticipant* m_pAppleMIDIParticipant; // AppleMIDI participant instance CAppleMIDIParticipant* m_pAppleMIDIParticipant; // AppleMIDI participant instance
CUDPMIDIReceiver* m_pUDPMIDIReceiver; CUDPMIDIReceiver* m_pUDPMIDIReceiver;
CSocket* m_pUDPSendSocket = nullptr;
CIPAddress m_UDPDestAddress;
unsigned m_UDPDestPort = 1999;
CIPAddress m_LastUDPSenderAddress;
unsigned m_LastUDPSenderPort = 0;
}; };
#endif #endif

Loading…
Cancel
Save