ref: 77dde8fdc4e1fc618105c7acede22dacbf51fda0
parent: 472abcff8be97ff23f8196412041624fc3e34ce4
parent: f9fe5cb507f5ae7a05ba80ab782dabcd8af55fc9
author: Bernhard Schelling <14200249+schellingb@users.noreply.github.com>
date: Thu Nov 18 20:31:25 EST 2021
Handle out-of-memory cases Merge pull request #67 from ell1e/tsf-malloc-check Fix TSF_MALLOC/TSF_REALLOC return values not checked for success
--- a/tsf.h
+++ b/tsf.h
@@ -125,6 +125,9 @@
};
// Thread safety:
+//
+// 1. Rendering / voices:
+//
// Your audio output which calls the tsf_render* functions will most likely
// run on a different thread than where the playback tsf_note* functions
// are called. In which case some sort of concurrency control like a
@@ -136,6 +139,13 @@
// There is a theoretical chance that ending notes would negatively influence
// a voice that is rendering at the time but it is hard to say.
// Also be aware, this has not been tested much.
+//
+// 2. Channels:
+//
+// Calls to tsf_channel_set_... functions may allocate new channels
+// if no channel with that number was previously used. Make sure to
+// create all channels at the beginning as required if you call tsf_render*
+// from a different thread.
// Setup the parameters for the voice render methods
// outputmode: if mono or stereo and how stereo channel data is ordered
@@ -151,7 +161,8 @@
// Depending on the soundfond, one note can cause many new voices to be started,
// so don't keep this number too low or otherwise sounds may not play.
// max_voices: maximum number to pre-allocate and set the limit to
-TSFDEF void tsf_set_max_voices(tsf* f, int max_voices);
+// (tsf_set_max_voices returns 0 if allocation failed, otherwise 1)
+TSFDEF int tsf_set_max_voices(tsf* f, int max_voices);
// Start playing a note
// preset_index: preset index >= 0 and < tsf_get_presetcount()
@@ -159,9 +170,10 @@
// vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full)
// bank: instrument bank number (alternative to preset_index)
// preset_number: preset number (alternative to preset_index)
-// (bank_note_on returns 0 if preset does not exist, otherwise 1)
-TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel);
-TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel);
+// (tsf_note_on returns 0 if the allocation of a new voice failed, otherwise 1)
+// (tsf_bank_note_on returns 0 if preset does not exist or allocation failed, otherwise 1)
+TSFDEF int tsf_note_on(tsf* f, int preset_index, int key, float vel);
+TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel);
// Stop playing a note
// (bank_note_off returns 0 if preset does not exist, otherwise 1)
@@ -194,28 +206,31 @@
// pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched)
// pitch_range: range of the pitch wheel in semitones (default 2.0, total +/- 2 semitones)
// tuning: tuning of all playing voices in semitones (default 0.0, standard (A440) tuning)
-// (set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1)
-TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index);
-TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0);
-TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank);
-TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number);
-TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan);
-TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume);
-TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel);
-TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range);
-TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning);
+// (tsf_set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1)
+// (tsf_channel_set_... return 0 if a new channel needed allocation and that failed, otherwise 1)
+TSFDEF int tsf_channel_set_presetindex(tsf* f, int channel, int preset_index);
+TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0);
+TSFDEF int tsf_channel_set_bank(tsf* f, int channel, int bank);
+TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number);
+TSFDEF int tsf_channel_set_pan(tsf* f, int channel, float pan);
+TSFDEF int tsf_channel_set_volume(tsf* f, int channel, float volume);
+TSFDEF int tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel);
+TSFDEF int tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range);
+TSFDEF int tsf_channel_set_tuning(tsf* f, int channel, float tuning);
// Start or stop playing notes on a channel (needs channel preset to be set)
// channel: channel number
// key: note value between 0 and 127 (60 being middle C)
// vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full)
-TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel);
+// (tsf_channel_note_on returns 0 on allocation failure of new voice, otherwise 1)
+TSFDEF int tsf_channel_note_on(tsf* f, int channel, int key, float vel);
TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key);
TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release
TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediatly
// Apply a MIDI control change to the channel (not all controllers are supported!)
-TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value);
+// (tsf_channel_midi_control returns 0 on allocation failure of new channel, otherwise 1)
+TSFDEF int tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value);
// Get current values set on the channels
TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel);
@@ -308,12 +323,10 @@
float* fontSamples;
struct tsf_voice* voices;
struct tsf_channels* channels;
- float* outputSamples;
int presetNum;
int voiceNum;
int maxVoiceNum;
- int outputSampleSize;
unsigned int voicePlayIndex;
enum TSFOutputMode outputmode;
@@ -682,7 +695,7 @@
else p->sustain = 1.0f - (p->sustain / 1000.0f);
}
-static void tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount)
+static int tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount)
{
enum { GenInstrument = 41, GenKeyRange = 43, GenVelRange = 44, GenSampleID = 53 };
// Read each preset.
@@ -736,6 +749,11 @@
}
preset->regions = (struct tsf_region*)TSF_MALLOC(preset->regionNum * sizeof(struct tsf_region));
+ if (!preset->regions)
+ {
+ preset->regionNum = 0;
+ return 0;
+ }
tsf_region_clear(&globalRegion, TSF_TRUE);
// Zones.
@@ -827,14 +845,19 @@
globalRegion = presetRegion;
}
}
+ return 1;
}
-static void tsf_load_samples(float** fontSamples, unsigned int* fontSampleCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream)
+static int tsf_load_samples(float** fontSamples, unsigned int* fontSampleCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream)
{
// Read sample data into float format buffer.
float* out; unsigned int samplesLeft, samplesToRead, samplesToConvert;
samplesLeft = *fontSampleCount = chunkSmpl->size / sizeof(short);
out = *fontSamples = (float*)TSF_MALLOC(samplesLeft * sizeof(float));
+ if (!fontSamples) {
+ *fontSamples = 0;
+ return 0;
+ }
for (; samplesLeft; samplesLeft -= samplesToRead)
{
short sampleBuffer[1024], *in = sampleBuffer;;
@@ -846,6 +869,7 @@
// If we ever need to compile for big-endian platforms, we'll need to byte-swap here.
*out++ = (float)(*in++ / 32767.0);
}
+ return 1;
}
static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate)
@@ -1215,6 +1239,8 @@
}
// Read hydra and locate sample data.
+ int hydra_alloc_failed = 0;
+ int samples_alloc_failed = 0;
TSF_MEMSET(&hydra, 0, sizeof(hydra));
while (tsf_riffchunk_read(&chunkHead, &chunkList, stream))
{
@@ -1221,7 +1247,7 @@
struct tsf_riffchunk chunk;
if (TSF_FourCCEquals(chunkList.id, "pdta"))
{
- while (tsf_riffchunk_read(&chunkList, &chunk, stream))
+ while (tsf_riffchunk_read(&chunkList, &chunk, stream) && !hydra_alloc_failed)
{
#define HandleChunk(chunkName) (TSF_FourCCEquals(chunk.id, #chunkName) && !(chunk.size % chunkName##SizeInFile)) \
{ \
@@ -1228,6 +1254,11 @@
int num = chunk.size / chunkName##SizeInFile, i; \
hydra.chunkName##Num = num; \
hydra.chunkName##s = (struct tsf_hydra_##chunkName*)TSF_MALLOC(num * sizeof(struct tsf_hydra_##chunkName)); \
+ if (!hydra.chunkName##s) \
+ { \
+ hydra_alloc_failed = 1; \
+ break; \
+ } \
for (i = 0; i < num; ++i) tsf_hydra_read_##chunkName(&hydra.chunkName##s[i], stream); \
}
enum
@@ -1247,38 +1278,65 @@
{
while (tsf_riffchunk_read(&chunkList, &chunk, stream))
{
- if (TSF_FourCCEquals(chunk.id, "smpl"))
+ if (TSF_FourCCEquals(chunk.id, "smpl") && !fontSamples && chunk.size >= sizeof(short))
{
- tsf_load_samples(&fontSamples, &fontSampleCount, &chunk, stream);
+ if (!tsf_load_samples(&fontSamples, &fontSampleCount, &chunk, stream))
+ {
+ samples_alloc_failed = 1;
+ }
}
else stream->skip(stream->data, chunk.size);
}
}
else stream->skip(stream->data, chunkList.size);
+ if (hydra_alloc_failed || samples_alloc_failed)
+ break;
}
- if (!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs)
+ if ((!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs) && !hydra_alloc_failed)
{
//if (e) *e = TSF_INVALID_INCOMPLETE;
}
- else if (fontSamples == TSF_NULL)
+ else if (fontSamples == TSF_NULL && !samples_alloc_failed)
{
//if (e) *e = TSF_INVALID_NOSAMPLEDATA;
}
- else
+ else if (!hydra_alloc_failed && !samples_alloc_failed)
{
res = (tsf*)TSF_MALLOC(sizeof(tsf));
- TSF_MEMSET(res, 0, sizeof(tsf));
- res->presetNum = hydra.phdrNum - 1;
- res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset));
- res->fontSamples = fontSamples;
- res->outSampleRate = 44100.0f;
- fontSamples = TSF_NULL; //don't free below
- tsf_load_presets(res, &hydra, fontSampleCount);
+ if (res)
+ {
+ TSF_MEMSET(res, 0, sizeof(tsf));
+ res->presetNum = hydra.phdrNum - 1;
+ res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset));
+ if (!res->presets)
+ {
+ res->presetNum = 0;
+ TSF_FREE(res);
+ res = TSF_NULL;
+ goto skipfontinit;
+ }
+ res->fontSamples = fontSamples;
+ fontSamples = TSF_NULL; //don't free below
+ res->outSampleRate = 44100.0f;
+
+ if (!tsf_load_presets(res, &hydra, fontSampleCount))
+ {
+ TSF_FREE(res);
+ res = TSF_NULL;
+ }
+ skipfontinit:
+ }
}
TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods);
TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags);
TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs);
TSF_FREE(fontSamples);
+ if (!res || hydra_alloc_failed || samples_alloc_failed)
+ {
+ //if (e) *e = TSF_INVALID_ALLOCFAIL;
+ if (res) tsf_close(res);
+ return TSF_NULL;
+ }
return res;
}
@@ -1286,16 +1344,20 @@
{
tsf* res;
if (!f) return TSF_NULL;
- if (!f->refCount)
- *(f->refCount = (int*)TSF_MALLOC(sizeof(int))) = 1;
+ if (!f->refCount) {
+ f->refCount = (int*)TSF_MALLOC(sizeof(int));
+ if (!f->refCount)
+ return TSF_NULL;
+ *f->refCount = 1;
+ }
res = (tsf*)TSF_MALLOC(sizeof(tsf));
- memcpy(res, f, sizeof(tsf));
+ if (!res) return TSF_NULL;
+ TSF_MEMCPY(res, f, sizeof(tsf));
res->voices = TSF_NULL;
res->voiceNum = 0;
res->channels = TSF_NULL;
- res->outputSamples = TSF_NULL;
- res->outputSampleSize = 0;
- (*res->refCount)++;
+
+ ++(*res->refCount);
return res;
}
@@ -1313,7 +1375,6 @@
}
TSF_FREE(f->channels);
TSF_FREE(f->voices);
- TSF_FREE(f->outputSamples);
TSF_FREE(f);
}
@@ -1363,23 +1424,28 @@
f->globalGainDB = (global_volume == 1.0f ? 0 : -tsf_gainToDecibels(1.0f / global_volume));
}
-TSFDEF void tsf_set_max_voices(tsf* f, int max_voices)
+TSFDEF int tsf_set_max_voices(tsf* f, int max_voices)
{
int i = f->voiceNum;
- f->voiceNum = f->maxVoiceNum = (f->voiceNum > max_voices ? f->voiceNum : max_voices);
- f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice));
- for (; i != max_voices; i++)
+ int newVoiceNum = (f->voiceNum > max_voices ? f->voiceNum : max_voices);
+ struct tsf_voice *newVoices = (struct tsf_voice*)TSF_REALLOC(f->voices, newVoiceNum * sizeof(struct tsf_voice));
+ if (!newVoices)
+ return 0;
+ f->voices = newVoices;
+ f->voiceNum = f->maxVoiceNum = newVoiceNum;
+ for (; i < max_voices; i++)
f->voices[i].playingPreset = -1;
+ return 1;
}
-TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel)
+TSFDEF int tsf_note_on(tsf* f, int preset_index, int key, float vel)
{
short midiVelocity = (short)(vel * 127);
int voicePlayIndex;
struct tsf_region *region, *regionEnd;
- if (preset_index < 0 || preset_index >= f->presetNum) return;
- if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return; }
+ if (preset_index < 0 || preset_index >= f->presetNum) return 1;
+ if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return 1; }
// Play all matching regions.
voicePlayIndex = f->voicePlayIndex++;
@@ -1405,7 +1471,12 @@
continue;
}
f->voiceNum += 4;
- f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice));
+ struct tsf_voice* newVoices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice));
+ if (!newVoices)
+ {
+ return 0;
+ }
+ f->voices = newVoices;
voice = &f->voices[f->voiceNum - 4];
voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1;
}
@@ -1452,6 +1523,7 @@
tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate);
tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate);
}
+ return 1;
}
TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel)
@@ -1458,8 +1530,7 @@
{
int preset_index = tsf_get_presetindex(f, bank, preset_number);
if (preset_index == -1) return 0;
- tsf_note_on(f, preset_index, key, vel);
- return 1;
+ return tsf_note_on(f, preset_index, key, vel);
}
TSFDEF void tsf_note_off(tsf* f, int preset_index, int key)
@@ -1508,31 +1579,33 @@
TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing)
{
float *floatSamples;
- int channelSamples = (f->outputmode == TSF_MONO ? 1 : 2) * samples, floatBufferSize = channelSamples * sizeof(float);
- short* bufferEnd = buffer + channelSamples;
- if (floatBufferSize > f->outputSampleSize)
- {
- TSF_FREE(f->outputSamples);
- f->outputSamples = (float*)TSF_MALLOC(floatBufferSize);
- f->outputSampleSize = floatBufferSize;
- }
+ float outputSamples[512];
- tsf_render_float(f, f->outputSamples, samples, TSF_FALSE);
+ while (samples > 0) {
+ const int maxChannelSamples = (sizeof(outputSamples) / sizeof(float)) / (f->outputmode == TSF_MONO ? 1 : 2);
+ int channelSamples = samples;
+ if (channelSamples > maxChannelSamples)
+ channelSamples = maxChannelSamples;
+ short* bufferEnd = buffer + channelSamples * (f->outputmode == TSF_MONO ? 1 : 2);
- floatSamples = f->outputSamples;
- if (flag_mixing)
- while (buffer != bufferEnd)
- {
- float v = *floatSamples++;
- int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f)));
- *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi));
- }
- else
- while (buffer != bufferEnd)
- {
- float v = *floatSamples++;
- *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f)));
- }
+ floatSamples = outputSamples;
+ tsf_render_float(f, floatSamples, channelSamples, TSF_FALSE);
+ samples -= channelSamples;
+
+ if (flag_mixing)
+ while (buffer != bufferEnd)
+ {
+ float v = *floatSamples++;
+ int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f)));
+ *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi));
+ }
+ else
+ while (buffer != bufferEnd)
+ {
+ float v = *floatSamples++;
+ *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f)));
+ }
+ }
}
TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing)
@@ -1563,11 +1636,19 @@
if (!f->channels)
{
f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
+ if (!f->channels)
+ return NULL;
f->channels->setupVoice = &tsf_channel_setup_voice;
f->channels->channelNum = 0;
f->channels->activeChannel = 0;
}
- else f->channels = (struct tsf_channels*)TSF_REALLOC(f->channels, sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
+ else
+ {
+ struct tsf_channels *newChannels = (struct tsf_channels*)TSF_REALLOC(f->channels, sizeof(struct tsf_channels) + sizeof(struct tsf_channel) * channel);
+ if (!newChannels)
+ return NULL;
+ f->channels = newChannels;
+ }
i = f->channels->channelNum;
f->channels->channelNum = channel + 1;
for (; i <= channel; i++)
@@ -1595,14 +1676,20 @@
tsf_voice_calcpitchratio(v, pitchShift, f->outSampleRate);
}
-TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index)
+TSFDEF int tsf_channel_set_presetindex(tsf* f, int channel, int preset_index)
{
- tsf_channel_init(f, channel)->presetIndex = (unsigned short)preset_index;
+ struct tsf_channel *c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
+ c->presetIndex = (unsigned short)preset_index;
+ return 1;
}
TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums)
{
struct tsf_channel *c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
int preset_index;
if (flag_mididrums)
{
@@ -1621,14 +1708,20 @@
return 0;
}
-TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank)
+TSFDEF int tsf_channel_set_bank(tsf* f, int channel, int bank)
{
- tsf_channel_init(f, channel)->bank = (unsigned short)bank;
+ struct tsf_channel *c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
+ c->bank = (unsigned short)bank;
+ return 1;
}
TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number)
{
struct tsf_channel *c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
int preset_index = tsf_get_presetindex(f, bank, preset_number);
if (preset_index == -1) return 0;
c->presetIndex = (unsigned short)preset_index;
@@ -1636,7 +1729,7 @@
return 1;
}
-TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan)
+TSFDEF int tsf_channel_set_pan(tsf* f, int channel, float pan)
{
struct tsf_voice *v, *vEnd;
for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++)
@@ -1647,50 +1740,66 @@
else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; }
else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); }
}
- tsf_channel_init(f, channel)->panOffset = pan - 0.5f;
+ struct tsf_channel *c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
+ c->panOffset = pan - 0.5f;
+ return 1;
}
-TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume)
+TSFDEF int tsf_channel_set_volume(tsf* f, int channel, float volume)
{
struct tsf_channel *c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
float gainDB = tsf_gainToDecibels(volume), gainDBChange = gainDB - c->gainDB;
struct tsf_voice *v, *vEnd;
- if (gainDBChange == 0) return;
+ if (gainDBChange == 0) return 1;
for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++)
if (v->playingChannel == channel && v->playingPreset != -1)
v->noteGainDB += gainDBChange;
c->gainDB = gainDB;
+ return 1;
}
-TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel)
+TSFDEF int tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel)
{
struct tsf_channel *c = tsf_channel_init(f, channel);
- if (c->pitchWheel == pitch_wheel) return;
+ if (!c)
+ return 0;
+ if (c->pitchWheel == pitch_wheel) return 1;
c->pitchWheel = (unsigned short)pitch_wheel;
tsf_channel_applypitch(f, channel, c);
+ return 1;
}
-TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range)
+TSFDEF int tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range)
{
struct tsf_channel *c = tsf_channel_init(f, channel);
- if (c->pitchRange == pitch_range) return;
+ if (!c)
+ return 0;
+ if (c->pitchRange == pitch_range) return 1;
c->pitchRange = pitch_range;
if (c->pitchWheel != 8192) tsf_channel_applypitch(f, channel, c);
+ return 1;
}
-TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning)
+TSFDEF int tsf_channel_set_tuning(tsf* f, int channel, float tuning)
{
struct tsf_channel *c = tsf_channel_init(f, channel);
- if (c->tuning == tuning) return;
+ if (!c)
+ return 0;
+ if (c->tuning == tuning) return 1;
c->tuning = tuning;
tsf_channel_applypitch(f, channel, c);
+ return 1;
}
-TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel)
+TSFDEF int tsf_channel_note_on(tsf* f, int channel, int key, float vel)
{
- if (!f->channels || channel >= f->channels->channelNum) return;
+ if (!f->channels || channel >= f->channels->channelNum) return 1;
f->channels->activeChannel = channel;
- tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel);
+ return tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel);
}
TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key)
@@ -1729,9 +1838,11 @@
tsf_voice_endquick(f, v);
}
-TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value)
+TSFDEF int tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value)
{
struct tsf_channel* c = tsf_channel_init(f, channel);
+ if (!c)
+ return 0;
switch (controller)
{
case 7 /*VOLUME_MSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME;
@@ -1742,14 +1853,14 @@
case 42 /*PAN_LSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x3F80) | control_value); goto TCMC_SET_PAN;
case 6 /*DATA_ENTRY_MSB*/ : c->midiData = (unsigned short)((c->midiData & 0x7F) | (control_value << 7)); goto TCMC_SET_DATA;
case 38 /*DATA_ENTRY_LSB*/ : c->midiData = (unsigned short)((c->midiData & 0x3F80) | control_value); goto TCMC_SET_DATA;
- case 0 /*BANK_SELECT_MSB*/ : c->bank = (unsigned short)(0x8000 | control_value); return; //bank select MSB alone acts like LSB
- case 32 /*BANK_SELECT_LSB*/ : c->bank = (unsigned short)((c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value); return;
- case 101 /*RPN_MSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F ) | (control_value << 7)); return;
- case 100 /*RPN_LSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value); return;
- case 98 /*NRPN_LSB*/ : c->midiRPN = 0xFFFF; return;
- case 99 /*NRPN_MSB*/ : c->midiRPN = 0xFFFF; return;
- case 120 /*ALL_SOUND_OFF*/ : tsf_channel_sounds_off_all(f, channel); return;
- case 123 /*ALL_NOTES_OFF*/ : tsf_channel_note_off_all(f, channel); return;
+ case 0 /*BANK_SELECT_MSB*/ : c->bank = (unsigned short)(0x8000 | control_value); return 1; //bank select MSB alone acts like LSB
+ case 32 /*BANK_SELECT_LSB*/ : c->bank = (unsigned short)((c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value); return 1;
+ case 101 /*RPN_MSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F ) | (control_value << 7)); return 1;
+ case 100 /*RPN_LSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value); return 1;
+ case 98 /*NRPN_LSB*/ : c->midiRPN = 0xFFFF; return 1;
+ case 99 /*NRPN_MSB*/ : c->midiRPN = 0xFFFF; return 1;
+ case 120 /*ALL_SOUND_OFF*/ : tsf_channel_sounds_off_all(f, channel); return 1;
+ case 123 /*ALL_NOTES_OFF*/ : tsf_channel_note_off_all(f, channel); return 1;
case 121 /*ALL_CTRL_OFF*/ :
c->midiVolume = c->midiExpression = 16383;
c->midiPan = 8192;
@@ -1760,21 +1871,21 @@
tsf_channel_set_pan(f, channel, 0.5f);
tsf_channel_set_pitchrange(f, channel, 2.0f);
tsf_channel_set_tuning(f, channel, 0);
- return;
+ return 1;
}
- return;
+ return 1;
TCMC_SET_VOLUME:
//Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI
tsf_channel_set_volume(f, channel, TSF_POWF((c->midiVolume / 16383.0f) * (c->midiExpression / 16383.0f), 3.0f));
- return;
+ return 1;
TCMC_SET_PAN:
tsf_channel_set_pan(f, channel, c->midiPan / 16383.0f);
- return;
+ return 1;
TCMC_SET_DATA:
if (c->midiRPN == 0) tsf_channel_set_pitchrange(f, channel, (c->midiData >> 7) + 0.01f * (c->midiData & 0x7F));
else if (c->midiRPN == 1) tsf_channel_set_tuning(f, channel, (int)c->tuning + ((float)c->midiData - 8192.0f) / 8192.0f); //fine tune
else if (c->midiRPN == 2 && controller == 6) tsf_channel_set_tuning(f, channel, ((float)control_value - 64.0f) + (c->tuning - (int)c->tuning)); //coarse tune
- return;
+ return 1;
}
TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel)