apply volExp on velocity input - PLEASE READ

DivDispatch::mapVelocity() now takes a float instead of an unsigned char
This commit is contained in:
tildearrow 2023-12-16 19:52:37 -05:00
parent 99dd85bcb4
commit 51b385a1ef
18 changed files with 39 additions and 24 deletions

View file

@ -594,10 +594,10 @@ class DivDispatch {
/** /**
* map MIDI velocity (from 0 to 127) to chip volume. * map MIDI velocity (from 0 to 127) to chip volume.
* @param ch the chip channel. -1 means N/A. * @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. * @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. * get the lowest note in a portamento.

View file

@ -3385,6 +3385,10 @@ void DivEngine::setMidiDirect(bool value) {
midiIsDirect=value; midiIsDirect=value;
} }
void DivEngine::setMidiVolExp(float value) {
midiVolExp=value;
}
void DivEngine::setMidiCallback(std::function<int(const TAMidiMessage&)> what) { void DivEngine::setMidiCallback(std::function<int(const TAMidiMessage&)> what) {
midiCallback=what; midiCallback=what;
} }

View file

@ -423,6 +423,7 @@ class DivEngine {
bool midiOutProgramChange; bool midiOutProgramChange;
int midiOutMode; int midiOutMode;
int midiOutTimeRate; int midiOutTimeRate;
float midiVolExp;
int softLockCount; int softLockCount;
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed; int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed, elapsedBars, elapsedBeats, curSpeed;
size_t curSubSongIndex; size_t curSubSongIndex;
@ -1184,6 +1185,9 @@ class DivEngine {
// set MIDI direct channel map // set MIDI direct channel map
void setMidiDirect(bool value); void setMidiDirect(bool value);
// set MIDI volume curve exponent
void setMidiVolExp(float value);
// set MIDI input callback // set MIDI input callback
// if the specified function returns -2, note feedback will be inhibited. // if the specified function returns -2, note feedback will be inhibited.
void setMidiCallback(std::function<int(const TAMidiMessage&)> what); void setMidiCallback(std::function<int(const TAMidiMessage&)> what);
@ -1261,6 +1265,7 @@ class DivEngine {
midiOutProgramChange(false), midiOutProgramChange(false),
midiOutMode(DIV_MIDI_MODE_NOTE), midiOutMode(DIV_MIDI_MODE_NOTE),
midiOutTimeRate(0), midiOutTimeRate(0),
midiVolExp(2.0f), // General MIDI standard
softLockCount(0), softLockCount(0),
subticks(0), subticks(0),
ticks(0), ticks(0),

View file

@ -102,9 +102,9 @@ bool DivDispatch::isVolGlobal() {
return false; 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)))); 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) { int DivDispatch::getPortaFloor(int ch) {

View file

@ -719,8 +719,8 @@ DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformAY8910::mapVelocity(int ch, unsigned char vel) { int DivPlatformAY8910::mapVelocity(int ch, float vel) {
return round(15.0*pow(((double)vel/127.0),0.33)); return round(15.0*pow(vel,0.33));
} }
unsigned char* DivPlatformAY8910::getRegisterPool() { unsigned char* DivPlatformAY8910::getRegisterPool() {

View file

@ -131,7 +131,7 @@ class DivPlatformAY8910: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void* getChanState(int chan); void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, unsigned char vel); int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void flushWrites(); void flushWrites();

View file

@ -718,8 +718,8 @@ DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformAY8930::mapVelocity(int ch, unsigned char vel) { int DivPlatformAY8930::mapVelocity(int ch, float vel) {
return round(31.0*pow(((double)vel/127.0),0.22)); return round(31.0*pow(vel,0.22));
} }
unsigned char* DivPlatformAY8930::getRegisterPool() { unsigned char* DivPlatformAY8930::getRegisterPool() {

View file

@ -132,7 +132,7 @@ class DivPlatformAY8930: public DivDispatch {
int dispatch(DivCommand c); int dispatch(DivCommand c);
void* getChanState(int chan); void* getChanState(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, unsigned char vel); int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -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 // -0.75dB per step
// -6: 64: 8 // -6: 64: 8
// -12: 32: 16 // -12: 32: 16
@ -133,7 +133,7 @@ class DivPlatformFMBase: public DivDispatch {
// -42: 1: 56 // -42: 1: 56
if (vel==0) return 0; if (vel==0) return 0;
if (vel==127) return 127; 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); friend void putDispatchChan(void*,int,int);

View file

@ -539,8 +539,8 @@ DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformPCE::mapVelocity(int ch, unsigned char vel) { int DivPlatformPCE::mapVelocity(int ch, float vel) {
return round(31.0*pow(((double)vel/127.0),0.22)); return round(31.0*pow(vel,0.22));
} }
unsigned char* DivPlatformPCE::getRegisterPool() { unsigned char* DivPlatformPCE::getRegisterPool() {

View file

@ -86,7 +86,7 @@ class DivPlatformPCE: public DivDispatch {
DivChannelModeHints getModeHints(int chan); DivChannelModeHints getModeHints(int chan);
DivSamplePos getSamplePos(int ch); DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, unsigned char vel); int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -462,8 +462,8 @@ DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformSMS::mapVelocity(int ch, unsigned char vel) { int DivPlatformSMS::mapVelocity(int ch, float vel) {
return round(15.0*pow(((double)vel/127.0),0.33)); return round(15.0*pow(vel,0.33));
} }
unsigned char* DivPlatformSMS::getRegisterPool() { unsigned char* DivPlatformSMS::getRegisterPool() {

View file

@ -79,7 +79,7 @@ class DivPlatformSMS: public DivDispatch {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, unsigned char vel); int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -308,8 +308,8 @@ DivDispatchOscBuffer* DivPlatformT6W28::getOscBuffer(int ch) {
return oscBuf[ch]; return oscBuf[ch];
} }
int DivPlatformT6W28::mapVelocity(int ch, unsigned char vel) { int DivPlatformT6W28::mapVelocity(int ch, float vel) {
return round(15.0*pow(((double)vel/127.0),0.33)); return round(15.0*pow(vel,0.33));
} }
unsigned char* DivPlatformT6W28::getRegisterPool() { unsigned char* DivPlatformT6W28::getRegisterPool() {

View file

@ -65,7 +65,7 @@ class DivPlatformT6W28: public DivDispatch {
DivMacroInt* getChanMacroInt(int ch); DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan); unsigned short getPan(int chan);
DivDispatchOscBuffer* getOscBuffer(int chan); DivDispatchOscBuffer* getOscBuffer(int chan);
int mapVelocity(int ch, unsigned char vel); int mapVelocity(int ch, float vel);
unsigned char* getRegisterPool(); unsigned char* getRegisterPool();
int getRegisterPoolSize(); int getRegisterPoolSize();
void reset(); void reset();

View file

@ -1373,7 +1373,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
if (note.on) { if (note.on) {
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1));
if (note.volume>=0 && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) { 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); logV("dispatching volume (%d -> %d)",note.volume,mappedVol);
dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,mappedVol)); dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,mappedVol));
} }

View file

@ -1003,7 +1003,7 @@ struct MIDIMap {
valueInputControlMSB(0), valueInputControlMSB(0),
valueInputControlLSB(0), valueInputControlLSB(0),
valueInputControlSingle(0), valueInputControlSingle(0),
volExp(1.0f), volExp(2.0f),
valueInputCurMSB(0), valueInputCurMSB(0),
valueInputCurLSB(0), valueInputCurLSB(0),
valueInputCurSingle(0) { valueInputCurSingle(0) {

View file

@ -1139,7 +1139,10 @@ void FurnaceGUI::drawSettings() {
// TODO // TODO
//ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume);
//ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); //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("Map Yamaha FM voice data to instruments",&midiMap.yamahaFMResponse)) settingsChanged=true;
if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true; if (ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange)) settingsChanged=true;
//ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); //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 (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) {
if (midiMap.volExp<0.01) midiMap.volExp=0.01; if (midiMap.volExp<0.01) midiMap.volExp=0.01;
if (midiMap.volExp>8.0) midiMap.volExp=8.0; if (midiMap.volExp>8.0) midiMap.volExp=8.0;
e->setMidiVolExp(midiMap.volExp);
settingsChanged=true; settingsChanged=true;
} rightClickable } rightClickable
float curve[128]; float curve[128];
@ -4025,6 +4029,7 @@ void FurnaceGUI::syncSettings() {
midiMap.compile(); midiMap.compile();
e->setMidiDirect(midiMap.directChannel); e->setMidiDirect(midiMap.directChannel);
e->setMidiVolExp(midiMap.volExp);
e->setMetronomeVol(((float)settings.metroVol)/100.0f); e->setMetronomeVol(((float)settings.metroVol)/100.0f);
e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f); e->setSamplePreviewVol(((float)settings.sampleVol)/100.0f);
} }