Add support for MIDI via raw UDP socket

This commit is contained in:
Dale Whinham
2022-03-22 15:31:32 +00:00
parent 9657a61a83
commit f8945cdea4
8 changed files with 184 additions and 1 deletions

View File

@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Support for network MIDI via raw UDP socket (new configuration file option). This is compatible with [MiSTer MidiLink](https://github.com/bbond007/MiSTer_MidiLink).
## Changed
- Update to circle-stdlib v15.12/Circle Step 44.4.1.

View File

@@ -24,6 +24,7 @@ OBJS := src/config.o \
src/net/applemidi.o \
src/net/ftpdaemon.o \
src/net/ftpworker.o \
src/net/udpmidi.o \
src/pisound.o \
src/power.o \
src/rommanager.o \

View File

@@ -108,6 +108,7 @@ CFG(default_gateway, CIPAddress, NetworkDefaultGateway, 0xc0a80101 )
CFG(dns_server, CIPAddress, NetworkDNSServer, 0xc0a80101 )
CFG(hostname, CString, NetworkHostname, "mt32-pi" )
CFG(rtp_midi, bool, NetworkRTPMIDI, true )
CFG(udp_midi, bool, NetworkUDPMIDI, true )
CFG(ftp, bool, NetworkFTPServer, true )
CFG(ftp_username, CString, NetworkFTPUsername, "mt32-pi" )
CFG(ftp_password, CString, NetworkFTPPassword, "mt32-pi" )

View File

@@ -55,6 +55,7 @@
#include "midiparser.h"
#include "net/applemidi.h"
#include "net/ftpdaemon.h"
#include "net/udpmidi.h"
#include "pisound.h"
#include "power.h"
#include "ringbuffer.h"
@@ -65,7 +66,7 @@
//#define MONITOR_TEMPERATURE
class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler
class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler, CUDPMIDIHandler
{
public:
CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI);
@@ -104,6 +105,9 @@ private:
virtual void OnAppleMIDIConnect(const CIPAddress* pIPAddress, const char* pName) override;
virtual void OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pName) override;
// CUDPMIDIHandler
virtual void OnUDPMIDIDataReceived(const u8* pData, size_t nSize) override { ParseMIDIBytes(pData, nSize); };
// Initialization
bool InitNetwork();
bool InitMT32Synth();
@@ -159,6 +163,7 @@ private:
CWPASupplicant m_WPASupplicant;
bool m_bNetworkReady;
CAppleMIDIParticipant* m_pAppleMIDIParticipant;
CUDPMIDIReceiver* m_pUDPMIDIReceiver;
CFTPDaemon* m_pFTPDaemon;
CBcmRandomNumberGenerator m_Random;

57
include/net/udpmidi.h Normal file
View File

@@ -0,0 +1,57 @@
//
// udpmidi.h
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2022 Dale Whinham <daleyo@gmail.com>
//
// This file is part of mt32-pi.
//
// mt32-pi is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// mt32-pi is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// mt32-pi. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _udpmidi_h
#define _udpmidi_h
#include <circle/net/ipaddress.h>
#include <circle/net/socket.h>
#include <circle/sched/task.h>
class CUDPMIDIHandler
{
public:
virtual void OnUDPMIDIDataReceived(const u8* pData, size_t nSize) = 0;
};
class CUDPMIDIReceiver : protected CTask
{
public:
CUDPMIDIReceiver(CUDPMIDIHandler* pHandler);
virtual ~CUDPMIDIReceiver() override;
bool Initialize();
virtual void Run() override;
private:
// UDP sockets
CSocket* m_pMIDISocket;
// Socket receive buffer
u8 m_MIDIBuffer[FRAME_BUFFER_SIZE];
// Callback handler
CUDPMIDIHandler* m_pHandler;
};
#endif

View File

@@ -480,6 +480,14 @@ hostname = mt32-pi
# Values: on*, off
rtp_midi = on
# Enable or disable the UDP MIDI server.
#
# This allows you to send MIDI data to mt32-pi via raw UDP socket on port 1999.
# Compatible with MiSTer MidiLink.
#
# Values: on*, off
udp_midi = on
# Enable or disable the embedded FTP server.
#
# This FTP server is a very basic implementation which DOES NOT feature any kind

