working RTP and UDP poc

pull/744/head
Ömer Şiar Baysal 1 week ago
parent 0a9eb7f565
commit 893b9f60f1
  1. 4
      src/Makefile
  2. 12
      src/mididevice.cpp
  3. 35
      src/minidexed.cpp
  4. 10
      src/minidexed.h
  5. 111
      src/net/ftpdaemon.cpp
  6. 47
      src/net/ftpdaemon.h
  7. 1206
      src/net/ftpworker.cpp
  8. 157
      src/net/ftpworker.h
  9. 193
      src/net/utility.h
  10. 106
      src/udpmididevice.cpp
  11. 76
      src/udpmididevice.h

@ -8,10 +8,10 @@ CMSIS_DIR = ../CMSIS_5/CMSIS
NET_DIR = ./net NET_DIR = ./net
OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \ OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \
mididevice.o rtpmididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \ mididevice.o udpmididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \
sysexfileloader.o performanceconfig.o perftimer.o \ sysexfileloader.o performanceconfig.o perftimer.o \
effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o \ effect_compressor.o effect_platervbstereo.o uibuttons.o midipin.o \
net/applemidi.o net/udpmidi.o net/ftpdaemon.o net/ftpworker.o net/applemidi.o net/udpmidi.o
OPTIMIZE = -O3 OPTIMIZE = -O3

