mirror of
https://github.com/dwhinham/mt32-pi.git
synced 2026-07-08 17:56:16 +00:00
WIP: Pisound TX working, but not at correct clock
TODO: Fix synth routing path
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
# Valid options: pi3, pi3-64, pi4, pi4-64
|
||||
BOARD?=pi3-64
|
||||
HDMI_CONSOLE?=0
|
||||
HDMI_CONSOLE?=1
|
||||
|
||||
# Serial bootloader config
|
||||
SERIALPORT?=/dev/ttyUSB0
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <circle/gpiomanager.h>
|
||||
#include <circle/i2cmaster.h>
|
||||
#include <circle/sched/scheduler.h>
|
||||
#include <circle/spimaster.h>
|
||||
#include <circle/spimasterdma.h>
|
||||
#include <circle/timer.h>
|
||||
|
||||
#include "config.h"
|
||||
@@ -58,7 +58,7 @@ protected:
|
||||
FATFS m_SDFileSystem;
|
||||
|
||||
CI2CMaster m_I2CMaster;
|
||||
CSPIMaster m_SPIMaster;
|
||||
CSPIMasterDMA m_SPIMaster;
|
||||
CGPIOManager m_GPIOManager;
|
||||
|
||||
private:
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include <circle/net/netsubsystem.h>
|
||||
#include <circle/sched/scheduler.h>
|
||||
#include <circle/sound/soundbasedevice.h>
|
||||
#include <circle/spimaster.h>
|
||||
#include <circle/spimasterdma.h>
|
||||
#include <circle/timer.h>
|
||||
#include <circle/types.h>
|
||||
#include <circle/usb/usbhcidevice.h>
|
||||
@@ -69,7 +69,7 @@
|
||||
class CMT32Pi : CMultiCoreSupport, CPower, CMIDIParser, CAppleMIDIHandler, CUDPMIDIHandler
|
||||
{
|
||||
public:
|
||||
CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI);
|
||||
CMT32Pi(CI2CMaster* pI2CMaster, CSPIMasterDMA* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI);
|
||||
virtual ~CMT32Pi() override;
|
||||
|
||||
bool Initialize(bool bSerialMIDIAvailable = true);
|
||||
@@ -148,7 +148,7 @@ private:
|
||||
CActLED* m_pActLED;
|
||||
|
||||
CI2CMaster* m_pI2CMaster;
|
||||
CSPIMaster* m_pSPIMaster;
|
||||
CSPIMasterDMA* m_pSPIMaster;
|
||||
CInterruptSystem* m_pInterrupt;
|
||||
CGPIOManager* m_pGPIOManager;
|
||||
CSerialDevice* m_pSerial;
|
||||
|
||||
@@ -25,23 +25,28 @@
|
||||
|
||||
#include <circle/gpiomanager.h>
|
||||
#include <circle/gpiopin.h>
|
||||
#include <circle/spimaster.h>
|
||||
#include <circle/sched/task.h>
|
||||
#include <circle/spimasterdma.h>
|
||||
#include <circle/spinlock.h>
|
||||
#include <circle/types.h>
|
||||
|
||||
#include "ringbuffer.h"
|
||||
|
||||
class CPisound
|
||||
class CPisound : protected CTask
|
||||
{
|
||||
public:
|
||||
using TMIDIReceiveHandler = void (*)(const u8* pData, size_t nSize);
|
||||
|
||||
CPisound(CSPIMaster* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate);
|
||||
~CPisound();
|
||||
CPisound(CSPIMasterDMA* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate);
|
||||
virtual ~CPisound() override;
|
||||
|
||||
bool Initialize();
|
||||
void RegisterMIDIReceiveHandler(TMIDIReceiveHandler pHandler) { m_pReceiveHandler = pHandler; }
|
||||
size_t SendMIDI(const u8* pData, size_t nSize);
|
||||
|
||||
virtual void Run() override;
|
||||
void DoTransfer();
|
||||
|
||||
private:
|
||||
u16 Transfer16(u16 nValue) const;
|
||||
size_t ReadBytes(u8* pOutBuffer, size_t nSize) const;
|
||||
@@ -53,7 +58,8 @@ private:
|
||||
static constexpr size_t MaxIDStringLength = 25;
|
||||
static constexpr size_t MaxVersionStringLength = 6;
|
||||
|
||||
CSPIMaster* m_pSPIMaster;
|
||||
CSpinLock m_Lock;
|
||||
CSPIMasterDMA* m_pSPIMaster;
|
||||
unsigned m_nSamplerate;
|
||||
|
||||
CGPIOPin m_SPIReset;
|
||||
|
||||
@@ -42,6 +42,7 @@ CKernel::CKernel(void)
|
||||
m_SDFileSystem{},
|
||||
|
||||
m_I2CMaster(1, true),
|
||||
m_SPIMaster(&mInterrupt),
|
||||
m_GPIOManager(&mInterrupt),
|
||||
|
||||
m_MT32Pi(&m_I2CMaster, &m_SPIMaster, &mInterrupt, &m_GPIOManager, &m_Serial, &m_USBHCI)
|
||||
|
||||
@@ -41,6 +41,8 @@ void CMIDIParser::ParseMIDIBytes(const u8* pData, size_t nSize, bool bIgnoreNote
|
||||
{
|
||||
u8 nByte = pData[i];
|
||||
|
||||
//CLogger::Get()->Write(MIDIParserName, LogNotice, "0x%02X", nByte);
|
||||
|
||||
// System Real-Time message - single byte, handle immediately
|
||||
// Can appear anywhere in the stream, even in between status/data bytes
|
||||
if (nByte >= 0xF8)
|
||||
|
||||
@@ -59,7 +59,7 @@ enum class TCustomSysExCommand : u8
|
||||
|
||||
CMT32Pi* CMT32Pi::s_pThis = nullptr;
|
||||
|
||||
CMT32Pi::CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI)
|
||||
CMT32Pi::CMT32Pi(CI2CMaster* pI2CMaster, CSPIMasterDMA* pSPIMaster, CInterruptSystem* pInterrupt, CGPIOManager* pGPIOManager, CSerialDevice* pSerialDevice, CUSBHCIDevice* pUSBHCI)
|
||||
: CMultiCoreSupport(CMemorySystem::Get()),
|
||||
CMIDIParser(),
|
||||
|
||||
@@ -712,6 +712,29 @@ void CMT32Pi::OnAppleMIDIDisconnect(const CIPAddress* pIPAddress, const char* pN
|
||||
LCDLog(TLCDLogType::Notice, "%s disconnected!", pName);
|
||||
}
|
||||
|
||||
void PrintRouting(const char* pDevice, u8 nRouting)
|
||||
{
|
||||
char Buffer[256];
|
||||
sprintf(Buffer, " - %-10s ->", pDevice);
|
||||
|
||||
if (nRouting == TMIDIRoutingDest::None)
|
||||
{
|
||||
strcat(Buffer, " none");
|
||||
LOGNOTE(Buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nRouting & TMIDIRoutingDest::Synth) strncat(Buffer, " synth", sizeof(Buffer));
|
||||
if (nRouting & TMIDIRoutingDest::GPIO) strncat(Buffer, " gpio", sizeof(Buffer));
|
||||
if (nRouting & TMIDIRoutingDest::Pisound) strncat(Buffer, " pisound", sizeof(Buffer));
|
||||
if (nRouting & TMIDIRoutingDest::USBMIDI) strncat(Buffer, " usb_midi", sizeof(Buffer));
|
||||
if (nRouting & TMIDIRoutingDest::USBSerial) strncat(Buffer, " usb_serial", sizeof(Buffer));
|
||||
if (nRouting & TMIDIRoutingDest::RTP) strncat(Buffer, " rtp", sizeof(Buffer));
|
||||
if (nRouting & TMIDIRoutingDest::UDP) strncat(Buffer, " udp", sizeof(Buffer));
|
||||
|
||||
LOGNOTE(Buffer);
|
||||
}
|
||||
|
||||
bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
|
||||
{
|
||||
if (nSize < 4)
|
||||
@@ -745,6 +768,15 @@ bool CMT32Pi::ParseCustomSysEx(const u8* pData, size_t nSize)
|
||||
case 4: m_pConfig->MIDIRouteRTP = nRouting; break;
|
||||
case 5: m_pConfig->MIDIRouteUDP = nRouting; break;
|
||||
}
|
||||
|
||||
LOGNOTE("MIDI routing setup:");
|
||||
PrintRouting("gpio", m_pConfig->MIDIRouteGPIO);
|
||||
PrintRouting("usb_midi", m_pConfig->MIDIRouteUSBMIDI);
|
||||
PrintRouting("usb_serial", m_pConfig->MIDIRouteUSBSerial);
|
||||
PrintRouting("pisound", m_pConfig->MIDIRoutePisound);
|
||||
PrintRouting("rtp", m_pConfig->MIDIRouteRTP);
|
||||
PrintRouting("udp", m_pConfig->MIDIRouteUDP);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1032,7 +1064,7 @@ void CMT32Pi::ProcessMIDIRouting(const TMIDIRouting& Routing, const u8* pData, s
|
||||
}
|
||||
|
||||
// Send to GPIO UART
|
||||
if (Routing & GPIO && m_bSerialMIDIEnabled)
|
||||
if ((Routing & GPIO) && m_bSerialMIDIEnabled)
|
||||
{
|
||||
const int nSendResult = m_pSerial->Write(pData, nSize);
|
||||
if (nSendResult < 0 || static_cast<size_t>(nSendResult) != nSize)
|
||||
@@ -1043,44 +1075,44 @@ void CMT32Pi::ProcessMIDIRouting(const TMIDIRouting& Routing, const u8* pData, s
|
||||
}
|
||||
|
||||
// Send to Pisound
|
||||
if (Routing & Pisound && m_pPisound)
|
||||
if ((Routing & Pisound) && m_pPisound)
|
||||
{
|
||||
if (m_pPisound->SendMIDI(pData, nSize) != nSize)
|
||||
{
|
||||
m_pLogger->Write(MT32PiName, LogError, "Pisound MIDI TX error");
|
||||
LOGERR("Pisound MIDI TX error");
|
||||
LCDLog(TLCDLogType::Error, "Pisound MIDI TX error!");
|
||||
}
|
||||
}
|
||||
|
||||
// Send to USB MIDI
|
||||
if (Routing & USBMIDI && m_pUSBMIDIDevice)
|
||||
if ((Routing & USBMIDI) && m_pUSBMIDIDevice)
|
||||
{
|
||||
if (m_pUSBMIDIDevice->SendPlainMIDI(0, pData, nSize) == false)
|
||||
{
|
||||
m_pLogger->Write(MT32PiName, LogError, "USB MIDI TX error");
|
||||
LOGERR("USB MIDI TX error");
|
||||
LCDLog(TLCDLogType::Error, "USB MIDI TX error!");
|
||||
}
|
||||
}
|
||||
|
||||
// Send to USB serial
|
||||
if (Routing & USBSerial && m_pUSBSerialDevice)
|
||||
if ((Routing & USBSerial) && m_pUSBSerialDevice)
|
||||
{
|
||||
const int nSendResult = m_pUSBSerialDevice->Write(pData, nSize);
|
||||
if (nSendResult < 0 || static_cast<size_t>(nSendResult) != nSize)
|
||||
{
|
||||
m_pLogger->Write(MT32PiName, LogError, "USB serial TX error %d (wanted to send %d bytes)", nSendResult, nSize);
|
||||
LOGERR("USB serial TX error %d (wanted to send %d bytes)", nSendResult, nSize);
|
||||
LCDLog(TLCDLogType::Error, "USB serial TX error!");
|
||||
}
|
||||
}
|
||||
|
||||
// Send to AppleMIDI
|
||||
if (Routing & RTP && m_pAppleMIDIParticipant)
|
||||
if ((Routing & RTP) && m_pAppleMIDIParticipant)
|
||||
{
|
||||
// TODO: AppleMIDIParticipant has no transmit functionality
|
||||
}
|
||||
|
||||
// Send to UDP MIDI
|
||||
if (Routing & UDP && m_pUDPMIDIReceiver)
|
||||
if ((Routing & UDP) && m_pUDPMIDIReceiver)
|
||||
{
|
||||
// TODO: UDPMIDIReceiver has no transmit functionality
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <circle/logger.h>
|
||||
#include <circle/timer.h>
|
||||
#include <circle/util.h>
|
||||
#include <circle/sched/scheduler.h>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
@@ -31,8 +32,8 @@
|
||||
LOGMODULE("pisound");
|
||||
|
||||
constexpr u8 SPIChipSelect = 0;
|
||||
constexpr u8 SPIDelayMicroseconds = 10;
|
||||
constexpr u32 SPIClockSpeed = 150000;
|
||||
// FIXME: Linux driver works at 150000Hz, but here we get data corruption hence lower clock speed - investigate
|
||||
constexpr u32 SPIClockSpeed = 15000;
|
||||
constexpr u8 SPITransferSize = 4;
|
||||
|
||||
constexpr u8 GPIOButton = 17;
|
||||
@@ -48,8 +49,10 @@ constexpr u8 GPIOSPIDataAvailable = 25;
|
||||
// Based on: https://github.com/raspberrypi/linux/blob/rpi-5.4.y/sound/soc/bcm/pisound.c
|
||||
// https://github.com/raspberrypi/linux/blob/rpi-5.4.y/arch/arm/boot/dts/overlays/pisound-overlay.dts
|
||||
|
||||
CPisound::CPisound(CSPIMaster* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate)
|
||||
: m_pSPIMaster(pSPIMaster),
|
||||
CPisound::CPisound(CSPIMasterDMA* pSPIMaster, CGPIOManager* pGPIOManager, unsigned nSamplerate)
|
||||
: CTask(TASK_STACK_SIZE, true),
|
||||
m_Lock(IRQ_LEVEL),
|
||||
m_pSPIMaster(pSPIMaster),
|
||||
m_nSamplerate(nSamplerate),
|
||||
|
||||
m_SPIReset(GPIOSPIReset, TGPIOMode::GPIOModeOutput),
|
||||
@@ -80,6 +83,8 @@ CPisound::~CPisound()
|
||||
m_OversamplingRatio0.SetMode(TGPIOMode::GPIOModeInputPullDown);
|
||||
m_OversamplingRatio1.SetMode(TGPIOMode::GPIOModeInputPullDown);
|
||||
m_OversamplingRatio2.SetMode(TGPIOMode::GPIOModeInputPullDown);
|
||||
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool CPisound::Initialize()
|
||||
@@ -106,7 +111,6 @@ bool CPisound::Initialize()
|
||||
}
|
||||
|
||||
// Setup SPI
|
||||
m_pSPIMaster->SetCSHoldTime(SPIDelayMicroseconds);
|
||||
m_pSPIMaster->SetClock(SPIClockSpeed);
|
||||
|
||||
// Reset the SPI device
|
||||
@@ -131,15 +135,18 @@ bool CPisound::Initialize()
|
||||
LOGNOTE("Firmware version: %s", m_FirmwareVersion);
|
||||
LOGNOTE("Hardware version: %s", m_HardwareVersion);
|
||||
|
||||
Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CPisound::SendMIDI(const u8* pData, size_t nSize)
|
||||
{
|
||||
// TODO
|
||||
m_MIDITxBuffer.Enqueue(pData, nSize);
|
||||
SPITask();
|
||||
return nSize;
|
||||
// Wake up task
|
||||
if (IsSuspended())
|
||||
Resume();
|
||||
|
||||
return m_MIDITxBuffer.Enqueue(pData, nSize);
|
||||
}
|
||||
|
||||
u16 CPisound::Transfer16(u16 nTxValue) const
|
||||
@@ -149,7 +156,7 @@ u16 CPisound::Transfer16(u16 nTxValue) const
|
||||
SPITxBuffer[0] = nTxValue >> 8;
|
||||
SPITxBuffer[1] = nTxValue & 0xFF;
|
||||
|
||||
if (m_pSPIMaster->WriteRead(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer)) < 0)
|
||||
if (m_pSPIMaster->WriteReadSync(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer)) < 0)
|
||||
return 0;
|
||||
|
||||
return (SPIRxBuffer[0] << 8) | SPIRxBuffer[1];
|
||||
@@ -246,11 +253,16 @@ void CPisound::SetOSRPins(unsigned bRatio1, unsigned bRatio2, unsigned bRatio3)
|
||||
void CPisound::DataAvailableInterruptHandler(void* pUserData)
|
||||
{
|
||||
CPisound* pThis = static_cast<CPisound*>(pUserData);
|
||||
pThis->SPITask();
|
||||
if (pThis->IsSuspended())
|
||||
pThis->Resume();
|
||||
}
|
||||
|
||||
void CPisound::SPITask()
|
||||
void CPisound::DoTransfer()
|
||||
{
|
||||
bool bHadData = false;
|
||||
|
||||
m_Lock.Acquire();
|
||||
|
||||
do
|
||||
{
|
||||
size_t nMIDIRxBytes = 0;
|
||||
@@ -260,6 +272,8 @@ void CPisound::SPITask()
|
||||
|
||||
u8 SPIRxBuffer[SPITransferSize];
|
||||
u8 SPITxBuffer[SPITransferSize];
|
||||
|
||||
bHadData = false;
|
||||
memset(SPIRxBuffer, 0, sizeof(SPIRxBuffer));
|
||||
memset(SPITxBuffer, 0, sizeof(SPITxBuffer));
|
||||
|
||||
@@ -272,15 +286,39 @@ void CPisound::SPITask()
|
||||
}
|
||||
|
||||
// Extract incoming MIDI bytes from SPI packet
|
||||
m_pSPIMaster->WriteRead(SPIChipSelect, SPITxBuffer, SPIRxBuffer, sizeof(SPIRxBuffer));
|
||||
for (size_t i = 0; i < sizeof(SPIRxBuffer); i += 2)
|
||||
if (m_pSPIMaster->WriteReadSync(SPIChipSelect, SPITxBuffer, SPIRxBuffer, SPITransferSize) != SPITransferSize)
|
||||
{
|
||||
LOGERR("SPI transfer failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < SPITransferSize; i += 2)
|
||||
{
|
||||
if (SPIRxBuffer[i])
|
||||
{
|
||||
MIDIRxBuffer[nMIDIRxBytes++] = SPIRxBuffer[i + 1];
|
||||
bHadData = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass MIDI bytes on to handler
|
||||
if (nMIDIRxBytes && m_pReceiveHandler)
|
||||
m_pReceiveHandler(MIDIRxBuffer, nMIDIRxBytes);
|
||||
} while (m_DataAvailable.Read() == HIGH || !m_MIDITxBuffer.IsEmpty());
|
||||
} while (bHadData || !m_MIDITxBuffer.IsEmpty() || m_DataAvailable.Read() == HIGH);
|
||||
|
||||
m_Lock.Release();
|
||||
}
|
||||
|
||||
void CPisound::Run()
|
||||
{
|
||||
CScheduler* const pScheduler = CScheduler::Get();
|
||||
|
||||
while (true)
|
||||
{
|
||||
DoTransfer();
|
||||
Suspend();
|
||||
|
||||
// Allow other tasks to run
|
||||
pScheduler->Yield();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user