ref: b9a4de3b3b29fe03e52c6d0a5853353fa2c5e21f
parent: 7f29067b40e7069910f1259fefee30f333af2ba4
author: Bernhard Schelling <schellingb@gmail.com>
date: Sun Oct 22 20:57:36 EDT 2017
Handle note_on/off more like the MIDI standard suggests: - Change note_on with a velocity of 0 to be handled as note_off - Change note_on so it doesn't cancel previous note_on of the same preset/key - Change note_off so it only stops a single corresponding note_on and not all notes playing on the same preset/key
--- a/tsf.h
+++ b/tsf.h
@@ -231,20 +231,20 @@
struct tsf
{
struct tsf_preset* presets;
- int presetNum;
-
float* fontSamples;
- int fontSampleCount;
+ struct tsf_voice* voices;
+ float* outputSamples;
- struct tsf_voice *voices;
+ int presetNum;
+ int fontSampleCount;
int voiceNum;
+ int outputSampleSize;
+ unsigned int voicePlayIndex;
float outSampleRate;
enum TSFOutputMode outputmode;
float globalGainDB, globalPanFactorLeft, globalPanFactorRight;
- float* outputSamples;
- int outputSampleSize;
};
#ifndef TSF_NO_STDIO
@@ -357,7 +357,7 @@
double pitchInputTimecents, pitchOutputFactor;
double sourceSamplePosition;
float noteGainDB, panFactorLeft, panFactorRight;
- unsigned int sampleEnd, loopStart, loopEnd;
+ unsigned int playIndex, sampleEnd, loopStart, loopEnd;
struct tsf_voice_envelope ampenv, modenv;
struct tsf_voice_lowpass lowpass;
struct tsf_voice_lfo modlfo, viblfo;
@@ -1150,21 +1150,22 @@
TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel)
{
- int midiVelocity = (int)(vel * 127);
+ int midiVelocity = (int)(vel * 127), voicePlayIndex;
TSF_BOOL haveGroupedNotesPlaying = TSF_FALSE;
struct tsf_voice *v, *vEnd; struct tsf_region *region, *regionEnd;
- if (preset_index < 0 || preset_index >= f->presetNum || midiVelocity <= 0) return;
+ if (preset_index < 0 || preset_index >= f->presetNum) return;
+ if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return; }
// Are any grouped notes playing? (Needed for group stopping) Also stop any voices still playing this note.
for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++)
{
if (v->playingPreset != preset_index) continue;
- if (v->playingKey == key) tsf_voice_endquick(v, f->outSampleRate);
if (v->region->group) haveGroupedNotesPlaying = TSF_TRUE;
}
// Play all matching regions.
+ voicePlayIndex = f->voicePlayIndex++;
for (region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++)
{
struct tsf_voice* voice = TSF_NULL; double adjustedPan; TSF_BOOL doLoop; float filterQDB;
@@ -1187,6 +1188,7 @@
voice->region = region;
voice->playingPreset = preset_index;
voice->playingKey = key;
+ voice->playIndex = voicePlayIndex;
// Pitch.
voice->curPitchWheel = 8192;
@@ -1235,10 +1237,21 @@
TSFDEF void tsf_note_off(tsf* f, int preset_index, int key)
{
- struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
+ struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast;
for (; v != vEnd; v++)
- if (v->playingPreset == preset_index && v->playingKey == key)
- tsf_voice_end(v, f->outSampleRate);
+ {
+ //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index
+ if (v->playingPreset != preset_index || v->playingKey != key) continue;
+ else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v;
+ else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v;
+ }
+ if (!vMatchFirst) return;
+ for (v = vMatchFirst; v <= vMatchLast; v++)
+ {
+ //Stop all voices with matching preset, key and the smallest play index which was enumerated above
+ if (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key) continue;
+ tsf_voice_end(v, f->outSampleRate);
+ }
}
TSFDEF void tsf_bank_note_off(tsf* f, int bank, int preset_number, int key)