diff --git a/build.sh b/build.sh
index ef9ce68..30772b1 100755
--- a/build.sh
+++ b/build.sh
@@ -14,19 +14,25 @@ else
export TOOLCHAIN_PREFIX="arm-none-eabi-"
fi
+# Define system options
+OPTIONS="-o USE_PWM_AUDIO_ON_ZERO -o SAVE_VFP_REGS_ON_IRQ -o REALTIME -o SCREEN_DMA_BURST_LENGTH=1"
+if [ "${RPI}" -gt "1" ]; then
+ OPTIONS="${OPTIONS} -o ARM_ALLOW_MULTI_CORE"
+fi
+
# Build circle-stdlib library
cd circle-stdlib/
make mrproper || true
-./configure -r ${RPI} --prefix "${TOOLCHAIN_PREFIX}"
-echo "DEFINE += -DUSE_PWM_AUDIO_ON_ZERO" >> libs/circle/Config.mk
-echo "DEFINE += -DSAVE_VFP_REGS_ON_IRQ" >> libs/circle/Config.mk
-echo "DEFINE += -DREALTIME" >> libs/circle/Config.mk
+./configure -r ${RPI} --prefix "${TOOLCHAIN_PREFIX}" ${OPTIONS}
make -j
# Build additional libraries
cd libs/circle/addon/display/
make clean || true
make -j
+cd ../sensor/
+make clean || true
+make -j
cd ../Properties/
make clean || true
make -j
diff --git a/circle-stdlib b/circle-stdlib
index 47d9deb..61cf3a4 160000
--- a/circle-stdlib
+++ b/circle-stdlib
@@ -1 +1 @@
-Subproject commit 47d9deb580f3d91201c2e275d3412abfbafdf156
+Subproject commit 61cf3a47bf93628039078b7c840e44432e52343e
diff --git a/src/Makefile b/src/Makefile
index f826c48..a5010e6 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,7 +8,7 @@ CMSIS_DIR = ../CMSIS_5/CMSIS
OBJS = main.o kernel.o minidexed.o config.o userinterface.o \
mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \
- sysexfileloader.o perftimer.o ky040.o
+ sysexfileloader.o perftimer.o
include ./Synth_Dexed.mk
include ./Rules.mk
diff --git a/src/Rules.mk b/src/Rules.mk
index 3552503..39b9f25 100644
--- a/src/Rules.mk
+++ b/src/Rules.mk
@@ -18,6 +18,7 @@ LIBS += \
$(NEWLIBDIR)/lib/libc.a \
$(NEWLIBDIR)/lib/libcirclenewlib.a \
$(CIRCLEHOME)/addon/display/libdisplay.a \
+ $(CIRCLEHOME)/addon/sensor/libsensor.a \
$(CIRCLEHOME)/addon/Properties/libproperties.a \
$(CIRCLEHOME)/addon/SDCard/libsdcard.a \
$(CIRCLEHOME)/lib/usb/libusb.a \
diff --git a/src/kernel.cpp b/src/kernel.cpp
index 2ad706d..1c9ed77 100644
--- a/src/kernel.cpp
+++ b/src/kernel.cpp
@@ -18,7 +18,6 @@
// along with this program. If not, see .
//
#include "kernel.h"
-#include
#include
#include
#include
@@ -65,27 +64,7 @@ bool CKernel::Initialize (void)
m_Config.Load ();
- // select the sound device
- const char *pSoundDevice = m_Config.GetSoundDevice ();
- if (strcmp (pSoundDevice, "i2s") == 0)
- {
- LOGNOTE ("I2S mode");
-
- m_pDexed = new CMiniDexedI2S (&m_Config, &mInterrupt, &m_GPIOManager, &m_I2CMaster);
- }
- else if (strcmp (pSoundDevice, "hdmi") == 0)
- {
- LOGNOTE ("HDMI mode");
-
- m_pDexed = new CMiniDexedHDMI (&m_Config, &mInterrupt, &m_GPIOManager);
- }
- else
- {
- LOGNOTE ("PWM mode");
-
- m_pDexed = new CMiniDexedPWM (&m_Config, &mInterrupt, &m_GPIOManager);
- }
-
+ m_pDexed = new CMiniDexed (&m_Config, &mInterrupt, &m_GPIOManager, &m_I2CMaster);
assert (m_pDexed);
if (!m_pDexed->Initialize ())
@@ -110,6 +89,8 @@ CStdlibApp::TShutdownMode CKernel::Run (void)
{
mScreen.Update ();
}
+
+ m_CPUThrottle.Update ();
}
return ShutdownHalt;
diff --git a/src/kernel.h b/src/kernel.h
index a2b5cc3..de9a5f0 100644
--- a/src/kernel.h
+++ b/src/kernel.h
@@ -21,6 +21,7 @@
#define _kernel_h
#include "circle_stdlib_app.h"
+#include
#include
#include
#include "config.h"
@@ -49,6 +50,7 @@ private:
private:
// do not change this order
CConfig m_Config;
+ CCPUThrottle m_CPUThrottle;
CGPIOManager m_GPIOManager;
CI2CMaster m_I2CMaster;
CMiniDexed *m_pDexed;
diff --git a/src/ky040.cpp b/src/ky040.cpp
deleted file mode 100644
index 24c040f..0000000
--- a/src/ky040.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-//
-// ky040.cpp
-//
-// Circle - A C++ bare metal environment for Raspberry Pi
-// Copyright (C) 2022 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 "ky040.h"
-#include
-
-static const unsigned SwitchDebounceDelayMillis = 50;
-static const unsigned SwitchTickDelayMillis = 500;
-
-CKY040::TState CKY040::s_NextState[StateUnknown][2][2] =
-{
- // {{CLK=0/DT=0, CLK=0/DT=1}, {CLK=1/DT=0, CLK=1/DT=1}}
-
- {{StateInvalid, StateCWStart}, {StateCCWStart, StateStart}}, // StateStart
-
- {{StateCWBothLow, StateCWStart}, {StateInvalid, StateStart}}, // StateCWStart
- {{StateCWBothLow, StateInvalid}, {StateCWFirstHigh, StateInvalid}}, // StateCWBothLow
- {{StateInvalid, StateInvalid}, {StateCWFirstHigh, StateStart}}, // StateCWFirstHigh
-
- {{StateCCWBothLow, StateInvalid}, {StateCCWStart, StateStart}}, // StateCCWStart
- {{StateCCWBothLow, StateCCWFirstHigh}, {StateInvalid, StateInvalid}}, // StateCCWBothLow
- {{StateInvalid, StateCCWFirstHigh}, {StateInvalid, StateStart}}, // StateCCWFirstHigh
-
- {{StateInvalid, StateInvalid}, {StateInvalid, StateStart}} // StateInvalid
-};
-
-CKY040::TEvent CKY040::s_Output[StateUnknown][2][2] =
-{
- // {{CLK=0/DT=0, CLK=0/DT=1}, {CLK=1/DT=0, CLK=1/DT=1}}
-
- {{EventUnknown, EventUnknown}, {EventUnknown, EventUnknown}}, // StateStart
-
- {{EventUnknown, EventUnknown}, {EventUnknown, EventUnknown}}, // StateCWStart
- {{EventUnknown, EventUnknown}, {EventUnknown, EventUnknown}}, // StateCWBothLow
- {{EventUnknown, EventUnknown}, {EventUnknown, EventClockwise}}, // StateCWFirstHigh
-
- {{EventUnknown, EventUnknown}, {EventUnknown, EventUnknown}}, // StateCCWStart
- {{EventUnknown, EventUnknown}, {EventUnknown, EventUnknown}}, // StateCCWBothLow
- {{EventUnknown, EventUnknown}, {EventUnknown, EventCounterclockwise}}, // StateCCWFirstHigh
-
- {{EventUnknown, EventUnknown}, {EventUnknown, EventUnknown}} // StateInvalid
-};
-
-CKY040::TSwitchState CKY040::s_NextSwitchState[SwitchStateUnknown][SwitchEventUnknown] =
-{
- // {SwitchEventDown, SwitchEventUp, SwitchEventTick}
-
- {SwitchStateDown, SwitchStateStart, SwitchStateStart}, // SwitchStateStart
- {SwitchStateDown, SwitchStateClick, SwitchStateHold}, // SwitchStateDown
- {SwitchStateDown2, SwitchStateClick, SwitchStateStart}, // SwitchStateClick
- {SwitchStateDown2, SwitchStateClick2, SwitchStateInvalid}, // SwitchStateDown2
- {SwitchStateDown3, SwitchStateClick2, SwitchStateStart}, // SwitchStateClick2
- {SwitchStateDown3, SwitchStateClick3, SwitchStateInvalid}, // SwitchStateDown3
- {SwitchStateInvalid, SwitchStateClick3, SwitchStateStart}, // SwitchStateClick3
- {SwitchStateHold, SwitchStateStart, SwitchStateHold}, // SwitchStateHold
- {SwitchStateInvalid, SwitchStateStart, SwitchStateInvalid} // SwitchStateInvalid
-};
-
-CKY040::TEvent CKY040::s_SwitchOutput[SwitchStateUnknown][SwitchEventUnknown] =
-{
- // {SwitchEventDown, SwitchEventUp, SwitchEventTick}
-
- {EventUnknown, EventUnknown, EventUnknown}, // SwitchStateStart
- {EventUnknown, EventUnknown, EventSwitchHold}, // SwitchStateDown
- {EventUnknown, EventUnknown, EventSwitchClick}, // SwitchStateClick
- {EventUnknown, EventUnknown, EventUnknown}, // SwitchStateDown2
- {EventUnknown, EventUnknown, EventSwitchDoubleClick}, // SwitchStateClick2
- {EventUnknown, EventUnknown, EventUnknown}, // SwitchStateDown3
- {EventUnknown, EventUnknown, EventSwitchTripleClick}, // SwitchStateClick3
- {EventUnknown, EventUnknown, EventSwitchHold}, // SwitchStateHold
- {EventUnknown, EventUnknown, EventUnknown} // SwitchStateInvalid
-};
-
-CKY040::CKY040 (unsigned nCLKPin, unsigned nDTPin, unsigned nSWPin, CGPIOManager *pGPIOManager)
-: m_CLKPin (nCLKPin, GPIOModeInputPullUp, pGPIOManager),
- m_DTPin (nDTPin, GPIOModeInputPullUp, pGPIOManager),
- m_SWPin (nSWPin, GPIOModeInputPullUp, pGPIOManager),
- m_bPollingMode (!pGPIOManager),
- m_bInterruptConnected (FALSE),
- m_pEventHandler (nullptr),
- m_State (StateStart),
- m_hDebounceTimer (0),
- m_hTickTimer (0),
- m_nLastSWLevel (HIGH),
- m_bDebounceActive (FALSE),
- m_SwitchState (SwitchStateStart),
- m_nSwitchLastTicks (0)
-{
-}
-
-CKY040::~CKY040 (void)
-{
- if (m_bInterruptConnected)
- {
- m_pEventHandler = nullptr;
-
- m_CLKPin.DisableInterrupt2 ();
- m_CLKPin.DisableInterrupt ();
- m_CLKPin.DisconnectInterrupt ();
-
- m_DTPin.DisableInterrupt2 ();
- m_DTPin.DisableInterrupt ();
- m_DTPin.DisconnectInterrupt ();
-
- m_SWPin.DisableInterrupt2 ();
- m_SWPin.DisableInterrupt ();
- m_SWPin.DisconnectInterrupt ();
- }
-
- if (m_hDebounceTimer)
- {
- CTimer::Get ()->CancelKernelTimer (m_hDebounceTimer);
- }
-
- if (m_hTickTimer)
- {
- CTimer::Get ()->CancelKernelTimer (m_hTickTimer);
- }
-}
-
-boolean CKY040::Initialize (void)
-{
- if (!m_bPollingMode)
- {
- assert (!m_bInterruptConnected);
- m_bInterruptConnected = TRUE;
-
- m_CLKPin.ConnectInterrupt (EncoderInterruptHandler, this);
- m_DTPin.ConnectInterrupt (EncoderInterruptHandler, this);
- m_SWPin.ConnectInterrupt (SwitchInterruptHandler, this);
-
- m_CLKPin.EnableInterrupt (GPIOInterruptOnFallingEdge);
- m_CLKPin.EnableInterrupt2 (GPIOInterruptOnRisingEdge);
-
- m_DTPin.EnableInterrupt (GPIOInterruptOnFallingEdge);
- m_DTPin.EnableInterrupt2 (GPIOInterruptOnRisingEdge);
-
- m_SWPin.EnableInterrupt (GPIOInterruptOnFallingEdge);
- m_SWPin.EnableInterrupt2 (GPIOInterruptOnRisingEdge);
- }
-
- return TRUE;
-}
-
-void CKY040::RegisterEventHandler (TEventHandler *pHandler, void *pParam)
-{
- assert (!m_pEventHandler);
- m_pEventHandler = pHandler;
- assert (m_pEventHandler);
- m_pEventParam = pParam;
-}
-
-unsigned CKY040::GetHoldSeconds (void) const
-{
- return m_nHoldCounter / 2;
-}
-
-void CKY040::Update (void)
-{
- assert (m_bPollingMode);
-
- EncoderInterruptHandler (this);
-
- // handle switch
- unsigned nTicks = CTimer::GetClockTicks ();
- unsigned nSW = m_SWPin.Read ();
-
- if (nSW != m_nLastSWLevel)
- {
- m_nLastSWLevel = nSW;
-
- m_bDebounceActive = TRUE;
- m_nDebounceLastTicks = CTimer::GetClockTicks ();
- }
- else
- {
- if ( m_bDebounceActive
- && nTicks - m_nDebounceLastTicks >= SwitchDebounceDelayMillis * (CLOCKHZ / 1000))
- {
- m_bDebounceActive = FALSE;
- m_nSwitchLastTicks = nTicks;
-
- if (m_pEventHandler)
- {
- (*m_pEventHandler) (nSW ? EventSwitchUp : EventSwitchDown,
- m_pEventParam);
- }
-
- HandleSwitchEvent (nSW ? SwitchEventUp : SwitchEventDown);
- }
-
- if (nTicks - m_nSwitchLastTicks >= SwitchTickDelayMillis * (CLOCKHZ / 1000))
- {
- m_nSwitchLastTicks = nTicks;
-
- HandleSwitchEvent (SwitchEventTick);
- }
- }
-}
-
-// generates the higher level switch events
-void CKY040::HandleSwitchEvent (TSwitchEvent SwitchEvent)
-{
- assert (SwitchEvent < SwitchEventUnknown);
- TEvent Event = s_SwitchOutput[m_SwitchState][SwitchEvent];
- TSwitchState NextState = s_NextSwitchState[m_SwitchState][SwitchEvent];
-
- if (NextState == SwitchStateHold)
- {
- if (m_SwitchState != SwitchStateHold)
- {
- m_nHoldCounter = 0;
- }
-
- m_nHoldCounter++;
- }
-
- m_SwitchState = NextState;
-
- if ( Event != EventUnknown
- && (Event != EventSwitchHold || !(m_nHoldCounter & 1)) // emit hold event each second
- && m_pEventHandler)
- {
- (*m_pEventHandler) (Event, m_pEventParam);
- }
-}
-
-void CKY040::EncoderInterruptHandler (void *pParam)
-{
- CKY040 *pThis = static_cast (pParam);
- assert (pThis != 0);
-
- unsigned nCLK = pThis->m_CLKPin.Read ();
- unsigned nDT = pThis->m_DTPin.Read ();
- assert (nCLK <= 1);
- assert (nDT <= 1);
-
- assert (pThis->m_State < StateUnknown);
- TEvent Event = s_Output[pThis->m_State][nCLK][nDT];
- pThis->m_State = s_NextState[pThis->m_State][nCLK][nDT];
-
- if ( Event != EventUnknown
- && pThis->m_pEventHandler)
- {
- (*pThis->m_pEventHandler) (Event, pThis->m_pEventParam);
- }
-}
-
-void CKY040::SwitchInterruptHandler (void *pParam)
-{
- CKY040 *pThis = static_cast (pParam);
- assert (pThis != 0);
-
- if (pThis->m_hDebounceTimer)
- {
- CTimer::Get ()->CancelKernelTimer (pThis->m_hDebounceTimer);
- }
-
- pThis->m_hDebounceTimer =
- CTimer::Get ()->StartKernelTimer (MSEC2HZ (SwitchDebounceDelayMillis),
- SwitchDebounceHandler, pThis, 0);
-}
-
-void CKY040::SwitchDebounceHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext)
-{
- CKY040 *pThis = static_cast (pParam);
- assert (pThis != 0);
-
- pThis->m_hDebounceTimer = 0;
-
- if (pThis->m_hTickTimer)
- {
- CTimer::Get ()->CancelKernelTimer (pThis->m_hTickTimer);
- }
-
- pThis->m_hTickTimer = CTimer::Get ()->StartKernelTimer (MSEC2HZ (SwitchTickDelayMillis),
- SwitchTickHandler, pThis, 0);
-
- unsigned nSW = pThis->m_SWPin.Read ();
-
- if (pThis->m_pEventHandler)
- {
- (*pThis->m_pEventHandler) (nSW ? EventSwitchUp : EventSwitchDown,
- pThis->m_pEventParam);
- }
-
- pThis->HandleSwitchEvent (nSW ? SwitchEventUp : SwitchEventDown);
-}
-
-void CKY040::SwitchTickHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext)
-{
- CKY040 *pThis = static_cast (pParam);
- assert (pThis != 0);
-
- pThis->m_hTickTimer = CTimer::Get ()->StartKernelTimer (MSEC2HZ (SwitchTickDelayMillis),
- SwitchTickHandler, pThis, 0);
-
- pThis->HandleSwitchEvent (SwitchEventTick);
-}
diff --git a/src/ky040.h b/src/ky040.h
deleted file mode 100644
index 052b06c..0000000
--- a/src/ky040.h
+++ /dev/null
@@ -1,153 +0,0 @@
-//
-// ky040.h
-//
-// Circle - A C++ bare metal environment for Raspberry Pi
-// Copyright (C) 2022 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 _sensor_ky040_h
-#define _sensor_ky040_h
-
-#include
-#include
-#include
-#include
-
-/// \note This driver supports an interrupt mode and a polling mode.
-
-class CKY040 /// Driver for KY-040 rotary encoder module
-{
-public:
- enum TEvent
- {
- EventClockwise,
- EventCounterclockwise,
-
- EventSwitchDown,
- EventSwitchUp,
- EventSwitchClick,
- EventSwitchDoubleClick,
- EventSwitchTripleClick,
- EventSwitchHold, ///< generated each second
-
- EventUnknown
- };
-
- typedef void TEventHandler (TEvent Event, void *pParam);
-
-public:
- /// \param nCLKPin GPIO pin number of clock pin (encoder pin A)
- /// \param nDTPin GPIO pin number of data pin (encoder pin B)
- /// \param nSWPin GPIO pin number of switch pin
- /// \param pGPIOManager Pointer to GPIO manager object (0 enables polling mode)
- CKY040 (unsigned nCLKPin, unsigned nDTPin, unsigned nSWPin, CGPIOManager *pGPIOManager = 0);
-
- ~CKY040 (void);
-
- /// \brief Operation successful?
- boolean Initialize (void);
-
- /// \brief Register a handler, to be called on an event from the encoder
- /// \param pHandler Pointer to the handler
- /// \param pParam Optional user parameter, handed over to the handler
- void RegisterEventHandler (TEventHandler *pHandler, void *pParam = 0);
-
- /// \return Number of seconds, the switch is hold down
- /// \note Only valid, when EventSwitchHold has been received.
- unsigned GetHoldSeconds (void) const;
-
- /// \brief Has to be called very frequently in polling mode
- void Update (void);
-
-private:
- enum TState
- {
- StateStart,
- StateCWStart,
- StateCWBothLow,
- StateCWFirstHigh,
- StateCCWStart,
- StateCCWBothLow,
- StateCCWFirstHigh,
- StateInvalid,
- StateUnknown
- };
-
- enum TSwitchState
- {
- SwitchStateStart,
- SwitchStateDown,
- SwitchStateClick,
- SwitchStateDown2,
- SwitchStateClick2,
- SwitchStateDown3,
- SwitchStateClick3,
- SwitchStateHold,
- SwitchStateInvalid,
- SwitchStateUnknown
- };
-
- enum TSwitchEvent
- {
- SwitchEventDown,
- SwitchEventUp,
- SwitchEventTick,
- SwitchEventUnknown
- };
-
-private:
- void HandleSwitchEvent (TSwitchEvent SwitchEvent);
-
- static void EncoderInterruptHandler (void *pParam);
- static void SwitchInterruptHandler (void *pParam);
-
- static void SwitchDebounceHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext);
- static void SwitchTickHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext);
-
-private:
- CGPIOPin m_CLKPin;
- CGPIOPin m_DTPin;
- CGPIOPin m_SWPin;
-
- boolean m_bPollingMode;
- boolean m_bInterruptConnected;
-
- TEventHandler *m_pEventHandler;
- void *m_pEventParam;
-
- // encoder
- TState m_State;
-
- static TState s_NextState[StateUnknown][2][2];
- static TEvent s_Output[StateUnknown][2][2];
-
- // switch low level
- TKernelTimerHandle m_hDebounceTimer;
- TKernelTimerHandle m_hTickTimer;
-
- unsigned m_nLastSWLevel;
- boolean m_bDebounceActive;
- unsigned m_nDebounceLastTicks;
-
- // switch higher level
- TSwitchState m_SwitchState;
- unsigned m_nSwitchLastTicks;
- unsigned m_nHoldCounter;
-
- static TSwitchState s_NextSwitchState[SwitchStateUnknown][SwitchEventUnknown];
- static TEvent s_SwitchOutput[SwitchStateUnknown][SwitchEventUnknown];
-};
-
-#endif
diff --git a/src/mididevice.cpp b/src/mididevice.cpp
index 8b0d570..4f92e61 100644
--- a/src/mididevice.cpp
+++ b/src/mididevice.cpp
@@ -31,6 +31,7 @@
#define MIDI_AFTERTOUCH 0b1010 // TODO
#define MIDI_CONTROL_CHANGE 0b1011
#define MIDI_CC_BANK_SELECT_MSB 0 // TODO
+ #define MIDI_CC_VOLUME 7
#define MIDI_CC_BANK_SELECT_LSB 32
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
@@ -130,6 +131,10 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
switch (pMessage[1])
{
+ case MIDI_CC_VOLUME:
+ m_pSynthesizer->SetVolume (pMessage[2]);
+ break;
+
case MIDI_CC_BANK_SELECT_LSB:
m_pSynthesizer->BankSelectLSB (pMessage[2]);
break;
diff --git a/src/minidexed.cpp b/src/minidexed.cpp
index 476a8f3..4217c35 100644
--- a/src/minidexed.cpp
+++ b/src/minidexed.cpp
@@ -19,18 +19,28 @@
//
#include "minidexed.h"
#include
+#include
+#include
+#include
+#include
+#include
#include
#include
LOGMODULE ("minidexed");
-CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOManager *pGPIOManager)
+CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
+ CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster)
: CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ()),
+#ifdef ARM_ALLOW_MULTI_CORE
+ CMultiCoreSupport (CMemorySystem::Get ()),
+#endif
m_pConfig (pConfig),
m_UI (this, pGPIOManager, pConfig),
m_PCKeyboard (this),
m_SerialMIDI (this, pInterrupt, pConfig),
m_bUseSerial (false),
+ m_pSoundDevice (0),
m_GetChunkTimer ("GetChunk",
1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()),
m_bProfileEnabled (m_pConfig->GetProfileEnabled ())
@@ -40,10 +50,38 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOMan
m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i);
assert (m_pMIDIKeyboard[i]);
}
+
+ // select the sound device
+ const char *pDeviceName = pConfig->GetSoundDevice ();
+ if (strcmp (pDeviceName, "i2s") == 0)
+ {
+ LOGNOTE ("I2S mode");
+
+ m_pSoundDevice = new CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize (), false,
+ pI2CMaster, pConfig->GetDACI2CAddress ());
+ }
+ else if (strcmp (pDeviceName, "hdmi") == 0)
+ {
+ LOGNOTE ("HDMI mode");
+
+ m_pSoundDevice = new CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize ());
+ }
+ else
+ {
+ LOGNOTE ("PWM mode");
+
+ m_pSoundDevice = new CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
+ pConfig->GetChunkSize ());
+ }
};
bool CMiniDexed::Initialize (void)
{
+ assert (m_pConfig);
+ assert (m_pSoundDevice);
+
if (!m_UI.Initialize ())
{
return false;
@@ -60,14 +98,41 @@ bool CMiniDexed::Initialize (void)
activate ();
+ SetVolume (100);
ProgramChange (0);
setTranspose (24);
+ // setup and start the sound device
+ if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ()))
+ {
+ LOGERR ("Cannot allocate sound queue");
+
+ return false;
+ }
+
+ m_pSoundDevice->SetWriteFormat (SoundFormatSigned16, 1); // 16-bit Mono
+
+ m_nQueueSizeFrames = m_pSoundDevice->GetQueueSizeFrames ();
+
+ m_pSoundDevice->Start ();
+
+#ifdef ARM_ALLOW_MULTI_CORE
+ // start secondary cores
+ if (!CMultiCoreSupport::Initialize ())
+ {
+ return false;
+ }
+#endif
+
return true;
}
void CMiniDexed::Process (bool bPlugAndPlayUpdated)
{
+#ifndef ARM_ALLOW_MULTI_CORE
+ ProcessSound ();
+#endif
+
for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++)
{
assert (m_pMIDIKeyboard[i]);
@@ -89,6 +154,21 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated)
}
}
+#ifdef ARM_ALLOW_MULTI_CORE
+
+void CMiniDexed::Run (unsigned nCore)
+{
+ if (nCore == 1)
+ {
+ while (1)
+ {
+ ProcessSound ();
+ }
+ }
+}
+
+#endif
+
CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void)
{
return &m_SysExFileLoader;
@@ -120,159 +200,42 @@ void CMiniDexed::ProgramChange (unsigned nProgram)
m_UI.ProgramChanged (nProgram);
}
-//// PWM //////////////////////////////////////////////////////////////////////
-
-CMiniDexedPWM::CMiniDexedPWM (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager)
-: CMiniDexed (pConfig, pInterrupt, pGPIOManager),
- CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
- pConfig->GetChunkSize ())
-{
-}
-
-bool CMiniDexedPWM::Initialize (void)
+void CMiniDexed::SetVolume (unsigned nVolume)
{
- if (!CMiniDexed::Initialize ())
- {
- return false;
- }
-
- return Start ();
-}
-
-unsigned CMiniDexedPWM::GetChunk (u32 *pBuffer, unsigned nChunkSize)
-{
- if (m_bProfileEnabled)
- {
- m_GetChunkTimer.Start ();
- }
-
- unsigned nResult = nChunkSize;
-
- int16_t SampleBuffer[nChunkSize/2];
- getSamples (nChunkSize/2, SampleBuffer);
-
- for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer
- {
- s32 nSample = SampleBuffer[i++];
- nSample += 32768;
- nSample *= GetRangeMax()/2;
- nSample /= 32768;
-
- *pBuffer++ = nSample; // 2 stereo channels
- *pBuffer++ = nSample;
- }
-
- if (m_bProfileEnabled)
+ if (nVolume > 127)
{
- m_GetChunkTimer.Stop ();
- }
-
- return nResult;
-};
-
-//// I2S //////////////////////////////////////////////////////////////////////
-
-CMiniDexedI2S::CMiniDexedI2S (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster)
-: CMiniDexed (pConfig, pInterrupt, pGPIOManager),
- CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
- pConfig->GetChunkSize (), false, pI2CMaster,
- pConfig->GetDACI2CAddress ())
-{
-}
-
-bool CMiniDexedI2S::Initialize (void)
-{
- if (!CMiniDexed::Initialize ())
- {
- return false;
- }
-
- return Start ();
-}
-
-unsigned CMiniDexedI2S::GetChunk (u32 *pBuffer, unsigned nChunkSize)
-{
- if (m_bProfileEnabled)
- {
- m_GetChunkTimer.Start ();
- }
-
- unsigned nResult = nChunkSize;
-
- int16_t SampleBuffer[nChunkSize/2];
- getSamples (nChunkSize/2, SampleBuffer);
-
- for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer
- {
- s32 nSample = SampleBuffer[i++];
- nSample <<= 8;
-
- *pBuffer++ = nSample; // 2 stereo channels
- *pBuffer++ = nSample;
- }
-
- if (m_bProfileEnabled)
- {
- m_GetChunkTimer.Stop ();
+ return;
}
- return nResult;
-};
-
-//// HDMI /////////////////////////////////////////////////////////////////////
+ setGain (nVolume / 127.0);
-CMiniDexedHDMI::CMiniDexedHDMI (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager)
-: CMiniDexed (pConfig, pInterrupt, pGPIOManager),
- CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (),
- pConfig->GetChunkSize ())
-{
+ m_UI.VolumeChanged (nVolume);
}
-bool CMiniDexedHDMI::Initialize (void)
+void CMiniDexed::ProcessSound (void)
{
- if (!CMiniDexed::Initialize ())
- {
- return false;
- }
-
- return Start ();
-}
+ assert (m_pSoundDevice);
-unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize)
-{
- if (m_bProfileEnabled)
+ unsigned nFrames = m_nQueueSizeFrames - m_pSoundDevice->GetQueueFramesAvail ();
+ if (nFrames >= m_nQueueSizeFrames/2)
{
- m_GetChunkTimer.Start ();
- }
-
- unsigned nResult = nChunkSize;
-
- int16_t SampleBuffer[nChunkSize/2];
- getSamples (nChunkSize/2, SampleBuffer);
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Start ();
+ }
- unsigned nFrame = 0;
- for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer
- {
- s32 nSample = SampleBuffer[i++];
- nSample <<= 8;
+ int16_t SampleBuffer[nFrames];
+ getSamples (nFrames, SampleBuffer);
- nSample = ConvertIEC958Sample (nSample, nFrame);
- if (++nFrame == IEC958_FRAMES_PER_BLOCK)
+ if ( m_pSoundDevice->Write (SampleBuffer, sizeof SampleBuffer)
+ != (int) sizeof SampleBuffer)
{
- nFrame = 0;
+ LOGERR ("Sound data dropped");
}
- *pBuffer++ = nSample; // 2 stereo channels
- *pBuffer++ = nSample;
- }
-
- if (m_bProfileEnabled)
- {
- m_GetChunkTimer.Stop();
+ if (m_bProfileEnabled)
+ {
+ m_GetChunkTimer.Stop ();
+ }
}
-
- return nResult;
-};
+}
diff --git a/src/minidexed.h b/src/minidexed.h
index d817f87..17ca610 100644
--- a/src/minidexed.h
+++ b/src/minidexed.h
@@ -33,24 +33,34 @@
#include
#include
#include
-#include
-#include
-#include
+#include
+#include
class CMiniDexed : public CDexedAdapter
+#ifdef ARM_ALLOW_MULTI_CORE
+ , public CMultiCoreSupport
+#endif
{
public:
CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager);
+ CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster);
- virtual bool Initialize (void);
+ bool Initialize (void);
void Process (bool bPlugAndPlayUpdated);
+#ifdef ARM_ALLOW_MULTI_CORE
+ void Run (unsigned nCore);
+#endif
+
CSysExFileLoader *GetSysExFileLoader (void);
void BankSelectLSB (unsigned nBankLSB);
void ProgramChange (unsigned nProgram);
+ void SetVolume (unsigned nVolume);
+
+private:
+ void ProcessSound (void);
private:
CConfig *m_pConfig;
@@ -63,48 +73,11 @@ private:
CSerialMIDIDevice m_SerialMIDI;
bool m_bUseSerial;
-protected:
+ CSoundBaseDevice *m_pSoundDevice;
+ unsigned m_nQueueSizeFrames;
+
CPerformanceTimer m_GetChunkTimer;
bool m_bProfileEnabled;
};
-//// PWM //////////////////////////////////////////////////////////////////////
-
-class CMiniDexedPWM : public CMiniDexed, public CPWMSoundBaseDevice
-{
-public:
- CMiniDexedPWM (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager);
-
- bool Initialize (void);
-
- unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize);
-};
-
-//// I2S //////////////////////////////////////////////////////////////////////
-
-class CMiniDexedI2S : public CMiniDexed, public CI2SSoundBaseDevice
-{
-public:
- CMiniDexedI2S (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster);
-
- bool Initialize (void);
-
- unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize);
-};
-
-//// HDMI /////////////////////////////////////////////////////////////////////
-
-class CMiniDexedHDMI : public CMiniDexed, public CHDMISoundBaseDevice
-{
-public:
- CMiniDexedHDMI (CConfig *pConfig, CInterruptSystem *pInterrupt,
- CGPIOManager *pGPIOManager);
-
- bool Initialize (void);
-
- unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize);
-};
-
#endif
diff --git a/src/userinterface.cpp b/src/userinterface.cpp
index 6cc5b90..6f573e2 100644
--- a/src/userinterface.cpp
+++ b/src/userinterface.cpp
@@ -38,7 +38,8 @@ CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManag
m_pRotaryEncoder (0),
m_UIMode (UIModeVoiceSelect),
m_nBank (0),
- m_nProgram (0)
+ m_nProgram (0),
+ m_nVolume (0)
{
}
@@ -73,7 +74,7 @@ bool CUserInterface::Initialize (void)
m_pLCDBuffered = new CWriteBufferDevice (m_pLCD);
assert (m_pLCDBuffered);
- LCDWrite ("\x1B[?25l"); // cursor off
+ LCDWrite ("\x1B[?25l\x1B""d+"); // cursor off, autopage mode
LOGDBG ("LCD initialized");
}
@@ -121,9 +122,9 @@ void CUserInterface::BankSelected (unsigned nBankLSB)
if (m_UIMode == UIModeBankSelect)
{
CString String;
- String.Format ("\n\r%-12uBANK%s", nBankLSB+1, BankName.c_str ());
+ String.Format ("%u", nBankLSB+1);
- LCDWrite (String);
+ DisplayWrite (String, "BANK", BankName.c_str ());
}
}
@@ -145,12 +146,68 @@ void CUserInterface::ProgramChanged (unsigned nProgram)
if (m_UIMode == UIModeVoiceSelect)
{
CString String;
- String.Format ("\n\r%-11uVOICE%s", nProgram, ProgramName);
+ String.Format ("%u", nProgram);
- LCDWrite (String);
+ DisplayWrite (String, "VOICE", ProgramName);
}
}
+void CUserInterface::VolumeChanged (unsigned nVolume)
+{
+ assert (nVolume < 128);
+ m_nVolume = nVolume;
+
+ if (m_UIMode == UIModeVolume)
+ {
+ char VolumeBar[CConfig::LCDColumns+1];
+ memset (VolumeBar, 0xFF, sizeof VolumeBar); // 0xFF is the block character
+ VolumeBar[nVolume * CConfig::LCDColumns / 127] = '\0';
+
+ DisplayWrite ("", "VOLUME", VolumeBar);
+ }
+}
+
+void CUserInterface::DisplayWrite (const char *pInstance, const char *pMenu,
+ const char *pParam, const char *pValue)
+{
+ assert (pInstance);
+ assert (pMenu);
+ assert (pParam);
+
+ CString Msg ("\x1B[H"); // cursor home
+
+ // first line
+ Msg.Append (pInstance);
+
+ size_t nLen = strlen (pInstance) + strlen (pMenu);
+ if (nLen < CConfig::LCDColumns)
+ {
+ for (unsigned i = CConfig::LCDColumns-nLen; i > 0; i--)
+ {
+ Msg.Append (" ");
+ }
+ }
+
+ Msg.Append (pMenu);
+
+ // second line
+ CString ParamValue (pParam);
+ if (pValue)
+ {
+ ParamValue.Append ("=");
+ ParamValue.Append (pValue);
+ }
+
+ Msg.Append (ParamValue);
+
+ if (ParamValue.GetLength () < CConfig::LCDColumns)
+ {
+ Msg.Append ("\x1B[K"); // clear end of line
+ }
+
+ LCDWrite (Msg);
+}
+
void CUserInterface::LCDWrite (const char *pString)
{
if (m_pLCDBuffered)
@@ -210,6 +267,22 @@ void CUserInterface::EncoderEventHandler (CKY040::TEvent Event)
}
break;
+ case UIModeVolume: {
+ const int Increment = 128 / CConfig::LCDColumns;
+
+ int nVolume = m_nVolume + nStep*Increment;
+ if (nVolume < 0)
+ {
+ nVolume = 0;
+ }
+ else if (nVolume > 127)
+ {
+ nVolume = 127;
+ }
+
+ m_pMiniDexed->SetVolume (nVolume);
+ } break;
+
default:
break;
}
diff --git a/src/userinterface.h b/src/userinterface.h
index 0ce874c..6e34d99 100644
--- a/src/userinterface.h
+++ b/src/userinterface.h
@@ -21,7 +21,7 @@
#define _userinterface_h
#include "config.h"
-#include "ky040.h"
+#include
#include
#include
#include
@@ -40,8 +40,17 @@ public:
void BankSelected (unsigned nBankLSB); // 0 .. 127
void ProgramChanged (unsigned nProgram); // 0 .. 127
+ void VolumeChanged (unsigned nVolume); // 0 .. 127
private:
+ // Print to display in this format:
+ // +----------------+
+ // |INSTANCE MENU|
+ // |PARAM[=VALUE] |
+ // +----------------+
+ void DisplayWrite (const char *pInstance, const char *pMenu,
+ const char *pParam, const char *pValue = nullptr);
+
void LCDWrite (const char *pString); // Print to optional HD44780 display
void EncoderEventHandler (CKY040::TEvent Event);
@@ -53,6 +62,7 @@ private:
UIModeStart,
UIModeVoiceSelect = UIModeStart,
UIModeBankSelect,
+ UIModeVolume,
UIModeUnknown
};
@@ -70,6 +80,7 @@ private:
unsigned m_nBank;
unsigned m_nProgram;
+ unsigned m_nVolume;
};
#endif