Skip to content

Commit 41b96ef

Browse files
Increase MIDI speed by 20% by saturating at end (earlephilhower#792)
By rendering into a 32b int we don't have to saturate check in the inner loop of the voice render, which is called multiple times per actual output block. That check is very expensive. In tsf_render_short make the input `short *buffer` have 2x the space as normal to allow the full 32b quantity for each sample to accumulate. After all voices have been rendered, then resample with saturation into int16_t values. About 24% faster on the Pico while still saturating properly.
1 parent 5cb3ef5 commit 41b96ef

File tree

4 files changed

+28
-15
lines changed

4 files changed

+28
-15
lines changed

src/AudioGeneratorMIDI.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,11 +559,11 @@ bool AudioGeneratorMIDI::loop() {
559559
break;
560560
}
561561
} else if (samplesToPlay) {
562-
numSamplesRendered = (sizeof(samplesRendered) / sizeof(samplesRendered[0])) / 2;
563-
if ((int)samplesToPlay < (int)(sizeof(samplesRendered) / sizeof(samplesRendered[0])) / 2) {
562+
numSamplesRendered = (sizeof(samplesRendered) / sizeof(samplesRendered[0])) / 4;
563+
if ((int)samplesToPlay < (int)(sizeof(samplesRendered) / sizeof(samplesRendered[0])) / 4) {
564564
numSamplesRendered = samplesToPlay;
565565
}
566-
tsf_render_short(g_tsf, samplesRendered, numSamplesRendered, 0);
566+
tsf_render_short_2x(g_tsf, samplesRendered, numSamplesRendered, 0);
567567
samplesToPlay -= numSamplesRendered;
568568
sentSamplesRendered = 0;
569569
} else {

src/AudioGeneratorMIDI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class AudioGeneratorMIDI : public AudioGenerator {
186186
bool sawEOF;
187187
int numSamplesRendered;
188188
int sentSamplesRendered ;
189-
short samplesRendered[256 * 2];
189+
short samplesRendered[256 * 2 * 2];
190190

191191
tsf *_tsf = nullptr;
192192
};

src/libtinysoundfont/tsf.h

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,12 @@ TSFDEF int tsf_active_voice_count(tsf* f);
210210
// buffer: target buffer of size samples * output_channels * sizeof(type)
211211
// samples: number of samples to render
212212
// flag_mixing: if 0 clear the buffer first, otherwise mix into existing data
213+
#ifndef TSF_SAMPLES_SHORT
213214
TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing CPP_DEFAULT0);
215+
#else
216+
// The short render needs space for a long (32b) for all samples, so pass in a buffer of 2x the expected size. Only the first `samples` samples will be valid on return
217+
TSFDEF void tsf_render_short_2x_buffer(tsf* f, short* buffer_2x, int samples, int flag_mixing CPP_DEFAULT0);
218+
#endif
214219
TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing CPP_DEFAULT0);
215220

216221
// Higher level channel based functions, set up channel parameters
@@ -1536,7 +1541,7 @@ static void tsf_voice_render(tsf* f, struct tsf_voice* v, float* outputBuffer, i
15361541
if (tmpLowpass.active || dynamicLowpass) v->lowpass = tmpLowpass;
15371542
}
15381543
#else
1539-
static void tsf_voice_render_short(tsf* f, struct tsf_voice* v, short* outputBuffer, int numSamples)
1544+
static void tsf_voice_render_short(tsf* f, struct tsf_voice* v, int32_t* outputBuffer, int numSamples)
15401545
{
15411546
#ifdef ESP8266
15421547
static unsigned int smps = 0;
@@ -1547,7 +1552,7 @@ static void tsf_voice_render_short(tsf* f, struct tsf_voice* v, short* outputBuf
15471552
#endif
15481553
TSF_CONST struct tsf_region* region = v->region;
15491554
TSF_CONST short* input = f->shortSamples;
1550-
short* outL = outputBuffer;
1555+
int32_t* outL = outputBuffer;
15511556
//short* outR = (f->outputmode == TSF_STEREO_UNWEAVED ? outL + numSamples : TSF_NULL);
15521557

15531558
// Cache some values, to give them at least some chance of ending up in registers.
@@ -1643,19 +1648,20 @@ static void tsf_voice_render_short(tsf* f, struct tsf_voice* v, short* outputBuf
16431648
// Do saturating adds for each channel
16441649
fixed16p16 smp;
16451650

1651+
// No clipping because it eats 20% of the performance on the M0+ Pico!
1652+
16461653
smp = *outL;
16471654
smp += (val * gainLeftF16P16) >> 16;
1648-
if (smp > 32767) smp = 32767;
1649-
else if (smp < -32768) smp = -32768;
1655+
// if (smp > 32767) smp = 32767;
1656+
// else if (smp < -32768) smp = -32768;
16501657
*outL++ = smp;
16511658

16521659
smp = *outL;
16531660
smp += (val * gainRightF16P16) >> 16;
1654-
if (smp > 32767) smp = 32767;
1655-
else if (smp < -32768) smp = -32768;
1661+
// if (smp > 32767) smp = 32767;
1662+
// else if (smp < -32768) smp = -32768;
16561663
*outL++ = smp;
16571664

1658-
16591665
// *outL++ += val * gainLeft;
16601666
// *outL++ += val * gainRight;
16611667

@@ -2161,13 +2167,20 @@ TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing
21612167
tsf_voice_render(f, v, buffer, samples);
21622168
}
21632169
#else
2164-
TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing)
2170+
TSFDEF void tsf_render_short_2x(tsf* f, short* buffer, int samples, int flag_mixing)
21652171
{
21662172
struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum;
2167-
if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(short) * samples);
2173+
int32_t *buffer32 = (int32_t *)buffer;
2174+
if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(short) * samples * 2 /* We sum in 32bs then downsample here to 16b to minimized saturation calcs */);
21682175
for (; v != vEnd; v++)
21692176
if (v->playingPreset != -1)
2170-
tsf_voice_render_short(f, v, buffer, samples);
2177+
tsf_voice_render_short(f, v, buffer32, samples);
2178+
for (int i = 0; i < samples * 2; i++) {
2179+
int32_t t = buffer32[i];
2180+
if (t > 32767) buffer[i] = 32767;
2181+
else if (t < -32768) buffer[i] = -32768;
2182+
else buffer[i] = t;
2183+
}
21712184
}
21722185
#endif
21732186

0 commit comments

Comments
 (0)