@ -169,6 +169,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
m_MIDISpinLock.Acquire (); m_MIDISpinLock.Acquire ();
printf ("MIDI-DEBUG: SPINLOCK ACQUIRED\n");
u8 ucStatus = pMessage[0]; u8 ucStatus = pMessage[0];
u8 ucChannel = ucStatus & 0x0F; u8 ucChannel = ucStatus & 0x0F;
@ -213,8 +214,10 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
// Process MIDI for each Tone Generator // Process MIDI for each Tone Generator
printf ("MIDI-DEBUG: EACH TONEGENERATOR LOOP\n");
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{ {
printf ("%u TONE GENERATOR", nTG);
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN) if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{ {
// MIDI SYSEX per MIDI channel // MIDI SYSEX per MIDI channel
@ -227,12 +230,15 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
else else
{ {
printf ("NOT AN SYSEX");
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:
printf ("MIDI-DEBUG: CASE MIDI NOTE ON\n");
if (nLength < 3) if (nLength < 3)
{ {
break; break;
@ -242,12 +248,15 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
{ {
if (pMessage[2] <= 127) if (pMessage[2] <= 127)
{ {
printf ("MIDI-DEBUG: KEYDOWN EVENT\n");
m_pSynthesizer->keydown (pMessage[1], m_pSynthesizer->keydown (pMessage[1],
pMessage[2], nTG); pMessage[2], nTG);
} }
} }
else else
{ {
printf ("MIDI-DEBUG: KEYUP EVENT\n");
//printf ("MIDI-RTP: %02X\n", m_pSynthesizer);
m_pSynthesizer->keyup (pMessage[1], nTG); m_pSynthesizer->keyup (pMessage[1], nTG);
} }
break; break;
@ -257,7 +266,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
{ {
break; break;
} }
printf ("MIDI-DEBUG: MIDI NOTE OFF\n");
m_pSynthesizer->keyup (pMessage[1], nTG); m_pSynthesizer->keyup (pMessage[1], nTG);
break; break;
@ -379,6 +388,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
} }
} }
m_MIDISpinLock.Release (); m_MIDISpinLock.Release ();
printf ("MIDI-DEBUG: SPINLOCK RELEASED\n");
} }
void CMIDIDevice::AddDevice (const char *pDeviceName) void CMIDIDevice::AddDevice (const char *pDeviceName)

@ -28,15 +28,17 @@
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <circle/net/netsubsystem.h> #include <circle/net/netsubsystem.h>
#include <circle/sched/scheduler.h> //#include <circle/sched/scheduler.h>
#include "circle_stdlib_app.h" //#include "circle_stdlib_app.h"
//#include "mididevice.h" //#include "mididevice.h"
/*
#define DRIVE "SD:" #define DRIVE "SD:"
#define FIRMWARE_PATH DRIVE "/firmware/" // firmware files must be provided here #define FIRMWARE_PATH DRIVE "/firmware/" // firmware files must be provided here
#define CONFIG_FILE DRIVE "/wpa_supplicant.conf" #define CONFIG_FILE DRIVE "/wpa_supplicant.conf"
#define FTPUSERNAME "admin"
#define FTPPASSWORD "admin"
/*
const char WLANFirmwarePath[] = "SD:firmware/"; const char WLANFirmwarePath[] = "SD:firmware/";
const char WLANConfigFile[] = "SD:wpa_supplicant.conf"; const char WLANConfigFile[] = "SD:wpa_supplicant.conf";
*/ */
@ -77,7 +79,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
*/ */
//CNetSubSystem* const pNet = CNetSubSystem::Get(); //CNetSubSystem* const pNet = CNetSubSystem::Get();
m_bNetworkReady(false), m_bNetworkReady(false),
m_RTPMIDI (this, pConfig, &m_UI) m_UDPMIDI (this, pConfig, &m_UI)
{ {
assert (m_pConfig); assert (m_pConfig);
@ -291,18 +293,24 @@ bool CMiniDexed::Initialize (void)
} }
#endif #endif
//InitNetwork(); //InitNetwork();
UpdateNetwork();
//CMIDIDevice->InitializeRTP(); //CMIDIDevice->InitializeRTP();
if (m_RTPMIDI.Initialize ()) m_pFTPDaemon = new CFTPDaemon(FTPUSERNAME, FTPPASSWORD);
if (!m_pFTPDaemon->Initialize())
{ {
LOGNOTE ("RTP MIDI interface enabled"); LOGERR("Failed to init FTP daemon");
delete m_pFTPDaemon;
m_pFTPDaemon = nullptr;
} }
else
LOGNOTE("FTP daemon initialized");
return true; return true;
} }
void CMiniDexed::Process (bool bPlugAndPlayUpdated) void CMiniDexed::Process (bool bPlugAndPlayUpdated)
{ {
CScheduler* const pScheduler = CScheduler::Get();
#ifndef ARM_ALLOW_MULTI_CORE #ifndef ARM_ALLOW_MULTI_CORE
ProcessSound (); ProcessSound ();
#endif #endif
@ -355,7 +363,9 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated)
{ {
m_GetChunkTimer.Dump (); m_GetChunkTimer.Dump ();
} }
UpdateNetwork();
// Allow other tasks to run
pScheduler->Yield();
} }
#ifdef ARM_ALLOW_MULTI_CORE #ifdef ARM_ALLOW_MULTI_CORE
@ -629,6 +639,7 @@ void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG)
{ {
m_SerialMIDI.SetChannel (uchChannel, nTG); m_SerialMIDI.SetChannel (uchChannel, nTG);
} }
m_UDPMIDI.SetChannel (uchChannel, nTG);
#ifdef ARM_ALLOW_MULTI_CORE #ifdef ARM_ALLOW_MULTI_CORE
unsigned nActiveTGs = 0; unsigned nActiveTGs = 0;
@ -1860,11 +1871,15 @@ void CMiniDexed::UpdateNetwork()
if (!m_bNetworkReady) if (!m_bNetworkReady)
{ {
m_bNetworkReady = true; m_bNetworkReady = true;
CString IPString; CString IPString;
pNet->GetConfig()->GetIPAddress()->Format(&IPString); pNet->GetConfig()->GetIPAddress()->Format(&IPString);
LOGNOTE("Network up and running at: %s", static_cast<const char *>(IPString)); LOGNOTE("Network up and running at: %s", static_cast<const char *>(IPString));
if (m_UDPMIDI.Initialize ())
{
LOGNOTE ("RTP MIDI interface enabled");
}
} }
else if (m_bNetworkReady && !bNetIsRunning) else if (m_bNetworkReady && !bNetIsRunning)
{ {

@ -38,6 +38,9 @@
#include <circle/i2cmaster.h> #include <circle/i2cmaster.h>
#include <circle/multicore.h> #include <circle/multicore.h>
#include <circle/sound/soundbasedevice.h> #include <circle/sound/soundbasedevice.h>
#include <circle/sched/scheduler.h>
////#include <circle/net/netsubsystem.h>
//#include <wlan/hostap/wpa_supplicant/wpasupplicant.h>
#include <circle/spinlock.h> #include <circle/spinlock.h>
#include "common.h" #include "common.h"
#include "effect_mixer.hpp" #include "effect_mixer.hpp"
@ -46,7 +49,8 @@
//#include <circle/net/netsubsystem.h> //#include <circle/net/netsubsystem.h>
//#include <wlan/bcm4343.h> //#include <wlan/bcm4343.h>
//#include <wlan/hostap/wpa_supplicant/wpasupplicant.h> //#include <wlan/hostap/wpa_supplicant/wpasupplicant.h>
#include "rtpmididevice.h" #include "udpmididevice.h"
#include "net/ftpdaemon.h"
class CMiniDexed class CMiniDexed
#ifdef ARM_ALLOW_MULTI_CORE #ifdef ARM_ALLOW_MULTI_CORE
@ -316,6 +320,7 @@ private:
bool m_bLoadPerformanceBusy; bool m_bLoadPerformanceBusy;
bool m_bSaveAsDeault; bool m_bSaveAsDeault;
bool m_bNetworkReady; bool m_bNetworkReady;
//CNetSubSystem* m_pNet;
//CWPASupplicant m_WPASupplicant; //CWPASupplicant m_WPASupplicant;
// Networking // Networking
//CNetSubSystem &mNet; //CNetSubSystem &mNet;
@ -327,7 +332,8 @@ private:
bool m_bNetworkReady; bool m_bNetworkReady;
CBcmRandomNumberGenerator m_Random; CBcmRandomNumberGenerator m_Random;
*/ */
CRTPMIDIDevice m_RTPMIDI; CUDPMIDIDevice m_UDPMIDI;
CFTPDaemon* m_pFTPDaemon;
}; };
#endif #endif

