Merge branch 'probonopd-main' into fx2

pull/495/head
abscisys 2 years ago
commit 3096a71598
  1. 10
      .github/workflows/build.yml
  2. 63
      src/Rules.mk
  3. 616
      src/circle_stdlib_app.h
  4. 762
      src/config.cpp
  5. 420
      src/config.h
  6. 2
      src/mididevice.cpp
  7. 84
      src/minidexed.cpp
  8. 2
      src/minidexed.h
  9. 175
      src/minidexed.ini
  10. 348
      src/serialmididevice.cpp
  11. 4
      src/userinterface.cpp

@ -20,11 +20,17 @@ jobs:
run: |
set -ex
git submodule update --init --recursive
- name: Use Circle develop branch for WM8960 and i2c display support until it is merged upstream
- name: Use Circle develop branch for SSD1306 display rotation support until it is merged upstream
run: |
set -ex
cd circle-stdlib/
git checkout e318f89 # Needed to support Circle develop?
cd -
cd circle-stdlib/libs/circle
git checkout 646c362 # develop
git checkout ec09d7e # develop
cd -
cd circle-stdlib/libs/circle-newlib
git checkout 48bf91d # needed for circle ec09d7e
cd -
- name: Install toolchains
run: |

@ -1,31 +1,32 @@
#
# Rules.mk
#
-include $(CIRCLE_STDLIB_DIR)/Config.mk
NEWLIBDIR ?= $(CIRCLE_STDLIB_DIR)/install/$(NEWLIB_ARCH)
CIRCLEHOME ?= $(CIRCLE_STDLIB_DIR)/libs/circle
include $(CIRCLEHOME)/Rules.mk
INCLUDE += \
-I $(CIRCLE_STDLIB_DIR)/include \
-I $(NEWLIBDIR)/include
LIBS += \
$(NEWLIBDIR)/lib/libm.a \
$(NEWLIBDIR)/lib/libc.a \
$(NEWLIBDIR)/lib/libcirclenewlib.a \
$(CIRCLEHOME)/addon/display/libdisplay.a \
$(CIRCLEHOME)/addon/sensor/libsensor.a \
$(CIRCLEHOME)/addon/Properties/libproperties.a \
$(CIRCLEHOME)/addon/SDCard/libsdcard.a \
$(CIRCLEHOME)/lib/usb/libusb.a \
$(CIRCLEHOME)/lib/input/libinput.a \
$(CIRCLEHOME)/addon/fatfs/libfatfs.a \
$(CIRCLEHOME)/lib/fs/libfs.a \
$(CIRCLEHOME)/lib/sched/libsched.a \
$(CIRCLEHOME)/lib/libcircle.a
-include $(DEPS)
#
# Rules.mk
#
-include $(CIRCLE_STDLIB_DIR)/Config.mk
NEWLIBDIR ?= $(CIRCLE_STDLIB_DIR)/install/$(NEWLIB_ARCH)
CIRCLEHOME ?= $(CIRCLE_STDLIB_DIR)/libs/circle
include $(CIRCLEHOME)/Rules.mk
INCLUDE += \
-I $(CIRCLE_STDLIB_DIR)/include \
-I $(NEWLIBDIR)/include
LIBS += \
$(NEWLIBDIR)/lib/libm.a \
$(NEWLIBDIR)/lib/libc.a \
$(NEWLIBDIR)/lib/libcirclenewlib.a \
$(CIRCLEHOME)/addon/display/libdisplay.a \
$(CIRCLEHOME)/addon/sensor/libsensor.a \
$(CIRCLEHOME)/addon/Properties/libproperties.a \
$(CIRCLEHOME)/addon/SDCard/libsdcard.a \
$(CIRCLEHOME)/lib/usb/libusb.a \
$(CIRCLEHOME)/lib/input/libinput.a \
$(CIRCLEHOME)/lib/sound/libsound.a \
$(CIRCLEHOME)/addon/fatfs/libfatfs.a \
$(CIRCLEHOME)/lib/fs/libfs.a \
$(CIRCLEHOME)/lib/sched/libsched.a \
$(CIRCLEHOME)/lib/libcircle.a
-include $(DEPS)

