Skip to content

Commit 5dc3fd3

Browse files
committed
implement NPRN protocl for controlling TGs with a MIDI controller
1 parent 753c205 commit 5dc3fd3

File tree

3 files changed

+191
-0
lines changed

3 files changed

+191
-0
lines changed

src/mididevice.cpp

+187
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ LOGMODULE ("mididevice");
5050
#define MIDI_CC_DETUNE_LEVEL 94
5151
#define MIDI_CC_ALL_SOUND_OFF 120
5252
#define MIDI_CC_ALL_NOTES_OFF 123
53+
#define MIDI_CC_NRPN_PARAM_MSB 99
54+
#define MIDI_CC_NRPN_PARAM_LSB 98
55+
#define MIDI_CC_NRPN_DATA_LSB 38
5356
#define MIDI_PROGRAM_CHANGE 0b1100
5457
#define MIDI_PITCH_BEND 0b1110
5558

@@ -58,6 +61,13 @@ LOGMODULE ("mididevice");
5861
#define MIDI_TIMING_CLOCK 0xF8
5962
#define MIDI_ACTIVE_SENSING 0xFE
6063

64+
#define MIDI_NRPN_PROGRAM_CHANGE 21
65+
#define MIDI_NRPN_SAVE_PERFORMANCE 22
66+
67+
uint8_t scale(uint8_t max, uint8_t value) {
68+
return (uint8_t)((((unsigned)value) * max) / 127);
69+
}
70+
6171
CMIDIDevice::TDeviceMap CMIDIDevice::s_DeviceMap;
6272

6373
CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInterface *pUI)
@@ -68,6 +78,8 @@ CMIDIDevice::CMIDIDevice (CMiniDexed *pSynthesizer, CConfig *pConfig, CUserInter
6878
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
6979
{
7080
m_ChannelMap[nTG] = Disabled;
81+
m_NRPNoffset[nTG] = 0;
82+
m_NRPNop[nTG] = 0;
7183
}
7284
}
7385