@ -0,0 +1,111 @@
//
// ftpdaemon.cpp
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2023 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/ipaddress.h>
#include <circle/net/netsubsystem.h>
#include <circle/string.h>
#include "ftpdaemon.h"
#include "ftpworker.h"
LOGMODULE("ftpd");
constexpr u16 ListenPort = 21;
constexpr u8 MaxConnections = 1;
CFTPDaemon::CFTPDaemon(const char* pUser, const char* pPassword)
: CTask(TASK_STACK_SIZE, true),
m_pListenSocket(nullptr),
m_pUser(pUser),
m_pPassword(pPassword)
{
}
CFTPDaemon::~CFTPDaemon()
{
if (m_pListenSocket)
delete m_pListenSocket;
}
bool CFTPDaemon::Initialize()
{
CNetSubSystem* const pNet = CNetSubSystem::Get();
if ((m_pListenSocket = new CSocket(pNet, IPPROTO_TCP)) == nullptr)
return false;
if (m_pListenSocket->Bind(ListenPort) != 0)
{
LOGERR("Couldn't bind to port %d", ListenPort);
return false;
}
if (m_pListenSocket->Listen() != 0)
{
LOGERR("Failed to listen on control socket");
return false;
}
// We started as a suspended task; run now that initialization is successful
Start();
return true;
}
void CFTPDaemon::Run()
{
assert(m_pListenSocket != nullptr);
LOGNOTE("Listener task spawned");
while (true)
{
CIPAddress ClientIPAddress;
u16 nClientPort;
LOGDBG("Listener: waiting for connection");
CSocket* pConnection = m_pListenSocket->Accept(&ClientIPAddress, &nClientPort);
if (pConnection == nullptr)
{
LOGERR("Unable to accept connection");
continue;
}
CString IPAddressString;
ClientIPAddress.Format(&IPAddressString);
LOGNOTE("Incoming connection from %s:%d", static_cast<const char*>(IPAddressString), nClientPort);
if (CFTPWorker::GetInstanceCount() >= MaxConnections)
{
pConnection->Send("421 Maximum number of connections reached.\r\n", 45, 0);
delete pConnection;
LOGWARN("Maximum number of connections reached");
continue;
}
// Spawn new worker
new CFTPWorker(pConnection, m_pUser, m_pPassword);
}
}