@ -1,307 +1,309 @@
/**
* This file has been taken from the circle-stdlib project:
* https://github.com/smuehlst/circle-stdlib
*
* Convenience classes that package different levels
* of functionality of Circle applications.
*
* Derive the kernel class of the application from one of
* the CStdlibApp* classes and implement at least the
* Run () method. Extend the Initalize () and Cleanup ()
* methods if necessary.
*/
#ifndef _circle_stdlib_app_h
#define _circle_stdlib_app_h
#include <circle/actled.h>
#include <circle/string.h>
#include <circle/koptions.h>
#include <circle/devicenameservice.h>
#include <circle/nulldevice.h>
#include <circle/exceptionhandler.h>
#include <circle/interrupt.h>
#include <circle/screen.h>
#include <circle/serial.h>
#include <circle/writebuffer.h>
#include <circle/timer.h>
#include <circle/logger.h>
#include <circle/usb/usbhcidevice.h>
#include <SDCard/emmc.h>
#include <circle/input/console.h>
#include <circle/sched/scheduler.h>
#include <circle/net/netsubsystem.h>
#include <wlan/bcm4343.h>
#include <wlan/hostap/wpa_supplicant/wpasupplicant.h>
#include <circle_glue.h>
#include <string.h>
/**
* Basic Circle Stdlib application that supports GPIO access.
*/
class CStdlibApp
{
public:
enum TShutdownMode
{
ShutdownNone,
ShutdownHalt,
ShutdownReboot
};
CStdlibApp (const char *kernel) :
FromKernel (kernel)
{
}
virtual ~CStdlibApp (void)
{
}
virtual bool Initialize (void)
{
return mInterrupt.Initialize ();
}
virtual void Cleanup (void)
{
}
virtual TShutdownMode Run (void) = 0;
const char *GetKernelName(void) const
{
return FromKernel;
}
protected:
CActLED mActLED;
CKernelOptions mOptions;
CDeviceNameService mDeviceNameService;
CNullDevice mNullDevice;
CExceptionHandler mExceptionHandler;
CInterruptSystem mInterrupt;
private:
char const *FromKernel;
};
/**
* Stdlib application that adds screen support
* to the basic CStdlibApp features.
*/
class CStdlibAppScreen : public CStdlibApp
{
public:
CStdlibAppScreen(const char *kernel)
: CStdlibApp (kernel),
mScreenUnbuffered (mOptions.GetWidth (), mOptions.GetHeight ()),
mScreen (&mScreenUnbuffered),
mbScreenAvailable (false),
mTimer (&mInterrupt),
mLogger (mOptions.GetLogLevel (), &mTimer)
{
}
virtual bool Initialize (void)
{
if (!CStdlibApp::Initialize ())
{
return false;
}
mbScreenAvailable = mScreenUnbuffered.Initialize ();
#if 0
if (!mSerial.Initialize (115200))
{
return false;
}
#endif
CDevice *pTarget =
mDeviceNameService.GetDevice (mOptions.GetLogDevice (), false);
if (pTarget == 0)
{
pTarget = &mScreen;
}
if (!mLogger.Initialize (pTarget))
{
return false;
}
return mTimer.Initialize ();
}
protected:
CScreenDevice mScreenUnbuffered;
//CSerialDevice mSerial;
CWriteBufferDevice mScreen;
bool mbScreenAvailable;
CTimer mTimer;
CLogger mLogger;
};
/**
* Stdlib application that adds stdio support
* to the CStdlibAppScreen functionality.
*/
class CStdlibAppStdio: public CStdlibAppScreen
{
private:
char const *mpPartitionName;
public:
// TODO transform to constexpr
// constexpr char static DefaultPartition[] = "emmc1-1";
#define CSTDLIBAPP_LEGACY_DEFAULT_PARTITION "emmc1-1"
#define CSTDLIBAPP_DEFAULT_PARTITION "SD:"
CStdlibAppStdio (const char *kernel,
const char *pPartitionName = CSTDLIBAPP_DEFAULT_PARTITION)
: CStdlibAppScreen (kernel),
mpPartitionName (pPartitionName),
mUSBHCI (&mInterrupt, &mTimer, TRUE),
mEMMC (&mInterrupt, &mTimer, &mActLED),
#if !defined(__aarch64__) || !defined(LEAVE_QEMU_ON_HALT)
//mConsole (&mScreen, TRUE)
mConsole (&mNullDevice, &mScreen)
#else
mConsole (&mScreen)
#endif
{
}
virtual bool Initialize (void)
{
if (!CStdlibAppScreen::Initialize ())
{
return false;
}
if (!mEMMC.Initialize ())
{
return false;
}
char const *partitionName = mpPartitionName;
// Recognize the old default partion name
if (strcmp(partitionName, CSTDLIBAPP_LEGACY_DEFAULT_PARTITION) == 0)
{
partitionName = CSTDLIBAPP_DEFAULT_PARTITION;
}
if (f_mount (&mFileSystem, partitionName, 1) != FR_OK)
{
mLogger.Write (GetKernelName (), LogError,
"Cannot mount partition: %s", partitionName);
return false;
}
#if !defined(__aarch64__) || !defined(LEAVE_QEMU_ON_HALT)
// The USB driver is not supported under 64-bit QEMU, so
// the initialization must be skipped in this case, or an
// exit happens here under 64-bit QEMU.
if (!mUSBHCI.Initialize ())
{
return false;
}
#endif
if (!mConsole.Initialize ())
{
return false;
}
// Initialize newlib stdio with a reference to Circle's file system and console
CGlueStdioInit (mFileSystem, mConsole);
mLogger.Write (GetKernelName (), LogNotice, "Compile time: " __DATE__ " " __TIME__);
return true;
}
virtual void Cleanup (void)
{
f_mount(0, "", 0);
CStdlibAppScreen::Cleanup ();
}
protected:
CUSBHCIDevice mUSBHCI;
CEMMCDevice mEMMC;
FATFS mFileSystem;
CConsole mConsole;
};
/**
* Stdlib application that adds network functionality
* to the CStdlibAppStdio features.
*/
class CStdlibAppNetwork: public CStdlibAppStdio
{
public:
#define CSTDLIBAPP_WLAN_FIRMWARE_PATH CSTDLIBAPP_DEFAULT_PARTITION "/firmware/"
#define CSTDLIBAPP_WLAN_CONFIG_FILE CSTDLIBAPP_DEFAULT_PARTITION "/wpa_supplicant.conf"
CStdlibAppNetwork (const char *kernel,
const char *pPartitionName = CSTDLIBAPP_DEFAULT_PARTITION,
const u8 *pIPAddress = 0, // use DHCP if pIPAddress == 0
const u8 *pNetMask = 0,
const u8 *pDefaultGateway = 0,
const u8 *pDNSServer = 0,
TNetDeviceType DeviceType = NetDeviceTypeEthernet)
: CStdlibAppStdio(kernel, pPartitionName),
mDeviceType (DeviceType),
mWLAN (CSTDLIBAPP_WLAN_FIRMWARE_PATH),
mNet(pIPAddress, pNetMask, pDefaultGateway, pDNSServer, DEFAULT_HOSTNAME, DeviceType),
mWPASupplicant (CSTDLIBAPP_WLAN_CONFIG_FILE)
{
}
virtual bool Initialize (bool const bWaitForActivate = true)
{
if (!CStdlibAppStdio::Initialize ())
{
return false;
}
if (mDeviceType == NetDeviceTypeWLAN)
{
if (!mWLAN.Initialize ())
{
return false;
}
}
if (!mNet.Initialize (false))
{
return false;
}
if (mDeviceType == NetDeviceTypeWLAN)
{
if (!mWPASupplicant.Initialize ())
{
return false;
}
}
while (bWaitForActivate && !mNet.IsRunning ())
{
mScheduler.Yield ();
}
return true;
}
protected:
CScheduler mScheduler;
TNetDeviceType mDeviceType;
CBcm4343Device mWLAN;
CNetSubSystem mNet;
CWPASupplicant mWPASupplicant;
};
#endif
/**
* This file has been taken from the circle-stdlib project:
* https://github.com/smuehlst/circle-stdlib
*
* Convenience classes that package different levels
* of functionality of Circle applications.
*
* Derive the kernel class of the application from one of
* the CStdlibApp* classes and implement at least the
* Run () method. Extend the Initalize () and Cleanup ()
* methods if necessary.
*/
#ifndef _circle_stdlib_app_h
#define _circle_stdlib_app_h
#include <circle/actled.h>
#include <circle/string.h>
#include <circle/koptions.h>
#include <circle/devicenameservice.h>
#include <circle/nulldevice.h>
#include <circle/exceptionhandler.h>
#include <circle/interrupt.h>
#include <circle/screen.h>
#include <circle/serial.h>
#include <circle/writebuffer.h>
#include <circle/timer.h>
#include <circle/logger.h>
#include <circle/usb/usbhcidevice.h>
#include <SDCard/emmc.h>
#include <circle/input/console.h>
#include <circle/sched/scheduler.h>
#include <circle/net/netsubsystem.h>
#include <wlan/bcm4343.h>
#include <wlan/hostap/wpa_supplicant/wpasupplicant.h>
#include <circle_glue.h>
#include <string.h>
/**
* Basic Circle Stdlib application that supports GPIO access.
*/
class CStdlibApp
{
public:
enum TShutdownMode
{
ShutdownNone,
ShutdownHalt,
ShutdownReboot
};
CStdlibApp (const char *kernel) :
FromKernel (kernel)
{
}
virtual ~CStdlibApp (void)
{
}
virtual bool Initialize (void)
{
return mInterrupt.Initialize ();
}
virtual void Cleanup (void)
{
}
virtual TShutdownMode Run (void) = 0;
const char *GetKernelName(void) const
{
return FromKernel;
}
protected:
CActLED mActLED;
CKernelOptions mOptions;
CDeviceNameService mDeviceNameService;
CNullDevice mNullDevice;
CExceptionHandler mExceptionHandler;
CInterruptSystem mInterrupt;
private:
char const *FromKernel;
};
/**
* Stdlib application that adds screen support
* to the basic CStdlibApp features.
*/
class CStdlibAppScreen : public CStdlibApp
{
public:
CStdlibAppScreen(const char *kernel)
: CStdlibApp (kernel),
mScreenUnbuffered (mOptions.GetWidth (), mOptions.GetHeight ()),
mScreen (&mScreenUnbuffered),
mbScreenAvailable (false),
mTimer (&mInterrupt),
mLogger (mOptions.GetLogLevel (), &mTimer)
{
}
virtual bool Initialize (void)
{
if (!CStdlibApp::Initialize ())
{
return false;
}
mbScreenAvailable = mScreenUnbuffered.Initialize ();
#if 0
if (!mSerial.Initialize (115200))
{
return false;
}
#endif
CDevice *pTarget =
mDeviceNameService.GetDevice (mOptions.GetLogDevice (), false);
if (pTarget == 0)
{
pTarget = &mScreen;
}
if (!mLogger.Initialize (pTarget))
{
return false;
}
return mTimer.Initialize ();
}
protected:
CScreenDevice mScreenUnbuffered;
//CSerialDevice mSerial;
CWriteBufferDevice mScreen;
bool mbScreenAvailable;
CTimer mTimer;
CLogger mLogger;
};
/**
* Stdlib application that adds stdio support
* to the CStdlibAppScreen functionality.
*/
class CStdlibAppStdio: public CStdlibAppScreen
{
private:
char const *mpPartitionName;
public:
// TODO transform to constexpr
// constexpr char static DefaultPartition[] = "emmc1-1";
#define CSTDLIBAPP_LEGACY_DEFAULT_PARTITION "emmc1-1"
#define CSTDLIBAPP_DEFAULT_PARTITION "SD:"
CStdlibAppStdio (const char *kernel,
const char *pPartitionName = CSTDLIBAPP_DEFAULT_PARTITION)
: CStdlibAppScreen (kernel),
mpPartitionName (pPartitionName),
mUSBHCI (&mInterrupt, &mTimer, TRUE),
mEMMC (&mInterrupt, &mTimer, &mActLED),
#if !defined(__aarch64__) || !defined(LEAVE_QEMU_ON_HALT)
//mConsole (&mScreen, TRUE)
mConsole (&mNullDevice, &mScreen)
#else
mConsole (&mScreen)
#endif
{
}
virtual bool Initialize (void)
{
if (!CStdlibAppScreen::Initialize ())
{
return false;
}
if (!mEMMC.Initialize ())
{
return false;
}
char const *partitionName = mpPartitionName;
// Recognize the old default partion name
if (strcmp(partitionName, CSTDLIBAPP_LEGACY_DEFAULT_PARTITION) == 0)
{
partitionName = CSTDLIBAPP_DEFAULT_PARTITION;
}
if (f_mount (&mFileSystem, partitionName, 1) != FR_OK)
{
mLogger.Write (GetKernelName (), LogError,
"Cannot mount partition: %s", partitionName);
return false;
}
#if !defined(__aarch64__) || !defined(LEAVE_QEMU_ON_HALT)
// The USB driver is not supported under 64-bit QEMU, so
// the initialization must be skipped in this case, or an
// exit happens here under 64-bit QEMU.
if (!mUSBHCI.Initialize ())
{
return false;
}
#endif
if (!mConsole.Initialize ())
{
return false;
}
// Initialize newlib stdio with a reference to Circle's console
// (Remove mFileSystem as a parameter to mirror change in circle-stdlib's
// commit "Remove obsolete FATFS-related code", dated Dec 2022)
CGlueStdioInit (mConsole);
mLogger.Write (GetKernelName (), LogNotice, "Compile time: " __DATE__ " " __TIME__);
return true;
}
virtual void Cleanup (void)
{
f_mount(0, "", 0);
CStdlibAppScreen::Cleanup ();
}
protected:
CUSBHCIDevice mUSBHCI;
CEMMCDevice mEMMC;
FATFS mFileSystem;
CConsole mConsole;
};
/**
* Stdlib application that adds network functionality
* to the CStdlibAppStdio features.
*/
class CStdlibAppNetwork: public CStdlibAppStdio
{
public:
#define CSTDLIBAPP_WLAN_FIRMWARE_PATH CSTDLIBAPP_DEFAULT_PARTITION "/firmware/"
#define CSTDLIBAPP_WLAN_CONFIG_FILE CSTDLIBAPP_DEFAULT_PARTITION "/wpa_supplicant.conf"
CStdlibAppNetwork (const char *kernel,
const char *pPartitionName = CSTDLIBAPP_DEFAULT_PARTITION,
const u8 *pIPAddress = 0, // use DHCP if pIPAddress == 0
const u8 *pNetMask = 0,
const u8 *pDefaultGateway = 0,
const u8 *pDNSServer = 0,
TNetDeviceType DeviceType = NetDeviceTypeEthernet)
: CStdlibAppStdio(kernel, pPartitionName),
mDeviceType (DeviceType),
mWLAN (CSTDLIBAPP_WLAN_FIRMWARE_PATH),
mNet(pIPAddress, pNetMask, pDefaultGateway, pDNSServer, DEFAULT_HOSTNAME, DeviceType),
mWPASupplicant (CSTDLIBAPP_WLAN_CONFIG_FILE)
{
}
virtual bool Initialize (bool const bWaitForActivate = true)
{
if (!CStdlibAppStdio::Initialize ())
{
return false;
}
if (mDeviceType == NetDeviceTypeWLAN)
{
if (!mWLAN.Initialize ())
{
return false;
}
}
if (!mNet.Initialize (false))
{
return false;
}
if (mDeviceType == NetDeviceTypeWLAN)
{
if (!mWPASupplicant.Initialize ())
{
return false;
}
}
while (bWaitForActivate && !mNet.IsRunning ())
{
mScheduler.Yield ();
}
return true;
}
protected:
CScheduler mScheduler;
TNetDeviceType mDeviceType;
CBcm4343Device mWLAN;
CNetSubSystem mNet;
CWPASupplicant mWPASupplicant;
};
#endif