@@ -347,6 +359,181 @@ void CMIDIDevice::MIDIMessageHandler (const u8 *pMessage, size_t nLength, unsign
347359
m_pSynthesizer->notesOff (pMessage[2], nTG);
348360
}
349361
break;
362+
363+
case MIDI_CC_NRPN_PARAM_MSB:
364+
if(pMessage[2] <= 6) {
365+
m_NRPNop[nTG] = pMessage[2];
366+
}
367+
break;
368+
369+
case MIDI_CC_NRPN_PARAM_LSB:
370+
m_NRPNoffset[nTG] = pMessage[2];
371+
break;
372+
373+
case MIDI_CC_NRPN_DATA_LSB:
374+
switch(m_NRPNoffset[nTG])
375+
{
376+
case MIDI_NRPN_PROGRAM_CHANGE:
377+
m_pSynthesizer->ProgramChange(pMessage[2], nTG);
378+
break;
379+
case MIDI_NRPN_SAVE_PERFORMANCE:
380+
m_pSynthesizer->SavePerformance();
381+
break;
382+
default:
383+
if(m_NRPNop[nTG] < 6)
384+
{
385+
switch(m_NRPNoffset[nTG]) {
386+
case DEXED_OP_EG_R1:
387+
m_pSynthesizer->m_pTG[nTG]->setOPRate(m_NRPNop[nTG], 0, scale(99, pMessage[2]));
388+
break;
389+
case DEXED_OP_EG_R2:
390+
m_pSynthesizer->m_pTG[nTG]->setOPRate(m_NRPNop[nTG], 1, scale(99, pMessage[2]));
391+
break;
392+
case DEXED_OP_EG_R3:
393+
m_pSynthesizer->m_pTG[nTG]->setOPRate(m_NRPNop[nTG], 2, scale(99, pMessage[2]));
394+
break;
395+
case DEXED_OP_EG_R4:
396+
m_pSynthesizer->m_pTG[nTG]->setOPRate(m_NRPNop[nTG], 3, scale(99, pMessage[2]));
397+
break;
398+
case DEXED_OP_EG_L1:
399+
m_pSynthesizer->m_pTG[nTG]->setOPLevel(m_NRPNop[nTG], 0, scale(99, pMessage[2]));
400+
break;
401+
case DEXED_OP_EG_L2:
402+
m_pSynthesizer->m_pTG[nTG]->setOPLevel(m_NRPNop[nTG], 1, scale(99, pMessage[2]));
403+
break;
404+
case DEXED_OP_EG_L3:
405+
m_pSynthesizer->m_pTG[nTG]->setOPLevel(m_NRPNop[nTG], 2, scale(99, pMessage[2]));
406+
break;
407+
case DEXED_OP_EG_L4:
408+
m_pSynthesizer->m_pTG[nTG]->setOPLevel(m_NRPNop[nTG], 3, scale(99, pMessage[2]));
409+
break;
410+
case DEXED_OP_LEV_SCL_BRK_PT:
411+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardLevelScalingBreakPoint(m_NRPNop[nTG], scale(99, pMessage[2]));
412+
break;
413+
case DEXED_OP_SCL_LEFT_DEPTH:
414+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardLevelScalingDepthLeft(m_NRPNop[nTG], scale(99, pMessage[2]));
415+
break;
416+
case DEXED_OP_SCL_RGHT_DEPTH:
417+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardLevelScalingDepthRight(m_NRPNop[nTG], scale(99, pMessage[2]));
418+
break;
419+
case DEXED_OP_SCL_LEFT_CURVE:
420+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardLevelScalingCurveLeft(m_NRPNop[nTG], scale(3, pMessage[2]));
421+
break;
422+
case DEXED_OP_SCL_RGHT_CURVE:
423+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardLevelScalingCurveRight(m_NRPNop[nTG], scale(3, pMessage[2]));
424+
break;
425+
case DEXED_OP_OSC_RATE_SCALE:
426+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardRateScale(m_NRPNop[nTG], scale(7, pMessage[2]));
427+
break;
428+
case DEXED_OP_AMP_MOD_SENS:
429+
m_pSynthesizer->m_pTG[nTG]->setOPAmpModulationSensity(m_NRPNop[nTG], scale(3, pMessage[2]));
430+
break;
431+
case DEXED_OP_KEY_VEL_SENS:
432+
m_pSynthesizer->m_pTG[nTG]->setOPKeyboardVelocitySensity(m_NRPNop[nTG], scale(7, pMessage[2]));
433+
break;
434+
case DEXED_OP_OUTPUT_LEV:
435+
m_pSynthesizer->m_pTG[nTG]->setOPOutputLevel(m_NRPNop[nTG], scale(99, pMessage[2]));
436+
break;
437+
case DEXED_OP_OSC_MODE:
438+
m_pSynthesizer->m_pTG[nTG]->setOPMode(m_NRPNop[nTG], scale(1, pMessage[2]));
439+
break;
440+
case DEXED_OP_FREQ_COARSE:
441+
m_pSynthesizer->m_pTG[nTG]->setOPFrequencyCoarse(m_NRPNop[nTG], scale(31, pMessage[2]));
442+
break;
443+
case DEXED_OP_FREQ_FINE:
444+
m_pSynthesizer->m_pTG[nTG]->setOPFrequencyFine(m_NRPNop[nTG], scale(99, pMessage[2]));
445+
break;
446+
case DEXED_OP_OSC_DETUNE:
447+
m_pSynthesizer->m_pTG[nTG]->setOPDetune(m_NRPNop[nTG], scale(14, pMessage[2]));
448+
break;
449+
}
450+
// TODO: send partial update, but this will do for now
451+
SendSystemExclusiveVoice(0, 0, nTG);
452+
}
453+
if(m_NRPNop[nTG] == 6) {
454+
switch(m_NRPNoffset[nTG]) {
455+
case DEXED_PITCH_EG_R1:
456+
m_pSynthesizer->m_pTG[nTG]->setPitchRate(0, scale(99, pMessage[2]));
457+
break;
458+
case DEXED_PITCH_EG_R2:
459+
m_pSynthesizer->m_pTG[nTG]->setPitchRate(1, scale(99, pMessage[2]));
460+
461+
break;
462+
case DEXED_PITCH_EG_R3:
463+
m_pSynthesizer->m_pTG[nTG]->setPitchRate(2, scale(99, pMessage[2]));
464+
465+
break;
466+
case DEXED_PITCH_EG_R4:
467+
m_pSynthesizer->m_pTG[nTG]->setPitchRate(3, scale(99, pMessage[2]));
468+
469+
break;
470+
case DEXED_PITCH_EG_L1:
471+
m_pSynthesizer->m_pTG[nTG]->setPitchLevel(0, scale(99, pMessage[2]));
472+
473+
break;
474+
case DEXED_PITCH_EG_L2:
475+
m_pSynthesizer->m_pTG[nTG]->setPitchLevel(1, scale(99, pMessage[2]));
476+
477+
break;
478+
case DEXED_PITCH_EG_L3:
479+
m_pSynthesizer->m_pTG[nTG]->setPitchLevel(2, scale(99, pMessage[2]));
480+
481+
break;
482+
case DEXED_PITCH_EG_L4:
483+
m_pSynthesizer->m_pTG[nTG]->setPitchLevel(3, scale(99, pMessage[2]));
484+
485+
break;
486+
case DEXED_ALGORITHM:
487+
m_pSynthesizer->m_pTG[nTG]->setAlgorithm(scale(31, pMessage[2]));
488+
489+
break;
490+
case DEXED_FEEDBACK:
491+
m_pSynthesizer->m_pTG[nTG]->setFeedback(scale(7, pMessage[2]));
492+
493+
break;
494+
case DEXED_OSC_KEY_SYNC:
495+
m_pSynthesizer->m_pTG[nTG]->setOscillatorSync(scale(1, pMessage[2]));
496+
497+
break;
498+
case DEXED_LFO_SPEED:
499+
m_pSynthesizer->m_pTG[nTG]->setLFOSpeed(scale(99, pMessage[2]));
500+
501+
break;
502+
case DEXED_LFO_DELAY:
503+
m_pSynthesizer->m_pTG[nTG]->setLFODelay(scale(99, pMessage[2]));
504+
505+
break;
506+
case DEXED_LFO_PITCH_MOD_DEP:
507+
m_pSynthesizer->m_pTG[nTG]->setLFOPitchModulationDepth(scale(99, pMessage[2]));
508+
509+
break;
510+
case DEXED_LFO_AMP_MOD_DEP:
511+
m_pSynthesizer->m_pTG[nTG]->setLFOAmpModulationDepth(scale(99, pMessage[2]));
512+
513+
break;
514+
case DEXED_LFO_SYNC:
515+
m_pSynthesizer->m_pTG[nTG]->setLFOSync(scale(1, pMessage[2]));
516+
517+
break;
518+
case DEXED_LFO_WAVE:
519+
m_pSynthesizer->m_pTG[nTG]->setLFOWaveform(scale(4, pMessage[2]));
520+
521+
break;
522+
case DEXED_LFO_PITCH_MOD_SENS:
523+
m_pSynthesizer->m_pTG[nTG]->setLFOPitchModulationSensitivity(scale(7, pMessage[2]));
524+
525+
break;
526+
case DEXED_TRANSPOSE:
527+
m_pSynthesizer->m_pTG[nTG]->setTranspose(scale(48, pMessage[2]));
528+
529+
break;
530+
}
531+
// TODO: send partial update, but this will do for now
532+
SendSystemExclusiveVoice(0, 0, nTG);
533+
}
534+
}
535+
break;
536+
350537
}
351538
break;
352539

src/mididevice.h

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class CMIDIDevice
6363
CUserInterface *m_pUI;
6464

6565
u8 m_ChannelMap[CConfig::ToneGenerators];
66+
u8 m_NRPNoffset[CConfig::ToneGenerators];
67+
u8 m_NRPNop[CConfig::ToneGenerators];
6668

6769
std::string m_DeviceName;
6870

src/minidexed.h

+2
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,9 @@ class CMiniDexed
234234

235235
int m_nParameter[ParameterUnknown]; // global (non-TG) parameters
236236

237+
public:
237238
CDexedAdapter *m_pTG[CConfig::ToneGenerators];
239+
private:
238240

239241
unsigned m_nVoiceBankID[CConfig::ToneGenerators];
240242
unsigned m_nVoiceBankIDMSB[CConfig::ToneGenerators];

0 commit comments

Comments
 (0)