@ -0,0 +1,47 @@
//
// ftpdaemon.h
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2023 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 _ftpdaemon_h
#define _ftpdaemon_h
#include <circle/net/socket.h>
#include <circle/sched/task.h>
class CFTPDaemon : protected CTask
{
public:
CFTPDaemon(const char* pUser, const char* pPassword);
virtual ~CFTPDaemon() override;
bool Initialize();
virtual void Run() override;
private:
// TCP sockets
CSocket* m_pListenSocket;
const char* m_pUser;
const char* m_pPassword;
};
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,157 @@
//
// ftpworker.h
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2023 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 _ftpworker_h
#define _ftpworker_h
#include <circle/net/ipaddress.h>
#include <circle/net/socket.h>
#include <circle/sched/task.h>
#include <circle/string.h>
// TODO: These may be incomplete/inaccurate
enum TFTPStatus
{
FileStatusOk = 150,
Success = 200,
SystemType = 215,
ReadyForNewUser = 220,
ClosingControl = 221,
TransferComplete = 226,
EnteringPassiveMode = 227,
UserLoggedIn = 230,
FileActionOk = 250,
PathCreated = 257,
PasswordRequired = 331,
AccountRequired = 332,
PendingFurtherInfo = 350,
ServiceNotAvailable = 421,
DataConnectionFailed = 425,
FileActionNotTaken = 450,
ActionAborted = 451,
CommandUnrecognized = 500,
SyntaxError = 501,
CommandNotImplemented = 502,
BadCommandSequence = 503,
NotLoggedIn = 530,
FileNotFound = 550,
FileNameNotAllowed = 553,
};
enum class TTransferMode
{
Active,
Passive,
};
enum class TDataType
{
ASCII,
Binary,
};
struct TFTPCommand;
struct TDirectoryListEntry;
class CFTPWorker : protected CTask
{
public:
CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword);
virtual ~CFTPWorker() override;
virtual void Run() override;
static u8 GetInstanceCount() { return s_nInstanceCount; }
private:
CSocket* OpenDataConnection();
bool SendStatus(TFTPStatus StatusCode, const char* pMessage);
bool CheckLoggedIn();
// Directory navigation
CString RealPath(const char* pInBuffer) const;
const TDirectoryListEntry* BuildDirectoryList(size_t& nOutEntries) const;
// FTP command handlers
bool System(const char* pArgs);
bool Username(const char* pArgs);
bool Port(const char* pArgs);
bool Passive(const char* pArgs);
bool Password(const char* pArgs);
bool Type(const char* pArgs);
bool Retrieve(const char* pArgs);
bool Store(const char* pArgs);
bool Delete(const char* pArgs);
bool MakeDirectory(const char* pArgs);
bool ChangeWorkingDirectory(const char* pArgs);
bool ChangeToParentDirectory(const char* pArgs);
bool PrintWorkingDirectory(const char* pArgs);
bool List(const char* pArgs);
bool ListFileNames(const char* pArgs);
bool RenameFrom(const char* pArgs);
bool RenameTo(const char* pArgs);
bool Bye(const char* pArgs);
bool NoOp(const char* pArgs);
CString m_LogName;
// Authentication
const char* m_pExpectedUser;
const char* m_pExpectedPassword;
// TCP sockets
CSocket* m_pControlSocket;
CSocket* m_pDataSocket;
u16 m_nDataSocketPort;
CIPAddress m_DataSocketIPAddress;
// Command/data buffers
char m_CommandBuffer[FRAME_BUFFER_SIZE];
u8 m_DataBuffer[FRAME_BUFFER_SIZE];
// Session state
CString m_User;
CString m_Password;
TDataType m_DataType;
TTransferMode m_TransferMode;
CString m_CurrentPath;
CString m_RenameFrom;
static void FatFsPathToFTPPath(const char* pInBuffer, char* pOutBuffer, size_t nSize);
static void FTPPathToFatFsPath(const char* pInBuffer, char* pOutBuffer, size_t nSize);
static void FatFsParentPath(const char* pInBuffer, char* pOutBuffer, size_t nSize);
static void FormatLastModifiedDate(u16 nDate, char* pOutBuffer, size_t nSize);
static void FormatLastModifiedTime(u16 nDate, char* pOutBuffer, size_t nSize);
static const TFTPCommand Commands[];
static u8 s_nInstanceCount;
};
#endif