@ -1,372 +1,390 @@
//
// 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);
#ifdef ARM_ALLOW_MULTI_CORE
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 256);
#else
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 1024);
#endif
m_nDACI2CAddress = m_Properties.GetNumber ("DACI2CAddress", 0);
m_bChannelsSwapped = m_Properties.GetNumber ("ChannelsSwapped", 0) != 0;
m_nMIDIBaudRate = m_Properties.GetNumber ("MIDIBaudRate", 31250);
const char *pMIDIThru = m_Properties.GetString ("MIDIThru");
if (pMIDIThru)
{
std::string Arg (pMIDIThru);
size_t nPos = Arg.find (',');
if (nPos != std::string::npos)
{
m_MIDIThruIn = Arg.substr (0, nPos);
m_MIDIThruOut = Arg.substr (nPos+1);
if ( m_MIDIThruIn.empty ()
|| m_MIDIThruOut.empty ())
{
m_MIDIThruIn.clear ();
m_MIDIThruOut.clear ();
}
}
}
m_bMIDIRXProgramChange = m_Properties.GetNumber ("MIDIRXProgramChange", 1) != 0;
m_bIgnoreAllNotesOff = m_Properties.GetNumber ("IgnoreAllNotesOff", 0) != 0;
m_bLCDEnabled = m_Properties.GetNumber ("LCDEnabled", 0) != 0;
m_nLCDPinEnable = m_Properties.GetNumber ("LCDPinEnable", 4);
m_nLCDPinRegisterSelect = m_Properties.GetNumber ("LCDPinRegisterSelect", 27);
m_nLCDPinReadWrite = m_Properties.GetNumber ("LCDPinReadWrite", 0);
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_nLCDI2CAddress = m_Properties.GetNumber ("LCDI2CAddress", 0);
m_nSSD1306LCDI2CAddress = m_Properties.GetNumber ("SSD1306LCDI2CAddress", 0);
m_nSSD1306LCDWidth = m_Properties.GetNumber ("SSD1306LCDWidth", 128);
m_nSSD1306LCDHeight = m_Properties.GetNumber ("SSD1306LCDHeight", 32);
m_nLCDColumns = m_Properties.GetNumber ("LCDColumns", 16);
m_nLCDRows = m_Properties.GetNumber ("LCDRows", 2);
m_nButtonPinPrev = m_Properties.GetNumber ("ButtonPinPrev", 0);
m_nButtonPinNext = m_Properties.GetNumber ("ButtonPinNext", 0);
m_nButtonPinBack = m_Properties.GetNumber ("ButtonPinBack", 11);
m_nButtonPinSelect = m_Properties.GetNumber ("ButtonPinSelect", 11);
m_nButtonPinHome = m_Properties.GetNumber ("ButtonPinHome", 11);
m_nButtonPinShortcut = m_Properties.GetNumber ("ButtonPinShortcut", 11);
m_ButtonActionPrev = m_Properties.GetString ("ButtonActionPrev", "");
m_ButtonActionNext = m_Properties.GetString ("ButtonActionNext", "");
m_ButtonActionBack = m_Properties.GetString ("ButtonActionBack", "doubleclick");
m_ButtonActionSelect = m_Properties.GetString ("ButtonActionSelect", "click");
m_ButtonActionHome = m_Properties.GetString ("ButtonActionHome", "longpress");
m_nDoubleClickTimeout = m_Properties.GetNumber ("DoubleClickTimeout", 400);
m_nLongPressTimeout = m_Properties.GetNumber ("LongPressTimeout", 600);
m_nMIDIButtonCh = m_Properties.GetNumber ("MIDIButtonCh", 0);
m_nMIDIButtonNotes = m_Properties.GetNumber ("MIDIButtonNotes", 0);
m_nMIDIButtonPrev = m_Properties.GetNumber ("MIDIButtonPrev", 0);
m_nMIDIButtonNext = m_Properties.GetNumber ("MIDIButtonNext", 0);
m_nMIDIButtonBack = m_Properties.GetNumber ("MIDIButtonBack", 0);
m_nMIDIButtonSelect = m_Properties.GetNumber ("MIDIButtonSelect", 0);
m_nMIDIButtonHome = m_Properties.GetNumber ("MIDIButtonHome", 0);
m_bEncoderEnabled = m_Properties.GetNumber ("EncoderEnabled", 0) != 0;
m_nEncoderPinClock = m_Properties.GetNumber ("EncoderPinClock", 10);
m_nEncoderPinData = m_Properties.GetNumber ("EncoderPinData", 9);
m_bMIDIDumpEnabled = m_Properties.GetNumber ("MIDIDumpEnabled", 0) != 0;
m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0;
m_bPerformanceSelectToLoad = m_Properties.GetNumber ("PerformanceSelectToLoad", 1) != 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;
}
bool CConfig::GetChannelsSwapped (void) const
{
return m_bChannelsSwapped;
}
unsigned CConfig::GetMIDIBaudRate (void) const
{
return m_nMIDIBaudRate;
}
const char *CConfig::GetMIDIThruIn (void) const
{
return m_MIDIThruIn.c_str ();
}
const char *CConfig::GetMIDIThruOut (void) const
{
return m_MIDIThruOut.c_str ();
}
bool CConfig::GetMIDIRXProgramChange (void) const
{
return m_bMIDIRXProgramChange;
}
bool CConfig::GetIgnoreAllNotesOff (void) const
{
return m_bIgnoreAllNotesOff;
}
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;
}
unsigned CConfig::GetLCDI2CAddress (void) const
{
return m_nLCDI2CAddress;
}
unsigned CConfig::GetSSD1306LCDI2CAddress (void) const
{
return m_nSSD1306LCDI2CAddress;
}
unsigned CConfig::GetSSD1306LCDWidth (void) const
{
return m_nSSD1306LCDWidth;
}
unsigned CConfig::GetSSD1306LCDHeight (void) const
{
return m_nSSD1306LCDHeight;
}
unsigned CConfig::GetLCDColumns (void) const
{
return m_nLCDColumns;
}
unsigned CConfig::GetLCDRows (void) const
{
return m_nLCDRows;
}
unsigned CConfig::GetButtonPinPrev (void) const
{
return m_nButtonPinPrev;
}
unsigned CConfig::GetButtonPinNext (void) const
{
return m_nButtonPinNext;
}
unsigned CConfig::GetButtonPinBack (void) const
{
return m_nButtonPinBack;
}
unsigned CConfig::GetButtonPinSelect (void) const
{
return m_nButtonPinSelect;
}
unsigned CConfig::GetButtonPinHome (void) const
{
return m_nButtonPinHome;
}
unsigned CConfig::GetButtonPinShortcut (void) const
{
return m_nButtonPinShortcut;
}
const char *CConfig::GetButtonActionPrev (void) const
{
return m_ButtonActionPrev.c_str();
}
const char *CConfig::GetButtonActionNext (void) const
{
return m_ButtonActionNext.c_str();
}
const char *CConfig::GetButtonActionBack (void) const
{
return m_ButtonActionBack.c_str();
}
const char *CConfig::GetButtonActionSelect (void) const
{
return m_ButtonActionSelect.c_str();
}
const char *CConfig::GetButtonActionHome (void) const
{
return m_ButtonActionHome.c_str();
}
unsigned CConfig::GetDoubleClickTimeout (void) const
{
return m_nDoubleClickTimeout;
}
unsigned CConfig::GetLongPressTimeout (void) const
{
return m_nLongPressTimeout;
}
unsigned CConfig::GetMIDIButtonCh (void) const
{
return m_nMIDIButtonCh;
}
unsigned CConfig::GetMIDIButtonNotes (void) const
{
return m_nMIDIButtonNotes;
}
unsigned CConfig::GetMIDIButtonPrev (void) const
{
return m_nMIDIButtonPrev;
}
unsigned CConfig::GetMIDIButtonNext (void) const
{
return m_nMIDIButtonNext;
}
unsigned CConfig::GetMIDIButtonBack (void) const
{
return m_nMIDIButtonBack;
}
unsigned CConfig::GetMIDIButtonSelect (void) const
{
return m_nMIDIButtonSelect;
}
unsigned CConfig::GetMIDIButtonHome (void) const
{
return m_nMIDIButtonHome;
}
bool CConfig::GetEncoderEnabled (void) const
{
return m_bEncoderEnabled;
}
unsigned CConfig::GetEncoderPinClock (void) const
{
return m_nEncoderPinClock;
}
unsigned CConfig::GetEncoderPinData (void) const
{
return m_nEncoderPinData;
}
bool CConfig::GetMIDIDumpEnabled (void) const
{
return m_bMIDIDumpEnabled;
}
bool CConfig::GetProfileEnabled (void) const
{
return m_bProfileEnabled;
}
bool CConfig::GetPerformanceSelectToLoad (void) const
{
return m_bPerformanceSelectToLoad;
}
//
// 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);
#ifdef ARM_ALLOW_MULTI_CORE
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 256);
#else
m_nChunkSize = m_Properties.GetNumber ("ChunkSize", m_SoundDevice == "hdmi" ? 384*6 : 1024);
#endif
m_nDACI2CAddress = m_Properties.GetNumber ("DACI2CAddress", 0);
m_bChannelsSwapped = m_Properties.GetNumber ("ChannelsSwapped", 0) != 0;
m_nMIDIBaudRate = m_Properties.GetNumber ("MIDIBaudRate", 31250);
const char *pMIDIThru = m_Properties.GetString ("MIDIThru");
if (pMIDIThru)
{
std::string Arg (pMIDIThru);
size_t nPos = Arg.find (',');
if (nPos != std::string::npos)
{
m_MIDIThruIn = Arg.substr (0, nPos);
m_MIDIThruOut = Arg.substr (nPos+1);
if ( m_MIDIThruIn.empty ()
|| m_MIDIThruOut.empty ())
{
m_MIDIThruIn.clear ();
m_MIDIThruOut.clear ();
}
}
}
m_bMIDIRXProgramChange = m_Properties.GetNumber ("MIDIRXProgramChange", 1) != 0;
m_bIgnoreAllNotesOff = m_Properties.GetNumber ("IgnoreAllNotesOff", 0) != 0;
m_bMIDIAutoVoiceDumpOnPC = m_Properties.GetNumber ("MIDIAutoVoiceDumpOnPC", 1) != 0;
m_bLCDEnabled = m_Properties.GetNumber ("LCDEnabled", 0) != 0;
m_nLCDPinEnable = m_Properties.GetNumber ("LCDPinEnable", 4);
m_nLCDPinRegisterSelect = m_Properties.GetNumber ("LCDPinRegisterSelect", 27);
m_nLCDPinReadWrite = m_Properties.GetNumber ("LCDPinReadWrite", 0);
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_nLCDI2CAddress = m_Properties.GetNumber ("LCDI2CAddress", 0);
m_nSSD1306LCDI2CAddress = m_Properties.GetNumber ("SSD1306LCDI2CAddress", 0);
m_nSSD1306LCDWidth = m_Properties.GetNumber ("SSD1306LCDWidth", 128);
m_nSSD1306LCDHeight = m_Properties.GetNumber ("SSD1306LCDHeight", 32);
m_bSSD1306LCDRotate = m_Properties.GetNumber ("SSD1306LCDRotate", 0) != 0;
m_bSSD1306LCDMirror = m_Properties.GetNumber ("SSD1306LCDMirror", 0) != 0;
m_nLCDColumns = m_Properties.GetNumber ("LCDColumns", 16);
m_nLCDRows = m_Properties.GetNumber ("LCDRows", 2);
m_nButtonPinPrev = m_Properties.GetNumber ("ButtonPinPrev", 0);
m_nButtonPinNext = m_Properties.GetNumber ("ButtonPinNext", 0);
m_nButtonPinBack = m_Properties.GetNumber ("ButtonPinBack", 11);
m_nButtonPinSelect = m_Properties.GetNumber ("ButtonPinSelect", 11);
m_nButtonPinHome = m_Properties.GetNumber ("ButtonPinHome", 11);
m_nButtonPinShortcut = m_Properties.GetNumber ("ButtonPinShortcut", 11);
m_ButtonActionPrev = m_Properties.GetString ("ButtonActionPrev", "");
m_ButtonActionNext = m_Properties.GetString ("ButtonActionNext", "");
m_ButtonActionBack = m_Properties.GetString ("ButtonActionBack", "doubleclick");
m_ButtonActionSelect = m_Properties.GetString ("ButtonActionSelect", "click");
m_ButtonActionHome = m_Properties.GetString ("ButtonActionHome", "longpress");
m_nDoubleClickTimeout = m_Properties.GetNumber ("DoubleClickTimeout", 400);
m_nLongPressTimeout = m_Properties.GetNumber ("LongPressTimeout", 600);
m_nMIDIButtonCh = m_Properties.GetNumber ("MIDIButtonCh", 0);
m_nMIDIButtonNotes = m_Properties.GetNumber ("MIDIButtonNotes", 0);
m_nMIDIButtonPrev = m_Properties.GetNumber ("MIDIButtonPrev", 0);
m_nMIDIButtonNext = m_Properties.GetNumber ("MIDIButtonNext", 0);
m_nMIDIButtonBack = m_Properties.GetNumber ("MIDIButtonBack", 0);
m_nMIDIButtonSelect = m_Properties.GetNumber ("MIDIButtonSelect", 0);
m_nMIDIButtonHome = m_Properties.GetNumber ("MIDIButtonHome", 0);
m_bEncoderEnabled = m_Properties.GetNumber ("EncoderEnabled", 0) != 0;
m_nEncoderPinClock = m_Properties.GetNumber ("EncoderPinClock", 10);
m_nEncoderPinData = m_Properties.GetNumber ("EncoderPinData", 9);
m_bMIDIDumpEnabled = m_Properties.GetNumber ("MIDIDumpEnabled", 0) != 0;
m_bProfileEnabled = m_Properties.GetNumber ("ProfileEnabled", 0) != 0;
m_bPerformanceSelectToLoad = m_Properties.GetNumber ("PerformanceSelectToLoad", 1) != 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;
}
bool CConfig::GetChannelsSwapped (void) const
{
return m_bChannelsSwapped;
}
unsigned CConfig::GetMIDIBaudRate (void) const
{
return m_nMIDIBaudRate;
}
const char *CConfig::GetMIDIThruIn (void) const
{
return m_MIDIThruIn.c_str ();
}
const char *CConfig::GetMIDIThruOut (void) const
{
return m_MIDIThruOut.c_str ();
}
bool CConfig::GetMIDIRXProgramChange (void) const
{
return m_bMIDIRXProgramChange;
}
bool CConfig::GetIgnoreAllNotesOff (void) const
{
return m_bIgnoreAllNotesOff;
}
bool CConfig::GetMIDIAutoVoiceDumpOnPC (void) const
{
return m_bMIDIAutoVoiceDumpOnPC;
}
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;
}
unsigned CConfig::GetLCDI2CAddress (void) const
{
return m_nLCDI2CAddress;
}
unsigned CConfig::GetSSD1306LCDI2CAddress (void) const
{
return m_nSSD1306LCDI2CAddress;
}
unsigned CConfig::GetSSD1306LCDWidth (void) const
{
return m_nSSD1306LCDWidth;
}
unsigned CConfig::GetSSD1306LCDHeight (void) const
{
return m_nSSD1306LCDHeight;
}
bool CConfig::GetSSD1306LCDRotate (void) const
{
return m_bSSD1306LCDRotate;
}
bool CConfig::GetSSD1306LCDMirror (void) const
{
return m_bSSD1306LCDMirror;
}
unsigned CConfig::GetLCDColumns (void) const
{
return m_nLCDColumns;
}
unsigned CConfig::GetLCDRows (void) const
{
return m_nLCDRows;
}
unsigned CConfig::GetButtonPinPrev (void) const
{
return m_nButtonPinPrev;
}
unsigned CConfig::GetButtonPinNext (void) const
{
return m_nButtonPinNext;
}
unsigned CConfig::GetButtonPinBack (void) const
{
return m_nButtonPinBack;
}
unsigned CConfig::GetButtonPinSelect (void) const
{
return m_nButtonPinSelect;
}
unsigned CConfig::GetButtonPinHome (void) const
{
return m_nButtonPinHome;
}
unsigned CConfig::GetButtonPinShortcut (void) const
{
return m_nButtonPinShortcut;
}
const char *CConfig::GetButtonActionPrev (void) const
{
return m_ButtonActionPrev.c_str();
}
const char *CConfig::GetButtonActionNext (void) const
{
return m_ButtonActionNext.c_str();
}
const char *CConfig::GetButtonActionBack (void) const
{
return m_ButtonActionBack.c_str();
}
const char *CConfig::GetButtonActionSelect (void) const
{
return m_ButtonActionSelect.c_str();
}
const char *CConfig::GetButtonActionHome (void) const
{
return m_ButtonActionHome.c_str();
}
unsigned CConfig::GetDoubleClickTimeout (void) const
{
return m_nDoubleClickTimeout;
}
unsigned CConfig::GetLongPressTimeout (void) const
{
return m_nLongPressTimeout;
}
unsigned CConfig::GetMIDIButtonCh (void) const
{
return m_nMIDIButtonCh;
}
unsigned CConfig::GetMIDIButtonNotes (void) const
{
return m_nMIDIButtonNotes;
}
unsigned CConfig::GetMIDIButtonPrev (void) const
{
return m_nMIDIButtonPrev;
}
unsigned CConfig::GetMIDIButtonNext (void) const
{
return m_nMIDIButtonNext;
}
unsigned CConfig::GetMIDIButtonBack (void) const
{
return m_nMIDIButtonBack;
}
unsigned CConfig::GetMIDIButtonSelect (void) const
{
return m_nMIDIButtonSelect;
}
unsigned CConfig::GetMIDIButtonHome (void) const
{
return m_nMIDIButtonHome;
}
bool CConfig::GetEncoderEnabled (void) const
{
return m_bEncoderEnabled;
}
unsigned CConfig::GetEncoderPinClock (void) const
{
return m_nEncoderPinClock;
}
unsigned CConfig::GetEncoderPinData (void) const
{
return m_nEncoderPinData;
}
bool CConfig::GetMIDIDumpEnabled (void) const
{
return m_bMIDIDumpEnabled;
}
bool CConfig::GetProfileEnabled (void) const
{
return m_bProfileEnabled;
}
bool CConfig::GetPerformanceSelectToLoad (void) const
{
return m_bPerformanceSelectToLoad;
}

