diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 6dcb6171e..5860a3736 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -594,10 +594,10 @@ class DivDispatch { /** * map MIDI velocity (from 0 to 127) to chip volume. * @param ch the chip channel. -1 means N/A. - * @param vel input velocity. + * @param vel input velocity, from 0.0 to 1.0. * @return output volume. */ - virtual int mapVelocity(int ch, unsigned char vel); + virtual int mapVelocity(int ch, float vel); /** * get the lowest note in a portamento. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index fc999fcd9..f8af13d80 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -3385,6 +3385,10 @@ void DivEngine::setMidiDirect(bool value) { midiIsDirect=value; } +void DivEngine::setMidiVolExp(float value) { + midiVolExp=value; +} + void DivEngine::setMidiCallback(std::function what) { midiCallback=what; } diff --git a/src/engine/engine.h b/src/engine/engine.h index db8cb7b34..fe0cd0991 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -423,6 +423,7 @@ class DivEngine { bool midiOutProgramChange; int midiOutMode; int midiOutTimeRate; + float midiVolExp; int softLockCount; int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed; size_t curSubSongIndex; @@ -1184,6 +1185,9 @@ class DivEngine { // set MIDI direct channel map void setMidiDirect(bool value); + // set MIDI volume curve exponent + void setMidiVolExp(float value); + // set MIDI input callback // if the specified function returns -2, note feedback will be inhibited. void setMidiCallback(std::function what); @@ -1261,6 +1265,7 @@ class DivEngine { midiOutProgramChange(false), midiOutMode(DIV_MIDI_MODE_NOTE), midiOutTimeRate(0), + midiVolExp(2.0f), // General MIDI standard softLockCount(0), subticks(0), ticks(0), diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index aaa27a494..9886dae37 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -102,9 +102,9 @@ bool DivDispatch::isVolGlobal() { return false; } -int DivDispatch::mapVelocity(int ch, unsigned char vel) { +int DivDispatch::mapVelocity(int ch, float vel) { const int volMax=MAX(1,dispatch(DivCommand(DIV_CMD_GET_VOLMAX,MAX(ch,0)))); - return (vel*volMax)/127; + return round(vel*volMax); } int DivDispatch::getPortaFloor(int ch) { diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index faf5b7a04..f8c5bde9e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -719,8 +719,8 @@ DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) { return oscBuf[ch]; } -int DivPlatformAY8910::mapVelocity(int ch, unsigned char vel) { - return round(15.0*pow(((double)vel/127.0),0.33)); +int DivPlatformAY8910::mapVelocity(int ch, float vel) { + return round(15.0*pow(vel,0.33)); } unsigned char* DivPlatformAY8910::getRegisterPool() { diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 86d527947..642ae59cf 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -131,7 +131,7 @@ class DivPlatformAY8910: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); - int mapVelocity(int ch, unsigned char vel); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void flushWrites(); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 022b36412..2199d5ccd 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -718,8 +718,8 @@ DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) { return oscBuf[ch]; } -int DivPlatformAY8930::mapVelocity(int ch, unsigned char vel) { - return round(31.0*pow(((double)vel/127.0),0.22)); +int DivPlatformAY8930::mapVelocity(int ch, float vel) { + return round(31.0*pow(vel,0.22)); } unsigned char* DivPlatformAY8930::getRegisterPool() { diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index c8b0f724a..64f8e9318 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -132,7 +132,7 @@ class DivPlatformAY8930: public DivDispatch { int dispatch(DivCommand c); void* getChanState(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); - int mapVelocity(int ch, unsigned char vel); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/fmsharedbase.h b/src/engine/platform/fmsharedbase.h index db420b408..3a12f96e6 100644 --- a/src/engine/platform/fmsharedbase.h +++ b/src/engine/platform/fmsharedbase.h @@ -122,7 +122,7 @@ class DivPlatformFMBase: public DivDispatch { } } - virtual int mapVelocity(int ch, unsigned char vel) { + virtual int mapVelocity(int ch, float vel) { // -0.75dB per step // -6: 64: 8 // -12: 32: 16 @@ -133,7 +133,7 @@ class DivPlatformFMBase: public DivDispatch { // -42: 1: 56 if (vel==0) return 0; if (vel==127) return 127; - return CLAMP(round(128.0-(56.0-log2(vel)*8.0)),0,127); + return CLAMP(round(128.0-(56.0-log2(vel*127.0)*8.0)),0,127); } friend void putDispatchChan(void*,int,int); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index d2e0ca6d7..1cff48278 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -539,8 +539,8 @@ DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) { return oscBuf[ch]; } -int DivPlatformPCE::mapVelocity(int ch, unsigned char vel) { - return round(31.0*pow(((double)vel/127.0),0.22)); +int DivPlatformPCE::mapVelocity(int ch, float vel) { + return round(31.0*pow(vel,0.22)); } unsigned char* DivPlatformPCE::getRegisterPool() { diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 26e13adda..591c94cef 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -86,7 +86,7 @@ class DivPlatformPCE: public DivDispatch { DivChannelModeHints getModeHints(int chan); DivSamplePos getSamplePos(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); - int mapVelocity(int ch, unsigned char vel); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index db18055fe..0b5e6183e 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -462,8 +462,8 @@ DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) { return oscBuf[ch]; } -int DivPlatformSMS::mapVelocity(int ch, unsigned char vel) { - return round(15.0*pow(((double)vel/127.0),0.33)); +int DivPlatformSMS::mapVelocity(int ch, float vel) { + return round(15.0*pow(vel,0.33)); } unsigned char* DivPlatformSMS::getRegisterPool() { diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index d89658462..092010a56 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -79,7 +79,7 @@ class DivPlatformSMS: public DivDispatch { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); - int mapVelocity(int ch, unsigned char vel); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/t6w28.cpp b/src/engine/platform/t6w28.cpp index 7460c2ecc..8fd0cb2ee 100644 --- a/src/engine/platform/t6w28.cpp +++ b/src/engine/platform/t6w28.cpp @@ -308,8 +308,8 @@ DivDispatchOscBuffer* DivPlatformT6W28::getOscBuffer(int ch) { return oscBuf[ch]; } -int DivPlatformT6W28::mapVelocity(int ch, unsigned char vel) { - return round(15.0*pow(((double)vel/127.0),0.33)); +int DivPlatformT6W28::mapVelocity(int ch, float vel) { + return round(15.0*pow(vel,0.33)); } unsigned char* DivPlatformT6W28::getRegisterPool() { diff --git a/src/engine/platform/t6w28.h b/src/engine/platform/t6w28.h index ccb89b76d..eae0f92f9 100644 --- a/src/engine/platform/t6w28.h +++ b/src/engine/platform/t6w28.h @@ -65,7 +65,7 @@ class DivPlatformT6W28: public DivDispatch { DivMacroInt* getChanMacroInt(int ch); unsigned short getPan(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); - int mapVelocity(int ch, unsigned char vel); + int mapVelocity(int ch, float vel); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 0c024d78d..19fedebe4 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1373,7 +1373,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { if (note.on) { dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { - int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],note.volume); + float curvedVol=pow((float)note.volume/127.0f,midiVolExp); + int mappedVol=disCont[dispatchOfChan[note.channel]].dispatch->mapVelocity(dispatchChanOfChan[note.channel],curvedVol); logV("dispatching volume (%d -> %d)",note.volume,mappedVol); dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,mappedVol)); } diff --git a/src/gui/gui.h b/src/gui/gui.h index b379ee743..f754a073d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1003,7 +1003,7 @@ struct MIDIMap { valueInputControlMSB(0), valueInputControlLSB(0), valueInputControlSingle(0), - volExp(1.0f), + volExp(2.0f), valueInputCurMSB(0), valueInputCurLSB(0), valueInputCurSingle(0) { diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 381c2eb9e..dbf1a9661 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1139,7 +1139,10 @@ void FurnaceGUI::drawSettings() { // TODO //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); - if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) settingsChanged=true; + if (ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel)) { + e->setMidiDirect(midiMap.directChannel); + settingsChanged=true; + } if (ImGui::Checkbox("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse)) settingsChanged=true; if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); @@ -1198,6 +1201,7 @@ void FurnaceGUI::drawSettings() { if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { if (midiMap.volExp<0.01) midiMap.volExp=0.01; if (midiMap.volExp>8.0) midiMap.volExp=8.0; + e->setMidiVolExp(midiMap.volExp); settingsChanged=true; } rightClickable float curve[128]; @@ -4025,6 +4029,7 @@ void FurnaceGUI::syncSettings() { midiMap.compile(); e->setMidiDirect(midiMap.directChannel); + e->setMidiVolExp(midiMap.volExp); e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f); }