@ -0,0 +1,193 @@
//
// utility.h
//
// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi
// Copyright (C) 2020-2023 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 _utility_h
#define _utility_h
#include <circle/string.h>
#include <circle/util.h>
// Macro to extract the string representation of an enum
#define CONFIG_ENUM_VALUE(VALUE, STRING) VALUE,
// Macro to extract the enum value
#define CONFIG_ENUM_STRING(VALUE, STRING) #STRING,
// Macro to declare the enum itself
#define CONFIG_ENUM(NAME, VALUES) enum class NAME { VALUES(CONFIG_ENUM_VALUE) }
// Macro to declare an array of string representations for an enum
#define CONFIG_ENUM_STRINGS(NAME, DATA) static const char* NAME##Strings[] = { DATA(CONFIG_ENUM_STRING) }
namespace Utility
{
// Templated function for clamping a value between a minimum and a maximum
template <class T>
constexpr T Clamp(const T& nValue, const T& nMin, const T& nMax)
{
return (nValue < nMin) ? nMin : (nValue > nMax) ? nMax : nValue;
}
// Templated function for taking the minimum of two values
template <class T>
constexpr T Min(const T& nLHS, const T& nRHS)
{
return nLHS < nRHS ? nLHS : nRHS;
}
// Templated function for taking the maximum of two values
template <class T>
constexpr T Max(const T& nLHS, const T& nRHS)
{
return nLHS > nRHS ? nLHS : nRHS;
}
// Function for performing a linear interpolation of a value
constexpr float Lerp(float nValue, float nMinA, float nMaxA, float nMinB, float nMaxB)
{
return nMinB + (nValue - nMinA) * ((nMaxB - nMinB) / (nMaxA - nMinA));
}
// Return number of elements in an array
template <class T, size_t N>
constexpr size_t ArraySize(const T(&)[N]) { return N; }
// Returns whether some value is a power of 2
template <class T>
constexpr bool IsPowerOfTwo(const T& nValue)
{
return nValue && ((nValue & (nValue - 1)) == 0);
}
// Rounds a number to a nearest multiple; only works for integer values/multiples
template <class T>
constexpr T RoundToNearestMultiple(const T& nValue, const T& nMultiple)
{
return ((nValue + nMultiple / 2) / nMultiple) * nMultiple;
}
// Convert between milliseconds and ticks of a 1MHz clock
template <class T>
constexpr T MillisToTicks(const T& nMillis)
{
return nMillis * 1000;
}
template <class T>
constexpr T TicksToMillis(const T& nTicks)
{
return nTicks / 1000;
}
// Computes the Roland checksum
constexpr u8 RolandChecksum(const u8* pData, size_t nSize)
{
u8 nSum = 0;
for (size_t i = 0; i < nSize; ++i)
nSum = (nSum + pData[i]) & 0x7F;
return 128 - nSum;
}
// Comparators for sorting
namespace Comparator
{
template<class T>
using TComparator = bool (*)(const T&, const T&);
template<class T>
inline bool LessThan(const T& ObjectA, const T& ObjectB)
{
return ObjectA < ObjectB;
}
template<class T>
inline bool GreaterThan(const T& ObjectA, const T& ObjectB)
{
return ObjectA > ObjectB;
}
inline bool CaseInsensitiveAscending(const CString& StringA, const CString& StringB)
{
return strcasecmp(StringA, StringB) < 0;
}
}
// Swaps two objects in-place
template<class T>
inline void Swap(T& ObjectA, T& ObjectB)
{
u8 Buffer[sizeof(T)];
memcpy(Buffer, &ObjectA, sizeof(T));
memcpy(&ObjectA, &ObjectB, sizeof(T));
memcpy(&ObjectB, Buffer, sizeof(T));
}
namespace
{
// Quicksort partition function (private)
template<class T>
size_t Partition(T* Items, Comparator::TComparator<T> Comparator, size_t nLow, size_t nHigh)
{
const size_t nPivotIndex = (nHigh + nLow) / 2;
T* Pivot = &Items[nPivotIndex];
while (true)
{
while (Comparator(Items[nLow], *Pivot))
++nLow;
while (Comparator(*Pivot, Items[nHigh]))
--nHigh;
if (nLow >= nHigh)
return nHigh;
Swap(Items[nLow], Items[nHigh]);
// Update pointer if pivot was swapped
if (nPivotIndex == nLow)
Pivot = &Items[nHigh];
else if (nPivotIndex == nHigh)
Pivot = &Items[nLow];
++nLow;
--nHigh;
}
}
}
// Sorts an array in-place using the Tony Hoare Quicksort algorithm
template <class T>
void QSort(T* Items, Comparator::TComparator<T> Comparator, size_t nLow, size_t nHigh)
{
if (nLow < nHigh)
{
size_t p = Partition(Items, Comparator, nLow, nHigh);
QSort(Items, Comparator, nLow, p);
QSort(Items, Comparator, p + 1, nHigh);
}
}
}
#endif

