From 08be352d242512898b2517615daebd4135ad20c1 Mon Sep 17 00:00:00 2001 From: probonopd Date: Wed, 23 Feb 2022 18:46:52 +0100 Subject: [PATCH] Move RPi code from Synth_Dexed to MiniDexed, thanks @rsta2 Move the Raspberry Pi specific code from the classes AudioSynthDexed* from the Synth_Dexed project to the MiniDexed project https://github.com/probonopd/MiniDexed/issues/8#issuecomment-1048590076 --- Synth_Dexed | 2 +- build.sh | 1 + src/Makefile | 2 +- src/kernel.cpp | 7 +- src/kernel.h | 4 +- src/minidexed.cpp | 232 +++++++++++++++++++++++++++++++++++++++++++++ src/minidexed.h | 97 +++++++++++++++++++ src/pckeyboard.cpp | 186 ++++++++++++++++++++++++++++++++++++ src/pckeyboard.h | 56 +++++++++++ 9 files changed, 579 insertions(+), 8 deletions(-) create mode 100644 src/minidexed.cpp create mode 100644 src/minidexed.h create mode 100644 src/pckeyboard.cpp create mode 100644 src/pckeyboard.h diff --git a/Synth_Dexed b/Synth_Dexed index cdaf1f6..86dba98 160000 --- a/Synth_Dexed +++ b/Synth_Dexed @@ -1 +1 @@ -Subproject commit cdaf1f6cdeb38e9f5cfb6f2d8bb93d7933153ab6 +Subproject commit 86dba9826c21295e72a8a38b17e1f2c001579310 diff --git a/build.sh b/build.sh index bd26acc..9ffcd89 100755 --- a/build.sh +++ b/build.sh @@ -18,6 +18,7 @@ fi cd circle-stdlib/ make mrproper || true ./configure -r ${RPI} --prefix "${TOOLCHAIN_PREFIX}" +echo "DEFINE += -DSAVE_VFP_REGS_ON_IRQ" >> libs/circle/Config.mk make -j$(nproc) cd .. diff --git a/src/Makefile b/src/Makefile index 62c9eea..c6e053f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,7 +5,7 @@ CIRCLE_STDLIB_DIR = ../circle-stdlib SYNTH_DEXED_DIR = ../Synth_Dexed/src -OBJS = main.o kernel.o $(SYNTH_DEXED_DIR)/synth_dexed.o +OBJS = main.o kernel.o minidexed.o pckeyboard.o $(SYNTH_DEXED_DIR)/synth_dexed.o INCLUDE += -I $(SYNTH_DEXED_DIR) diff --git a/src/kernel.cpp b/src/kernel.cpp index effb7a4..674d124 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -3,7 +3,6 @@ // #include "kernel.h" #include -#include #include #include @@ -48,19 +47,19 @@ bool CKernel::Initialize (void) { LOGNOTE ("I2S mode"); - m_pDexed = new AudioSynthDexedI2S (16, SAMPLE_RATE, &mInterrupt, &m_I2CMaster); + m_pDexed = new CMiniDexedI2S (16, SAMPLE_RATE, &mInterrupt, &m_I2CMaster); } else if (strcmp (pSoundDevice, "sndhdmi") == 0) { LOGNOTE ("HDMI mode"); - m_pDexed = new AudioSynthDexedHDMI (16, SAMPLE_RATE, &mInterrupt); + m_pDexed = new CMiniDexedHDMI (16, SAMPLE_RATE, &mInterrupt); } else { LOGNOTE ("PWM mode"); - m_pDexed = new AudioSynthDexedPWM (16, SAMPLE_RATE, &mInterrupt); + m_pDexed = new CMiniDexedPWM (16, SAMPLE_RATE, &mInterrupt); } if (!m_pDexed->Initialize ()) diff --git a/src/kernel.h b/src/kernel.h index 5d4aa56..1018013 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -6,7 +6,7 @@ #include "circle_stdlib_app.h" #include -#include "synth_dexed.h" +#include "minidexed.h" enum TShutdownMode { @@ -28,7 +28,7 @@ public: private: // do not change this order CI2CMaster m_I2CMaster; - AudioSynthDexed *m_pDexed; + CMiniDexed *m_pDexed; }; #endif diff --git a/src/minidexed.cpp b/src/minidexed.cpp new file mode 100644 index 0000000..572fd2b --- /dev/null +++ b/src/minidexed.cpp @@ -0,0 +1,232 @@ +// +// minidexed.cpp +// +#include "minidexed.h" +#include + +#define MIDI_NOTE_OFF 0b1000 +#define MIDI_NOTE_ON 0b1001 + +CMiniDexed *CMiniDexed::s_pThis = 0; + +bool CMiniDexed::Initialize (void) +{ + if (!m_Serial.Initialize(31250)) + { + return false; + } + + m_bUseSerial = true; + + activate(); + + return true; +} + +void CMiniDexed::Process(boolean bPlugAndPlayUpdated) +{ + if (m_pMIDIDevice != 0) + { + return; + } + + if (bPlugAndPlayUpdated) + { + m_pMIDIDevice = + (CUSBMIDIDevice *) CDeviceNameService::Get ()->GetDevice ("umidi1", FALSE); + if (m_pMIDIDevice != 0) + { + m_pMIDIDevice->RegisterRemovedHandler (USBDeviceRemovedHandler); + m_pMIDIDevice->RegisterPacketHandler (MIDIPacketHandler); + + return; + } + } + + m_PCKeyboard.Process (bPlugAndPlayUpdated); + + if (!m_bUseSerial) + { + return; + } + + // Read serial MIDI data + u8 Buffer[20]; + int nResult = m_Serial.Read (Buffer, sizeof Buffer); + if (nResult <= 0) + { + return; + } + + // Process MIDI messages + // See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message + for (int i = 0; i < nResult; i++) + { + u8 uchData = Buffer[i]; + + switch (m_nSerialState) + { + case 0: + MIDIRestart: + if ((uchData & 0xE0) == 0x80) // Note on or off, all channels + { + m_SerialMessage[m_nSerialState++] = uchData; + } + break; + + case 1: + case 2: + if (uchData & 0x80) // got status when parameter expected + { + m_nSerialState = 0; + + goto MIDIRestart; + } + + m_SerialMessage[m_nSerialState++] = uchData; + + if (m_nSerialState == 3) // message is complete + { + MIDIPacketHandler (0, m_SerialMessage, sizeof m_SerialMessage); + + m_nSerialState = 0; + } + break; + + default: + assert (0); + break; + } + } +} + +void CMiniDexed::MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength) +{ + assert (s_pThis != 0); + + // The packet contents are just normal MIDI data - see + // https://www.midi.org/specifications/item/table-1-summary-of-midi-message + + if (nLength < 3) + { + return; + } + + u8 ucStatus = pPacket[0]; + //u8 ucChannel = ucStatus & 0x0F; + u8 ucType = ucStatus >> 4; + u8 ucKeyNumber = pPacket[1]; + u8 ucVelocity = pPacket[2]; + + if (ucType == MIDI_NOTE_ON) + { + s_pThis->keydown(ucKeyNumber,ucVelocity); + } + else if (ucType == MIDI_NOTE_OFF) + { + s_pThis->keyup(ucKeyNumber); + } +} + +void CMiniDexed::USBDeviceRemovedHandler (CDevice *pDevice, void *pContext) +{ + if (s_pThis->m_pMIDIDevice == (CUSBMIDIDevice *) pDevice) + { + s_pThis->m_pMIDIDevice = 0; + } +} + +bool CMiniDexedPWM::Initialize (void) +{ + if (!CMiniDexed::Initialize()) + { + return false; + } + + return Start (); +} + +unsigned CMiniDexedPWM::GetChunk(u32 *pBuffer, unsigned nChunkSize) +{ + unsigned nResult = nChunkSize; + + int16_t int16_buf[nChunkSize/2]; + + getSamples(nChunkSize/2, int16_buf); + + for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer + { + s32 nSample = int16_buf[i++]; + nSample += 32768; + nSample *= GetRangeMax()/2; + nSample /= 32768; + + *pBuffer++ = nSample; // 2 stereo channels + *pBuffer++ = nSample; + } + return(nResult); +}; + +bool CMiniDexedI2S::Initialize (void) +{ + if (!CMiniDexed::Initialize()) + { + return false; + } + + return Start (); +} + +unsigned CMiniDexedI2S::GetChunk(u32 *pBuffer, unsigned nChunkSize) +{ + unsigned nResult = nChunkSize; + + int16_t int16_buf[nChunkSize/2]; + + getSamples(nChunkSize/2, int16_buf); + + for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer + { + s32 nSample = int16_buf[i++]; + nSample <<= 8; + + *pBuffer++ = nSample; // 2 stereo channels + *pBuffer++ = nSample; + } + return(nResult); +}; + +bool CMiniDexedHDMI::Initialize (void) +{ + if (!CMiniDexed::Initialize()) + { + return false; + } + + return Start (); +} + +unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize) +{ + unsigned nResult = nChunkSize; + + int16_t int16_buf[nChunkSize/2]; + unsigned nFrame = 0; + + getSamples(nChunkSize/2, int16_buf); + + for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer + { + s32 nSample = int16_buf[i++]; + nSample <<= 8; + + nSample = ConvertIEC958Sample (nSample, nFrame); + + if (++nFrame == IEC958_FRAMES_PER_BLOCK) + nFrame = 0; + + *pBuffer++ = nSample; // 2 stereo channels + *pBuffer++ = nSample; + } + return(nResult); +}; diff --git a/src/minidexed.h b/src/minidexed.h new file mode 100644 index 0000000..251addc --- /dev/null +++ b/src/minidexed.h @@ -0,0 +1,97 @@ +// +// minidexed.h +// +#ifndef _minidexed_h +#define _minidexed_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pckeyboard.h" + +#define SAMPLE_RATE 48000 + +#define CHUNK_SIZE 2048 +#define CHUNK_SIZE_HDMI (384 * 10) + +#define DAC_I2C_ADDRESS 0 // I2C slave address of the DAC (0 for auto probing) + +class CMiniDexed : public Dexed +{ + public: + CMiniDexed(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) +: Dexed(max_notes,(int)sample_rate), + m_pMIDIDevice (0), + m_PCKeyboard (this), + m_Serial (pInterrupt, TRUE), + m_bUseSerial (FALSE), + m_nSerialState (0) + { + s_pThis = this; + }; + + virtual bool Initialize (void); + void Process(boolean bPlugAndPlayUpdated); + + protected: + static void MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength); + static void KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]); + static void USBDeviceRemovedHandler (CDevice *pDevice, void *pContext); + CUSBMIDIDevice * volatile m_pMIDIDevice; + CPCKeyboard m_PCKeyboard; + CSerialDevice m_Serial; + boolean m_bUseSerial; + unsigned m_nSerialState; + u8 m_SerialMessage[3]; + + static CMiniDexed *s_pThis; +}; + +class CMiniDexedPWM : public CMiniDexed, public CPWMSoundBaseDevice +{ + public: + CMiniDexedPWM(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) +: CMiniDexed(max_notes,(int)sample_rate, pInterrupt), + CPWMSoundBaseDevice (pInterrupt, sample_rate, CHUNK_SIZE) + { + } + + bool Initialize (void); + unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); +}; + +class CMiniDexedI2S : public CMiniDexed, public CI2SSoundBaseDevice +{ + public: + CMiniDexedI2S(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt, CI2CMaster *pI2CMaster) +: CMiniDexed(max_notes,(int)sample_rate, pInterrupt), + CI2SSoundBaseDevice (pInterrupt, sample_rate, CHUNK_SIZE, FALSE, pI2CMaster, DAC_I2C_ADDRESS) + { + } + + bool Initialize (void); + unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); +}; + +class CMiniDexedHDMI : public CMiniDexed, public CHDMISoundBaseDevice +{ + public: + CMiniDexedHDMI(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) +: CMiniDexed(max_notes,(int)sample_rate, pInterrupt), + CHDMISoundBaseDevice (pInterrupt, sample_rate, CHUNK_SIZE_HDMI) + { + } + + bool Initialize (void); + unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); +}; + +#endif diff --git a/src/pckeyboard.cpp b/src/pckeyboard.cpp new file mode 100644 index 0000000..153cc31 --- /dev/null +++ b/src/pckeyboard.cpp @@ -0,0 +1,186 @@ +// +// pckeyboard.cpp +// +// MiniSynth Pi - A virtual analogue synthesizer for Raspberry Pi +// Copyright (C) 2017-2020 R. Stange +// +// 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 . +// +#include "pckeyboard.h" +#include "minidexed.h" +#include +#include +#include + +struct TKeyInfo +{ + char KeyCode; // upper case letter or digit + u8 KeyNumber; // MIDI number +}; + +// KeyCode is valid for standard QWERTY keyboard +static TKeyInfo KeyTable[] = +{ + {',', 72}, // C4 + {'M', 71}, // B4 + {'J', 70}, // A#4 + {'N', 69}, // A4 + {'H', 68}, // G#3 + {'B', 67}, // G3 + {'G', 66}, // F#3 + {'V', 65}, // F3 + {'C', 64}, // E3 + {'D', 63}, // D#3 + {'X', 62}, // D3 + {'S', 61}, // C#3 + {'Z', 60}, // C3 + {'U', 59}, // B3 + {'7', 58}, // A#3 + {'Y', 57}, // A3 + {'6', 56}, // G#2 + {'T', 55}, // G2 + {'5', 54}, // F#2 + {'R', 53}, // F2 + {'E', 52}, // E2 + {'3', 51}, // D#2 + {'W', 50}, // D2 + {'2', 49}, // C#2 + {'Q', 48} // C2 +}; + +CPCKeyboard *CPCKeyboard::s_pThis = 0; + +CPCKeyboard::CPCKeyboard (CMiniDexed *pSynthesizer) +: m_pSynthesizer (pSynthesizer), + m_pKeyboard (0) +{ + s_pThis = this; + + memset (m_LastKeys, 0, sizeof m_LastKeys); +} + +CPCKeyboard::~CPCKeyboard (void) +{ + m_pSynthesizer = 0; + + s_pThis = 0; +} + +void CPCKeyboard::Process (boolean bPlugAndPlayUpdated) +{ + if (!bPlugAndPlayUpdated) + { + return; + } + + if (m_pKeyboard == 0) + { + m_pKeyboard = + (CUSBKeyboardDevice *) CDeviceNameService::Get ()->GetDevice ("ukbd1", FALSE); + if (m_pKeyboard != 0) + { + m_pKeyboard->RegisterKeyStatusHandlerRaw (KeyStatusHandlerRaw); + + m_pKeyboard->RegisterRemovedHandler (DeviceRemovedHandler); + } + } +} + +void CPCKeyboard::KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]) +{ + assert (s_pThis != 0); + assert (s_pThis->m_pSynthesizer != 0); + + // report released keys + for (unsigned i = 0; i < 6; i++) + { + u8 ucKeyCode = s_pThis->m_LastKeys[i]; + if ( ucKeyCode != 0 + && !FindByte (RawKeys, ucKeyCode, 6)) + { + u8 ucKeyNumber = GetKeyNumber (ucKeyCode); + if (ucKeyNumber != 0) + { + s_pThis->m_pSynthesizer->keyup (ucKeyNumber); + } + } + } + + // report pressed keys + for (unsigned i = 0; i < 6; i++) + { + u8 ucKeyCode = RawKeys[i]; + if ( ucKeyCode != 0 + && !FindByte (s_pThis->m_LastKeys, ucKeyCode, 6)) + { + u8 ucKeyNumber = GetKeyNumber (ucKeyCode); + if (ucKeyNumber != 0) + { + s_pThis->m_pSynthesizer->keydown (ucKeyNumber, 100); + } + } + } + + memcpy (s_pThis->m_LastKeys, RawKeys, sizeof s_pThis->m_LastKeys); +} + +u8 CPCKeyboard::GetKeyNumber (u8 ucKeyCode) +{ + char chKey; + if (0x04 <= ucKeyCode && ucKeyCode <= 0x1D) + { + chKey = ucKeyCode-'\x04'+'A'; // key code of 'A' is 0x04 + } + else if (0x1E <= ucKeyCode && ucKeyCode <= 0x26) + { + chKey = ucKeyCode-'\x1E'+'1'; // key code of '1' is 0x1E + } + else if (ucKeyCode == 0x36) + { + chKey = ','; // key code of ',' is 0x36 + } + else + { + return 0; + } + + for (unsigned i = 0; i < sizeof KeyTable / sizeof KeyTable[0]; i++) + { + if (KeyTable[i].KeyCode == chKey) + { + return KeyTable[i].KeyNumber; + } + } + + return 0; +} + +boolean CPCKeyboard::FindByte (const u8 *pBuffer, u8 ucByte, unsigned nLength) +{ + while (nLength-- > 0) + { + if (*pBuffer++ == ucByte) + { + return TRUE; + } + } + + return FALSE; +} + +void CPCKeyboard::DeviceRemovedHandler (CDevice *pDevice, void *pContext) +{ + assert (s_pThis != 0); + s_pThis->m_pKeyboard = 0; +} diff --git a/src/pckeyboard.h b/src/pckeyboard.h new file mode 100644 index 0000000..26fc991 --- /dev/null +++ b/src/pckeyboard.h @@ -0,0 +1,56 @@ +// +// pckeyboard.h +// +// MiniSynth Pi - A virtual analogue synthesizer for Raspberry Pi +// Copyright (C) 2017-2020 R. Stange +// +// 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 . +// +#ifndef _pckeyboard_h +#define _pckeyboard_h + +#include +#include +#include + +class CMiniDexed; + +class CPCKeyboard +{ +public: + CPCKeyboard (CMiniDexed *pSynthesizer); + ~CPCKeyboard (void); + + void Process (boolean bPlugAndPlayUpdated); + +private: + static void KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]); + + static u8 GetKeyNumber (u8 ucKeyCode); + + static boolean FindByte (const u8 *pBuffer, u8 ucByte, unsigned nLength); + + static void DeviceRemovedHandler (CDevice *pDevice, void *pContext); + +private: + CMiniDexed *m_pSynthesizer; + + CUSBKeyboardDevice * volatile m_pKeyboard; + + u8 m_LastKeys[6]; + + static CPCKeyboard *s_pThis; +}; + +#endif