Merge branch 'main' into usb-audio

usb-audio
probonopd 6 days ago committed by GitHub
commit 3588f688a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 12
      .github/workflows/build.yml
  2. 1
      .gitignore
  3. 4
      README.md
  4. 46
      hwconfig/DT-DX.override
  5. 33
      hwconfig/customize.sh
  6. 32
      hwconfig/pirate_audio.override
  7. 59
      src/config.cpp
  8. 22
      src/config.h
  9. 104
      src/mididevice.cpp
  10. 9
      src/mididevice.h
  11. 33
      src/minidexed.ini
  12. 98
      src/uibuttons.cpp
  13. 35
      src/uibuttons.h
  14. 86
      src/uimenu.cpp
  15. 3
      src/uimenu.h
  16. 40
      src/userinterface.cpp

@ -7,7 +7,6 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
Build:
@ -19,6 +18,10 @@ jobs:
- name: Get specific commits of git submodules
run: |
sh -ex ./submod.sh
- name: Apply patches
run: |
# Put git hash in startup message
sed -i "s/Loading.../$(date +%Y%m%d)-$(git rev-parse --short HEAD)/g" src/userinterface.cpp
- name: Install toolchains
run: |
set -ex
@ -82,6 +85,13 @@ jobs:
zip -r ../MiniDexed_$GITHUB_RUN_NUMBER_$(date +%Y-%m-%d)-$(git rev-parse --short HEAD).zip *
echo "artifactName=MiniDexed_$GITHUB_RUN_NUMBER_$(date +%Y-%m-%d)-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
cd -
- name: Hardware configration files
run: |
cd hwconfig
sh -ex ./customize.sh
cd -
mkdir -p ./sdcard/hardware/
cp -r ./hwconfig/minidexed_* ./sdcard/minidexed.ini ./sdcard/hardware/
- uses: actions/upload-artifact@v3
with:
name: ${{ env.artifactName }} # Exported above

1
.gitignore vendored

@ -49,3 +49,4 @@ sdcard
CMSIS_5/
Synth_Dexed/
circle-stdlib/
minidexed_*