@ -1,207 +1,213 @@
//
// 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 <circle/sysconfig.h>
#include <string>
class CConfig // Configuration for MiniDexed
{
public:
#ifndef ARM_ALLOW_MULTI_CORE
static const unsigned ToneGenerators = 1;
#else
static const unsigned TGsCore1 = 2; // process 2 TGs on core 1
static const unsigned TGsCore23 = 3; // process 3 TGs on core 2 and 3 each
static const unsigned ToneGenerators = TGsCore1 + 2*TGsCore23;
#endif
#if RASPPI == 1
static const unsigned MaxNotes = 8; // polyphony
#else
static const unsigned MaxNotes = 16;
#endif
static const unsigned MaxChunkSize = 4096;
#if RASPPI <= 3
static const unsigned MaxUSBMIDIDevices = 2;
#else
static const unsigned MaxUSBMIDIDevices = 4;
#endif
// TODO - Leave this for uimenu.cpp for now, but it will need to be dynamic at some point...
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
bool GetChannelsSwapped (void) const;
// MIDI
unsigned GetMIDIBaudRate (void) const;
const char *GetMIDIThruIn (void) const; // "" if not specified
const char *GetMIDIThruOut (void) const; // "" if not specified
bool GetMIDIRXProgramChange (void) const; // true if not specified
bool GetIgnoreAllNotesOff (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;
unsigned GetLCDI2CAddress (void) const;
// SSD1306 LCD
unsigned GetSSD1306LCDI2CAddress (void) const;
unsigned GetSSD1306LCDWidth (void) const;
unsigned GetSSD1306LCDHeight (void) const;
unsigned GetLCDColumns (void) const;
unsigned GetLCDRows (void) const;
// GPIO Button Navigation
// GPIO pin numbers are chip numbers, not header positions
unsigned GetButtonPinPrev (void) const;
unsigned GetButtonPinNext (void) const;
unsigned GetButtonPinBack (void) const;
unsigned GetButtonPinSelect (void) const;
unsigned GetButtonPinHome (void) const;
unsigned GetButtonPinShortcut (void) const;
// Action type for buttons: "click", "doubleclick", "longpress", ""
const char *GetButtonActionPrev (void) const;
const char *GetButtonActionNext (void) const;
const char *GetButtonActionBack (void) const;
const char *GetButtonActionSelect (void) const;
const char *GetButtonActionHome (void) const;
// Timeouts for button events in milliseconds
unsigned GetDoubleClickTimeout (void) const;
unsigned GetLongPressTimeout (void) const;
// MIDI Button Navigation
unsigned GetMIDIButtonCh (void) const;
unsigned GetMIDIButtonNotes (void) const;
unsigned GetMIDIButtonPrev (void) const;
unsigned GetMIDIButtonNext (void) const;
unsigned GetMIDIButtonBack (void) const;
unsigned GetMIDIButtonSelect (void) const;
unsigned GetMIDIButtonHome (void) const;
// KY-040 Rotary Encoder
// GPIO pin numbers are chip numbers, not header positions
bool GetEncoderEnabled (void) const;
unsigned GetEncoderPinClock (void) const;
unsigned GetEncoderPinData (void) const;
// Debug
bool GetMIDIDumpEnabled (void) const;
bool GetProfileEnabled (void) const;
// Load performance mode. 0 for load just rotating encoder, 1 load just when Select is pushed
bool GetPerformanceSelectToLoad (void) const;
private:
CPropertiesFatFsFile m_Properties;
std::string m_SoundDevice;
unsigned m_nSampleRate;
unsigned m_nChunkSize;
unsigned m_nDACI2CAddress;
bool m_bChannelsSwapped;
unsigned m_nMIDIBaudRate;
std::string m_MIDIThruIn;
std::string m_MIDIThruOut;
bool m_bMIDIRXProgramChange;
bool m_bIgnoreAllNotesOff;
bool m_bLCDEnabled;
unsigned m_nLCDPinEnable;
unsigned m_nLCDPinRegisterSelect;
unsigned m_nLCDPinReadWrite;
unsigned m_nLCDPinData4;
unsigned m_nLCDPinData5;
unsigned m_nLCDPinData6;
unsigned m_nLCDPinData7;
unsigned m_nLCDI2CAddress;
unsigned m_nSSD1306LCDI2CAddress;
unsigned m_nSSD1306LCDWidth;
unsigned m_nSSD1306LCDHeight;
unsigned m_nLCDColumns;
unsigned m_nLCDRows;
unsigned m_nButtonPinPrev;
unsigned m_nButtonPinNext;
unsigned m_nButtonPinBack;
unsigned m_nButtonPinSelect;
unsigned m_nButtonPinHome;
unsigned m_nButtonPinShortcut;
std::string m_ButtonActionPrev;
std::string m_ButtonActionNext;
std::string m_ButtonActionBack;
std::string m_ButtonActionSelect;
std::string m_ButtonActionHome;
unsigned m_nDoubleClickTimeout;
unsigned m_nLongPressTimeout;
unsigned m_nMIDIButtonCh;
unsigned m_nMIDIButtonNotes;
unsigned m_nMIDIButtonPrev;
unsigned m_nMIDIButtonNext;
unsigned m_nMIDIButtonBack;
unsigned m_nMIDIButtonSelect;
unsigned m_nMIDIButtonHome;
bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
unsigned m_nEncoderPinData;
bool m_bMIDIDumpEnabled;
bool m_bProfileEnabled;
bool m_bPerformanceSelectToLoad;
};
#endif
//
// 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 <circle/sysconfig.h>
#include <string>
class CConfig // Configuration for MiniDexed
{
public:
#ifndef ARM_ALLOW_MULTI_CORE
static const unsigned ToneGenerators = 1;
#else
static const unsigned TGsCore1 = 2; // process 2 TGs on core 1
static const unsigned TGsCore23 = 3; // process 3 TGs on core 2 and 3 each
static const unsigned ToneGenerators = TGsCore1 + 2*TGsCore23;
#endif
#if RASPPI == 1
static const unsigned MaxNotes = 8; // polyphony
#else
static const unsigned MaxNotes = 16;
#endif
static const unsigned MaxChunkSize = 4096;
#if RASPPI <= 3
static const unsigned MaxUSBMIDIDevices = 2;
#else
static const unsigned MaxUSBMIDIDevices = 4;
#endif
// TODO - Leave this for uimenu.cpp for now, but it will need to be dynamic at some point...
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
bool GetChannelsSwapped (void) const;
// MIDI
unsigned GetMIDIBaudRate (void) const;
const char *GetMIDIThruIn (void) const; // "" if not specified
const char *GetMIDIThruOut (void) const; // "" if not specified
bool GetMIDIRXProgramChange (void) const; // true if not specified
bool GetIgnoreAllNotesOff (void) const;
bool GetMIDIAutoVoiceDumpOnPC (void) const; // true if not specified
// 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;
unsigned GetLCDI2CAddress (void) const;
// SSD1306 LCD
unsigned GetSSD1306LCDI2CAddress (void) const;
unsigned GetSSD1306LCDWidth (void) const;
unsigned GetSSD1306LCDHeight (void) const;
bool GetSSD1306LCDRotate (void) const;
bool GetSSD1306LCDMirror (void) const;
unsigned GetLCDColumns (void) const;
unsigned GetLCDRows (void) const;
// GPIO Button Navigation
// GPIO pin numbers are chip numbers, not header positions
unsigned GetButtonPinPrev (void) const;
unsigned GetButtonPinNext (void) const;
unsigned GetButtonPinBack (void) const;
unsigned GetButtonPinSelect (void) const;
unsigned GetButtonPinHome (void) const;
unsigned GetButtonPinShortcut (void) const;
// Action type for buttons: "click", "doubleclick", "longpress", ""
const char *GetButtonActionPrev (void) const;
const char *GetButtonActionNext (void) const;
const char *GetButtonActionBack (void) const;
const char *GetButtonActionSelect (void) const;
const char *GetButtonActionHome (void) const;
// Timeouts for button events in milliseconds
unsigned GetDoubleClickTimeout (void) const;
unsigned GetLongPressTimeout (void) const;
// MIDI Button Navigation
unsigned GetMIDIButtonCh (void) const;
unsigned GetMIDIButtonNotes (void) const;
unsigned GetMIDIButtonPrev (void) const;
unsigned GetMIDIButtonNext (void) const;
unsigned GetMIDIButtonBack (void) const;
unsigned GetMIDIButtonSelect (void) const;
unsigned GetMIDIButtonHome (void) const;
// KY-040 Rotary Encoder
// GPIO pin numbers are chip numbers, not header positions
bool GetEncoderEnabled (void) const;
unsigned GetEncoderPinClock (void) const;
unsigned GetEncoderPinData (void) const;
// Debug
bool GetMIDIDumpEnabled (void) const;
bool GetProfileEnabled (void) const;
// Load performance mode. 0 for load just rotating encoder, 1 load just when Select is pushed
bool GetPerformanceSelectToLoad (void) const;
private:
CPropertiesFatFsFile m_Properties;
std::string m_SoundDevice;
unsigned m_nSampleRate;
unsigned m_nChunkSize;
unsigned m_nDACI2CAddress;
bool m_bChannelsSwapped;
unsigned m_nMIDIBaudRate;
std::string m_MIDIThruIn;
std::string m_MIDIThruOut;
bool m_bMIDIRXProgramChange;
bool m_bIgnoreAllNotesOff;
bool m_bMIDIAutoVoiceDumpOnPC;
bool m_bLCDEnabled;
unsigned m_nLCDPinEnable;
unsigned m_nLCDPinRegisterSelect;
unsigned m_nLCDPinReadWrite;
unsigned m_nLCDPinData4;
unsigned m_nLCDPinData5;
unsigned m_nLCDPinData6;
unsigned m_nLCDPinData7;
unsigned m_nLCDI2CAddress;
unsigned m_nSSD1306LCDI2CAddress;
unsigned m_nSSD1306LCDWidth;
unsigned m_nSSD1306LCDHeight;
bool m_bSSD1306LCDRotate;
bool m_bSSD1306LCDMirror;
unsigned m_nLCDColumns;
unsigned m_nLCDRows;
unsigned m_nButtonPinPrev;
unsigned m_nButtonPinNext;
unsigned m_nButtonPinBack;
unsigned m_nButtonPinSelect;
unsigned m_nButtonPinHome;
unsigned m_nButtonPinShortcut;
std::string m_ButtonActionPrev;
std::string m_ButtonActionNext;
std::string m_ButtonActionBack;
std::string m_ButtonActionSelect;
std::string m_ButtonActionHome;
unsigned m_nDoubleClickTimeout;
unsigned m_nLongPressTimeout;
unsigned m_nMIDIButtonCh;
unsigned m_nMIDIButtonNotes;
unsigned m_nMIDIButtonPrev;
unsigned m_nMIDIButtonNext;
unsigned m_nMIDIButtonBack;
unsigned m_nMIDIButtonSelect;
unsigned m_nMIDIButtonHome;
bool m_bEncoderEnabled;
unsigned m_nEncoderPinClock;
unsigned m_nEncoderPinData;
bool m_bMIDIDumpEnabled;
bool m_bProfileEnabled;
bool m_bPerformanceSelectToLoad;
};
#endif

@ -206,7 +206,7 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{
// MIDI SYSEX per MIDI channel
uint8_t ucSysExChannel = (pMessage[2] & 0x07);
uint8_t ucSysExChannel = (pMessage[2] & 0x0F);
if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode)
{
LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG);

@ -20,9 +20,9 @@
#include "minidexed.h"
#include <circle/logger.h>
#include <circle/memory.h>
#include <circle/pwmsoundbasedevice.h>
#include <circle/i2ssoundbasedevice.h>
#include <circle/hdmisoundbasedevice.h>
#include <circle/sound/pwmsoundbasedevice.h>
#include <circle/sound/i2ssoundbasedevice.h>
#include <circle/sound/hdmisoundbasedevice.h>
#include <circle/gpiopin.h>
#include <string.h>
#include <stdio.h>
@ -459,6 +459,8 @@ void CMiniDexed::BankSelectLSB (unsigned nBankLSB, unsigned nTG)
void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
{
assert (m_pConfig);
nProgram=constrain((int)nProgram,0,31);
assert (nTG < CConfig::ToneGenerators);
@ -469,7 +471,16 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG)
assert (m_pTG[nTG]);
m_pTG[nTG]->loadVoiceParameters (Buffer);
m_SerialMIDI.SendSystemExclusiveVoice(nProgram,0,nTG);
if (m_pConfig->GetMIDIAutoVoiceDumpOnPC())
{
// Only do the voice dump back out over MIDI if we have a specific
// MIDI channel configured for this TG
if (m_nMIDIChannel[nTG] < CMIDIDevice::Channels)
{
m_SerialMIDI.SendSystemExclusiveVoice(nProgram,0,nTG);
}
}
m_UI.ParameterChanged ();
}
@ -600,6 +611,8 @@ void CMiniDexed::SetResonance (int nResonance, unsigned nTG)
void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
assert (uchChannel < CMIDIDevice::ChannelUnknown);
m_nMIDIChannel[nTG] = uchChannel;
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
@ -1370,14 +1383,57 @@ void CMiniDexed::ProcessSound (void)
#ifdef PLATE_REVERB_ENABLE
// swap stereo channels if needed
uint8_t indexL=0, indexR=1;
if (m_bChannelsSwapped)
#ifdef MIXING_CONSOLE_ENABLE
// // swap stereo channels if needed
uint8_t indexL = StereoChannels::Left;
uint8_t indexR = StereoChannels::Right;
if(this->m_bChannelsSwapped)
{
indexL=1;
indexR=0;
indexL = StereoChannels::Left;
indexR = StereoChannels::Right;
}
// BEGIN TG mixing
float32_t tmp_float[nFrames * 2];
int16_t tmp_int[nFrames * 2];
float32_t SampleBuffer[2][nFrames];
if(nMasterVolume > 0.0f)
{
for (uint8_t i = 0; i < CConfig::ToneGenerators; i++)
{
this->mixing_console_->setInputSampleBuffer(i, m_OutputLevel[i]);
}
this->m_FXSpinLock.Acquire ();
this->mixing_console_->process(SampleBuffer[indexL], SampleBuffer[indexR]);
this->m_FXSpinLock.Release ();
// Convert dual float array (left, right) to single int16 array (left/right)
this->nMasterVolume = constrain(this->nMasterVolume, 0.0f, 1.0f);
if(this->nMasterVolume == 1.0f)
{
memcpy(tmp_float, SampleBuffer[indexL], nFrames * sizeof(float32_t));
memcpy(tmp_float + nFrames, SampleBuffer[indexR], nFrames * sizeof(float32_t));
}
else // 0.0 < this->nMasterVolume < 1.0
{
arm_scale_f32(SampleBuffer[indexL], this->nMasterVolume, tmp_float, nFrames);
arm_scale_f32(SampleBuffer[indexR], this->nMasterVolume, tmp_float + nFrames, nFrames);
}
arm_float_to_q15(tmp_float, tmp_int, nFrames * 2);
}
else
arm_fill_q15(0, tmp_int, nFrames * 2);
#endif
#ifdef PLATE_REVERB_ENABLE
uint8_t indexL=0, indexR=1;
// BEGIN TG mixing
float32_t tmp_float[nFrames*2];
int16_t tmp_int[nFrames*2];
@ -1438,6 +1494,14 @@ void CMiniDexed::ProcessSound (void)
#endif
// END adding FXRack
// swap stereo channels if needed prior to writing back out
if (m_bChannelsSwapped)
{
indexL=1;
indexR=0;
}
// Convert dual float array (left, right) to single int16 array (left/right)
for(uint16_t i=0; i<nFrames;i++)
{
@ -1804,7 +1868,7 @@ void CMiniDexed::getSysExVoiceDump(uint8_t* dest, uint8_t nTG)
dest[0] = 0xF0; // SysEx start
dest[1] = 0x43; // ID=Yamaha
dest[2] = GetTGParameter(TGParameterMIDIChannel, nTG); // Sub-status and MIDI channel
dest[2] = 0x00 | m_nMIDIChannel[nTG]; // 0x0c Sub-status 0 and MIDI channel
dest[3] = 0x00; // Format number (0=1 voice)
dest[4] = 0x01; // Byte count MSB
dest[5] = 0x1B; // Byte count LSB

@ -38,7 +38,7 @@
#include <circle/gpiomanager.h>
#include <circle/i2cmaster.h>
#include <circle/multicore.h>
#include <circle/soundbasedevice.h>
#include <circle/sound/soundbasedevice.h>
#include <circle/spinlock.h>
#include "common.h"
#include "effect_mixer.hpp"

@ -1,86 +1,89 @@
#
# minidexed.ini
#
# Sound device
#SoundDevice=i2s
SoundDevice=pwm
#SoundDevice=hdmi
SampleRate=48000
#ChunkSize=256
DACI2CAddress=0
ChannelsSwapped=0
# MIDI
MIDIBaudRate=31250
#MIDIThru=umidi1,ttyS1
MIDIRXProgramChange=1
IgnoreAllNotesOff=0
# HD44780 LCD
LCDEnabled=1
LCDPinEnable=17
LCDPinRegisterSelect=4
LCDPinReadWrite=0
LCDPinData4=22
LCDPinData5=23
LCDPinData6=24
LCDPinData7=25
LCDI2CAddress=0x00
# SSD1306 LCD
# For a 128x32 display, set LCDColumns=20; LCDRows=2
# For a 128x64 display, set LCDColumns=20; LCDRows=4
SSD1306LCDI2CAddress=0x0
SSD1306LCDWidth=128
SSD1306LCDHeight=32
# Default is 16x2 display (e.g. HD44780)
LCDColumns=16
LCDRows=2
# GPIO Button Navigation
# Any buttons set to 0 will be ignored
ButtonPinPrev=0
ButtonActionPrev=
ButtonPinNext=0
ButtonActionNext=
ButtonPinBack=11
ButtonActionBack=longpress
ButtonPinSelect=11
ButtonActionSelect=click
ButtonPinHome=11
ButtonActionHome=doubleclick
ButtonPinShortcut=11
# (Shortcut doesn't have an action)
# Timeouts in milliseconds for double click and long press
DoubleClickTimeout=400
LongPressTimeout=400
# MIDI Button Navigation
# Specify MIDI CC to act as a button
# NB: Off < 64 < ON
# CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni
# If MIDIButtonNotes>0 then treat MIDIButton
# numbers as MIDI Note numbers note CC numbers.
MIDIButtonCh=0
MIDIButtonNotes=0
MIDIButtonNotes=1
MIDIButtonPrev=00
MIDIButtonNext=02
MIDIButtonBack=03
MIDIButtonSelect=04
MIDIButtonHome=06
# KY-040 Rotary Encoder
EncoderEnabled=1
EncoderPinClock=10
EncoderPinData=9
# Debug
MIDIDumpEnabled=0
ProfileEnabled=0
# Performance
PerformanceSelectToLoad=1
#
# minidexed.ini
#
# Sound device
#SoundDevice=i2s
SoundDevice=pwm
#SoundDevice=hdmi
SampleRate=48000
#ChunkSize=256
DACI2CAddress=0
ChannelsSwapped=0
# MIDI
MIDIBaudRate=31250
#MIDIThru=umidi1,ttyS1
MIDIRXProgramChange=1
IgnoreAllNotesOff=0
MIDIAutoVoiceDumpOnPC=1
# HD44780 LCD
LCDEnabled=1
LCDPinEnable=17
LCDPinRegisterSelect=4
LCDPinReadWrite=0
LCDPinData4=22
LCDPinData5=23
LCDPinData6=24
LCDPinData7=25
LCDI2CAddress=0x00
# SSD1306 LCD
# For a 128x32 display, set LCDColumns=20; LCDRows=2
# For a 128x64 display, set LCDColumns=20; LCDRows=4
SSD1306LCDI2CAddress=0x0
SSD1306LCDWidth=128
SSD1306LCDHeight=32
SSD1306LCDRotate=0
SSD1306LCDMirror=0
# Default is 16x2 display (e.g. HD44780)
LCDColumns=16
LCDRows=2
# GPIO Button Navigation
# Any buttons set to 0 will be ignored
ButtonPinPrev=0
ButtonActionPrev=
ButtonPinNext=0
ButtonActionNext=
ButtonPinBack=11
ButtonActionBack=longpress
ButtonPinSelect=11
ButtonActionSelect=click
ButtonPinHome=11
ButtonActionHome=doubleclick
ButtonPinShortcut=11
# (Shortcut doesn't have an action)
# Timeouts in milliseconds for double click and long press
DoubleClickTimeout=400
LongPressTimeout=400
# MIDI Button Navigation
# Specify MIDI CC to act as a button
# NB: Off < 64 < ON
# CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni
# If MIDIButtonNotes>0 then treat MIDIButton
# numbers as MIDI Note numbers note CC numbers.
MIDIButtonCh=0
MIDIButtonNotes=0
MIDIButtonNotes=1
MIDIButtonPrev=00
MIDIButtonNext=02
MIDIButtonBack=03
MIDIButtonSelect=04
MIDIButtonHome=06
# KY-040 Rotary Encoder
EncoderEnabled=1
EncoderPinClock=10
EncoderPinData=9
# Debug
MIDIDumpEnabled=0
ProfileEnabled=0
# Performance
PerformanceSelectToLoad=1

@ -1,171 +1,177 @@
//
// serialmididevice.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 "serialmididevice.h"
#include <assert.h>
LOGMODULE("serialmididevice");
CSerialMIDIDevice::CSerialMIDIDevice (CMiniDexed *pSynthesizer, CInterruptSystem *pInterrupt,
CConfig *pConfig, CUserInterface *pUI)
: CMIDIDevice (pSynthesizer, pConfig, pUI),
m_pConfig (pConfig),
m_Serial (pInterrupt, TRUE),
m_nSerialState (0),
m_nSysEx (0),
m_SendBuffer (&m_Serial)
{
AddDevice ("ttyS1");
}
CSerialMIDIDevice::~CSerialMIDIDevice (void)
{
m_nSerialState = 255;
}
boolean CSerialMIDIDevice::Initialize (void)
{
assert (m_pConfig);
boolean res = m_Serial.Initialize (m_pConfig->GetMIDIBaudRate ());
unsigned ser_options = m_Serial.GetOptions();
// Ensure CR->CRLF translation is disabled for MIDI links
ser_options &= ~(SERIAL_OPTION_ONLCR);
m_Serial.SetOptions(ser_options);
return res;
}
void CSerialMIDIDevice::Process (void)
{
m_SendBuffer.Update ();
// Read serial MIDI data
u8 Buffer[100];
int nResult = m_Serial.Read (Buffer, sizeof Buffer);
if (nResult <= 0)
{
if(nResult!=0)
LOGERR("Serial.Read() error: %d\n",nResult);
return;
}
if (m_pConfig->GetMIDIDumpEnabled ())
{
printf("Incoming MIDI data:");
for (uint16_t i = 0; i < nResult; i++)
{
if((i % 8) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",Buffer[i]);
}
printf("\n");
}
// Process MIDI messages
// See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message
// "Running status" see: https://www.lim.di.unimi.it/IEEE/MIDI/SOT5.HTM#Running-
for (int i = 0; i < nResult; i++)
{
u8 uchData = Buffer[i];
if(uchData == 0xF0)
{
// SYSEX found
m_SerialMessage[m_nSysEx++]=uchData;
continue;
}
if(m_nSysEx > 0)
{
m_SerialMessage[m_nSysEx++]=uchData;
if ((uchData & 0x80) == 0x80 || m_nSysEx >= MAX_MIDI_MESSAGE)
{
if(uchData == 0xF7)
MIDIMessageHandler (m_SerialMessage, m_nSysEx);
m_nSysEx = 0;
}
continue;
}
else
{
switch (m_nSerialState)
{
case 0:
MIDIRestart:
if ( (uchData & 0x80) == 0x80 // status byte, all channels
&& (uchData & 0xF0) != 0xF0) // ignore system messages
{
m_SerialMessage[m_nSerialState++] = uchData;
}
break;
case 1:
case 2:
DATABytes:
if (uchData & 0x80) // got status when parameter expected
{
m_nSerialState = 0;
goto MIDIRestart;
}
m_SerialMessage[m_nSerialState++] = uchData;
if ( (m_SerialMessage[0] & 0xE0) == 0xC0
|| m_nSerialState == 3 // message is complete
|| (m_SerialMessage[0] & 0xF0) == 0xD0) // channel aftertouch
{
MIDIMessageHandler (m_SerialMessage, m_nSerialState);
m_nSerialState = 4; // State 4 for test if 4th byte is a status byte or a data byte
}
break;
case 4:
if ((uchData & 0x80) == 0) // true data byte, false status byte
{
m_nSerialState = 1;
goto DATABytes;
}
else
{
m_nSerialState = 0;
goto MIDIRestart;
}
break;
default:
assert (0);
break;
}
}
}
}
void CSerialMIDIDevice::Send (const u8 *pMessage, size_t nLength, unsigned nCable)
{
m_SendBuffer.Write (pMessage, nLength);
}
//
// serialmididevice.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 "serialmididevice.h"
#include <assert.h>
LOGMODULE("serialmididevice");
CSerialMIDIDevice::CSerialMIDIDevice (CMiniDexed *pSynthesizer, CInterruptSystem *pInterrupt,
CConfig *pConfig, CUserInterface *pUI)
: CMIDIDevice (pSynthesizer, pConfig, pUI),
m_pConfig (pConfig),
m_Serial (pInterrupt, TRUE),
m_nSerialState (0),
m_nSysEx (0),
m_SendBuffer (&m_Serial)
{
AddDevice ("ttyS1");
}
CSerialMIDIDevice::~CSerialMIDIDevice (void)
{
m_nSerialState = 255;
}
boolean CSerialMIDIDevice::Initialize (void)
{
assert (m_pConfig);
boolean res = m_Serial.Initialize (m_pConfig->GetMIDIBaudRate ());
unsigned ser_options = m_Serial.GetOptions();
// Ensure CR->CRLF translation is disabled for MIDI links
ser_options &= ~(SERIAL_OPTION_ONLCR);
m_Serial.SetOptions(ser_options);
return res;
}
void CSerialMIDIDevice::Process (void)
{
m_SendBuffer.Update ();
// Read serial MIDI data
u8 Buffer[100];
int nResult = m_Serial.Read (Buffer, sizeof Buffer);
if (nResult <= 0)
{
if(nResult!=0)
LOGERR("Serial.Read() error: %d\n",nResult);
return;
}
if (m_pConfig->GetMIDIDumpEnabled ())
{
printf("Incoming MIDI data:");
for (uint16_t i = 0; i < nResult; i++)
{
if((i % 8) == 0)
printf("\n%04d:",i);
printf(" 0x%02x",Buffer[i]);
}
printf("\n");
}
// Process MIDI messages
// See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message
// "Running status" see: https://www.lim.di.unimi.it/IEEE/MIDI/SOT5.HTM#Running-
for (int i = 0; i < nResult; i++)
{
u8 uchData = Buffer[i];
if(uchData == 0xF0)
{
// SYSEX found
m_SerialMessage[m_nSysEx++]=uchData;
continue;
}
// System Real Time messages may appear anywhere in the byte stream, so handle them specially
if(uchData == 0xF8 || uchData == 0xFA || uchData == 0xFB || uchData == 0xFC || uchData == 0xFE || uchData == 0xFF)
{
MIDIMessageHandler (&uchData, 1);
continue;
}
else if(m_nSysEx > 0)
{
m_SerialMessage[m_nSysEx++]=uchData;
if ((uchData & 0x80) == 0x80 || m_nSysEx >= MAX_MIDI_MESSAGE)
{
if(uchData == 0xF7)
MIDIMessageHandler (m_SerialMessage, m_nSysEx);
m_nSysEx = 0;
}
continue;
}
else
{
switch (m_nSerialState)
{
case 0:
MIDIRestart:
if ( (uchData & 0x80) == 0x80 // status byte, all channels
&& (uchData & 0xF0) != 0xF0) // ignore system messages
{
m_SerialMessage[m_nSerialState++] = uchData;
}
break;
case 1:
case 2:
DATABytes:
if (uchData & 0x80) // got status when parameter expected
{
m_nSerialState = 0;
goto MIDIRestart;
}
m_SerialMessage[m_nSerialState++] = uchData;
if ( (m_SerialMessage[0] & 0xE0) == 0xC0
|| m_nSerialState == 3 // message is complete
|| (m_SerialMessage[0] & 0xF0) == 0xD0) // channel aftertouch
{
MIDIMessageHandler (m_SerialMessage, m_nSerialState);
m_nSerialState = 4; // State 4 for test if 4th byte is a status byte or a data byte
}
break;
case 4:
if ((uchData & 0x80) == 0) // true data byte, false status byte
{
m_nSerialState = 1;
goto DATABytes;
}
else
{
m_nSerialState = 0;
goto MIDIRestart;
}
break;
default:
assert (0);
break;
}
}
}
}
void CSerialMIDIDevice::Send (const u8 *pMessage, size_t nLength, unsigned nCable)
{
m_SendBuffer.Write (pMessage, nLength);
}

@ -58,7 +58,9 @@ bool CUserInterface::Initialize (void)
unsigned i2caddr = m_pConfig->GetLCDI2CAddress ();
unsigned ssd1306addr = m_pConfig->GetSSD1306LCDI2CAddress ();
if (ssd1306addr != 0) {
m_pSSD1306 = new CSSD1306Device (m_pConfig->GetSSD1306LCDWidth (), m_pConfig->GetSSD1306LCDHeight (), m_pI2CMaster, ssd1306addr);
m_pSSD1306 = new CSSD1306Device (m_pConfig->GetSSD1306LCDWidth (), m_pConfig->GetSSD1306LCDHeight (),
m_pI2CMaster, ssd1306addr,
m_pConfig->GetSSD1306LCDRotate (), m_pConfig->GetSSD1306LCDMirror ());
LOGDBG ("LCD: SSD1306");
if (!m_pSSD1306->Initialize ())
{

Loading…
Cancel
Save