From 314196e132bb34925a6c0b8c1e68a3cca6e23abe Mon Sep 17 00:00:00 2001 From: Rene Stange Date: Tue, 1 Mar 2022 11:29:25 +0100 Subject: [PATCH] Make synth parameters configurable * Add class CConfig, which holds the configuration * Add template config file minidexed.ini * Register panic handler in CKernel to allow to display assertions * Fix: Performance timer did not show correct percent value with HDMI --- build.sh | 3 ++ src/Makefile | 2 +- src/Rules.mk | 1 + src/config.cpp | 132 ++++++++++++++++++++++++++++++++++++++++++++++ src/config.h | 91 ++++++++++++++++++++++++++++++++ src/kernel.cpp | 32 ++++++++--- src/kernel.h | 9 +++- src/minidexed.cpp | 89 +++++++++++++------------------ src/minidexed.h | 61 ++++++++++----------- src/minidexed.ini | 26 +++++++++ 10 files changed, 353 insertions(+), 93 deletions(-) create mode 100644 src/config.cpp create mode 100644 src/config.h create mode 100644 src/minidexed.ini diff --git a/build.sh b/build.sh index 2d52b92..e529a66 100755 --- a/build.sh +++ b/build.sh @@ -26,6 +26,9 @@ make -j cd libs/circle/addon/display/ make clean || true make -j +cd ../Properties/ +make clean || true +make -j cd ../../../.. cd .. diff --git a/src/Makefile b/src/Makefile index 8204121..49e3361 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 minidexed.o pckeyboard.o sysexfileloader.o perftimer.o \ +OBJS = main.o kernel.o minidexed.o config.o pckeyboard.o sysexfileloader.o perftimer.o \ $(SYNTH_DEXED_DIR)/synth_dexed.o INCLUDE += -I $(SYNTH_DEXED_DIR) diff --git a/src/Rules.mk b/src/Rules.mk index 1b5ea06..3552503 100644 --- a/src/Rules.mk +++ b/src/Rules.mk @@ -18,6 +18,7 @@ LIBS += \ $(NEWLIBDIR)/lib/libc.a \ $(NEWLIBDIR)/lib/libcirclenewlib.a \ $(CIRCLEHOME)/addon/display/libdisplay.a \ + $(CIRCLEHOME)/addon/Properties/libproperties.a \ $(CIRCLEHOME)/addon/SDCard/libsdcard.a \ $(CIRCLEHOME)/lib/usb/libusb.a \ $(CIRCLEHOME)/lib/input/libinput.a \ diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..b9a7ec6 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,132 @@ +// +// config.cpp +// +// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi +// Copyright (C) 2022 The MiniDexed Team +// +// Original author of this class: +// 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 "config.h" + +CConfig::CConfig (FATFS *pFileSystem) +: m_Properties ("minidexed.ini", pFileSystem) +{ +} + +CConfig::~CConfig (void) +{ +} + +void CConfig::Load (void) +{ + m_Properties.Load (); + + m_SoundDevice = m_Properties.GetString ("SoundDevice", "pwm"); + + m_nSampleRate = m_Properties.GetNumber ("SampleRate", 48000); + m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 256); + m_nDACI2CAddress = m_Properties.GetNumber ("DACI2CAddress", 0); + + m_nMIDIBaudRate = m_Properties.GetNumber ("MIDIBaudRate", 31250); + + m_bLCDEnabled = m_Properties.GetNumber ("LCDEnabled", 0) != 0; + m_nLCDPinEnable = m_Properties.GetNumber ("LCDPinEnable", 17); + m_nLCDPinRegisterSelect = m_Properties.GetNumber ("LCDPinRegisterSelect", 18); + m_nLCDPinReadWrite = m_Properties.GetNumber ("LCDPinReadWrite", 19); + m_nLCDPinData4 = m_Properties.GetNumber ("LCDPinData4", 22); + m_nLCDPinData5 = m_Properties.GetNumber ("LCDPinData5", 23); + m_nLCDPinData6 = m_Properties.GetNumber ("LCDPinData6", 24); + m_nLCDPinData7 = m_Properties.GetNumber ("LCDPinData7", 25); + + m_bMIDIDumpEnabled = m_Properties.GetNumber ("MIDIDumpEnabled", 0) != 0; + m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0; +} + +const char *CConfig::GetSoundDevice (void) const +{ + return m_SoundDevice.c_str (); +} + +unsigned CConfig::GetSampleRate (void) const +{ + return m_nSampleRate; +} + +unsigned CConfig::GetChunkSize (void) const +{ + return m_nChunkSize; +} + +unsigned CConfig::GetDACI2CAddress (void) const +{ + return m_nDACI2CAddress; +} + +unsigned CConfig::GetMIDIBaudRate (void) const +{ + return m_nMIDIBaudRate; +} + +bool CConfig::GetLCDEnabled (void) const +{ + return m_bLCDEnabled; +} + +unsigned CConfig::GetLCDPinEnable (void) const +{ + return m_nLCDPinEnable; +} + +unsigned CConfig::GetLCDPinRegisterSelect (void) const +{ + return m_nLCDPinRegisterSelect; +} + +unsigned CConfig::GetLCDPinReadWrite (void) const +{ + return m_nLCDPinReadWrite; +} + +unsigned CConfig::GetLCDPinData4 (void) const +{ + return m_nLCDPinData4; +} + +unsigned CConfig::GetLCDPinData5 (void) const +{ + return m_nLCDPinData5; +} + +unsigned CConfig::GetLCDPinData6 (void) const +{ + return m_nLCDPinData6; +} + +unsigned CConfig::GetLCDPinData7 (void) const +{ + return m_nLCDPinData7; +} + +bool CConfig::GetMIDIDumpEnabled (void) const +{ + return m_bMIDIDumpEnabled; +} + +bool CConfig::GetProfileEnabled (void) const +{ + return m_bProfileEnabled; +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..085de66 --- /dev/null +++ b/src/config.h @@ -0,0 +1,91 @@ +// +// config.h +// +// MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi +// Copyright (C) 2022 The MiniDexed Team +// +// Original author of this class: +// 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 _config_h +#define _config_h + +#include +#include +#include + +class CConfig // Configuration for MiniDexed +{ +public: + static const unsigned MaxNotes = 16; // polyphony + + static const unsigned LCDColumns = 16; // HD44780 LCD + static const unsigned LCDRows = 2; + +public: + CConfig (FATFS *pFileSystem); + ~CConfig (void); + + void Load (void); + + // Sound device + const char *GetSoundDevice (void) const; + unsigned GetSampleRate (void) const; + unsigned GetChunkSize (void) const; + unsigned GetDACI2CAddress (void) const; // 0 for auto probing + + // MIDI + unsigned GetMIDIBaudRate (void) const; + + // HD44780 LCD + // GPIO pin numbers are chip numbers, not header positions + bool GetLCDEnabled (void) const; + unsigned GetLCDPinEnable (void) const; + unsigned GetLCDPinRegisterSelect (void) const; + unsigned GetLCDPinReadWrite (void) const; // set to 0 if not connected + unsigned GetLCDPinData4 (void) const; + unsigned GetLCDPinData5 (void) const; + unsigned GetLCDPinData6 (void) const; + unsigned GetLCDPinData7 (void) const; + + // Debug + bool GetMIDIDumpEnabled (void) const; + bool GetProfileEnabled (void) const; + +private: + CPropertiesFatFsFile m_Properties; + + std::string m_SoundDevice; + unsigned m_nSampleRate; + unsigned m_nChunkSize; + unsigned m_nDACI2CAddress; + + unsigned m_nMIDIBaudRate; + + bool m_bLCDEnabled; + unsigned m_nLCDPinEnable; + unsigned m_nLCDPinRegisterSelect; + unsigned m_nLCDPinReadWrite; + unsigned m_nLCDPinData4; + unsigned m_nLCDPinData5; + unsigned m_nLCDPinData6; + unsigned m_nLCDPinData7; + + bool m_bMIDIDumpEnabled; + bool m_bProfileEnabled; +}; + +#endif diff --git a/src/kernel.cpp b/src/kernel.cpp index 29a2fab..650abe8 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -5,20 +5,27 @@ #include #include #include +#include #include "voices.c" LOGMODULE ("kernel"); +CKernel *CKernel::s_pThis = 0; + CKernel::CKernel (void) : CStdlibAppStdio ("minidexed"), + m_Config (&mFileSystem), m_I2CMaster (CMachineInfo::Get ()->GetDevice (DeviceI2CMaster), TRUE), m_pDexed (0) { + s_pThis = this; + // mActLED.Blink (5); // show we are alive } CKernel::~CKernel(void) { + s_pThis = 0; } bool CKernel::Initialize (void) @@ -28,25 +35,29 @@ bool CKernel::Initialize (void) return FALSE; } + mLogger.RegisterPanicHandler (PanicHandler); + + m_Config.Load (); + // select the sound device - const char *pSoundDevice = mOptions.GetSoundDevice (); - if (strcmp (pSoundDevice, "sndi2s") == 0) + const char *pSoundDevice = m_Config.GetSoundDevice (); + if (strcmp (pSoundDevice, "i2s") == 0) { LOGNOTE ("I2S mode"); - m_pDexed = new CMiniDexedI2S (16, SAMPLE_RATE, &mInterrupt, &m_I2CMaster); + m_pDexed = new CMiniDexedI2S (&m_Config, &mInterrupt, &m_I2CMaster); } - else if (strcmp (pSoundDevice, "sndhdmi") == 0) + else if (strcmp (pSoundDevice, "hdmi") == 0) { LOGNOTE ("HDMI mode"); - m_pDexed = new CMiniDexedHDMI (16, SAMPLE_RATE, &mInterrupt); + m_pDexed = new CMiniDexedHDMI (&m_Config, &mInterrupt); } else { LOGNOTE ("PWM mode"); - m_pDexed = new CMiniDexedPWM (16, SAMPLE_RATE, &mInterrupt); + m_pDexed = new CMiniDexedPWM (&m_Config, &mInterrupt); } if (!m_pDexed->Initialize ()) @@ -71,4 +82,11 @@ CStdlibApp::TShutdownMode CKernel::Run (void) } return ShutdownHalt; -} \ No newline at end of file +} + +void CKernel::PanicHandler (void) +{ + EnableIRQs (); + + s_pThis->mScreen.Update (4096); +} diff --git a/src/kernel.h b/src/kernel.h index 2802979..2870db2 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -6,6 +6,7 @@ #include "circle_stdlib_app.h" #include +#include "config.h" #include "minidexed.h" enum TShutdownMode @@ -25,10 +26,16 @@ public: TShutdownMode Run (void); +private: + static void PanicHandler (void); + private: // do not change this order + CConfig m_Config; CI2CMaster m_I2CMaster; CMiniDexed *m_pDexed; + + static CKernel *s_pThis; }; -#endif \ No newline at end of file +#endif diff --git a/src/minidexed.cpp b/src/minidexed.cpp index a67877c..12a9cb8 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -2,13 +2,9 @@ // minidexed.cpp // #include "minidexed.h" -#include "perftimer.h" #include #include -#define MIDI_DUMP -#define PROFILE - #define MIDI_NOTE_OFF 0b1000 #define MIDI_NOTE_ON 0b1001 #define MIDI_AFTERTOUCH 0xA0 @@ -20,25 +16,21 @@ CMiniDexed *CMiniDexed::s_pThis = 0; -extern uint8_t voices_bank[1][32][156]; - -#ifdef PROFILE -CPerformanceTimer GetChunkTimer ("GetChunk", 1000000U * CHUNK_SIZE/2 / SAMPLE_RATE); -#endif - bool CMiniDexed::Initialize (void) { m_SysExFileLoader.Load (); - if (!m_Serial.Initialize(31250)) + if (!m_Serial.Initialize(m_pConfig->GetMIDIBaudRate ())) { return false; } - - if (!m_LCD.Initialize ()) + if (m_pLCD) { - return FALSE; + if (!m_pLCD->Initialize ()) + { + return FALSE; + } } m_bUseSerial = true; @@ -53,9 +45,10 @@ bool CMiniDexed::Initialize (void) void CMiniDexed::Process(boolean bPlugAndPlayUpdated) { -#ifdef PROFILE - GetChunkTimer.Dump (); -#endif + if (m_pConfig->GetProfileEnabled ()) + { + m_GetChunkTimer.Dump (); + } if (m_pMIDIDevice != 0) { @@ -139,24 +132,25 @@ void CMiniDexed::MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLeng // The packet contents are just normal MIDI data - see // https://www.midi.org/specifications/item/table-1-summary-of-midi-message -#ifdef MIDI_DUMP - switch (nLength) + if (s_pThis->m_pConfig->GetMIDIDumpEnabled ()) { - case 1: - printf ("MIDI %u: %02X\n", nCable, (unsigned) pPacket[0]); - break; - - case 2: - printf ("MIDI %u: %02X %02X\n", nCable, - (unsigned) pPacket[0], (unsigned) pPacket[1]); - break; - - case 3: - printf ("MIDI %u: %02X %02X %02X\n", nCable, - (unsigned) pPacket[0], (unsigned) pPacket[1], (unsigned) pPacket[2]); - break; + switch (nLength) + { + case 1: + printf ("MIDI %u: %02X\n", nCable, (unsigned) pPacket[0]); + break; + + case 2: + printf ("MIDI %u: %02X %02X\n", nCable, + (unsigned) pPacket[0], (unsigned) pPacket[1]); + break; + + case 3: + printf ("MIDI %u: %02X %02X %02X\n", nCable, + (unsigned) pPacket[0], (unsigned) pPacket[1], (unsigned) pPacket[2]); + break; + } } -#endif if (pPacket[0] == MIDI_CONTROL_CHANGE) { @@ -247,9 +241,7 @@ bool CMiniDexedPWM::Initialize (void) unsigned CMiniDexedPWM::GetChunk(u32 *pBuffer, unsigned nChunkSize) { -#ifdef PROFILE - GetChunkTimer.Start(); -#endif + m_GetChunkTimer.Start(); unsigned nResult = nChunkSize; @@ -268,9 +260,7 @@ unsigned CMiniDexedPWM::GetChunk(u32 *pBuffer, unsigned nChunkSize) *pBuffer++ = nSample; } -#ifdef PROFILE - GetChunkTimer.Stop(); -#endif + m_GetChunkTimer.Stop(); return(nResult); }; @@ -287,9 +277,7 @@ bool CMiniDexedI2S::Initialize (void) unsigned CMiniDexedI2S::GetChunk(u32 *pBuffer, unsigned nChunkSize) { -#ifdef PROFILE - GetChunkTimer.Start(); -#endif + m_GetChunkTimer.Start(); unsigned nResult = nChunkSize; @@ -306,9 +294,7 @@ unsigned CMiniDexedI2S::GetChunk(u32 *pBuffer, unsigned nChunkSize) *pBuffer++ = nSample; } -#ifdef PROFILE - GetChunkTimer.Stop(); -#endif + m_GetChunkTimer.Stop(); return(nResult); }; @@ -325,9 +311,7 @@ bool CMiniDexedHDMI::Initialize (void) unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize) { -#ifdef PROFILE - GetChunkTimer.Start(); -#endif + m_GetChunkTimer.Start(); unsigned nResult = nChunkSize; @@ -350,14 +334,15 @@ unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize) *pBuffer++ = nSample; } -#ifdef PROFILE - GetChunkTimer.Stop(); -#endif + m_GetChunkTimer.Stop(); return(nResult); }; void CMiniDexed::LCDWrite (const char *pString) { - m_LCD.Write (pString, strlen (pString)); + if (m_pLCD) + { + m_pLCD->Write (pString, strlen (pString)); + } } diff --git a/src/minidexed.h b/src/minidexed.h index 72635b4..79605c7 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -15,42 +15,36 @@ #include #include #include +#include "config.h" #include "sysexfileloader.h" #include "pckeyboard.h" +#include "perftimer.h" #include -#define SAMPLE_RATE 48000 - -#define CHUNK_SIZE (2 * 64) -#define CHUNK_SIZE_HDMI (384 * 5) - -#define DAC_I2C_ADDRESS 0 // I2C slave address of the DAC (0 for auto probing) - -// HD44780 LCD configuration -#define COLUMNS 16 -#define ROWS 2 -// GPIO pins (Brcm numbering) -#define EN_PIN 17 // Enable -#define RS_PIN 18 // Register Select -#define RW_PIN 19 // Read/Write (set to 0 if not connected) -#define D4_PIN 22 // Data 4 -#define D5_PIN 23 // Data 5 -#define D6_PIN 24 // Data 6 -#define D7_PIN 25 // Data 7 - class CMiniDexed : public Dexed { public: - CMiniDexed(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) -: Dexed(max_notes,(int)sample_rate), + CMiniDexed(CConfig *pConfig, CInterruptSystem *pInterrupt) +: Dexed (CConfig::MaxNotes, pConfig->GetSampleRate ()), m_pMIDIDevice (0), m_PCKeyboard (this), m_Serial (pInterrupt, TRUE), m_bUseSerial (FALSE), m_nSerialState (0), - m_LCD (COLUMNS, ROWS, D4_PIN, D5_PIN, D6_PIN, D7_PIN, EN_PIN, RS_PIN, RW_PIN) + m_GetChunkTimer ("GetChunk", 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), + m_pConfig (pConfig), + m_pLCD (0) { s_pThis = this; + + if (pConfig->GetLCDEnabled ()) + { + m_pLCD = new CHD44780Device (CConfig::LCDColumns, CConfig::LCDRows, + pConfig->GetLCDPinData4 (), pConfig->GetLCDPinData5 (), + pConfig->GetLCDPinData6 (), pConfig->GetLCDPinData7 (), + pConfig->GetLCDPinEnable (), pConfig->GetLCDPinRegisterSelect (), + pConfig->GetLCDPinReadWrite ()); + } }; virtual bool Initialize (void); @@ -69,8 +63,10 @@ class CMiniDexed : public Dexed unsigned m_nSerialState; u8 m_SerialMessage[3]; CSysExFileLoader m_SysExFileLoader; + CPerformanceTimer m_GetChunkTimer; private: - CHD44780Device m_LCD; + CConfig *m_pConfig; + CHD44780Device *m_pLCD; static CMiniDexed *s_pThis; }; @@ -78,9 +74,9 @@ class CMiniDexed : public Dexed 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) + CMiniDexedPWM(CConfig *pConfig, CInterruptSystem *pInterrupt) +: CMiniDexed(pConfig, pInterrupt), + CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize ()) { } @@ -91,9 +87,10 @@ class CMiniDexedPWM : public CMiniDexed, public CPWMSoundBaseDevice 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) + CMiniDexedI2S(CConfig *pConfig, CInterruptSystem *pInterrupt, CI2CMaster *pI2CMaster) +: CMiniDexed(pConfig, pInterrupt), + CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize (), + FALSE, pI2CMaster, pConfig->GetDACI2CAddress ()) { } @@ -104,9 +101,9 @@ class CMiniDexedI2S : public CMiniDexed, public CI2SSoundBaseDevice 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) + CMiniDexedHDMI(CConfig *pConfig, CInterruptSystem *pInterrupt) +: CMiniDexed(pConfig, pInterrupt), + CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize ()) { } diff --git a/src/minidexed.ini b/src/minidexed.ini new file mode 100644 index 0000000..6f7ae4c --- /dev/null +++ b/src/minidexed.ini @@ -0,0 +1,26 @@ +# +# minidexed.ini +# + +# Sound device +SoundDevice=pwm +SampleRate=48000 +#ChunkSize=256 +DACI2CAddress=0 + +# MIDI +MIDIBaudRate=31250 + +# HD44780 LCD +LCDEnabled=1 +LCDPinEnable=17 +LCDPinRegisterSelect=18 +LCDPinReadWrite=19 +LCDPinData4=22 +LCDPinData5=23 +LCDPinData6=24 +LCDPinData7=25 + +# Debug +MIDIDumpEnabled=1 +ProfileEnabled=1