diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbd4bdd..7ee4ebb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: | diff --git a/src/Rules.mk b/src/Rules.mk index 9fe4536..d466b74 100644 --- a/src/Rules.mk +++ b/src/Rules.mk @@ -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) diff --git a/src/circle_stdlib_app.h b/src/circle_stdlib_app.h index a61ba49..3c38f0d 100644 --- a/src/circle_stdlib_app.h +++ b/src/circle_stdlib_app.h @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/** - * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * 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 diff --git a/src/config.cpp b/src/config.cpp index 48e5058..b5279a8 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -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 -// -// 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); -#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 +// +// 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); +#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; +} diff --git a/src/config.h b/src/config.h index ba0b56a..8a34239 100644 --- a/src/config.h +++ b/src/config.h @@ -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 -// -// 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 -#include - -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 +// +// 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 +#include + +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 diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 5b32b50..6eab469 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -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); diff --git a/src/minidexed.cpp b/src/minidexed.cpp index eb5a6c9..0a84471 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -20,9 +20,9 @@ #include "minidexed.h" #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -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 #include #include -#include +#include #include #include "common.h" #include "effect_mixer.hpp" diff --git a/src/minidexed.ini b/src/minidexed.ini index f882cbc..bb3edd7 100644 --- a/src/minidexed.ini +++ b/src/minidexed.ini @@ -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 diff --git a/src/serialmididevice.cpp b/src/serialmididevice.cpp index e6103b5..883fd4d 100644 --- a/src/serialmididevice.cpp +++ b/src/serialmididevice.cpp @@ -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 -// -// 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 -#include -#include "serialmididevice.h" -#include - -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 +// +// 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 +#include +#include "serialmididevice.h" +#include + +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); +} diff --git a/src/userinterface.cpp b/src/userinterface.cpp index 978a821..308c052 100644 --- a/src/userinterface.cpp +++ b/src/userinterface.cpp @@ -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 ()) {