@ -2,7 +2,7 @@
![minidexed](https://user-images.githubusercontent.com/2480569/161813414-bb156a1c-efec-44c0-802a-8926412a08e0.jpg)
MiniDexed is a FM synthesizer closely modeled on the famous DX7 by a well-known Japanese manufacturer running on a bare metal Raspberry Pi (without a Linux kernel or operating system). On Raspberry Pi 2 and larger, it can run 8 tone generators, not unlike the TX816/TX802 (8 DX7 instances without the keyboard in one box). [Featured by HACKADAY](https://hackaday.com/2022/04/19/bare-metal-gives-this-pi-some-classic-synths/), [Adafruit](https://blog.adafruit.com/2022/04/25/free-yamaha-dx7-synth-emulator-on-a-raspberry-pi/), and [Synth Geekery](https://www.youtube.com/watch?v=TDSy5nnm0jA).
MiniDexed is a FM synthesizer closely modeled on the famous DX7 by a well-known Japanese manufacturer running on a bare metal Raspberry Pi (without a Linux kernel or operating system). On Raspberry Pi 2 and larger, it can run 8 tone generators, not unlike the TX816/TX802 (8 DX7 instances without the keyboard in one box). [Featured by HACKADAY](https://hackaday.com/2022/04/19/bare-metal-gives-this-pi-some-classic-synths/), [Adafruit](https://blog.adafruit.com/2022/04/25/free-yamaha-dx7-synth-emulator-on-a-raspberry-pi/), [The MagPi magazine](https://magpi.raspberrypi.com/articles/mini-dexed) (Issue 142 June 2024, [PDF](https://magpi.raspberrypi.com/issues/142)) and [Synth Geekery](https://www.youtube.com/watch?v=TDSy5nnm0jA).
## Demo songs
@ -98,6 +98,8 @@ This project stands on the shoulders of giants. Special thanks to:
- [Banana71](https://github.com/Banana71) for the sound design of the [Soundplantage](https://github.com/Banana71/Soundplantage) performances shipped with MiniDexed
- [BobanSpasic](https://github.com/BobanSpasic) for the [MiniDexedLibrarian](https://github.com/BobanSpasic/MiniDexedLibrarian) software, [MiniDexed performance converter](https://github.com/BobanSpasic/MDX_PerfConv) and [collection of performances for MiniDexed](https://github.com/BobanSpasic/MDX_Vault)
- [diyelectromusic](https://github.com/diyelectromusic/) for many [contributions](https://github.com/probonopd/MiniDexed/commits?author=diyelectromusic)
- [dwhinham/mt32-pi](https://github.com/dwhinham/mt32-pi) for creating networking support for Circle
- [omersiar](https://github.com/omersiar) for porting networking support to MiniDexed
## Stargazers over time
[![Stargazers over time](https://starchart.cc/probonopd/MiniDexed.svg?variant=adaptive)](https://starchart.cc/probonopd/MiniDexed)

@ -0,0 +1,46 @@
# DTronics DT-DX
# https://www.dtronics.nl/dt-dx
SoundDevice=i2s
SampleRate=22000
ChunkSize=256
DACI2CAddress=0x0
ChannelsSwapped=1
LCDEnabled=1
LCDPinEnable=17
LCDPinRegisterSelect=27
LCDPinReadWrite=16
LCDPinData4=22
LCDPinData5=23
LCDPinData6=24
LCDPinData7=25
LCDI2CAddress=0x00
SSD1306LCDI2CAddress=0x00
SSD1306LCDWidth=128
SSD1306LCDHeight=32
SSD1306LCDRotate=0
SSD1306LCDMirror=0
LCDColumns=16
LCDRows=2
ButtonPinPrev=0
ButtonActionPrev=0
ButtonPinNext=0
ButtonActionNext=0
ButtonPinBack=26
ButtonActionBack=longpress
ButtonPinSelect=26
ButtonActionSelect=click
ButtonPinHome=26
ButtonActionHome=doubleclick
ButtonPinShortcut=26
DoubleClickTimeout=400
LongPressTimeout=400
EncoderEnabled=1
EncoderPinClock=6
EncoderPinData=5

@ -0,0 +1,33 @@
#!/bin/sh
# This script creates a set of ini files from the *.override files
# to provide customized configurations for well-known hardware
# Find all files named *.override, and run the following on each of them
for file in *.override; do
# Copy the file minidexed.ini to the name of this file but with .ini extension instead
name_of_ini_file=minidexed_$(echo "$file" | sed 's/\.override$/.ini/')
cp ../src/minidexed.ini "$name_of_ini_file"
# Change the values in the ini file, leaving the rest of the file unchanged
while IFS='=' read -r key value; do
# Skip empty lines and comments
if [ -z "$key" ] || [ "${key#\#}" != "$key" ]; then
continue
fi
value=$(echo "$value" | tr -d '\r')
if [ -n "$value" ]; then
sed -i "s/^$key=.*/$key=$value/" "$name_of_ini_file"
fi
done < "$file"
# Process the last line of the override file separately, if it doesn't end with a newline
if [ -n "$key" ]; then
value=$(echo "$value" | tr -d '\r')
if [ -n "$value" ]; then
sed -i "s/^$key=.*/$key=$value/" "$name_of_ini_file"
fi
fi
echo "Created $name_of_ini_file"
done

@ -0,0 +1,32 @@
# Pimoroni Pirate Audio (screen, buttons and audio output)
# https://shop.pimoroni.com/search?q=pirate%20audio
SoundDevice=i2s
LCDEnabled=1
SPIBus=0
ST7789Enabled=1
ST7789Data=9
ST7789Select=1
ST7789Reset=
ST7789Backlight=13
ST7789Width=240
ST7789Height=240
ST7789Rotation=90
LCDColumns=15
LCDRows=2
ButtonPinPrev=5
ButtonActionPrev=click
ButtonPinNext=6
ButtonActionNext=click
ButtonPinBack=16
ButtonActionBack=click
ButtonPinSelect=24
ButtonActionSelect=click
ButtonPinHome=16
ButtonActionHome=doubleclick
ButtonPinShortcut=0
EncoderEnabled=0

@ -109,6 +109,10 @@ void CConfig::Load (void)
m_bHeaderlessSysExVoices = m_Properties.GetNumber ("HeaderlessSysExVoices", 0) != 0;
m_bExpandPCAcrossBanks = m_Properties.GetNumber ("ExpandPCAcrossBanks", 1) != 0;
m_nMIDISystemCCVol = m_Properties.GetNumber ("MIDISystemCCVol", 0);
m_nMIDISystemCCPan = m_Properties.GetNumber ("MIDISystemCCPan", 0);
m_nMIDISystemCCDetune = m_Properties.GetNumber ("MIDISystemCCDetune", 0);
m_bLCDEnabled = m_Properties.GetNumber ("LCDEnabled", 0) != 0;
m_nLCDPinEnable = m_Properties.GetNumber ("LCDPinEnable", 4);
m_nLCDPinRegisterSelect = m_Properties.GetNumber ("LCDPinRegisterSelect", 27);
@ -160,11 +164,15 @@ void CConfig::Load (void)
m_nButtonPinPgmUp = m_Properties.GetNumber ("ButtonPinPgmUp", 0);
m_nButtonPinPgmDown = m_Properties.GetNumber ("ButtonPinPgmDown", 0);
m_nButtonPinBankUp = m_Properties.GetNumber ("ButtonPinBankUp", 0);
m_nButtonPinBankDown = m_Properties.GetNumber ("ButtonPinBankDown", 0);
m_nButtonPinTGUp = m_Properties.GetNumber ("ButtonPinTGUp", 0);
m_nButtonPinTGDown = m_Properties.GetNumber ("ButtonPinTGDown", 0);
m_ButtonActionPgmUp = m_Properties.GetString ("ButtonActionPgmUp", "");
m_ButtonActionPgmDown = m_Properties.GetString ("ButtonActionPgmDown", "");
m_ButtonActionBankUp = m_Properties.GetString ("ButtonActionBankUp", "");
m_ButtonActionBankDown = m_Properties.GetString ("ButtonActionBankDown", "");
m_ButtonActionTGUp = m_Properties.GetString ("ButtonActionTGUp", "");
m_ButtonActionTGDown = m_Properties.GetString ("ButtonActionTGDown", "");
@ -178,6 +186,8 @@ void CConfig::Load (void)
m_nMIDIButtonPgmUp = m_Properties.GetNumber ("MIDIButtonPgmUp", 0);
m_nMIDIButtonPgmDown = m_Properties.GetNumber ("MIDIButtonPgmDown", 0);
m_nMIDIButtonBankUp = m_Properties.GetNumber ("MIDIButtonBankUp", 0);
m_nMIDIButtonBankDown = m_Properties.GetNumber ("MIDIButtonBankDown", 0);
m_nMIDIButtonTGUp = m_Properties.GetNumber ("MIDIButtonTGUp", 0);
m_nMIDIButtonTGDown = m_Properties.GetNumber ("MIDIButtonTGDown", 0);
@ -283,6 +293,11 @@ unsigned CConfig::GetEngineType (void) const
return m_EngineType;
}
bool CConfig::GetQuadDAC8Chan (void) const
{
return m_bQuadDAC8Chan;
}
unsigned CConfig::GetMIDIBaudRate (void) const
{
return m_nMIDIBaudRate;
@ -323,9 +338,19 @@ bool CConfig::GetExpandPCAcrossBanks (void) const
return m_bExpandPCAcrossBanks;
}
bool CConfig::GetQuadDAC8Chan (void) const
unsigned CConfig::GetMIDISystemCCVol (void) const
{
return m_bQuadDAC8Chan;
return m_nMIDISystemCCVol;
}
unsigned CConfig::GetMIDISystemCCPan (void) const
{
return m_nMIDISystemCCPan;
}
unsigned CConfig::GetMIDISystemCCDetune (void) const
{
return m_nMIDISystemCCDetune;
}
bool CConfig::GetLCDEnabled (void) const
@ -542,6 +567,16 @@ unsigned CConfig::GetButtonPinPgmDown (void) const
return m_nButtonPinPgmDown;
}
unsigned CConfig::GetButtonPinBankUp (void) const
{
return m_nButtonPinBankUp;
}
unsigned CConfig::GetButtonPinBankDown (void) const
{
return m_nButtonPinBankDown;
}
unsigned CConfig::GetButtonPinTGUp (void) const
{
return m_nButtonPinTGUp;
@ -562,6 +597,16 @@ const char *CConfig::GetButtonActionPgmDown (void) const
return m_ButtonActionPgmDown.c_str();
}
const char *CConfig::GetButtonActionBankUp (void) const
{
return m_ButtonActionBankUp.c_str();
}
const char *CConfig::GetButtonActionBankDown (void) const
{
return m_ButtonActionBankDown.c_str();
}
const char *CConfig::GetButtonActionTGUp (void) const
{
return m_ButtonActionTGUp.c_str();
@ -617,6 +662,16 @@ unsigned CConfig::GetMIDIButtonPgmDown (void) const
return m_nMIDIButtonPgmDown;
}
unsigned CConfig::GetMIDIButtonBankUp (void) const
{
return m_nMIDIButtonBankUp;
}
unsigned CConfig::GetMIDIButtonBankDown (void) const
{
return m_nMIDIButtonBankDown;
}
unsigned CConfig::GetMIDIButtonTGUp (void) const
{
return m_nMIDIButtonTGUp;

@ -117,6 +117,7 @@ public:
unsigned GetDACI2CAddress (void) const; // 0 for auto probing
bool GetChannelsSwapped (void) const;
unsigned GetEngineType (void) const;
bool GetQuadDAC8Chan (void) const; // false if not specified
// MIDI
unsigned GetMIDIBaudRate (void) const;
@ -127,7 +128,9 @@ public:
bool GetMIDIAutoVoiceDumpOnPC (void) const; // false if not specified
bool GetHeaderlessSysExVoices (void) const; // false if not specified
bool GetExpandPCAcrossBanks (void) const; // true if not specified
bool GetQuadDAC8Chan (void) const; // false if not specified
unsigned GetMIDISystemCCVol (void) const;
unsigned GetMIDISystemCCPan (void) const;
unsigned GetMIDISystemCCDetune (void) const;
// HD44780 LCD
// GPIO pin numbers are chip numbers, not header positions
@ -191,12 +194,16 @@ public:
// GPIO pin numbers are chip numbers, not header positions
unsigned GetButtonPinPgmUp (void) const;
unsigned GetButtonPinPgmDown (void) const;
unsigned GetButtonPinBankUp (void) const;
unsigned GetButtonPinBankDown (void) const;
unsigned GetButtonPinTGUp (void) const;
unsigned GetButtonPinTGDown (void) const;
// Action type for buttons: "click", "doubleclick", "longpress", ""
const char *GetButtonActionPgmUp (void) const;
const char *GetButtonActionPgmDown (void) const;
const char *GetButtonActionBankUp (void) const;
const char *GetButtonActionBankDown (void) const;
const char *GetButtonActionTGUp (void) const;
const char *GetButtonActionTGDown (void) const;
@ -212,6 +219,8 @@ public:
// MIDI Button Program and TG Selection
unsigned GetMIDIButtonPgmUp (void) const;
unsigned GetMIDIButtonPgmDown (void) const;
unsigned GetMIDIButtonBankUp (void) const;
unsigned GetMIDIButtonBankDown (void) const;
unsigned GetMIDIButtonTGUp (void) const;
unsigned GetMIDIButtonTGDown (void) const;
@ -245,6 +254,7 @@ private:
unsigned m_nDACI2CAddress;
bool m_bChannelsSwapped;
unsigned m_EngineType;
bool m_bQuadDAC8Chan;
unsigned m_nMIDIBaudRate;
std::string m_MIDIThruIn;
@ -254,7 +264,9 @@ private:
bool m_bMIDIAutoVoiceDumpOnPC;
bool m_bHeaderlessSysExVoices;
bool m_bExpandPCAcrossBanks;
bool m_bQuadDAC8Chan;
unsigned m_nMIDISystemCCVol;
unsigned m_nMIDISystemCCPan;
unsigned m_nMIDISystemCCDetune;
bool m_bLCDEnabled;
unsigned m_nLCDPinEnable;
@ -297,6 +309,8 @@ private:
unsigned m_nButtonPinShortcut;
unsigned m_nButtonPinPgmUp;
unsigned m_nButtonPinPgmDown;
unsigned m_nButtonPinBankUp;
unsigned m_nButtonPinBankDown;
unsigned m_nButtonPinTGUp;
unsigned m_nButtonPinTGDown;
@ -307,6 +321,8 @@ private:
std::string m_ButtonActionHome;
std::string m_ButtonActionPgmUp;
std::string m_ButtonActionPgmDown;
std::string m_ButtonActionBankUp;
std::string m_ButtonActionBankDown;
std::string m_ButtonActionTGUp;
std::string m_ButtonActionTGDown;
@ -322,6 +338,8 @@ private:
unsigned m_nMIDIButtonHome;
unsigned m_nMIDIButtonPgmUp;
unsigned m_nMIDIButtonPgmDown;
unsigned m_nMIDIButtonBankUp;
unsigned m_nMIDIButtonBankDown;
unsigned m_nMIDIButtonTGUp;
unsigned m_nMIDIButtonTGDown;

@ -53,6 +53,21 @@ LOGMODULE ("mididevice");
#define MIDI_PROGRAM_CHANGE 0b1100
#define MIDI_PITCH_BEND 0b1110
// MIDI "System" level (i.e. all TG) custom CC maps
// Note: Even if number of TGs is not 8, there are only 8
// available to be used in the mappings here.
#define NUM_MIDI_CC_MAPS 8
const unsigned MIDISystemCCMap[NUM_MIDI_CC_MAPS][8] = {
{0,0,0,0,0,0,0,0}, // 0 = disabled
{16,17,18,19,80,81,82,83}, // 1 = General Purpose Controllers 1-8
{20,21,22,23,24,25,26,27},
{52,53,54,55,56,57,58,59},
{102,103,104,105,106,107,108,109},
{110,111,112,113,114,115,116,117},
{3,9,14,15,28,29,30,31},
{35,41,46,47,60,61,62,63}
};
#define MIDI_SYSTEM_EXCLUSIVE_BEGIN 0xF0
#define MIDI_SYSTEM_EXCLUSIVE_END 0xF7
#define MIDI_TIMING_CLOCK 0xF8
@ -69,6 +84,34 @@ CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInter
{
m_ChannelMap[nTG] = Disabled;
}
m_nMIDISystemCCVol = m_pConfig->GetMIDISystemCCVol();
m_nMIDISystemCCPan = m_pConfig->GetMIDISystemCCPan();
m_nMIDISystemCCDetune = m_pConfig->GetMIDISystemCCDetune();
m_MIDISystemCCBitmap[0] = 0;
m_MIDISystemCCBitmap[1] = 0;
m_MIDISystemCCBitmap[2] = 0;
m_MIDISystemCCBitmap[3] = 0;
for (int tg=0; tg<8; tg++)
{
if (m_nMIDISystemCCVol != 0) {
u8 cc = MIDISystemCCMap[m_nMIDISystemCCVol][tg];
m_MIDISystemCCBitmap[cc>>5] |= (1<<(cc%32));
}
if (m_nMIDISystemCCPan != 0) {
u8 cc = MIDISystemCCMap[m_nMIDISystemCCPan][tg];
m_MIDISystemCCBitmap[cc>>5] |= (1<<(cc%32));
}
if (m_nMIDISystemCCDetune != 0) {
u8 cc = MIDISystemCCMap[m_nMIDISystemCCDetune][tg];
m_MIDISystemCCBitmap[cc>>5] |= (1<<(cc%32));
}
}
if (m_pConfig->GetMIDIDumpEnabled ()) {
LOGNOTE("MIDI System CC Map: %08X %08X %08X %08X", m_MIDISystemCCBitmap[3],m_MIDISystemCCBitmap[2],m_MIDISystemCCBitmap[1],m_MIDISystemCCBitmap[0]);
}
}
CMIDIDevice::~CMIDIDevice (void)
@ -239,7 +282,9 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
}
// Process MIDI for each active Tone Generator
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators(); nTG++)
bool bSystemCCHandled = false;
bool bSystemCCChecked = false;
for (unsigned nTG = 0; nTG < m_pConfig->GetToneGenerators() && !bSystemCCHandled; nTG++)
{
if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN)
{
@ -373,6 +418,17 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
m_pSynthesizer->notesOff (pMessage[2], nTG);
}
break;
default:
// Check for system-level, cross-TG MIDI Controls, but only do it once.
// Also, if successfully handled, then no need to process other TGs,
// so it is possible to break out of the main TG loop too.
// Note: We handle this here so we get the TG MIDI channel checking.
if (!bSystemCCChecked) {
bSystemCCHandled = HandleMIDISystemCC(pMessage[1], pMessage[2]);
bSystemCCChecked = true;
}
break;
}
break;
@ -418,6 +474,52 @@ void CMIDIDevice::AddDevice (const char *pDeviceName)
s_DeviceMap.insert (std::pair<std::string, CMIDIDevice *> (pDeviceName, this));
}
bool CMIDIDevice::HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval)
{
// This only makes sense when there are at least 8 TGs.
// Note: If more than 8 TGs then only 8 TGs are controllable this way.
if (m_pConfig->GetToneGenerators() < 8) {
return false;
}
// Quickly reject any CCs not in the configured maps
if ((m_MIDISystemCCBitmap[ucCC>>5] & (1<<(ucCC%32))) == 0) {
// Not in the map
return false;
}
// Not looking for duplicate CCs so return once handled
for (unsigned tg=0; tg<8; tg++) {
if (m_nMIDISystemCCVol != 0) {
if (ucCC == MIDISystemCCMap[m_nMIDISystemCCVol][tg]) {
m_pSynthesizer->SetVolume (ucCCval, tg);
return true;
}
}
if (m_nMIDISystemCCPan != 0) {
if (ucCC == MIDISystemCCMap[m_nMIDISystemCCPan][tg]) {
m_pSynthesizer->SetPan (ucCCval, tg);
return true;
}
}
if (m_nMIDISystemCCDetune != 0) {
if (ucCC == MIDISystemCCMap[m_nMIDISystemCCDetune][tg]) {
if (ucCCval == 0)
{
m_pSynthesizer->SetMasterTune (0, tg);
}
else
{
m_pSynthesizer->SetMasterTune (maplong (ucCCval, 1, 127, -99, 99), tg);
}
return true;
}
}
}
return false;
}
void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG)
{
int16_t sysex_return;

@ -60,6 +60,10 @@ protected:
void MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable = 0);
void AddDevice (const char *pDeviceName);
void HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG);
private:
bool HandleMIDISystemCC(const u8 ucCC, const u8 ucCCval);
private:
CMiniDexed *m_pSynthesizer;
CConfig *m_pConfig;
@ -67,6 +71,11 @@ private:
u8 m_ChannelMap[CConfig::AllToneGenerators];
unsigned m_nMIDISystemCCVol;
unsigned m_nMIDISystemCCPan;
unsigned m_nMIDISystemCCDetune;
u32 m_MIDISystemCCBitmap[4]; // to allow for 128 bit entries
std::string m_DeviceName;
typedef std::unordered_map<std::string, CMIDIDevice *> TDeviceMap;

@ -95,12 +95,16 @@ ButtonActionHome=doubleclick
ButtonPinShortcut=11
# (Shortcut doesn't have an action)
# GPIO Program/TG Selection
# GPIO Program/Bank/TG Selection
# Any buttons set to 0 will be ignored
ButtonPinPgmUp=0
ButtonActionPgmUp=
ButtonPinPgmDown=0
ButtonActionPgmDown=
ButtonPinBankUp=0
ButtonActionBankUp=
ButtonPinBankDown=0
ButtonActionBankDown=
ButtonPinTGUp=0
ButtonActionTGUp=
ButtonPinTGDown=0
@ -116,17 +120,24 @@ LongPressTimeout=400
# CC channel: 0=OFF; 1-16 MIDI Ch; >16 Omni
# If MIDIButtonNotes>0 then treat MIDIButton numbers as MIDI
# Note numbers, triggered with NoteOn/NoteOff, not CC numbers.
MIDIButtonCh=0
MIDIButtonCh=17
MIDIButtonNotes=0
MIDIButtonPrev=0
MIDIButtonNext=0
MIDIButtonBack=0
MIDIButtonSelect=0
MIDIButtonHome=0
MIDIButtonPgmUp=0
MIDIButtonPgmDown=0
MIDIButtonTGUp=0
MIDIButtonTGDown=0
# Arrow left
MIDIButtonPrev=46
# Arrow right
MIDIButtonNext=47
# Arrow up
MIDIButtonBack=48
# Arrow down
MIDIButtonSelect=49
# Home button
MIDIButtonHome=50
MIDIButtonPgmUp=51
MIDIButtonPgmDown=52
MIDIButtonBankUp=53
MIDIButtonBankDown=54
MIDIButtonTGUp=55
MIDIButtonTGDown=56
# KY-040 Rotary Encoder
EncoderEnabled=1

@ -257,50 +257,8 @@ CUIButton::BtnTrigger CUIButton::triggerTypeFromString(const char* triggerString
}
CUIButtons::CUIButtons (
unsigned prevPin, const char *prevAction,
unsigned nextPin, const char *nextAction,
unsigned backPin, const char *backAction,
unsigned selectPin, const char *selectAction,
unsigned homePin, const char *homeAction,
unsigned pgmUpPin, const char *pgmUpAction,
unsigned pgmDownPin, const char *pgmDownAction,
unsigned TGUpPin, const char *TGUpAction,
unsigned TGDownPin, const char *TGDownAction,
unsigned doubleClickTimeout, unsigned longPressTimeout,
unsigned notesMidi, unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi,
unsigned pgmUpMidi, unsigned pgmDownMidi, unsigned TGUpMidi, unsigned TGDownMidi
)
: m_doubleClickTimeout(doubleClickTimeout),
m_longPressTimeout(longPressTimeout),
m_prevPin(prevPin),
m_prevAction(CUIButton::triggerTypeFromString(prevAction)),
m_nextPin(nextPin),
m_nextAction(CUIButton::triggerTypeFromString(nextAction)),
m_backPin(backPin),
m_backAction(CUIButton::triggerTypeFromString(backAction)),
m_selectPin(selectPin),
m_selectAction(CUIButton::triggerTypeFromString(selectAction)),
m_homePin(homePin),
m_homeAction(CUIButton::triggerTypeFromString(homeAction)),
m_pgmUpPin(pgmUpPin),
m_pgmUpAction(CUIButton::triggerTypeFromString(pgmUpAction)),
m_pgmDownPin(pgmDownPin),
m_pgmDownAction(CUIButton::triggerTypeFromString(pgmDownAction)),
m_TGUpPin(TGUpPin),
m_TGUpAction(CUIButton::triggerTypeFromString(TGUpAction)),
m_TGDownPin(TGDownPin),
m_TGDownAction(CUIButton::triggerTypeFromString(TGDownAction)),
m_notesMidi(notesMidi),
m_prevMidi(ccToMidiPin(prevMidi)),
m_nextMidi(ccToMidiPin(nextMidi)),
m_backMidi(ccToMidiPin(backMidi)),
m_selectMidi(ccToMidiPin(selectMidi)),
m_homeMidi(ccToMidiPin(homeMidi)),
m_pgmUpMidi(ccToMidiPin(pgmUpMidi)),
m_pgmDownMidi(ccToMidiPin(pgmDownMidi)),
m_TGUpMidi(ccToMidiPin(TGUpMidi)),
m_TGDownMidi(ccToMidiPin(TGDownMidi)),
CUIButtons::CUIButtons (CConfig *pConfig)
: m_pConfig(pConfig),
m_eventHandler (0),
m_lastTick (0)
{
@ -312,6 +270,46 @@ CUIButtons::~CUIButtons (void)
boolean CUIButtons::Initialize (void)
{
assert (m_pConfig);
// Read the button configuration
m_doubleClickTimeout = m_pConfig->GetDoubleClickTimeout ();
m_longPressTimeout = m_pConfig->GetLongPressTimeout ();
m_prevPin = m_pConfig->GetButtonPinPrev ();
m_prevAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionPrev ());
m_nextPin = m_pConfig->GetButtonPinNext ();
m_nextAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionNext ());
m_backPin = m_pConfig->GetButtonPinBack ();
m_backAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionBack ());
m_selectPin = m_pConfig->GetButtonPinSelect ();
m_selectAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionSelect ());
m_homePin = m_pConfig->GetButtonPinHome ();
m_homeAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionHome ());
m_pgmUpPin = m_pConfig->GetButtonPinPgmUp ();
m_pgmUpAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionPgmUp ());
m_pgmDownPin = m_pConfig->GetButtonPinPgmDown ();
m_pgmDownAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionPgmDown ());
m_BankUpPin = m_pConfig->GetButtonPinBankUp ();
m_BankUpAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionBankUp ());
m_BankDownPin = m_pConfig->GetButtonPinBankDown ();
m_BankDownAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionBankDown ());
m_TGUpPin = m_pConfig->GetButtonPinTGUp ();
m_TGUpAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionTGUp ());
m_TGDownPin = m_pConfig->GetButtonPinTGDown ();
m_TGDownAction = CUIButton::triggerTypeFromString( m_pConfig->GetButtonActionTGDown ());
m_notesMidi = ccToMidiPin( m_pConfig->GetMIDIButtonNotes ());
m_prevMidi = ccToMidiPin( m_pConfig->GetMIDIButtonPrev ());
m_nextMidi = ccToMidiPin( m_pConfig->GetMIDIButtonNext ());
m_backMidi = ccToMidiPin( m_pConfig->GetMIDIButtonBack ());
m_selectMidi = ccToMidiPin( m_pConfig->GetMIDIButtonSelect ());
m_homeMidi = ccToMidiPin( m_pConfig->GetMIDIButtonHome ());
m_pgmUpMidi = ccToMidiPin( m_pConfig->GetMIDIButtonPgmUp ());
m_pgmDownMidi = ccToMidiPin( m_pConfig->GetMIDIButtonPgmDown ());
m_BankUpMidi = ccToMidiPin( m_pConfig->GetMIDIButtonBankUp ());
m_BankDownMidi = ccToMidiPin( m_pConfig->GetMIDIButtonBankDown ());
m_TGUpMidi = ccToMidiPin( m_pConfig->GetMIDIButtonTGUp ());
m_TGDownMidi = ccToMidiPin( m_pConfig->GetMIDIButtonTGDown ());
// First sanity check and convert the timeouts:
// Internally values are in tenths of a millisecond, but config values
// are in milliseconds
@ -332,16 +330,16 @@ boolean CUIButtons::Initialize (void)
// longpress. We may not initialise all of the buttons.
// MIDI buttons only support a single click.
unsigned pins[MAX_BUTTONS] = {
m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin, m_pgmUpPin, m_pgmDownPin, m_TGUpPin, m_TGDownPin,
m_prevMidi, m_nextMidi, m_backMidi, m_selectMidi, m_homeMidi, m_pgmUpMidi, m_pgmDownMidi, m_TGUpMidi, m_TGDownMidi
m_prevPin, m_nextPin, m_backPin, m_selectPin, m_homePin, m_pgmUpPin, m_pgmDownPin, m_BankUpPin, m_BankDownPin, m_TGUpPin, m_TGDownPin,
m_prevMidi, m_nextMidi, m_backMidi, m_selectMidi, m_homeMidi, m_pgmUpMidi, m_pgmDownMidi, m_BankUpMidi, m_BankDownMidi, m_TGUpMidi, m_TGDownMidi
};
CUIButton::BtnTrigger triggers[MAX_BUTTONS] = {
// Normal buttons
m_prevAction, m_nextAction, m_backAction, m_selectAction, m_homeAction,
m_pgmUpAction, m_pgmDownAction, m_TGUpAction, m_TGDownAction,
m_pgmUpAction, m_pgmDownAction, m_BankUpAction, m_BankDownAction, m_TGUpAction, m_TGDownAction,
// MIDI Buttons only support a single click (at present)
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick,
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick
CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick, CUIButton::BtnTriggerClick
};
CUIButton::BtnEvent events[MAX_BUTTONS] = {
// Normal buttons
@ -352,6 +350,8 @@ boolean CUIButtons::Initialize (void)
CUIButton::BtnEventHome,
CUIButton::BtnEventPgmUp,
CUIButton::BtnEventPgmDown,
CUIButton::BtnEventBankUp,
CUIButton::BtnEventBankDown,
CUIButton::BtnEventTGUp,
CUIButton::BtnEventTGDown,
// MIDI buttons
@ -362,6 +362,8 @@ boolean CUIButtons::Initialize (void)
CUIButton::BtnEventHome,
CUIButton::BtnEventPgmUp,
CUIButton::BtnEventPgmDown,
CUIButton::BtnEventBankUp,
CUIButton::BtnEventBankDown,
CUIButton::BtnEventTGUp,
CUIButton::BtnEventTGDown
};

