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
pull/37/head
Rene Stange 3 years ago
parent 1a68a427de
commit 314196e132
  1. 3
      build.sh
  2. 2
      src/Makefile
  3. 1
      src/Rules.mk
  4. 132
      src/config.cpp
  5. 91
      src/config.h
  6. 32
      src/kernel.cpp
  7. 9
      src/kernel.h
  8. 89
      src/minidexed.cpp
  9. 61
      src/minidexed.h
  10. 26
      src/minidexed.ini

@ -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 ..

@ -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)

@ -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 \

@ -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 <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 "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;
}

@ -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 <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 _config_h
#define _config_h
#include <fatfs/ff.h>
#include <Properties/propertiesfatfsfile.h>
#include <string>
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

@ -5,20 +5,27 @@
#include <iostream>
#include <string.h>
#include <circle/logger.h>
#include <circle/synchronize.h>
#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;
}
}
void CKernel::PanicHandler (void)
{
EnableIRQs ();
s_pThis->mScreen.Update (4096);
}

@ -6,6 +6,7 @@
#include "circle_stdlib_app.h"
#include <circle/i2cmaster.h>
#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
#endif

@ -2,13 +2,9 @@
// minidexed.cpp
//
#include "minidexed.h"
#include "perftimer.h"
#include <circle/devicenameservice.h>
#include <stdio.h>
#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));
}
}

@ -15,42 +15,36 @@
#include <circle/pwmsoundbasedevice.h>
#include <circle/i2ssoundbasedevice.h>
#include <circle/hdmisoundbasedevice.h>
#include "config.h"
#include "sysexfileloader.h"
#include "pckeyboard.h"
#include "perftimer.h"
#include <display/hd44780device.h>
#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 ())
{
}

@ -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
Loading…
Cancel
Save