View File

@@ -82,6 +82,7 @@ CMT32Pi::CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSyste
m_WPASupplicant(WLANConfigFile),
m_bNetworkReady(false),
m_pAppleMIDIParticipant(nullptr),
m_pUDPMIDIReceiver(nullptr),
m_pFTPDaemon(nullptr),
m_pLCD(nullptr),
@@ -852,6 +853,19 @@ void CMT32Pi::UpdateNetwork()
m_pLogger->Write(MT32PiName, LogNotice, "AppleMIDI receiver initialized");
}
if (m_pConfig->NetworkUDPMIDI)
{
m_pUDPMIDIReceiver = new CUDPMIDIReceiver(this);
if (!m_pUDPMIDIReceiver->Initialize())
{
m_pLogger->Write(MT32PiName, LogError, "Failed to init UDP MIDI receiver");
delete m_pUDPMIDIReceiver;
m_pUDPMIDIReceiver = nullptr;
}
else
m_pLogger->Write(MT32PiName, LogNotice, "UDP MIDI receiver initialized");
}
if (m_pConfig->NetworkFTPServer)
{
m_pFTPDaemon = new CFTPDaemon(m_pConfig->NetworkFTPUsername, m_pConfig->NetworkFTPPassword);
@@ -873,6 +887,8 @@ void CMT32Pi::UpdateNetwork()
delete m_pAppleMIDIParticipant;
m_pAppleMIDIParticipant = nullptr;
delete m_pUDPMIDIReceiver;
m_pUDPMIDIReceiver = nullptr;
delete m_pFTPDaemon;
m_pFTPDaemon = nullptr;
}

91
src/net/udpmidi.cpp Normal file
View File

@@ -0,0 +1,91 @@
//
// udpmidi.cpp
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2022 Dale Whinham <daleyo@gmail.com>
//
// This file is part of mt32-pi.
//
// mt32-pi is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// mt32-pi is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// mt32-pi. If not, see <http://www.gnu.org/licenses/>.
//
#include <circle/logger.h>
#include <circle/net/in.h>
#include <circle/net/netsubsystem.h>
#include <circle/sched/scheduler.h>
#include "net/udpmidi.h"
constexpr u16 MIDIPort = 1999;
const char UDPMIDIName[] = "udpmidi";
CUDPMIDIReceiver::CUDPMIDIReceiver(CUDPMIDIHandler* pHandler)
: CTask(TASK_STACK_SIZE, true),
m_pMIDISocket(nullptr),
m_MIDIBuffer{0},
m_pHandler(pHandler)
{
}
CUDPMIDIReceiver::~CUDPMIDIReceiver()
{
if (m_pMIDISocket)
delete m_pMIDISocket;
}
bool CUDPMIDIReceiver::Initialize()
{
assert(m_pMIDISocket == nullptr);
CLogger* const pLogger = CLogger::Get();
CNetSubSystem* const pNet = CNetSubSystem::Get();
if ((m_pMIDISocket = new CSocket(pNet, IPPROTO_UDP)) == nullptr)
return false;
if (m_pMIDISocket->Bind(MIDIPort) != 0)
{
pLogger->Write(UDPMIDIName, LogError, "Couldn't bind to port %d", MIDIPort);
return false;
}
// We started as a suspended task; run now that initialization is successful
Start();
return true;
}
void CUDPMIDIReceiver::Run()
{
assert(m_pHandler != nullptr);
assert(m_pMIDISocket != nullptr);
CLogger* const pLogger = CLogger::Get();
CScheduler* const pScheduler = CScheduler::Get();
while (true)
{
// Blocking call
const int nMIDIResult = m_pMIDISocket->Receive(m_MIDIBuffer, sizeof(m_MIDIBuffer), 0);
if (nMIDIResult < 0)
pLogger->Write(UDPMIDIName, LogError, "MIDI socket receive error: %d", nMIDIResult);
else if (nMIDIResult > 0)
m_pHandler->OnUDPMIDIDataReceived(m_MIDIBuffer, nMIDIResult);
// Allow other tasks to run
pScheduler->Yield();
}
}