@ -27,8 +27,8 @@
#define BUTTONS_UPDATE_NUM_TICKS 100
#define DEBOUNCE_TIME 20
#define MAX_GPIO_BUTTONS 9 // 5 UI buttons, 4 Program/TG Select buttons
#define MAX_MIDI_BUTTONS 9
#define MAX_GPIO_BUTTONS 11 // 5 UI buttons, 6 Program/Bank/TG Select buttons
#define MAX_MIDI_BUTTONS 11
#define MAX_BUTTONS (MAX_GPIO_BUTTONS+MAX_MIDI_BUTTONS)
class CUIButtons;
@ -54,9 +54,11 @@ public:
BtnEventHome = 5,
BtnEventPgmUp = 6,
BtnEventPgmDown = 7,
BtnEventTGUp = 8,
BtnEventTGDown = 9,
BtnEventUnknown = 10
BtnEventBankUp = 8,
BtnEventBankDown = 9,
BtnEventTGUp = 10,
BtnEventTGDown = 11,
BtnEventUnknown = 12
};
CUIButton (void);
@ -111,20 +113,7 @@ public:
typedef void BtnEventHandler (CUIButton::BtnEvent Event, void *param);
public:
CUIButtons (
unsigned prevPin, const char *prevAction,
unsigned nextPin, const char *nextAction,
unsigned backPin, const char *backAction,
unsigned selectPin, const char *selectAction,
unsigned homePin, const char *homeAction,
unsigned pgmUpPin, const char *pgmUpAction,
unsigned pgmDownPin, const char *pgmDownAction,
unsigned TGUpPin, const char *TGUpAction,
unsigned TGDownPin, const char *TGDownAction,
unsigned doubleClickTimeout, unsigned longPressTimeout,
unsigned notesMidi, unsigned prevMidi, unsigned nextMidi, unsigned backMidi, unsigned selectMidi, unsigned homeMidi,
unsigned pgmUpMidi, unsigned pgmDownMidi, unsigned TGUpMidi, unsigned TGDownMidi
);
CUIButtons (CConfig *pConfig);
~CUIButtons (void);
boolean Initialize (void);
@ -138,6 +127,8 @@ public:
void BtnMIDICmdHandler (unsigned nMidiCmd, unsigned nMidiData1, unsigned nMidiData2);
private:
CConfig *m_pConfig;
// Array of normal GPIO buttons and "MIDI buttons"
CUIButton m_buttons[MAX_BUTTONS];
@ -163,6 +154,10 @@ private:
CUIButton::BtnTrigger m_pgmUpAction;
unsigned m_pgmDownPin;
CUIButton::BtnTrigger m_pgmDownAction;
unsigned m_BankUpPin;
CUIButton::BtnTrigger m_BankUpAction;
unsigned m_BankDownPin;
CUIButton::BtnTrigger m_BankDownAction;
unsigned m_TGUpPin;
CUIButton::BtnTrigger m_TGUpAction;
unsigned m_TGDownPin;
@ -178,6 +173,8 @@ private:
unsigned m_pgmUpMidi;
unsigned m_pgmDownMidi;
unsigned m_BankUpMidi;
unsigned m_BankDownMidi;
unsigned m_TGUpMidi;
unsigned m_TGDownMidi;

