diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..6839059 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,23 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "ARM_ALLOW_MULTI_CORE" + ], + "windowsSdkVersion": "10.0.19041.0", + "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64/cl.exe", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "windows-msvc-x64", + "configurationProvider": "ms-vscode.makefile-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/build.sh b/build.sh index ce7e55e..4ddc245 100755 --- a/build.sh +++ b/build.sh @@ -1,36 +1,47 @@ #!/bin/bash +set +x + usage() { # Display Help echo "This script launch the build of the code." echo echo "options:" - echo "-h Print this Help." - echo "-RPI Set Raspberry PI version" - echo "-clean Run clean before building" - echo "-full Run full build" - echo "-all Run the entire build" + echo " -h Print this Help." + echo " -d|--debug Set the debug mode" + echo " -RPI Set Raspberry PI version" + echo " -clean Run clean before building" + echo " -full Run full build" + echo " -all Run the entire build" } -export clean=0; -export minimal=1 +export PATH=$(readlink -f ./gcc-*/bin/):$PATH + +export CLEAN=0; +export INCREMENTAL_BUILD=1 while true do case "$1" in - -h|--help) usage ; exit 0;; - -RPI) export RPI=$2 ; shift;; - -clean) export clean=1 ; shift;; - -all) export minimal=0 ; shift;; + -h|--help) usage ; shift ; exit 0;; + -d|--debug) set -x ; shift;; + -RPI) export RPI=$2 ; shift 2;; + -clean) export CLEAN=1 ; shift;; + -all) export INCREMENTAL_BUILD=0 ; shift;; --) shift ; break;; - *) break;; + *) if [ $1 ] + then + echo "Error processing param #$1#" + exit 1 + else + break + fi;; esac done - if [ -z "${RPI}" ] ; then - echo "\$RPI missing, exting" + echo "\$RPI missing, exiting" exit 1 fi @@ -46,12 +57,12 @@ if [ "${RPI}" -gt "1" ]; then OPTIONS="${OPTIONS} -o ARM_ALLOW_MULTI_CORE" fi -if [ ${minimal} -eq 1 ] +if [ ${INCREMENTAL_BUILD} -eq 0 ] then # Build circle-stdlib library cd circle-stdlib/ - if [ ${clean} -eq 1 ] + if [ ${CLEAN} -eq 1 ] then make mrproper || true fi @@ -61,21 +72,21 @@ then # Build additional libraries cd libs/circle/addon/display/ - if [ ${clean} -eq 1 ] + if [ ${CLEAN} -eq 1 ] then make clean || true fi make -j cd ../sensor/ - if [ ${clean} -eq 1 ] + if [ ${CLEAN} -eq 1 ] then make clean || true fi make -j cd ../Properties/ - if [ ${clean} -eq 1 ] + if [ ${CLEAN} -eq 1 ] then make clean || true fi @@ -86,9 +97,9 @@ then cd .. fi -# Build MiniDexed +# Build MaxiDexed cd src -if [ ${clean} -eq 1 ] +if [ ${CLEAN} -eq 1 ] then make clean || true fi diff --git a/make-all.sh b/make-all.sh index 5cc45e5..7c9ab0d 100755 --- a/make-all.sh +++ b/make-all.sh @@ -1,3 +1,3 @@ #!/bin/bash -./make.sh -RPI 4 -prepenv -sysex -zip $* \ No newline at end of file +./make.sh -RPI 4 -build -boot -sysex -zip $* \ No newline at end of file diff --git a/make.sh b/make.sh index 42b9165..6a7ad39 100755 --- a/make.sh +++ b/make.sh @@ -1,5 +1,6 @@ #!/bin/bash +set -e set +x # this script install the dev environment and assume that the repository is alreadw cloned @@ -16,7 +17,8 @@ usage() echo " Set Raspberry PI version" echo " -prepenv Prepare the development environment" echo " -build Run build" - echo " -FETCH_SYSEX Fetch all FETCH_SYSEX" + echo " -boot Fetch boot files" + echo " -sysex Fetch all System Exclusive cartridge" echo " -zip Create the resulting zip file" echo " -opt Run RUN_OPTIONALITIES" echo @@ -25,6 +27,7 @@ usage() export RPI=4 export SETUP_ENV=0 export BUILD=0 +export FETCH_BOOT=0 export CREATE_ZIP=0 export FETCH_SYSEX=0 export RUN_OPTIONALITIES=0 @@ -36,12 +39,18 @@ while true ; do -r|-RPI|--raspberrypi) export RPI=$2 ; shift 2;; -prepenv) export SETUP_ENV=1 ; shift;; -build) export BUILD=1 ; shift;; - -FETCH_SYSEX) export FETCH_SYSEX=1 ; shift;; + -boot) export FETCH_BOOT=1 ; shift;; + -sysex) export FETCH_SYSEX=1 ; shift;; -zip) export CREATE_ZIP=1 ; shift;; -opt) export RUN_OPTIONALITIES=1 ; shift;; --) shift ; break ;; - *) break ;; - # *) echo "Internal error! Remaining params: #$*#" ; exit 1;; + *) if [ $1 ] + then + echo "Error processing param #$1#" + exit 1 + else + break + fi;; esac done @@ -59,6 +68,10 @@ then # Recursively pull git submodules git submodule update --init --recursive + cd circle-stdlib/libs/circle + git checkout develop # move to develop branch + cd - + # Install toolchain if [ "${RPI}" -gt 2 ] then @@ -76,28 +89,27 @@ then wget https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz fi - tar xvf gcc-arm-*-*.tar.xz + echo "Unpacking" + tar xf --checkpoint gcc-arm-*-*.tar.xz fi if [ ${BUILD} -eq 1 ] then - export PATH=$(readlink -f ./gcc-*/bin/):$PATH - # Build dependencies and MiniDexed - ./build.sh -clean -all + ./build.sh -RPI ${RPI} -clean -all cp ./src/kernel*.img ./kernels/ +fi - if [ ${SETUP_ENV} -eq 1 ] +if [ ${FETCH_BOOT} -eq 1 ] +then + # Get Raspberry Pi boot files + cd ./circle-stdlib/libs/circle/boot + make + if [ "${RPI}" -gt 2 ] then - # Get Raspberry Pi boot files - cd ./circle-stdlib/libs/circle/boot - make - if [ "${RPI}" -gt 2 ] - then - make armstub64 - fi - cd - + make armstub64 fi + cd - fi if [ ${CREATE_ZIP} -eq 1 ] diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..b1786a6 --- /dev/null +++ b/setup.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +./make.sh -RPI 4 -prepenv -build -boot -sysex -zip $* \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 7b22440..0c662a5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,20 +9,26 @@ SYNTH_DEXED_DIR = ../Synth_Dexed/src CMSIS_DIR = ../CMSIS_5/CMSIS U8G2_DIR = ../u8g2 -BUILD_DIR = ../build +# BUILD_DIR = ../build -MAXIDEXED_BUILD_DIR = $(BUILD_DIR)/maxi-dexed +# MAXIDEXED_BUILD_DIR = $(BUILD_DIR)/maxi-dexed -CIRCLE_STDLIB_BUILD_DIR = $(BUILD_DIR)/circle-stdlib -SYNTH_DEXED_BUILD_DIR = $(BUILD_DIR)/Synth_Dexed/src -CMSIS_BUILD_DIR = $(BUILD_DIR)/CMSIS_5/CMSIS -U8G2_BUILD_DIR = $(BUILD_DIR)/u8g2 +# CIRCLE_STDLIB_BUILD_DIR = $(BUILD_DIR)/circle-stdlib +# SYNTH_DEXED_BUILD_DIR = $(BUILD_DIR)/Synth_Dexed/src +# CMSIS_BUILD_DIR = $(BUILD_DIR)/CMSIS_5/CMSIS +# U8G2_BUILD_DIR = $(BUILD_DIR)/u8g2 -SOURCES = $(wildcard $(MAXIDEXED_SRC_DIR)/*.cpp) -OBJECTS = $(patsubst $(MAXIDEXED_SRC_DIR)/%.cpp,$(MAXIDEXED_BUILD_DIR)/%.o,$(SOURCES)) +# SOURCES = $(wildcard $(MAXIDEXED_SRC_DIR)/*.cpp) +# OBJECTS = $(patsubst $(MAXIDEXED_SRC_DIR)/%.cpp,$(MAXIDEXED_BUILD_DIR)/%.o,$(SOURCES)) -dir: - mkdir -p $(MAXIDEXED_BUILD_DIR) +# dir: +# mkdir -p $(MAXIDEXED_BUILD_DIR) + +OBJS = main.o kernel.o minidexed.o config.o userinterface.o uimenu.o \ + mididevice.o midikeyboard.o serialmididevice.o pckeyboard.o \ + sysexfileloader.o performanceconfig.o perftimer.o \ + effect_compressor.o effect_platervbstereo.o +# ui.o screens.o userinterfaceext.o OPTIMIZE = -O3 diff --git a/src/mididevice.cpp b/src/mididevice.cpp index 23f53f9..8df191b 100644 --- a/src/mididevice.cpp +++ b/src/mididevice.cpp @@ -20,27 +20,42 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . // + +#include #include "mididevice.h" #include "minidexed.h" #include "config.h" #include #include +LOGMODULE ("mididevice"); + #define MIDI_NOTE_OFF 0b1000 #define MIDI_NOTE_ON 0b1001 #define MIDI_AFTERTOUCH 0b1010 // TODO #define MIDI_CONTROL_CHANGE 0b1011 - #define MIDI_CC_BANK_SELECT_MSB 0 // TODO - #define MIDI_CC_MODULATION 1 - #define MIDI_CC_VOLUME 7 + #define MIDI_CC_BANK_SELECT_MSB 0 + #define MIDI_CC_MODULATION 1 + #define MIDI_CC_VOLUME 7 + #define MIDI_CC_PAN_POSITION 10 #define MIDI_CC_BANK_SELECT_LSB 32 #define MIDI_CC_BANK_SUSTAIN 64 + #define MIDI_CC_RESONANCE 71 + #define MIDI_CC_FREQUENCY_CUTOFF 74 + #define MIDI_CC_REVERB_LEVEL 91 + #define MIDI_CC_DETUNE_LEVEL 94 + #define MIDI_CC_ALL_SOUND_OFF 120 + #define MIDI_CC_ALL_NOTES_OFF 123 #define MIDI_PROGRAM_CHANGE 0b1100 #define MIDI_PITCH_BEND 0b1110 +#define MIDI_SYSTEM_EXCLUSIVE_BEGIN 0xF0 +#define MIDI_SYSTEM_EXCLUSIVE_END 0xF7 #define MIDI_TIMING_CLOCK 0xF8 #define MIDI_ACTIVE_SENSING 0xFE +CMIDIDevice::TDeviceMap CMIDIDevice::s_DeviceMap; + CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig) : m_pSynthesizer (pSynthesizer), m_pConfig (pConfig) @@ -70,8 +85,6 @@ u8 CMIDIDevice::GetChannel (unsigned nTG) const void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsigned nCable) { - assert (m_pSynthesizer != 0); - // The packet contents are just normal MIDI data - see // https://www.midi.org/specifications/item/table-1-summary-of-midi-message @@ -97,113 +110,380 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign (unsigned) pMessage[0], (unsigned) pMessage[1], (unsigned) pMessage[2]); break; + default: + switch(pMessage[0]) + { + case MIDI_SYSTEM_EXCLUSIVE_BEGIN: + printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength)); + for (uint16_t i = 0; i < nLength; i++) + { + if((i % 16) == 0) + printf("\n%04d:",i); + printf(" 0x%02x",pMessage[i]); + } + printf("\n"); + break; + default: + printf("MIDI%u: Unhandled MIDI event type %0x02x\n",nCable,pMessage[0]); + } + break; + } + } + + // Only for debugging: +/* + if(pMessage[0]==MIDI_SYSTEM_EXCLUSIVE_BEGIN) + { + printf("MIDI%u: SysEx data length: [%d]:",nCable, uint16_t(nLength)); + for (uint16_t i = 0; i < nLength; i++) + { + if((i % 16) == 0) + printf("\n%04d:",i); + printf(" 0x%02x",pMessage[i]); + } + printf("\n"); + } +*/ + + // Handle MIDI Thru + if (m_DeviceName.compare (m_pConfig->GetMIDIThruIn ()) == 0) + { + TDeviceMap::const_iterator Iterator; + + Iterator = s_DeviceMap.find (m_pConfig->GetMIDIThruOut ()); + if (Iterator != s_DeviceMap.end ()) + { + Iterator->second->Send (pMessage, nLength, nCable); } } if (nLength < 2) { + LOGERR("MIDI message is shorter than 2 bytes!"); return; } + m_MIDISpinLock.Acquire (); + u8 ucStatus = pMessage[0]; u8 ucChannel = ucStatus & 0x0F; u8 ucType = ucStatus >> 4; - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) + // GLOBAL MIDI SYSEX + if (pMessage[0] == MIDI_SYSTEM_EXCLUSIVE_BEGIN && pMessage[3] == 0x04 && pMessage[4] == 0x01 && pMessage[nLength-1] == MIDI_SYSTEM_EXCLUSIVE_END) // MASTER VOLUME + { + float32_t nMasterVolume=((pMessage[5] & 0x7c) & ((pMessage[6] & 0x7c) <<7))/(1<<14); + LOGNOTE("Master volume: %f",nMasterVolume); + m_pSynthesizer->setMasterVolume(nMasterVolume); + } + else { - if ( m_ChannelMap[nTG] == ucChannel - || m_ChannelMap[nTG] == OmniMode) + for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) { - switch (ucType) + if (ucStatus == MIDI_SYSTEM_EXCLUSIVE_BEGIN) { - case MIDI_NOTE_ON: - if (nLength < 3) + // MIDI SYSEX per MIDI channel + uint8_t ucSysExChannel = (pMessage[2] & 0x07); + if (m_ChannelMap[nTG] == ucSysExChannel || m_ChannelMap[nTG] == OmniMode) { - break; + LOGNOTE("MIDI-SYSEX: channel: %u, len: %u, TG: %u",m_ChannelMap[nTG],nLength,nTG); + HandleSystemExclusive(pMessage, nLength, nCable, nTG); } - - if (pMessage[2] > 0) + } + else + { + if ( m_ChannelMap[nTG] == ucChannel + || m_ChannelMap[nTG] == OmniMode) { - if (pMessage[2] <= 127) + switch (ucType) { - m_pSynthesizer->keydown (pMessage[1], - pMessage[2], nTG); - } - } - else - { - m_pSynthesizer->keyup (pMessage[1], nTG); - } - break; - - case MIDI_NOTE_OFF: - if (nLength < 3) - { - break; - } + case MIDI_NOTE_ON: + if (nLength < 3) + { + break; + } + + if (pMessage[2] > 0) + { + if (pMessage[2] <= 127) + { + m_pSynthesizer->keydown (pMessage[1], + pMessage[2], nTG); + } + } + else + { + m_pSynthesizer->keyup (pMessage[1], nTG); + } + break; + + case MIDI_NOTE_OFF: + if (nLength < 3) + { + break; + } + + m_pSynthesizer->keyup (pMessage[1], nTG); + break; + + case MIDI_CONTROL_CHANGE: + if (nLength < 3) + { + break; + } + + switch (pMessage[1]) + { + case MIDI_CC_MODULATION: + m_pSynthesizer->setModWheel (pMessage[2], nTG); + m_pSynthesizer->ControllersRefresh (nTG); + break; + + case MIDI_CC_VOLUME: + m_pSynthesizer->SetVolume (pMessage[2], nTG); + break; + + case MIDI_CC_PAN_POSITION: + m_pSynthesizer->SetPan (pMessage[2], nTG); + break; + + case MIDI_CC_BANK_SELECT_MSB: + { + int nBank = m_pSynthesizer->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG); + nBank &= 0b00000001111111; // keep the LSB and zeroes the MSB + nBank += pMessage[2] << 7; + m_pSynthesizer->BankSelect (nBank, nTG); + } + break; - m_pSynthesizer->keyup (pMessage[1], nTG); - break; - - case MIDI_CONTROL_CHANGE: - if (nLength < 3) - { - break; + case MIDI_CC_BANK_SELECT_LSB: + { + int nBank = m_pSynthesizer->GetTGParameter(CMiniDexed::TGParameterVoiceBank, nTG); + nBank &= 0b11111110000000; // keep the MSB and zeroes the LSB + nBank += pMessage[2]; + m_pSynthesizer->BankSelect (nBank, nTG); + } + break; + + case MIDI_CC_BANK_SUSTAIN: + m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG); + break; + + case MIDI_CC_RESONANCE: + m_pSynthesizer->SetResonance (maplong (pMessage[2], 0, 127, 0, 99), nTG); + break; + + case MIDI_CC_FREQUENCY_CUTOFF: + m_pSynthesizer->SetCutoff (maplong (pMessage[2], 0, 127, 0, 99), nTG); + break; + + case MIDI_CC_REVERB_LEVEL: + m_pSynthesizer->SetReverbSend (maplong (pMessage[2], 0, 127, 0, 99), nTG); + break; + + case MIDI_CC_DETUNE_LEVEL: + if (pMessage[2] == 0) + { + // "0 to 127, with 0 being no celeste (detune) effect applied at all." + m_pSynthesizer->SetMasterTune (0, nTG); + } + else + { + m_pSynthesizer->SetMasterTune (maplong (pMessage[2], 1, 127, -99, 99), nTG); + } + break; + + case MIDI_CC_ALL_SOUND_OFF: + m_pSynthesizer->panic (pMessage[2], nTG); + break; + + case MIDI_CC_ALL_NOTES_OFF: + m_pSynthesizer->notesOff (pMessage[2], nTG); + break; + } + break; + + case MIDI_PROGRAM_CHANGE: + // do program change only if enabled in config + if( m_pConfig->GetMIDIRXProgramChange() ) + m_pSynthesizer->ProgramChange (pMessage[1], nTG); + break; + + case MIDI_PITCH_BEND: { + if (nLength < 3) + { + break; + } + + s16 nValue = pMessage[1]; + nValue |= (s16) pMessage[2] << 7; + nValue -= 0x2000; + + m_pSynthesizer->setPitchbend (nValue, nTG); + } break; + + default: + break; + } } + } + } + } + m_MIDISpinLock.Release (); +} - switch (pMessage[1]) - { - case MIDI_CC_MODULATION: - m_pSynthesizer->setModWheel (pMessage[2], nTG); - m_pSynthesizer->ControllersRefresh (nTG); - break; +void CMIDIDevice::AddDevice (const char *pDeviceName) +{ + assert (pDeviceName); - case MIDI_CC_VOLUME: - m_pSynthesizer->SetVolume (pMessage[2], nTG); - break; + assert (m_DeviceName.empty ()); + m_DeviceName = pDeviceName; + assert (!m_DeviceName.empty ()); - case MIDI_CC_BANK_SELECT_MSB: - { - int bank = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterVoiceBank, nTG); - bank = (pMessage[2] << 7) + (bank & 0b01111111); - m_pSynthesizer->BankSelect (bank, nTG); - } - break; + s_DeviceMap.insert (std::pair (pDeviceName, this)); +} - case MIDI_CC_BANK_SELECT_LSB: - { - int bank = m_pSynthesizer->GetTGParameter (CMiniDexed::TGParameterVoiceBank, nTG); - bank = (bank & 0b011111110000000) + pMessage[2]; - m_pSynthesizer->BankSelect (bank, nTG); - } - break; +void CMIDIDevice::HandleSystemExclusive(const uint8_t* pMessage, const size_t nLength, const unsigned nCable, const uint8_t nTG) +{ + int16_t sysex_return; - case MIDI_CC_BANK_SUSTAIN: - m_pSynthesizer->setSustain (pMessage[2] >= 64, nTG); - break; - } - break; + sysex_return = m_pSynthesizer->checkSystemExclusive(pMessage, nLength, nTG); + LOGDBG("SYSEX handler return value: %d", sysex_return); - case MIDI_PROGRAM_CHANGE: - m_pSynthesizer->ProgramChange (pMessage[1], nTG); - break; + switch (sysex_return) + { + case -1: + LOGERR("SysEx end status byte not detected."); + break; + case -2: + LOGERR("SysEx vendor not Yamaha."); + break; + case -3: + LOGERR("Unknown SysEx parameter change."); + break; + case -4: + LOGERR("Unknown SysEx voice or function."); + break; + case -5: + LOGERR("Not a SysEx voice bulk upload."); + break; + case -6: + LOGERR("Wrong length for SysEx voice bulk upload (not 155)."); + break; + case -7: + LOGERR("Checksum error for one voice."); + break; + case -8: + LOGERR("Not a SysEx bank bulk upload."); + break; + case -9: + LOGERR("Wrong length for SysEx bank bulk upload (not 4096)."); + case -10: + LOGERR("Checksum error for bank."); + break; + case -11: + LOGERR("Unknown SysEx message."); + break; + case 64: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setMonoMode(pMessage[5],nTG); + break; + case 65: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setPitchbendRange(pMessage[5],nTG); + break; + case 66: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setPitchbendStep(pMessage[5],nTG); + break; + case 67: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setPortamentoMode(pMessage[5],nTG); + break; + case 68: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setPortamentoGlissando(pMessage[5],nTG); + break; + case 69: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setPortamentoTime(pMessage[5],nTG); + break; + case 70: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setModWheelRange(pMessage[5],nTG); + break; + case 71: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setModWheelTarget(pMessage[5],nTG); + break; + case 72: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setFootControllerRange(pMessage[5],nTG); + break; + case 73: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setFootControllerTarget(pMessage[5],nTG); + break; + case 74: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setBreathControllerRange(pMessage[5],nTG); + break; + case 75: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setBreathControllerTarget(pMessage[5],nTG); + break; + case 76: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setAftertouchRange(pMessage[5],nTG); + break; + case 77: + LOGDBG("SysEx Function parameter change: %d Value %d",pMessage[4],pMessage[5]); + m_pSynthesizer->setAftertouchTarget(pMessage[5],nTG); + break; + case 100: + // load sysex-data into voice memory + LOGDBG("One Voice bulk upload"); + m_pSynthesizer->loadVoiceParameters(pMessage,nTG); + break; + case 200: + LOGDBG("Bank bulk upload."); + //TODO: add code for storing a bank bulk upload + LOGNOTE("Currently code for storing a bulk bank upload is missing!"); + break; + default: + if(sysex_return >= 300 && sysex_return < 500) + { + LOGDBG("SysEx voice parameter change: Parameter %d value: %d",pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5]); + m_pSynthesizer->setVoiceDataElement(pMessage[4] + ((pMessage[3] & 0x03) * 128), pMessage[5],nTG); + switch(pMessage[4] + ((pMessage[3] & 0x03) * 128)) + { + case 134: + m_pSynthesizer->notesOff(0,nTG); + break; + } + } + else if(sysex_return >= 500 && sysex_return < 600) + { + LOGDBG("SysEx send voice %u request",sysex_return-500); + SendSystemExclusiveVoice(sysex_return-500, nCable, nTG); + } + break; + } +} - case MIDI_PITCH_BEND: { - if (nLength < 3) - { - break; - } +void CMIDIDevice::SendSystemExclusiveVoice(uint8_t nVoice, const unsigned nCable, uint8_t nTG) +{ + uint8_t voicedump[163]; - s16 nValue = pMessage[1]; - nValue |= (s16) pMessage[2] << 7; - nValue -= 0x2000; + // Get voice sysex dump from TG + m_pSynthesizer->getSysExVoiceDump(voicedump, nTG); - m_pSynthesizer->setPitchbend (nValue, nTG); - } break; + TDeviceMap::const_iterator Iterator; - default: - break; - } - } - } -} + // send voice dump to all MIDI interfaces + for(Iterator = s_DeviceMap.begin(); Iterator != s_DeviceMap.end(); ++Iterator) + { + Iterator->second->Send (voicedump, sizeof(voicedump)*sizeof(uint8_t)); + LOGDBG("Send SYSEX voice dump %u to \"%s\"",nVoice,Iterator->first.c_str()); + } +} diff --git a/src/minidexed.cpp b/src/minidexed.cpp index 2211bc4..59e5abe 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,9 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CMultiCoreSupport (CMemorySystem::Get ()), #endif m_pConfig (pConfig), - m_UI (this, pGPIOManager, pConfig), + m_bSavePerformanceNewFile (false), + m_bSetNewPerformance (false), + m_UI (this, pGPIOManager, pI2CMaster, pConfig), m_PerformanceConfig (pFileSystem), m_PCKeyboard (this, pConfig), m_SerialMIDI (this, pInterrupt, pConfig), @@ -46,9 +49,9 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, #ifdef ARM_ALLOW_MULTI_CORE m_nActiveTGsLog2 (0), #endif - m_GetChunkTimer ("GetChunk", - 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), - m_bProfileEnabled (m_pConfig->GetProfileEnabled ()) + m_GetChunkTimer ("GetChunk", 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), + m_bProfileEnabled (m_pConfig->GetProfileEnabled ()), + m_bSavePerformance (false) { assert (m_pConfig); @@ -62,12 +65,18 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, m_nCutoff[i] = 99; m_nResonance[i] = 0; m_nMIDIChannel[i] = CMIDIDevice::Disabled; + m_nPitchBendRange[i] = 2; + m_nPitchBendStep[i] = 0; + m_nPortamentoMode[i] = 0; + m_nPortamentoGlissando[i] = 0; + m_nPortamentoTime[i] = 0; m_nNoteLimitLow[i] = 0; m_nNoteLimitHigh[i] = 127; m_nNoteShift[i] = 0; m_nReverbSend[i] = 0; + m_uchOPMask[i] = 0b111111; // All operators on m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ()); assert (m_pTG[i]); @@ -117,6 +126,8 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, } #endif + setMasterVolume(1.0); + // BEGIN setup tg_mixer tg_mixer = new AudioStereoMixer(pConfig->GetChunkSize()/2); // END setup tgmixer @@ -165,7 +176,7 @@ bool CMiniDexed::Initialize (void) m_pTG[i]->setTranspose (24); - m_pTG[i]->setPBController (12, 1); + m_pTG[i]->setPBController (2, 0); m_pTG[i]->setMWController (99, 7, 0); tg_mixer->pan(i,mapfloat(m_nPan[i],0,127,0.0f,1.0f)); @@ -176,39 +187,19 @@ bool CMiniDexed::Initialize (void) if (m_PerformanceConfig.Load ()) { - for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) - { - BankSelect (m_PerformanceConfig.GetBankNumber (nTG), nTG); - ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG); - SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG); - SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG); - SetPan (m_PerformanceConfig.GetPan (nTG), nTG); - SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG); - SetCutoff (m_PerformanceConfig.GetCutoff (nTG), nTG); - SetResonance (m_PerformanceConfig.GetResonance (nTG), nTG); - - m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG); - m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG); - m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG); - - SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), 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 (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ()); + LoadPerformanceParameters(); } else { SetMIDIChannel (CMIDIDevice::OmniMode, 0); } - + + // load performances file list, and attempt to create the performance folder + if (!m_PerformanceConfig.ListPerformances()) + { + LOGERR ("Cannot create internal Performance folder, new performances can't be created"); + } + // setup and start the sound device if (!m_pSoundDevice->AllocateQueueFrames (m_pConfig->GetChunkSize ())) { @@ -259,6 +250,25 @@ void CMiniDexed::Process (bool bPlugAndPlayUpdated) m_UI.Process (); + if (m_bSavePerformance) + { + DoSavePerformance (); + + m_bSavePerformance = false; + } + + if (m_bSavePerformanceNewFile) + { + DoSavePerformanceNewFile (); + m_bSavePerformanceNewFile = false; + } + + if (m_bSetNewPerformance) + { + DoSetNewPerformance (); + m_bSetNewPerformance = false; + } + if (m_bProfileEnabled) { m_GetChunkTimer.Dump (); @@ -352,6 +362,7 @@ void CMiniDexed::ProgramChange (unsigned nProgram, unsigned nTG) assert (m_pTG[nTG]); m_pTG[nTG]->loadVoiceParameters (Buffer); + m_SerialMIDI.SendSystemExclusiveVoice(nProgram,0,nTG); m_UI.ParameterChanged (); } @@ -433,6 +444,8 @@ void CMiniDexed::SetResonance (int nResonance, unsigned nTG) m_UI.ParameterChanged (); } + + void CMiniDexed::SetMIDIChannel (uint8_t uchChannel, unsigned nTG) { assert (nTG < CConfig::ToneGenerators); @@ -521,6 +534,24 @@ void CMiniDexed::setSustain(bool sustain, unsigned nTG) m_pTG[nTG]->setSustain (sustain); } +void CMiniDexed::panic(uint8_t value, unsigned nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + if (value == 0) { + m_pTG[nTG]->panic (); + } +} + +void CMiniDexed::notesOff(uint8_t value, unsigned nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + if (value == 0) { + m_pTG[nTG]->notesOff (); + } +} + void CMiniDexed::setModWheel (uint8_t value, unsigned nTG) { assert (nTG < CConfig::ToneGenerators); @@ -633,6 +664,11 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT case TGParameterMasterTune: SetMasterTune (nValue, nTG); break; case TGParameterCutoff: SetCutoff (nValue, nTG); break; case TGParameterResonance: SetResonance (nValue, nTG); break; + case TGParameterPitchBendRange: setPitchbendRange (nValue, nTG); break; + case TGParameterPitchBendStep: setPitchbendStep (nValue, nTG); break; + case TGParameterPortamentoMode: setPortamentoMode (nValue, nTG); break; + case TGParameterPortamentoGlissando: setPortamentoGlissando (nValue, nTG); break; + case TGParameterPortamentoTime: setPortamentoTime (nValue, nTG); break; case TGParameterMIDIChannel: assert (0 <= nValue && nValue <= 255); @@ -662,6 +698,11 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG) case TGParameterResonance: return m_nResonance[nTG]; case TGParameterMIDIChannel: return m_nMIDIChannel[nTG]; case TGParameterReverbSend: return m_nReverbSend[nTG]; + case TGParameterPitchBendRange: return m_nPitchBendRange[nTG]; + case TGParameterPitchBendStep: return m_nPitchBendStep[nTG]; + case TGParameterPortamentoMode: return m_nPortamentoMode[nTG]; + case TGParameterPortamentoGlissando: return m_nPortamentoGlissando[nTG]; + case TGParameterPortamentoTime: return m_nPortamentoTime[nTG]; default: assert (0); @@ -677,6 +718,22 @@ void CMiniDexed::SetVoiceParameter (uint8_t uchOffset, uint8_t uchValue, unsigne if (nOP < 6) { + if (uchOffset == DEXED_OP_ENABLE) + { + if (uchValue) + { + m_uchOPMask[nTG] |= 1 << nOP; + } + else + { + m_uchOPMask[nTG] &= ~(1 << nOP); + } + + m_pTG[nTG]->setOPAll (m_uchOPMask[nTG]); + + return; + } + nOP = 5 - nOP; // OPs are in reverse order } @@ -694,6 +751,11 @@ uint8_t CMiniDexed::GetVoiceParameter (uint8_t uchOffset, unsigned nOP, unsigned if (nOP < 6) { + if (uchOffset == DEXED_OP_ENABLE) + { + return !!(m_uchOPMask[nTG] & (1 << nOP)); + } + nOP = 5 - nOP; // OPs are in reverse order } @@ -805,56 +867,70 @@ void CMiniDexed::ProcessSound (void) } // BEGIN TG mixing - for (uint8_t i = 0; i < CConfig::ToneGenerators; i++) - { - tg_mixer->doAddMix(i,m_OutputLevel[i]); - reverb_send_mixer->doAddMix(i,m_OutputLevel[i]); - } - // END TG mixing - - // BEGIN create SampleBuffer for holding audio data - float32_t SampleBuffer[2][nFrames]; - // END create SampleBuffer for holding audio data - - // get the mix of all TGs - tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]); + float32_t tmp_float[nFrames*2]; + int16_t tmp_int[nFrames*2]; - // BEGIN adding reverb - if (m_nParameter[ParameterReverbEnable]) + if(nMasterVolume > 0.0) { - float32_t ReverbBuffer[2][nFrames]; - float32_t ReverbSendBuffer[2][nFrames]; - - arm_fill_f32(0.0f, ReverbBuffer[indexL], nFrames); - arm_fill_f32(0.0f, ReverbBuffer[indexR], nFrames); - arm_fill_f32(0.0f, ReverbSendBuffer[indexR], nFrames); - arm_fill_f32(0.0f, ReverbSendBuffer[indexL], nFrames); - - m_ReverbSpinLock.Acquire (); - - reverb_send_mixer->getMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]); - reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames); + for (uint8_t i = 0; i < CConfig::ToneGenerators; i++) + { + tg_mixer->doAddMix(i,m_OutputLevel[i]); + reverb_send_mixer->doAddMix(i,m_OutputLevel[i]); + } + // END TG mixing + + // BEGIN create SampleBuffer for holding audio data + float32_t SampleBuffer[2][nFrames]; + // END create SampleBuffer for holding audio data - // scale down and add left reverb buffer by reverb level - arm_scale_f32(ReverbBuffer[indexL], reverb->get_level(), ReverbBuffer[indexL], nFrames); - arm_add_f32(SampleBuffer[indexL], ReverbBuffer[indexL], SampleBuffer[indexL], nFrames); - // scale down and add right reverb buffer by reverb level - arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames); - arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames); + // get the mix of all TGs + tg_mixer->getMix(SampleBuffer[indexL], SampleBuffer[indexR]); - m_ReverbSpinLock.Release (); - } - // END adding reverb + // BEGIN adding reverb + if (m_nParameter[ParameterReverbEnable]) + { + float32_t ReverbBuffer[2][nFrames]; + float32_t ReverbSendBuffer[2][nFrames]; - // Convert dual float array (left, right) to single int16 array (left/right) - float32_t tmp_float[nFrames*2]; - int16_t tmp_int[nFrames*2]; - for(uint16_t i=0; igetMix(ReverbSendBuffer[indexL], ReverbSendBuffer[indexR]); + reverb->doReverb(ReverbSendBuffer[indexL],ReverbSendBuffer[indexR],ReverbBuffer[indexL], ReverbBuffer[indexR],nFrames); + + // scale down and add left reverb buffer by reverb level + arm_scale_f32(ReverbBuffer[indexL], reverb->get_level(), ReverbBuffer[indexL], nFrames); + arm_add_f32(SampleBuffer[indexL], ReverbBuffer[indexL], SampleBuffer[indexL], nFrames); + // scale down and add right reverb buffer by reverb level + arm_scale_f32(ReverbBuffer[indexR], reverb->get_level(), ReverbBuffer[indexR], nFrames); + arm_add_f32(SampleBuffer[indexR], ReverbBuffer[indexR], SampleBuffer[indexR], nFrames); + + m_ReverbSpinLock.Release (); + } + // END adding reverb + + // Convert dual float array (left, right) to single int16 array (left/right) + for(uint16_t i=0; i0.0 && nMasterVolume <1.0) + { + tmp_float[i*2]=SampleBuffer[indexL][i] * nMasterVolume; + tmp_float[(i*2)+1]=SampleBuffer[indexR][i] * nMasterVolume; + } + else if(nMasterVolume == 1.0) + { + tmp_float[i*2]=SampleBuffer[indexL][i]; + tmp_float[(i*2)+1]=SampleBuffer[indexR][i]; + } + } + arm_float_to_q15(tmp_float,tmp_int,nFrames*2); } - arm_float_to_q15(tmp_float,tmp_int,nFrames*2); + else + arm_fill_q15(0, tmp_int, nFrames * 2); if (m_pSoundDevice->Write (tmp_int, sizeof(tmp_int)) != (int) sizeof(tmp_int)) { @@ -871,6 +947,13 @@ void CMiniDexed::ProcessSound (void) #endif bool CMiniDexed::SavePerformance (void) +{ + m_bSavePerformance = true; + + return true; +} + +bool CMiniDexed::DoSavePerformance (void) { for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) { @@ -882,11 +965,18 @@ bool CMiniDexed::SavePerformance (void) m_PerformanceConfig.SetDetune (m_nMasterTune[nTG], nTG); m_PerformanceConfig.SetCutoff (m_nCutoff[nTG], nTG); m_PerformanceConfig.SetResonance (m_nResonance[nTG], nTG); + m_PerformanceConfig.SetPitchBendRange (m_nPitchBendRange[nTG], nTG); + m_PerformanceConfig.SetPitchBendStep (m_nPitchBendStep[nTG], nTG); + m_PerformanceConfig.SetPortamentoMode (m_nPortamentoMode[nTG], nTG); + m_PerformanceConfig.SetPortamentoGlissando (m_nPortamentoGlissando[nTG], nTG); + m_PerformanceConfig.SetPortamentoTime (m_nPortamentoTime[nTG], nTG); m_PerformanceConfig.SetNoteLimitLow (m_nNoteLimitLow[nTG], nTG); m_PerformanceConfig.SetNoteLimitHigh (m_nNoteLimitHigh[nTG], nTG); m_PerformanceConfig.SetNoteShift (m_nNoteShift[nTG], nTG); - + m_pTG[nTG]->getVoiceData(m_nRawVoiceData); + m_PerformanceConfig.SetVoiceDataToTxt (m_nRawVoiceData, nTG); + m_PerformanceConfig.SetReverbSend (m_nReverbSend[nTG], nTG); } @@ -901,3 +991,364 @@ bool CMiniDexed::SavePerformance (void) return m_PerformanceConfig.Save (); } + +void CMiniDexed::setMonoMode(uint8_t mono, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setMonoMode(constrain(mono, 0, 1)); + m_pTG[nTG]->doRefreshVoice(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setPitchbendRange(uint8_t range, uint8_t nTG) +{ + range = constrain (range, 0, 12); + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + m_nPitchBendRange[nTG] = range; + + m_pTG[nTG]->setPitchbendRange(range); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setPitchbendStep(uint8_t step, uint8_t nTG) +{ + step= constrain (step, 0, 12); + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + m_nPitchBendStep[nTG] = step; + + m_pTG[nTG]->setPitchbendStep(step); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setPortamentoMode(uint8_t mode, uint8_t nTG) +{ + mode= constrain (mode, 0, 1); + + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + m_nPortamentoMode[nTG] = mode; + + m_pTG[nTG]->setPortamentoMode(mode); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setPortamentoGlissando(uint8_t glissando, uint8_t nTG) +{ + glissando = constrain (glissando, 0, 1); + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + m_nPortamentoGlissando[nTG] = glissando; + + m_pTG[nTG]->setPortamentoGlissando(glissando); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setPortamentoTime(uint8_t time, uint8_t nTG) +{ + time = constrain (time, 0, 99); + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + m_nPortamentoTime[nTG] = time; + + m_pTG[nTG]->setPortamentoTime(time); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setModWheelRange(uint8_t range, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setModWheelRange(constrain(range, 0, 99)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setModWheelTarget(uint8_t target, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setModWheelTarget(constrain(target, 0, 7)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setFootControllerRange(uint8_t range, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setFootControllerRange(constrain(range, 0, 99)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setFootControllerTarget(uint8_t target, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setFootControllerTarget(constrain(target, 0, 7)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setBreathControllerRange(uint8_t range, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setBreathControllerRange(constrain(range, 0, 99)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setBreathControllerTarget(uint8_t target, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setBreathControllerTarget(constrain(target, 0, 7)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setAftertouchRange(uint8_t range, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setAftertouchRange(constrain(range, 0, 99)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setAftertouchTarget(uint8_t target, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setAftertouchTarget(constrain(target, 0, 7)); + m_pTG[nTG]->ControllersRefresh(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::loadVoiceParameters(const uint8_t* data, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + uint8_t voice[161]; + + memcpy(voice, data, sizeof(uint8_t)*161); + + // fix voice name + for (uint8_t i = 0; i < 10; i++) + { + if (voice[151 + i] > 126) // filter characters + voice[151 + i] = 32; + } + + m_pTG[nTG]->loadVoiceParameters(&voice[6]); + m_pTG[nTG]->doRefreshVoice(); + m_UI.ParameterChanged (); +} + +void CMiniDexed::setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->setVoiceDataElement(constrain(data, 0, 155),constrain(number, 0, 99)); + //m_pTG[nTG]->doRefreshVoice(); + m_UI.ParameterChanged (); +} + +int16_t CMiniDexed::checkSystemExclusive(const uint8_t* pMessage,const uint16_t nLength, uint8_t nTG) +{ + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + return(m_pTG[nTG]->checkSystemExclusive(pMessage, nLength)); +} + +void CMiniDexed::getSysExVoiceDump(uint8_t* dest, uint8_t nTG) +{ + uint8_t checksum = 0; + uint8_t data[155]; + + assert (nTG < CConfig::ToneGenerators); + assert (m_pTG[nTG]); + + m_pTG[nTG]->getVoiceData(data); + + dest[0] = 0xF0; // SysEx start + dest[1] = 0x43; // ID=Yamaha + dest[2] = GetTGParameter(TGParameterMIDIChannel, nTG); // Sub-status and MIDI channel + dest[3] = 0x00; // Format number (0=1 voice) + dest[4] = 0x01; // Byte count MSB + dest[5] = 0x1B; // Byte count LSB + for (uint8_t n = 0; n < 155; n++) + { + checksum -= data[n]; + dest[6 + n] = data[n]; + } + dest[161] = checksum & 0x7f; // Checksum + dest[162] = 0xF7; // SysEx end +} + +void CMiniDexed::setMasterVolume (float32_t vol) +{ + if(vol < 0.0) + vol = 0.0; + else if(vol > 1.0) + vol = 1.0; + + nMasterVolume=vol; +} + +std::string CMiniDexed::GetPerformanceFileName(unsigned nID) +{ + return m_PerformanceConfig.GetPerformanceFileName(nID); +} + +std::string CMiniDexed::GetPerformanceName(unsigned nID) +{ + return m_PerformanceConfig.GetPerformanceName(nID); +} + +unsigned CMiniDexed::GetLastPerformance() +{ + return m_PerformanceConfig.GetLastPerformance(); +} + + + +unsigned CMiniDexed::GetActualPerformanceID() +{ + return m_PerformanceConfig.GetActualPerformanceID(); +} + +void CMiniDexed::SetActualPerformanceID(unsigned nID) +{ + m_PerformanceConfig.SetActualPerformanceID(nID); +} + +unsigned CMiniDexed::GetMenuSelectedPerformanceID() +{ + return m_PerformanceConfig.GetMenuSelectedPerformanceID(); +} + +void CMiniDexed::SetMenuSelectedPerformanceID(unsigned nID) +{ + m_PerformanceConfig.SetMenuSelectedPerformanceID(nID); +} + + +bool CMiniDexed::SetNewPerformance(unsigned nID) +{ + m_bSetNewPerformance = true; + m_nSetNewPerformanceID = nID; + + return true; +} + +bool CMiniDexed::DoSetNewPerformance (void) +{ + unsigned nID = m_nSetNewPerformanceID; + m_PerformanceConfig.SetNewPerformance(nID); + + if (m_PerformanceConfig.Load ()) + { + LoadPerformanceParameters(); + return true; + } + else + { + SetMIDIChannel (CMIDIDevice::OmniMode, 0); + return false; + } +} + +bool CMiniDexed::SavePerformanceNewFile () +{ + m_bSavePerformanceNewFile = m_PerformanceConfig.GetInternalFolderOk(); + return m_bSavePerformanceNewFile; +} + +bool CMiniDexed::DoSavePerformanceNewFile (void) +{ + std::string nPerformanceName=""; // for future enhacements: capability to write performance name + if (m_PerformanceConfig.CreateNewPerformanceFile(nPerformanceName)) + { + if(SavePerformance()) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + +} + + +void CMiniDexed::LoadPerformanceParameters(void) +{ + for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++) + { + + BankSelect (m_PerformanceConfig.GetBankNumber (nTG), nTG); + ProgramChange (m_PerformanceConfig.GetVoiceNumber (nTG), nTG); + SetMIDIChannel (m_PerformanceConfig.GetMIDIChannel (nTG), nTG); + SetVolume (m_PerformanceConfig.GetVolume (nTG), nTG); + SetPan (m_PerformanceConfig.GetPan (nTG), nTG); + SetMasterTune (m_PerformanceConfig.GetDetune (nTG), nTG); + SetCutoff (m_PerformanceConfig.GetCutoff (nTG), nTG); + SetResonance (m_PerformanceConfig.GetResonance (nTG), nTG); + setPitchbendRange (m_PerformanceConfig.GetPitchBendRange (nTG), nTG); + setPitchbendStep (m_PerformanceConfig.GetPitchBendStep (nTG), nTG); + setPortamentoMode (m_PerformanceConfig.GetPortamentoMode (nTG), nTG); + setPortamentoGlissando (m_PerformanceConfig.GetPortamentoGlissando (nTG), nTG); + setPortamentoTime (m_PerformanceConfig.GetPortamentoTime (nTG), nTG); + + m_nNoteLimitLow[nTG] = m_PerformanceConfig.GetNoteLimitLow (nTG); + m_nNoteLimitHigh[nTG] = m_PerformanceConfig.GetNoteLimitHigh (nTG); + m_nNoteShift[nTG] = m_PerformanceConfig.GetNoteShift (nTG); + + if(m_PerformanceConfig.VoiceDataFilled(nTG)) + { + uint8_t* tVoiceData = m_PerformanceConfig.GetVoiceDataFromTxt(nTG); + m_pTG[nTG]->loadVoiceParameters(tVoiceData); + } + + SetReverbSend (m_PerformanceConfig.GetReverbSend (nTG), 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 (ParameterReverbLevel, m_PerformanceConfig.GetReverbLevel ()); +} + diff --git a/src/minidexed.h b/src/minidexed.h index 96740ad..0b9dc98 100644 --- a/src/minidexed.h +++ b/src/minidexed.h @@ -76,12 +76,47 @@ public: void keydown (int16_t pitch, uint8_t velocity, unsigned nTG); void setSustain (bool sustain, unsigned nTG); + void panic (uint8_t value, unsigned nTG); + void notesOff (uint8_t value, unsigned nTG); void setModWheel (uint8_t value, unsigned nTG); void setPitchbend (int16_t value, unsigned nTG); void ControllersRefresh (unsigned nTG); void SetReverbSend (unsigned nReverbSend, unsigned nTG); // 0 .. 127 + void setMonoMode(uint8_t mono, uint8_t nTG); + void setPitchbendRange(uint8_t range, uint8_t nTG); + void setPitchbendStep(uint8_t step, uint8_t nTG); + void setPortamentoMode(uint8_t mode, uint8_t nTG); + void setPortamentoGlissando(uint8_t glissando, uint8_t nTG); + void setPortamentoTime(uint8_t time, uint8_t nTG); + void setModWheelRange(uint8_t range, uint8_t nTG); + void setModWheelTarget(uint8_t target, uint8_t nTG); + void setFootControllerRange(uint8_t range, uint8_t nTG); + void setFootControllerTarget(uint8_t target, uint8_t nTG); + void setBreathControllerRange(uint8_t range, uint8_t nTG); + void setBreathControllerTarget(uint8_t target, uint8_t nTG); + void setAftertouchRange(uint8_t range, uint8_t nTG); + void setAftertouchTarget(uint8_t target, uint8_t nTG); + void loadVoiceParameters(const uint8_t* data, uint8_t nTG); + void setVoiceDataElement(uint8_t data, uint8_t number, uint8_t nTG); + void getSysExVoiceDump(uint8_t* dest, uint8_t nTG); + + int16_t checkSystemExclusive(const uint8_t* pMessage, const uint16_t nLength, uint8_t nTG); + + std::string GetPerformanceFileName(unsigned nID); + std::string GetPerformanceName(unsigned nID); + unsigned GetLastPerformance(); + unsigned GetActualPerformanceID(); + void SetActualPerformanceID(unsigned nID); + bool SetNewPerformance(unsigned nID); + bool SavePerformanceNewFile (); + unsigned GetMenuSelectedPerformanceID(); + void SetMenuSelectedPerformanceID(unsigned nID); + + bool DoSavePerformanceNewFile (void); + bool DoSetNewPerformance (void); + enum TParameter { ParameterCompressorEnable, @@ -109,6 +144,11 @@ public: TGParameterResonance, TGParameterMIDIChannel, TGParameterReverbSend, + TGParameterPitchBendRange, + TGParameterPitchBendStep, + TGParameterPortamentoMode, + TGParameterPortamentoGlissando, + TGParameterPortamentoTime, TGParameterUnknown }; @@ -123,10 +163,14 @@ public: std::string GetVoiceName (unsigned nTG); bool SavePerformance (void); + bool DoSavePerformance (void); + + void setMasterVolume (float32_t vol); private: int16_t ApplyNoteLimits (int16_t pitch, unsigned nTG); // returns < 0 to ignore note - + uint8_t m_uchOPMask[CConfig::ToneGenerators]; + void LoadPerformanceParameters(void); void ProcessSound (void); #ifdef ARM_ALLOW_MULTI_CORE @@ -155,12 +199,28 @@ private: int m_nCutoff[CConfig::ToneGenerators]; int m_nResonance[CConfig::ToneGenerators]; unsigned m_nMIDIChannel[CConfig::ToneGenerators]; + unsigned m_nPitchBendRange[CConfig::ToneGenerators]; + unsigned m_nPitchBendStep[CConfig::ToneGenerators]; + unsigned m_nPortamentoMode[CConfig::ToneGenerators]; + unsigned m_nPortamentoGlissando[CConfig::ToneGenerators]; + unsigned m_nPortamentoTime[CConfig::ToneGenerators]; unsigned m_nNoteLimitLow[CConfig::ToneGenerators]; unsigned m_nNoteLimitHigh[CConfig::ToneGenerators]; int m_nNoteShift[CConfig::ToneGenerators]; unsigned m_nReverbSend[CConfig::ToneGenerators]; + + uint8_t m_nRawVoiceData[156]; + + + bool m_bSavePerformanceNewFile; + bool m_bSetNewPerformance; + unsigned m_nSetNewPerformanceID; + + float32_t nMasterVolume; + + CUserInterface m_UI; CSysExFileLoader m_SysExFileLoader; @@ -190,6 +250,8 @@ private: AudioStereoMixer* reverb_send_mixer; CSpinLock m_ReverbSpinLock; + + bool m_bSavePerformance; }; #endif diff --git a/src/userinterface.cpp b/src/userinterface.cpp index ff1a97a..71bbb82 100644 --- a/src/userinterface.cpp +++ b/src/userinterface.cpp @@ -27,9 +27,10 @@ LOGMODULE ("ui"); -CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManager, CConfig *pConfig) +CUserInterface::CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, CConfig *pConfig) : m_pMiniDexed (pMiniDexed), m_pGPIOManager (pGPIOManager), + m_pI2CMaster (pI2CMaster), m_pConfig (pConfig), m_pLCD (0), m_pLCDBuffered (0), @@ -52,14 +53,23 @@ bool CUserInterface::Initialize (void) if (m_pConfig->GetLCDEnabled ()) { - m_pLCD = new CHD44780Device (CConfig::LCDColumns, CConfig::LCDRows, - m_pConfig->GetLCDPinData4 (), - m_pConfig->GetLCDPinData5 (), - m_pConfig->GetLCDPinData6 (), - m_pConfig->GetLCDPinData7 (), - m_pConfig->GetLCDPinEnable (), - m_pConfig->GetLCDPinRegisterSelect (), - m_pConfig->GetLCDPinReadWrite ()); + unsigned i2caddr = m_pConfig->GetLCDI2CAddress (); + if (i2caddr == 0) + { + m_pLCD = new CHD44780Device (CConfig::LCDColumns, CConfig::LCDRows, + m_pConfig->GetLCDPinData4 (), + m_pConfig->GetLCDPinData5 (), + m_pConfig->GetLCDPinData6 (), + m_pConfig->GetLCDPinData7 (), + m_pConfig->GetLCDPinEnable (), + m_pConfig->GetLCDPinRegisterSelect (), + m_pConfig->GetLCDPinReadWrite ()); + } + else + { + m_pLCD = new CHD44780Device (m_pI2CMaster, i2caddr, + CConfig::LCDColumns, CConfig::LCDRows); + } assert (m_pLCD); if (!m_pLCD->Initialize ()) @@ -215,7 +225,7 @@ void CUserInterface::EncoderEventHandler (CKY040::TEvent Event) break; case CKY040::EventSwitchHold: - if (m_pRotaryEncoder->GetHoldSeconds () >= 10) + if (m_pRotaryEncoder->GetHoldSeconds () >= 120) { delete m_pLCD; // reset LCD diff --git a/src/userinterface.h b/src/userinterface.h index 404bbfd..f3c4163 100644 --- a/src/userinterface.h +++ b/src/userinterface.h @@ -26,13 +26,14 @@ #include #include #include +#include class CMiniDexed; class CUserInterface { public: - CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManager, CConfig *pConfig); + CUserInterface (CMiniDexed *pMiniDexed, CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster, CConfig *pConfig); ~CUserInterface (void); bool Initialize (void); @@ -53,12 +54,14 @@ public: private: void LCDWrite (const char *pString); // Print to optional HD44780 display + void EncoderEventHandler (CKY040::TEvent Event); static void EncoderEventStub (CKY040::TEvent Event, void *pParam); private: CMiniDexed *m_pMiniDexed; CGPIOManager *m_pGPIOManager; + CI2CMaster *m_pI2CMaster; CConfig *m_pConfig; CHD44780Device *m_pLCD;