From 8ce2db511b1d88d3a42cea796b5d0d591af4b73d Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Sat, 9 May 2026 20:37:17 +0200 Subject: [PATCH] added Midi NRPN --- src/plug/JaySynth.cpp | 51 ++++++++++----- src/plug/JaySynth.h | 149 +++++++++++++++++++++++------------------- src/plug/MidiNrpn.cpp | 98 +++++++++++++++++++++++++++ src/plug/MidiNrpn.h | 30 +++++++++ src/plug/config.mk | 1 + 5 files changed, 243 insertions(+), 86 deletions(-) create mode 100644 src/plug/MidiNrpn.cpp create mode 100644 src/plug/MidiNrpn.h diff --git a/src/plug/JaySynth.cpp b/src/plug/JaySynth.cpp index b41113d..709a4b5 100644 --- a/src/plug/JaySynth.cpp +++ b/src/plug/JaySynth.cpp @@ -896,22 +896,16 @@ void JaySynth::handlePitchWheel (const int midiChannel, const int wheelValue) } -void JaySynth::handleController (const int midiChannel, - const int controllerNumber, - const int controllerValue) +void JaySynth::handleController (const midiCC_info_t &midiCC_info) { - midiCC_info.channel = midiChannel; - midiCC_info.ID = controllerNumber; - midiCC_info.value = (synth_float_t)controllerValue/127; - - last_midiCC_info[controllerNumber] = midiCC_info; - listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_MIDICC, &midiCC_info); + last_midiCC_info[midiCC_info.ID] = midiCC_info; + listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_MIDICC, (midiCC_info_t*)&midiCC_info); - switch (controllerNumber) + switch (midiCC_info.ID) { - case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break; - case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break; - case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break; + case 0x40: handleSustainPedal (midiCC_info.channel, midiCC_info.value_raw >= 64); break; + case 0x42: handleSostenutoPedal (midiCC_info.channel, midiCC_info.value_raw >= 64); break; + case 0x43: handleSoftPedal (midiCC_info.channel, midiCC_info.value_raw >= 64); break; default: break; } @@ -921,8 +915,8 @@ void JaySynth::handleController (const int midiChannel, { JaySynthVoice* const voice = voices.getUnchecked (i); - if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) - voice->controllerMoved (controllerNumber, controllerValue); + if (midiCC_info.channel <= 0 || voice->isPlayingChannel (midiCC_info.channel)) + voice->controllerMoved (midiCC_info.ID, midiCC_info.value_raw); } } @@ -1096,9 +1090,30 @@ void JaySynth::handleMidiEvent (const MidiMessage& m) } if (m.isController()) { - handleController (m.getChannel(), - m.getControllerNumber(), - m.getControllerValue()); + midiCC_info_t midiCC_info; + midiCC_info.channel = m.getChannel(); + midiCC_info.ID = m.getControllerNumber(); + midiCC_info.type = MIDI_CONTROLLER_TYPE_CC; + midiCC_info.value_raw = m.getControllerValue(); + midiCC_info.value = (synth_float_t)midiCC_info.value_raw/127; + + SynthDebug("CC: controllerNumber=%d, controllerValue=%d", midiCC_info.ID, midiCC_info.value_raw); + + bool isNrpnProcessing = mMidiNrpn.process(midiCC_info.ID, midiCC_info.value_raw); + if (mMidiNrpn.isValue()) + { + midiCC_info.ID = mMidiNrpn.getCommand(); + midiCC_info.type = MIDI_CONTROLLER_TYPE_NRPN; + midiCC_info.value_raw = mMidiNrpn.consumeValue(); + midiCC_info.value = (synth_float_t)midiCC_info.value_raw/1000; + SynthDebug("NRPN: controllerNumber=%d, controllerValue=%d", midiCC_info.ID, midiCC_info.value_raw); + } + + if (!isNrpnProcessing) + { + handleController (midiCC_info); + } + } if (m.isMidiMachineControlMessage()) { diff --git a/src/plug/JaySynth.h b/src/plug/JaySynth.h index 82acca5..8f93ee2 100644 --- a/src/plug/JaySynth.h +++ b/src/plug/JaySynth.h @@ -16,6 +16,7 @@ #include "JaySynthVoice.h" #include "JaySynthMonophonicMGR.h" #include "JaySynthMidiCC.h" +#include "MidiNrpn.h" #define GET_NUM_VOICES max_num_voices @@ -46,7 +47,83 @@ private: class JaySynth : public Synthesiser { public: - JaySynth(int num_voices, String pathToWaves); + enum + { + SYNTH_PER_VOICE_CONTROL_VELKEY = 0, + SYNTH_PER_VOICE_CONTROL_SIZE + }; + + enum + { + SYNTH_CHANGED_PARAM = 0, + SYNTH_CHANGED_MIDICC, + SYNTH_CHANGED_NUM_VOICES_PLAYING, + SYNTH_CHANGED_NOTE_PRESSED, + SYNTH_CHANGED_NOTE_RELEASED, + SYNTH_CHANGED_MIDICLOCK, + SYNTH_CHANGED_SIZE + }; + + enum + { + MIDI_CONTROLLER_TYPE_CC = 0, + MIDI_CONTROLLER_TYPE_NRPN, + MIDI_CONTROLLER_TYPE_RPN, + MIDI_CONTROLLER_TYPE_SIZE + }; + + typedef struct _smidiCC_info_t + { + int type; + int channel; + int ID; + int value_raw; + synth_float_t value; + } midiCC_info_t; + + typedef struct _smidi_note_info_t + { + int note; + synth_float_t velocity; + + } midi_note_info_t; + + typedef struct _smidi_quarter_clock_info_t + { + uint32_t type; + uint32_t bpm; + } midi_quarter_clock_info_t; + + midiCC_info_t* getLastMidiCC_infos(void) + { + return last_midiCC_info; + } + + midiCC_info_t* getLastMidiCC_info(int controllerID) + { + if (controllerID < NUM_MIDI_CONTROLLERS) + return &last_midiCC_info[controllerID]; + + return NULL; + } + + midi_note_info_t* getLastMidiNote_infos() + { + return &last_midi_note_info; + } + + void setMidiCC_editBuffer(JaySynthMidiCC *pMidiCC) + { + pMidCC_editBuffer = pMidiCC; + } + + typedef struct _sper_voice_control_t + { + synth_float_t ctrl[SYNTH_PER_VOICE_CONTROL_SIZE][SYNTH_NUM_PARAMS]; + + } per_voice_control_t; + + JaySynth(int num_voices, String pathToWaves); ~JaySynth(); void setSampleRate (synth_float_t sampleRate); void setBufsize (int bufsize); @@ -59,7 +136,8 @@ public: synth_float_t getVolume(void); int lastPitchWheelValue, lastmidiChannel; void handlePitchWheel (int midiChannel, int wheelValue); - void handleController (int midiChannel, int controllerNumber, int controllerValue); + void handleController (int midiChannel, int controllerNumber, int controllerValue) {} + void handleController (const midiCC_info_t &midiCC_info); void updateParameters(void); void updateParameter(int paramID); void humanizeModeChanged(void); @@ -147,72 +225,6 @@ public: listeners.add (listener); } - enum - { - SYNTH_PER_VOICE_CONTROL_VELKEY = 0, - SYNTH_PER_VOICE_CONTROL_SIZE - }; - - enum - { - SYNTH_CHANGED_PARAM = 0, - SYNTH_CHANGED_MIDICC, - SYNTH_CHANGED_NUM_VOICES_PLAYING, - SYNTH_CHANGED_NOTE_PRESSED, - SYNTH_CHANGED_NOTE_RELEASED, - SYNTH_CHANGED_MIDICLOCK, - SYNTH_CHANGED_SIZE - }; - - typedef struct _smidiCC_info_t - { - int channel; - int ID; - synth_float_t value; - } midiCC_info_t; - - typedef struct _smidi_note_info_t - { - int note; - synth_float_t velocity; - - } midi_note_info_t; - - typedef struct _smidi_quarter_clock_info_t - { - uint32_t type; - uint32_t bpm; - } midi_quarter_clock_info_t; - - midiCC_info_t* getLastMidiCC_infos(void) - { - return last_midiCC_info; - } - - midiCC_info_t* getLastMidiCC_info(int controllerID) - { - if (controllerID < NUM_MIDI_CONTROLLERS) - return &last_midiCC_info[controllerID]; - - return NULL; - } - - midi_note_info_t* getLastMidiNote_infos() - { - return &last_midi_note_info; - } - - void setMidiCC_editBuffer(JaySynthMidiCC *pMidiCC) - { - pMidCC_editBuffer = pMidiCC; - } - - typedef struct _sper_voice_control_t - { - synth_float_t ctrl[SYNTH_PER_VOICE_CONTROL_SIZE][SYNTH_NUM_PARAMS]; - - } per_voice_control_t; - //============================================================================== private: @@ -263,6 +275,7 @@ private: bool m_isMidiClockStarted; void updateMidiTiming(const MidiMessage& m); + MidiNrpn mMidiNrpn; }; diff --git a/src/plug/MidiNrpn.cpp b/src/plug/MidiNrpn.cpp new file mode 100644 index 0000000..818ac8b --- /dev/null +++ b/src/plug/MidiNrpn.cpp @@ -0,0 +1,98 @@ +#include "MidiNrpn.h" +#include "synth/synth_types.h" + +MidiNrpn::MidiNrpn() +: mState(0) +, mCommand(0) +, mValue(0) +{ + +} + +MidiNrpn::~MidiNrpn() +{ + +} + +void MidiNrpn::reset() +{ + mState = 0; +} + +bool MidiNrpn::isCommand() +{ + return mState >= 2; +} + +int MidiNrpn::getCommand() +{ + if (!isCommand()) + { + return -1; + } + return mCommand; +} + +bool MidiNrpn::isValue() +{ + return mState == 4; +} + +int MidiNrpn::consumeValue() +{ + if (!isValue()) + { + return -1; + } + mState = 2; + return mValue; +} + +bool MidiNrpn::process(uint8_t controllerNumber, uint8_t controllerValue) +{ + bool isNrpnProcessing = false; + + int state_nxt = mState; + + switch (mState) + { + case 1: + if (controllerNumber == 98) + { + state_nxt = 2; + mCommand |= (uint16_t)controllerValue & 0x00FF; + isNrpnProcessing = true; + } + break; + + case 2: + if (controllerNumber == 6) + { + state_nxt = 3; + mValue = (uint16_t)controllerValue << 8 & 0xFF00; + isNrpnProcessing = true; + } + break; + + case 3: + if (controllerNumber == 38) + { + state_nxt = 4; + mValue |= (uint16_t)controllerValue & 0x00FF; + } + break; + + default: + break; + } + + if (controllerNumber == 99) + { + state_nxt = 1; + mCommand = (uint16_t)controllerValue << 8 & 0xFF00; + isNrpnProcessing = true; + } + + mState = state_nxt; + return isNrpnProcessing; +} diff --git a/src/plug/MidiNrpn.h b/src/plug/MidiNrpn.h new file mode 100644 index 0000000..1e2d91d --- /dev/null +++ b/src/plug/MidiNrpn.h @@ -0,0 +1,30 @@ +// MidiNrpn.h + +#ifndef _MIDI_NRPN_H_ +#define _MIDI_NRPN_H_ + +#include + +class MidiNrpn +{ + public: + MidiNrpn(); + ~MidiNrpn(); + + void reset(); + bool isCommand(); + int getCommand(); + + bool isValue(); + int consumeValue(); + + bool process(uint8_t controllerNumber, uint8_t controllerValue); + + private: + + int mState; + int mCommand; + int mValue; +}; + +#endif //_MIDI_NRPN_H_ diff --git a/src/plug/config.mk b/src/plug/config.mk index cf5d380..ab6a903 100644 --- a/src/plug/config.mk +++ b/src/plug/config.mk @@ -13,3 +13,4 @@ CXX_SRCS += PluginProcessor.cpp CXX_SRCS += VelKey_PopUp.cpp CXX_SRCS += JaySynth.cpp CXX_SRCS += JK_MidiClock.cpp +CXX_SRCS += MidiNrpn.cpp