@ -431,6 +431,11 @@ void CUIMenu::EventHandler (TMenuEvent Event)
PgmUpDownHandler(Event);
break;
case MenuEventBankUp:
case MenuEventBankDown:
BankUpDownHandler(Event);
break;
case MenuEventTGUp:
case MenuEventTGDown:
TGUpDownHandler(Event);
@ -1375,6 +1380,87 @@ void CUIMenu::PgmUpDownHandler (TMenuEvent Event)
}
}
void CUIMenu::BankUpDownHandler (TMenuEvent Event)
{
if (m_pMiniDexed->GetParameter (CMiniDexed::ParameterPerformanceSelectChannel) != CMIDIDevice::Disabled)
{
// Bank Up/Down acts on performances
unsigned nLastPerformanceBank = m_pMiniDexed->GetLastPerformanceBank();
unsigned nPerformanceBank = m_nSelectedPerformanceBankID;
unsigned nStartBank = nPerformanceBank;
//LOGNOTE("Performance Bank actual=%d, last=%d", nPerformanceBank, nLastPerformanceBank);
if (Event == MenuEventBankDown)
{
do
{
if (nPerformanceBank == 0)
{
// Wrap around
nPerformanceBank = nLastPerformanceBank;
}
else if (nPerformanceBank > 0)
{
--nPerformanceBank;
}
} while ((m_pMiniDexed->IsValidPerformanceBank(nPerformanceBank) != true) && (nPerformanceBank != nStartBank));
m_nSelectedPerformanceBankID = nPerformanceBank;
// Switch to the new bank and select the first performance voice
m_pMiniDexed->SetParameter (CMiniDexed::ParameterPerformanceBank, nPerformanceBank);
m_pMiniDexed->SetFirstPerformance();
//LOGNOTE("Performance Bank new=%d, last=%d", m_nSelectedPerformanceBankID, nLastPerformanceBank);
}
else // MenuEventBankUp
{
do
{
if (nPerformanceBank == nLastPerformanceBank)
{
// Wrap around
nPerformanceBank = 0;
}
else if (nPerformanceBank < nLastPerformanceBank)
{
++nPerformanceBank;
}
} while ((m_pMiniDexed->IsValidPerformanceBank(nPerformanceBank) != true) && (nPerformanceBank != nStartBank));
m_nSelectedPerformanceBankID = nPerformanceBank;
m_pMiniDexed->SetParameter (CMiniDexed::ParameterPerformanceBank, nPerformanceBank);
m_pMiniDexed->SetFirstPerformance();
//LOGNOTE("Performance Bank new=%d, last=%d", m_nSelectedPerformanceBankID, nLastPerformanceBank);
}
}
else
{
// Bank Up/Down acts on voices within a TG.
// If we're not in the root menu, then see if we are already in a TG menu,
// then find the current TG number. Otherwise assume TG1 (nTG=0).
unsigned nTG = 0;
if (m_MenuStackMenu[0] == s_MainMenu && (m_pCurrentMenu == s_TGMenu) || (m_MenuStackMenu[1] == s_TGMenu)) {
nTG = m_nMenuStackSelection[0];
}
assert (nTG < CConfig::AllToneGenerators);
if (nTG < m_nToneGenerators)
{
int nBank = m_pMiniDexed->GetTGParameter (CMiniDexed::TGParameterVoiceBank, nTG);
assert (Event == MenuEventBankDown || Event == MenuEventBankUp);
if (Event == MenuEventBankDown)
{
//LOGNOTE("BankDown");
nBank = m_pMiniDexed->GetSysExFileLoader ()->GetNextBankDown(nBank);
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nBank, nTG);
}
else
{
//LOGNOTE("BankUp");
nBank = m_pMiniDexed->GetSysExFileLoader ()->GetNextBankUp(nBank);
m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterVoiceBank, nBank, nTG);
}
}
}
}
void CUIMenu::TGUpDownHandler (TMenuEvent Event)
{
// This will update the menus to position it for the next TG up or down

@ -48,6 +48,8 @@ public:
MenuEventPressAndStepUp,
MenuEventPgmUp,
MenuEventPgmDown,
MenuEventBankUp,
MenuEventBankDown,
MenuEventTGUp,
MenuEventTGDown,
MenuEventUnknown
@ -119,6 +121,7 @@ private:
void OPShortcutHandler (TMenuEvent Event);
void PgmUpDownHandler (TMenuEvent Event);
void BankUpDownHandler (TMenuEvent Event);
void TGUpDownHandler (TMenuEvent Event);
static void TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext);