@ -0,0 +1,106 @@
//
// udpmididevice.cpp
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
//
// Original author of this class:
// R. Stange <rsta2@o2online.de>
//
// 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <circle/logger.h>
#include <cstring>
#include "udpmididevice.h"
#include <assert.h>
//#define VIRTUALCABLE 24
LOGMODULE("rtpmididevice");
CUDPMIDIDevice::CUDPMIDIDevice (CMiniDexed *pSynthesizer,
CConfig *pConfig, CUserInterface *pUI)
: CMIDIDevice (pSynthesizer, pConfig, pUI),
m_pSynthesizer (pSynthesizer),
m_pConfig (pConfig)
//m_Serial (pInterrupt, TRUE),
//m_nSerialState (0),
//m_nSysEx (0),
//m_SendBuffer (&m_Serial)
{
AddDevice ("udp");
/*for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
m_ChannelMap[nTG] = Disabled;
}*/
}
CUDPMIDIDevice::~CUDPMIDIDevice (void)
{
m_pSynthesizer = 0;
}
boolean CUDPMIDIDevice::Initialize (void)
{
m_pAppleMIDIParticipant = new CAppleMIDIParticipant(&m_Random, this);
if (!m_pAppleMIDIParticipant->Initialize())
{
LOGERR("Failed to init RTP listener");
return false; //continue without rtp midi
}
else
LOGNOTE("RTP Listener initialized");
return true;
m_pUDPMIDIReceiver = new CUDPMIDIReceiver(this);
if (!m_pUDPMIDIReceiver->Initialize())
{
LOGERR("Failed to init UDP MIDI receiver");
delete m_pUDPMIDIReceiver;
m_pUDPMIDIReceiver = nullptr;
}
else
LOGNOTE("UDP MIDI receiver initialized");
}
// Methods to handle MIDI events
void CUDPMIDIDevice::OnAppleMIDIDataReceived(const u8* pData, size_t nSize)
{
LOGNOTE("Recieved RTPUDP MIDI Data");
printf ("MIDI-RTP: %02X %02X\n",
(unsigned) pData[0], (unsigned) pData[1]);
MIDIMessageHandler(pData, nSize);
}
void CUDPMIDIDevice::OnAppleMIDIConnect(const CIPAddress* pIPAddress, const char* pName)
{
LOGNOTE("RTP Device connected");
//AddDevice ("udp1");
}
void CUDPMIDIDevice::OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pName)
{
LOGNOTE("RTP Device disconnected");
}
void CUDPMIDIDevice::OnUDPMIDIDataReceived(const u8* pData, size_t nSize)
{
LOGNOTE("Recieved UDP MIDI Data");
printf ("MIDI-UDP: %02X %02X\n",
(unsigned) pData[0], (unsigned) pData[1]);
MIDIMessageHandler(pData, nSize);
}

@ -0,0 +1,76 @@
//
// udpmididevice.h
//
// Virtual midi device for data recieved on network
//
// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi
// Copyright (C) 2022 The MiniDexed Team
//
// Original author of this class:
// R. Stange <rsta2@o2online.de>
//
// 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _rtpmididevice_h
#define _rtpmididevice_h
#include "mididevice.h"
#include "config.h"
#include "net/applemidi.h"
#include "net/udpmidi.h"
//#include <circle/interrupt.h>
//#include <circle/serial.h>
//#include <circle/writebuffer.h>
//#include <circle/types.h>
class CMiniDexed;
class CUDPMIDIDevice : CAppleMIDIHandler, CUDPMIDIHandler, public CMIDIDevice
{
public:
CUDPMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI);
~CUDPMIDIDevice (void);
boolean Initialize (void);
virtual void OnAppleMIDIDataReceived(const u8* pData, size_t nSize) override;
virtual void OnAppleMIDIConnect(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;
//void OnAppleMIDIDataReceived(const u8* pData, size_t nSize);
//void OnAppleMIDIConnect(const CIPAddress* pIPAddress, const char* pName);
//void OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pName);
//void Process (void);
//void Send (const u8 *pMessage, size_t nLength, unsigned nCable = 0) override;
private:
CMiniDexed *m_pSynthesizer;
CConfig *m_pConfig;
//u8 m_ChannelMap[CConfig::ToneGenerators];
//CSerialDevice m_Serial;
//unsigned m_nSerialState;
//unsigned m_nSysEx;
//u8 m_SerialMessage[MAX_MIDI_MESSAGE];
//CWriteBufferDevice m_SendBuffer;
CBcmRandomNumberGenerator m_Random;
//CAppleMIDIHandler* m_MIDIHandler;
CAppleMIDIParticipant* m_pAppleMIDIParticipant; // AppleMIDI participant instance
CUDPMIDIReceiver* m_pUDPMIDIReceiver;
};
#endif
Loading…
Cancel
Save