Merge branch 'main' of https://github.com/probonopd/MiniDexed into probonopd-main

pull/73/head
Holger Wirtz 3 years ago
commit 1d3fc4363f
  1. 26
      README.md
  2. BIN
      src/.minidexed.cpp.swp
  3. 1
      src/dexedadapter.h
  4. 125
      src/minidexed.cpp
  5. 4
      src/minidexed.h
  6. 20
      src/performance.ini
  7. 206
      src/performanceconfig.cpp
  8. 40
      src/performanceconfig.h
  9. 125
      src/uimenu.cpp
  10. 10
      src/uimenu.h
  11. 25
      src/userinterface.cpp
  12. 1
      src/userinterface.h

@ -1,12 +1,10 @@
# MiniDexed ![](https://github.com/probonopd/MiniDexed/actions/workflows/build.yml/badge.svg)
![image](https://user-images.githubusercontent.com/2480569/161439882-99932f84-5abb-4a43-9fd5-87fb491f12a2.png)
![minidexed](https://user-images.githubusercontent.com/2480569/161813414-bb156a1c-efec-44c0-802a-8926412a08e0.jpg)
[Dexed](https://asb2m10.github.io/dexed/) is a FM synthesizer closely modeled on the famous DX7 by a well-known Japanese manufacturer. MiniDexed is a port to run it on a bare metal Raspberry Pi (without a Linux kernel or operating system). __This is a work in progress. Contributions are highly welcome.__
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 Pi Raspberry 2 and larger, it can run 8 tone generators, basically creating an open source equivalent of the TX816/TX802 (8 DX7 instances without the keyboard in one box).
## TODO
Contributions are highly welcome.
## Features
- [x] Get [Synth_Dexed](https://codeberg.org/dcoredump/Synth_Dexed) to build with [circle-stdlib](https://github.com/smuehlst/circle-stdlib)
- [x] Upload SD card contents to [GitHub Releases](../../releases)
@ -32,7 +30,11 @@
- [x] Add reverb effect
- [ ] Make it possible to assign voice parameters to sliders and knobs on MIDI controllers
I am wondering whether we can run multiple Dexed instances, in order to recreate basically an open source equivalent of the TX802 (8 DX7 instances without the keyboard in one box).
## System Requirements
* Raspberry Pi 1, 2, 3, 4, or 400 (Zero and Zero 2 can be used but need HDMI or a supported i2c DAC for audio out). On Raspberry Pi 1 and on Raspberry Pi Zero there will be severely limited functionality (only one tone generator instead of 8)
* A PCM5102A or PCM5122 based DAC or HDMI display or audio extractor for good sound quality. If you don't have this, you can use the headphone jack on the Raspberry Pi but on anything but the Raspberry 4 the sound quality will be seriously limited
* Optionally (but highly recommended), an [alphanumeric 1602 LCD Display](https://www.berrybase.de/en/sensors-modules/displays/alphanumeric-displays/alphanumerisches-lcd-16x2-gr-252-n/gelb) and a [KY-040 rotary encoder](https://www.berrybase.de/en/components/passive-components/potentiometer/rotary-encoder/drehregler/rotary-encoder-mit-breakoutboard-ohne-gewinde-und-mutter)
## Usage
@ -40,7 +42,7 @@ I am wondering whether we can run multiple Dexed instances, in order to recreate
* Download from [GitHub Releases](../../releases)
* Unzip
* Put the files into the root directory of a FAT32 formatted partition on SD/microSD card
* Put SD/microSD card into Raspberry Pi 1, 2, 3 or 4 (Zero and Zero 2 can probably be used but need HDMI or a supported i2c DAC for audio out)
* Put SD/microSD card into Raspberry Pi 1, 2, 3 or 4, or 400 (Zero and Zero 2 can be used but need HDMI or a supported i2c DAC for audio out)
* Attach headphones to the headphone jack using `SoundDevice=pwm` in `minidexed.ini` (default) (poor audio quality)
* Alternatively, attach a PCM5102A or PCM5122 based DAC and select i2c sound output using `SoundDevice=i2s` in `minidexed.ini` (best audio quality)
* Alternatively, attach a HDMI display with sound and select HDMI sound output using `SoundDevice=hdmi` in `minidexed.ini` (this may introduce slight latency)
@ -52,11 +54,11 @@ I am wondering whether we can run multiple Dexed instances, in order to recreate
## Pinout
All devices on Raspberry Pi GPIOs are optional.
All devices on Raspberry Pi GPIOs are **optional**.
__CAUTION:__ All GPIO numbers are [chip numbers](https://pinout.xyz/), not header positions.
|GPIO | Device | | Function | Direction | Commant|
|GPIO | Device | | Function | Direction | Comment|
|---|---|---|---|---|---|
|14 | UART | | TXD | | OUT | | serial MIDI|
|15 | UART | | RXD | | IN | | serial MIDI|
@ -85,9 +87,13 @@ __CAUTION:__ All GPIO numbers are [chip numbers](https://pinout.xyz/), not heade
|04 | NONE | | | | | | can generate clock signal|
|20 | NONE | | | | | | may be used for DAC DIN|
## Downloading
Compiled versions are available on [GitHub Releases](../../releases). Just download and put on a FAT32 formatted SD card.
## Building locally
E.g., to build for Raspberry Pi 4 on a Ubuntu 20.04 build system, you can use the following example. See [`build.yml`](../../tree/main/.github/workflows/build.yml) for complete build steps that create versions for Raspberry Pi 1, 2, 3,and 4 in 32-bit and 64-bit as required.
If you need to build the source code yoursel, you can use the following example, e.g., to build for Raspberry Pi 4 on a Ubuntu 20.04 build system. See [`build.yml`](../../tree/main/.github/workflows/build.yml) for complete build steps that create versions for Raspberry Pi 1, 2, 3,and 4 in 32-bit and 64-bit as required.
```
# Choose your RPi

Binary file not shown.

@ -34,7 +34,6 @@ public:
CDexedAdapter (uint8_t maxnotes, int rate)
: Dexed (maxnotes, rate)
{
Dexed::setCompressor(true);
}
void loadVoiceParameters (uint8_t* data)

@ -118,8 +118,11 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
//tg_mixer = new AudioStereoMixer<8>();
// END setup tg_mixer
SetParameter (ParameterCompressorEnable, 1);
// BEGIN setup reverb
reverb = new AudioEffectPlateReverb(pConfig->GetSampleRate());
SetParameter (ParameterReverbEnable, 1);
SetParameter (ParameterReverbSize, 70);
SetParameter (ParameterReverbHighDamp, 50);
SetParameter (ParameterReverbLowDamp, 50);
@ -176,6 +179,16 @@ bool CMiniDexed::Initialize (void)
m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG);
m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG);
}
// Effects
SetParameter (ParameterCompressorEnable, m_PerformanceConfig.GetCompressorEnable () ? 1 : 0);
SetParameter (ParameterReverbEnable, m_PerformanceConfig.GetReverbEnable () ? 1 : 0);
SetParameter (ParameterReverbSize, m_PerformanceConfig.GetReverbSize ());
SetParameter (ParameterReverbHighDamp, m_PerformanceConfig.GetReverbHighDamp ());
SetParameter (ParameterReverbLowDamp, m_PerformanceConfig.GetReverbLowDamp ());
SetParameter (ParameterReverbLowPass, m_PerformanceConfig.GetReverbLowPass ());
SetParameter (ParameterReverbDiffusion, m_PerformanceConfig.GetReverbDiffusion ());
SetParameter (ParameterReverbSend, m_PerformanceConfig.GetReverbSend ());
}
else
{
@ -494,25 +507,70 @@ void CMiniDexed::SetParameter (TParameter Parameter, int nValue)
assert (Parameter < ParameterUnknown);
m_nParameter[Parameter] = nValue;
float fValue = nValue / 99.0;
m_ReverbSpinLock.Acquire ();
switch (Parameter)
{
<<<<<<< HEAD
case ParameterReverbSize: reverb->size (fValue); break;
case ParameterReverbHighDamp: reverb->hidamp (fValue); break;
case ParameterReverbLowDamp: reverb->lodamp (fValue); break;
case ParameterReverbLowPass: reverb->lowpass (fValue); break;
case ParameterReverbDiffusion: reverb->diffusion (fValue); break;
case ParameterReverbLevel: reverb->level (fValue); break;
=======
case ParameterCompressorEnable:
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
assert (m_pTG[nTG]);
m_pTG[nTG]->setCompressor (!!nValue);
}
break;
case ParameterReverbEnable:
m_ReverbSpinLock.Acquire ();
reverb->set_bypass (!nValue);
m_ReverbSpinLock.Release ();
break;
case ParameterReverbSize:
m_ReverbSpinLock.Acquire ();
reverb->size (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
case ParameterReverbHighDamp:
m_ReverbSpinLock.Acquire ();
reverb->hidamp (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
case ParameterReverbLowDamp:
m_ReverbSpinLock.Acquire ();
reverb->lodamp (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
case ParameterReverbLowPass:
m_ReverbSpinLock.Acquire ();
reverb->lowpass (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
case ParameterReverbDiffusion:
m_ReverbSpinLock.Acquire ();
reverb->diffusion (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
case ParameterReverbSend:
m_ReverbSpinLock.Acquire ();
reverb->send (nValue / 99.0);
m_ReverbSpinLock.Release ();
break;
default:
assert (0);
break;
}
m_ReverbSpinLock.Release ();
}
int CMiniDexed::GetParameter (TParameter Parameter)
@ -725,18 +783,21 @@ void CMiniDexed::ProcessSound (void)
// END stereo panorama
// BEGIN adding reverb
float32_t ReverbBuffer[2][nFrames];
m_ReverbSpinLock.Acquire ();
reverb->doReverb(SampleBuffer[indexL],SampleBuffer[indexR],ReverbBuffer[0], ReverbBuffer[1],nFrames);
m_ReverbSpinLock.Release ();
// scale down and add left reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[0], reverb->get_level(), ReverbBuffer[0], nFrames);
arm_add_f32(SampleBuffer[indexL], ReverbBuffer[0], SampleBuffer[indexL], nFrames);
// scale down and add right reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[1], reverb->get_level(), ReverbBuffer[1], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[1], SampleBuffer[indexR], nFrames);
if (m_nParameter[ParameterReverbEnable])
{
float32_t ReverbBuffer[2][nFrames];
m_ReverbSpinLock.Acquire ();
reverb->doReverb(SampleBuffer[indexL],SampleBuffer[indexR],ReverbBuffer[0], ReverbBuffer[1],nFrames);
m_ReverbSpinLock.Release ();
// scale down and add left reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[0], reverb->get_level(), ReverbBuffer[0], nFrames);
arm_add_f32(SampleBuffer[indexL], ReverbBuffer[0], SampleBuffer[indexL], nFrames);
// scale down and add right reverb buffer by reverb level
arm_scale_f32(ReverbBuffer[1], reverb->get_level(), ReverbBuffer[1], nFrames);
arm_add_f32(SampleBuffer[indexR], ReverbBuffer[1], SampleBuffer[indexR], nFrames);
}
// END adding reverb
// Convert dual float array (left, right) to single int16 array (left/right)
@ -762,3 +823,31 @@ void CMiniDexed::ProcessSound (void)
}
#endif
bool CMiniDexed::SavePerformance (void)
{
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
m_PerformanceConfig.SetBankNumber (m_nVoiceBankID[nTG], nTG);
m_PerformanceConfig.SetVoiceNumber (m_nProgram[nTG], nTG);
m_PerformanceConfig.SetMIDIChannel (m_nMIDIChannel[nTG], nTG);
m_PerformanceConfig.SetVolume (m_nVolume[nTG], nTG);
m_PerformanceConfig.SetPan (m_nPan[nTG], nTG);
m_PerformanceConfig.SetDetune (m_nMasterTune[nTG], nTG);
m_PerformanceConfig.SetNoteLimitLow (m_nNoteLimitLow[nTG], nTG);
m_PerformanceConfig.SetNoteLimitHigh (m_nNoteLimitHigh[nTG], nTG);
m_PerformanceConfig.SetNoteShift (m_nNoteShift[nTG], nTG);
}
m_PerformanceConfig.SetCompressorEnable (!!m_nParameter[ParameterCompressorEnable]);
m_PerformanceConfig.SetReverbEnable (!!m_nParameter[ParameterReverbEnable]);
m_PerformanceConfig.SetReverbSize (m_nParameter[ParameterReverbSize]);
m_PerformanceConfig.SetReverbHighDamp (m_nParameter[ParameterReverbHighDamp]);
m_PerformanceConfig.SetReverbLowDamp (m_nParameter[ParameterReverbLowDamp]);
m_PerformanceConfig.SetReverbLowPass (m_nParameter[ParameterReverbLowPass]);
m_PerformanceConfig.SetReverbDiffusion (m_nParameter[ParameterReverbDiffusion]);
m_PerformanceConfig.SetReverbSend (m_nParameter[ParameterReverbSend]);
return m_PerformanceConfig.Save ();
}

@ -79,6 +79,8 @@ public:
enum TParameter
{
ParameterCompressorEnable,
ParameterReverbEnable,
ParameterReverbSize,
ParameterReverbHighDamp,
ParameterReverbLowDamp,
@ -112,6 +114,8 @@ public:
std::string GetVoiceName (unsigned nTG);
bool SavePerformance (void);
private:
int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note

@ -100,3 +100,23 @@ Detune8=0
NoteLimitLow8=0
NoteLimitHigh8=127
NoteShift8=0
# Effects
#CompressorEnable=1 # 0: off, 1: on
#ReverbEnable=1 # 0: off, 1: on
#ReverbSize=70 # 0 .. 99
#ReverbHighDamp=50 # 0 .. 99
#ReverbLowDamp=50 # 0 .. 99
#ReverbLowPass=30 # 0 .. 99
#ReverbDiffusion=65 # 0 .. 99
#ReverbSend=80 # 0 .. 99
# Effects
CompressorEnable=1
ReverbEnable=1
ReverbSize=70
ReverbHighDamp=50
ReverbLowDamp=50
ReverbLowPass=30
ReverbDiffusion=65
ReverbSend=80

@ -91,9 +91,81 @@ bool CPerformanceConfig::Load (void)
m_nNoteShift[nTG] = m_Properties.GetSignedNumber (PropertyName, 0);
}
m_bCompressorEnable = m_Properties.GetNumber ("CompressorEnable", 1) != 0;
m_bReverbEnable = m_Properties.GetNumber ("ReverbEnable", 1) != 0;
m_nReverbSize = m_Properties.GetNumber ("ReverbSize", 70);
m_nReverbHighDamp = m_Properties.GetNumber ("ReverbHighDamp", 50);
m_nReverbLowDamp = m_Properties.GetNumber ("ReverbLowDamp", 50);
m_nReverbLowPass = m_Properties.GetNumber ("ReverbLowPass", 30);
m_nReverbDiffusion = m_Properties.GetNumber ("ReverbDiffusion", 65);
m_nReverbSend = m_Properties.GetNumber ("ReverbSend", 80);
return bResult;
}
bool CPerformanceConfig::Save (void)
{
m_Properties.RemoveAll ();
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
CString PropertyName;
PropertyName.Format ("BankNumber%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nBankNumber[nTG]);
PropertyName.Format ("VoiceNumber%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nVoiceNumber[nTG]+1);
PropertyName.Format ("MIDIChannel%u", nTG+1);
unsigned nMIDIChannel = m_nMIDIChannel[nTG];
if (nMIDIChannel < CMIDIDevice::Channels)
{
nMIDIChannel++;
}
else if (nMIDIChannel == CMIDIDevice::OmniMode)
{
nMIDIChannel = 255;
}
else
{
nMIDIChannel = 0;
}
m_Properties.SetNumber (PropertyName, nMIDIChannel);
PropertyName.Format ("Volume%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nVolume[nTG]);
PropertyName.Format ("Pan%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nPan[nTG]);
PropertyName.Format ("Detune%u", nTG+1);
m_Properties.SetSignedNumber (PropertyName, m_nDetune[nTG]);
PropertyName.Format ("NoteLimitLow%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nNoteLimitLow[nTG]);
PropertyName.Format ("NoteLimitHigh%u", nTG+1);
m_Properties.SetNumber (PropertyName, m_nNoteLimitHigh[nTG]);
PropertyName.Format ("NoteShift%u", nTG+1);
m_Properties.SetSignedNumber (PropertyName, m_nNoteShift[nTG]);
}
m_Properties.SetNumber ("CompressorEnable", m_bCompressorEnable ? 1 : 0);
m_Properties.SetNumber ("ReverbEnable", m_bReverbEnable ? 1 : 0);
m_Properties.SetNumber ("ReverbSize", m_nReverbSize);
m_Properties.SetNumber ("ReverbHighDamp", m_nReverbHighDamp);
m_Properties.SetNumber ("ReverbLowDamp", m_nReverbLowDamp);
m_Properties.SetNumber ("ReverbLowPass", m_nReverbLowPass);
m_Properties.SetNumber ("ReverbDiffusion", m_nReverbDiffusion);
m_Properties.SetNumber ("ReverbSend", m_nReverbSend);
return m_Properties.Save ();
}
unsigned CPerformanceConfig::GetBankNumber (unsigned nTG) const
{
assert (nTG < CConfig::ToneGenerators);
@ -147,3 +219,137 @@ int CPerformanceConfig::GetNoteShift (unsigned nTG) const
assert (nTG < CConfig::ToneGenerators);
return m_nNoteShift[nTG];
}
void CPerformanceConfig::SetBankNumber (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nBankNumber[nTG] = nValue;
}
void CPerformanceConfig::SetVoiceNumber (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nVoiceNumber[nTG] = nValue;
}
void CPerformanceConfig::SetMIDIChannel (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nMIDIChannel[nTG] = nValue;
}
void CPerformanceConfig::SetVolume (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nVolume[nTG] = nValue;
}
void CPerformanceConfig::SetPan (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nPan[nTG] = nValue;
}
void CPerformanceConfig::SetDetune (int nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nDetune[nTG] = nValue;
}
void CPerformanceConfig::SetNoteLimitLow (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nNoteLimitLow[nTG] = nValue;
}
void CPerformanceConfig::SetNoteLimitHigh (unsigned nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nNoteLimitHigh[nTG] = nValue;
}
void CPerformanceConfig::SetNoteShift (int nValue, unsigned nTG)
{
assert (nTG < CConfig::ToneGenerators);
m_nNoteShift[nTG] = nValue;
}
bool CPerformanceConfig::GetCompressorEnable (void) const
{
return m_bCompressorEnable;
}
bool CPerformanceConfig::GetReverbEnable (void) const
{
return m_bReverbEnable;
}
unsigned CPerformanceConfig::GetReverbSize (void) const
{
return m_nReverbSize;
}
unsigned CPerformanceConfig::GetReverbHighDamp (void) const
{
return m_nReverbHighDamp;
}
unsigned CPerformanceConfig::GetReverbLowDamp (void) const
{
return m_nReverbLowDamp;
}
unsigned CPerformanceConfig::GetReverbLowPass (void) const
{
return m_nReverbLowPass;
}
unsigned CPerformanceConfig::GetReverbDiffusion (void) const
{
return m_nReverbDiffusion;
}
unsigned CPerformanceConfig::GetReverbSend (void) const
{
return m_nReverbSend;
}
void CPerformanceConfig::SetCompressorEnable (bool bValue)
{
m_bCompressorEnable = bValue;
}
void CPerformanceConfig::SetReverbEnable (bool bValue)
{
m_bReverbEnable = bValue;
}
void CPerformanceConfig::SetReverbSize (unsigned nValue)
{
m_nReverbSize = nValue;
}
void CPerformanceConfig::SetReverbHighDamp (unsigned nValue)
{
m_nReverbHighDamp = nValue;
}
void CPerformanceConfig::SetReverbLowDamp (unsigned nValue)
{
m_nReverbLowDamp = nValue;
}
void CPerformanceConfig::SetReverbLowPass (unsigned nValue)
{
m_nReverbLowPass = nValue;
}
void CPerformanceConfig::SetReverbDiffusion (unsigned nValue)
{
m_nReverbDiffusion = nValue;
}
void CPerformanceConfig::SetReverbSend (unsigned nValue)
{
m_nReverbSend = nValue;
}

@ -35,6 +35,8 @@ public:
bool Load (void);
bool Save (void);
// TG#
unsigned GetBankNumber (unsigned nTG) const; // 0 .. 127
unsigned GetVoiceNumber (unsigned nTG) const; // 0 .. 31
@ -46,6 +48,35 @@ public:
unsigned GetNoteLimitHigh (unsigned nTG) const; // 0 .. 127
int GetNoteShift (unsigned nTG) const; // -24 .. 24
void SetBankNumber (unsigned nValue, unsigned nTG);
void SetVoiceNumber (unsigned nValue, unsigned nTG);
void SetMIDIChannel (unsigned nValue, unsigned nTG);
void SetVolume (unsigned nValue, unsigned nTG);
void SetPan (unsigned nValue, unsigned nTG);
void SetDetune (int nValue, unsigned nTG);
void SetNoteLimitLow (unsigned nValue, unsigned nTG);
void SetNoteLimitHigh (unsigned nValue, unsigned nTG);
void SetNoteShift (int nValue, unsigned nTG);
// Effects
bool GetCompressorEnable (void) const;
bool GetReverbEnable (void) const;
unsigned GetReverbSize (void) const; // 0 .. 99
unsigned GetReverbHighDamp (void) const; // 0 .. 99
unsigned GetReverbLowDamp (void) const; // 0 .. 99
unsigned GetReverbLowPass (void) const; // 0 .. 99
unsigned GetReverbDiffusion (void) const; // 0 .. 99
unsigned GetReverbSend (void) const; // 0 .. 99
void SetCompressorEnable (bool bValue);
void SetReverbEnable (bool bValue);
void SetReverbSize (unsigned nValue);
void SetReverbHighDamp (unsigned nValue);
void SetReverbLowDamp (unsigned nValue);
void SetReverbLowPass (unsigned nValue);
void SetReverbDiffusion (unsigned nValue);
void SetReverbSend (unsigned nValue);
private:
CPropertiesFatFsFile m_Properties;
@ -58,6 +89,15 @@ private:
unsigned m_nNoteLimitLow[CConfig::ToneGenerators];
unsigned m_nNoteLimitHigh[CConfig::ToneGenerators];
int m_nNoteShift[CConfig::ToneGenerators];
bool m_bCompressorEnable;
bool m_bReverbEnable;
unsigned m_nReverbSize;
unsigned m_nReverbHighDamp;
unsigned m_nReverbLowDamp;
unsigned m_nReverbLowPass;
unsigned m_nReverbDiffusion;
unsigned m_nReverbSend;
};
#endif

@ -37,6 +37,7 @@ const CUIMenu::TMenuItem CUIMenu::s_MenuRoot[] =
{0}
};
// inserting menu items before "TG1" affect TGShortcutHandler()
const CUIMenu::TMenuItem CUIMenu::s_MainMenu[] =
{
{"TG1", MenuHandler, s_TGMenu, 0},
@ -48,8 +49,9 @@ const CUIMenu::TMenuItem CUIMenu::s_MainMenu[] =
{"TG6", MenuHandler, s_TGMenu, 5},
{"TG7", MenuHandler, s_TGMenu, 6},
{"TG8", MenuHandler, s_TGMenu, 7},
{"Reverb", MenuHandler, s_ReverbMenu},
#endif
{"Effects", MenuHandler, s_EffectsMenu},
{"Save", MenuHandler, s_SaveMenu},
{0}
};
@ -67,10 +69,20 @@ const CUIMenu::TMenuItem CUIMenu::s_TGMenu[] =
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_EffectsMenu[] =
{
{"Compress", EditGlobalParameter, 0, CMiniDexed::ParameterCompressorEnable},
#ifdef ARM_ALLOW_MULTI_CORE
{"Reverb", MenuHandler, s_ReverbMenu},
#endif
{0}
};
#ifdef ARM_ALLOW_MULTI_CORE
const CUIMenu::TMenuItem CUIMenu::s_ReverbMenu[] =
{
{"Enable", EditGlobalParameter, 0, CMiniDexed::ParameterReverbEnable},
{"Size", EditGlobalParameter, 0, CMiniDexed::ParameterReverbSize},
{"High damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbHighDamp},
{"Low damp", EditGlobalParameter, 0, CMiniDexed::ParameterReverbLowDamp},
@ -138,15 +150,23 @@ const CUIMenu::TMenuItem CUIMenu::s_OperatorMenu[] =
{0}
};
const CUIMenu::TMenuItem CUIMenu::s_SaveMenu[] =
{
{"Performance", SavePerformance},
{0}
};
// must match CMiniDexed::TParameter
const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::TGParameterUnknown] =
{
{0, 99, 1}, // ParameterReverbSize
{0, 99, 1}, // ParameterReverbHighDamp
{0, 99, 1}, // ParameterReverbLowDamp
{0, 99, 1}, // ParameterReverbLowPass
{0, 99, 1}, // ParameterReverbDiffusion
{0, 99, 1}, // ParameterReverbSend
const CUIMenu::TParameter CUIMenu::s_GlobalParameter[CMiniDexed::ParameterUnknown] =
{
{0, 1, 1, ToOnOff}, // ParameterCompessorEnable
{0, 1, 1, ToOnOff}, // ParameterReverbEnable
{0, 99, 1}, // ParameterReverbSize
{0, 99, 1}, // ParameterReverbHighDamp
{0, 99, 1}, // ParameterReverbLowDamp
{0, 99, 1}, // ParameterReverbLowPass
{0, 99, 1}, // ParameterReverbDiffusion
{0, 99, 1} // ParameterReverbSend
};
// must match CMiniDexed::TTGParameter
@ -316,8 +336,7 @@ void CUIMenu::MenuHandler (CUIMenu *pUIMenu, TMenuEvent Event)
break;
default:
assert (0);
break;
return;
}
if (pUIMenu->m_pCurrentMenu) // if this is another menu?
@ -410,6 +429,11 @@ void CUIMenu::EditVoiceBankNumber (CUIMenu *pUIMenu, TMenuEvent Event)
CMiniDexed::TGParameterVoiceBank, nValue, nTG);
break;
case MenuEventPressAndStepDown:
case MenuEventPressAndStepUp:
pUIMenu->TGShortcutHandler (Event);
return;
default:
return;
}
@ -453,6 +477,11 @@ void CUIMenu::EditProgramNumber (CUIMenu *pUIMenu, TMenuEvent Event)
pUIMenu->m_pMiniDexed->SetTGParameter (CMiniDexed::TGParameterProgram, nValue, nTG);
break;
case MenuEventPressAndStepDown:
case MenuEventPressAndStepUp:
pUIMenu->TGShortcutHandler (Event);
return;
default:
return;
}
@ -500,6 +529,11 @@ void CUIMenu::EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event)
pUIMenu->m_pMiniDexed->SetTGParameter (Param, nValue, nTG);
break;
case MenuEventPressAndStepDown:
case MenuEventPressAndStepUp:
pUIMenu->TGShortcutHandler (Event);
return;
default:
return;
}
@ -547,6 +581,11 @@ void CUIMenu::EditVoiceParameter (CUIMenu *pUIMenu, TMenuEvent Event)
pUIMenu->m_pMiniDexed->SetVoiceParameter (nParam, nValue, CMiniDexed::NoOP, nTG);
break;
case MenuEventPressAndStepDown:
case MenuEventPressAndStepUp:
pUIMenu->TGShortcutHandler (Event);
return;
default:
return;
}
@ -595,6 +634,11 @@ void CUIMenu::EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event)
pUIMenu->m_pMiniDexed->SetVoiceParameter (nParam, nValue, nOP, nTG);
break;
case MenuEventPressAndStepDown:
case MenuEventPressAndStepUp:
pUIMenu->TGShortcutHandler (Event);
return;
default:
return;
}
@ -610,6 +654,27 @@ void CUIMenu::EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event)
nValue > rParam.Minimum, nValue < rParam.Maximum);
}
void CUIMenu::SavePerformance (CUIMenu *pUIMenu, TMenuEvent Event)
{
if (Event != MenuEventUpdate)
{
return;
}
bool bOK = pUIMenu->m_pMiniDexed->SavePerformance ();
const char *pMenuName =
pUIMenu->m_MenuStackParent[pUIMenu->m_nCurrentMenuDepth-1]
[pUIMenu->m_nMenuStackItem[pUIMenu->m_nCurrentMenuDepth-1]].Name;
pUIMenu->m_pUI->DisplayWrite (pMenuName,
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
bOK ? "Completed" : "Error",
false, false);
CTimer::Get ()->StartKernelTimer (MSEC2HZ (1500), TimerHandler, 0, pUIMenu);
}
string CUIMenu::GetGlobalValueString (unsigned nParameter, int nValue)
{
string Result;
@ -796,3 +861,41 @@ string CUIMenu::ToOscillatorDetune (int nValue)
return Result;
}
void CUIMenu::TGShortcutHandler (TMenuEvent Event)
{
assert (m_nCurrentMenuDepth >= 2);
assert (m_MenuStackMenu[0] = s_MainMenu);
unsigned nTG = m_nMenuStackSelection[0];
assert (nTG < CConfig::ToneGenerators);
assert (m_nMenuStackItem[1] == nTG);
assert (m_nMenuStackParameter[1] == nTG);
assert ( Event == MenuEventPressAndStepDown
|| Event == MenuEventPressAndStepUp);
if (Event == MenuEventPressAndStepDown)
{
nTG--;
}
else
{
nTG++;
}
if (nTG < CConfig::ToneGenerators)
{
m_nMenuStackSelection[0] = nTG;
m_nMenuStackItem[1] = nTG;
m_nMenuStackParameter[1] = nTG;
EventHandler (MenuEventUpdate);
}
}
void CUIMenu::TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext)
{
CUIMenu *pThis = static_cast<CUIMenu *> (pContext);
assert (pThis);
pThis->EventHandler (MenuEventBack);
}

@ -24,6 +24,7 @@
#define _uimenu_h
#include <string>
#include <circle/timer.h>
class CMiniDexed;
class CUserInterface;
@ -42,6 +43,8 @@ public:
MenuEventHome,
MenuEventStepDown,
MenuEventStepUp,
MenuEventPressAndStepDown,
MenuEventPressAndStepUp,
MenuEventUnknown
};
@ -79,6 +82,7 @@ private:
static void EditTGParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditVoiceParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditOPParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void SavePerformance (CUIMenu *pUIMenu, TMenuEvent Event);
static std::string GetGlobalValueString (unsigned nParameter, int nValue);
static std::string GetTGValueString (unsigned nTGParameter, int nValue);
@ -98,6 +102,10 @@ private:
static std::string ToOscillatorMode (int nValue);
static std::string ToOscillatorDetune (int nValue);
void TGShortcutHandler (TMenuEvent Event);
static void TimerHandler (TKernelTimerHandle hTimer, void *pParam, void *pContext);
private:
CUserInterface *m_pUI;
CMiniDexed *m_pMiniDexed;
@ -118,9 +126,11 @@ private:
static const TMenuItem s_MenuRoot[];
static const TMenuItem s_MainMenu[];
static const TMenuItem s_TGMenu[];
static const TMenuItem s_EffectsMenu[];
static const TMenuItem s_ReverbMenu[];
static const TMenuItem s_EditVoiceMenu[];
static const TMenuItem s_OperatorMenu[];
static const TMenuItem s_SaveMenu[];
static const TParameter s_GlobalParameter[];
static const TParameter s_TGParameter[];

@ -34,6 +34,7 @@ CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManag
m_pLCD (0),
m_pLCDBuffered (0),
m_pRotaryEncoder (0),
m_bSwitchPressed (false),
m_Menu (this, pMiniDexed)
{
}
@ -177,12 +178,22 @@ void CUserInterface::EncoderEventHandler (CKY040::TEvent Event)
{
switch (Event)
{
case CKY040::EventSwitchDown:
m_bSwitchPressed = true;
break;
case CKY040::EventSwitchUp:
m_bSwitchPressed = false;
break;
case CKY040::EventClockwise:
m_Menu.EventHandler (CUIMenu::MenuEventStepUp);
m_Menu.EventHandler (m_bSwitchPressed ? CUIMenu::MenuEventPressAndStepUp
: CUIMenu::MenuEventStepUp);
break;
case CKY040::EventCounterclockwise:
m_Menu.EventHandler (CUIMenu::MenuEventStepDown);
m_Menu.EventHandler (m_bSwitchPressed ? CUIMenu::MenuEventPressAndStepDown
: CUIMenu::MenuEventStepDown);
break;
case CKY040::EventSwitchClick:
@ -193,17 +204,17 @@ void CUserInterface::EncoderEventHandler (CKY040::TEvent Event)
m_Menu.EventHandler (CUIMenu::MenuEventSelect);
break;
case CKY040::EventSwitchTripleClick:
m_Menu.EventHandler (CUIMenu::MenuEventHome);
break;
case CKY040::EventSwitchHold:
if (m_pRotaryEncoder->GetHoldSeconds () >= 3)
if (m_pRotaryEncoder->GetHoldSeconds () >= 10)
{
delete m_pLCD; // reset LCD
reboot ();
}
else
{
m_Menu.EventHandler (CUIMenu::MenuEventHome);
}
break;
default:

@ -64,6 +64,7 @@ private:
CWriteBufferDevice *m_pLCDBuffered;
CKY040 *m_pRotaryEncoder;
bool m_bSwitchPressed;
CUIMenu m_Menu;
};

Loading…
Cancel
Save