@ -162,37 +162,7 @@ bool CUserInterface::Initialize (void)
LOGDBG ("LCD initialized");
}
m_pUIButtons = new CUIButtons ( m_pConfig->GetButtonPinPrev (),
m_pConfig->GetButtonActionPrev (),
m_pConfig->GetButtonPinNext (),
m_pConfig->GetButtonActionNext (),
m_pConfig->GetButtonPinBack (),
m_pConfig->GetButtonActionBack (),
m_pConfig->GetButtonPinSelect (),
m_pConfig->GetButtonActionSelect (),
m_pConfig->GetButtonPinHome (),
m_pConfig->GetButtonActionHome (),
m_pConfig->GetButtonPinPgmUp (),
m_pConfig->GetButtonActionPgmUp (),
m_pConfig->GetButtonPinPgmDown (),
m_pConfig->GetButtonActionPgmDown (),
m_pConfig->GetButtonPinTGUp (),
m_pConfig->GetButtonActionTGUp (),
m_pConfig->GetButtonPinTGDown (),
m_pConfig->GetButtonActionTGDown (),
m_pConfig->GetDoubleClickTimeout (),
m_pConfig->GetLongPressTimeout (),
m_pConfig->GetMIDIButtonNotes (),
m_pConfig->GetMIDIButtonPrev (),
m_pConfig->GetMIDIButtonNext (),
m_pConfig->GetMIDIButtonBack (),
m_pConfig->GetMIDIButtonSelect (),
m_pConfig->GetMIDIButtonHome (),
m_pConfig->GetMIDIButtonPgmUp (),
m_pConfig->GetMIDIButtonPgmDown (),
m_pConfig->GetMIDIButtonTGUp (),
m_pConfig->GetMIDIButtonTGDown ()
);
m_pUIButtons = new CUIButtons ( m_pConfig );
assert (m_pUIButtons);
if (!m_pUIButtons->Initialize ())
@ -397,6 +367,14 @@ void CUserInterface::UIButtonsEventHandler (CUIButton::BtnEvent Event)
m_Menu.EventHandler (CUIMenu::MenuEventPgmDown);
break;
case CUIButton::BtnEventBankUp:
m_Menu.EventHandler (CUIMenu::MenuEventBankUp);
break;
case CUIButton::BtnEventBankDown:
m_Menu.EventHandler (CUIMenu::MenuEventBankDown);
break;
case CUIButton::BtnEventTGUp:
m_Menu.EventHandler (CUIMenu::MenuEventTGUp);
break;

Loading…
Cancel
Save