shithub: sf2mid

Download patch

ref: 77dde8fdc4e1fc618105c7acede22dacbf51fda0
parent: 472abcff8be97ff23f8196412041624fc3e34ce4
parent: f9fe5cb507f5ae7a05ba80ab782dabcd8af55fc9
author: Bernhard Schelling <[email protected]>
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)