fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 13 Jan 2012 03:15:19 -0500
changeset 561 87bdb4c81c0b
parent 545 32e5ed415a34
child 585 f2943eac3fd7
permissions -rw-r--r--
Fixed memory crash loading Ogg Vorbis files on Windows
The pointer to the audio data could come from SDL_LoadWAV_RW() or from our other loaders, and we need to use the correct free() to release the memory. So we'll just use the SDL memory functions for consistency.
This pretty much only matters on Windows where we can't guarantee a certain C runtime so we provide our own malloc() and friends.
chewi@506
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@518
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
chewi@506
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
chewi@506
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
chewi@506
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
chewi@506
    20
slouken@518
    21
  James Le Cuirot
slouken@518
    22
  chewi@aura-online.co.uk
chewi@506
    23
*/
chewi@506
    24
slouken@532
    25
#ifdef USE_FLUIDSYNTH_MIDI
slouken@532
    26
chewi@506
    27
#include <stdio.h>
chewi@506
    28
#include <sys/types.h>
chewi@506
    29
slouken@532
    30
#include "SDL_mixer.h"
slouken@532
    31
#include "fluidsynth.h"
slouken@532
    32
chewi@506
    33
static Uint16 format;
chewi@506
    34
static Uint8 channels;
chewi@506
    35
static int freq;
chewi@506
    36
chewi@506
    37
int fluidsynth_check_soundfont(const char *path, void *data)
chewi@506
    38
{
chewi@506
    39
	FILE *file = fopen(path, "r");
chewi@506
    40
chewi@506
    41
	if (file) {
chewi@506
    42
		fclose(file);
chewi@506
    43
		return 1;
chewi@506
    44
	} else {
chewi@506
    45
		Mix_SetError("Failed to access the SoundFont %s", path);
chewi@506
    46
		return 0;
chewi@506
    47
	}
chewi@506
    48
}
chewi@506
    49
chewi@506
    50
int fluidsynth_load_soundfont(const char *path, void *data)
chewi@506
    51
{
chewi@506
    52
	/* If this fails, it's too late to try Timidity so pray that at least one works. */
chewi@506
    53
	fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1);
chewi@506
    54
	return 1;
chewi@506
    55
}
chewi@506
    56
chewi@506
    57
int fluidsynth_init(SDL_AudioSpec *mixer)
chewi@506
    58
{
chewi@506
    59
	if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL))
chewi@506
    60
		return -1;
chewi@506
    61
chewi@506
    62
	format = mixer->format;
chewi@506
    63
	channels = mixer->channels;
chewi@506
    64
	freq = mixer->freq;
chewi@506
    65
chewi@506
    66
	return 0;
chewi@506
    67
}
chewi@506
    68
slouken@542
    69
static FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data)
chewi@506
    70
{
chewi@506
    71
	FluidSynthMidiSong *song;
chewi@506
    72
	fluid_settings_t *settings = NULL;
chewi@506
    73
slouken@545
    74
	if (!Mix_Init(MIX_INIT_FLUIDSYNTH)) {
slouken@545
    75
		return NULL;
slouken@545
    76
	}
slouken@545
    77
slouken@561
    78
	if ((song = SDL_malloc(sizeof(FluidSynthMidiSong)))) {
chewi@506
    79
		memset(song, 0, sizeof(FluidSynthMidiSong));
chewi@506
    80
chewi@506
    81
		if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) {
chewi@506
    82
			if ((settings = fluidsynth.new_fluid_settings())) {
chewi@506
    83
				fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq);
chewi@506
    84
chewi@506
    85
				if ((song->synth = fluidsynth.new_fluid_synth(settings))) {
chewi@506
    86
					if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) {
chewi@506
    87
						if ((song->player = fluidsynth.new_fluid_player(song->synth))) {
chewi@506
    88
							if (function(song, data)) return song;
chewi@506
    89
							fluidsynth.delete_fluid_player(song->player);
chewi@506
    90
						} else {
chewi@506
    91
							Mix_SetError("Failed to create FluidSynth player");
chewi@506
    92
						}
chewi@506
    93
					}
chewi@506
    94
					fluidsynth.delete_fluid_synth(song->synth);
chewi@506
    95
				} else {
chewi@506
    96
					Mix_SetError("Failed to create FluidSynth synthesizer");
chewi@506
    97
				}
chewi@506
    98
				fluidsynth.delete_fluid_settings(settings);
chewi@506
    99
			} else {
chewi@506
   100
				Mix_SetError("Failed to create FluidSynth settings");
chewi@506
   101
			}
chewi@506
   102
		} else {
chewi@506
   103
			Mix_SetError("Failed to set up audio conversion");
chewi@506
   104
		}
slouken@561
   105
		SDL_free(song);
chewi@506
   106
	} else {
chewi@506
   107
		Mix_SetError("Insufficient memory for song");
chewi@506
   108
	}
chewi@506
   109
	return NULL;
chewi@506
   110
}
chewi@506
   111
slouken@542
   112
static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data)
chewi@506
   113
{
chewi@506
   114
	off_t offset;
chewi@506
   115
	size_t size;
chewi@506
   116
	char *buffer;
chewi@506
   117
	SDL_RWops *rw = (SDL_RWops*) data;
chewi@506
   118
chewi@506
   119
	offset = SDL_RWtell(rw);
chewi@506
   120
	SDL_RWseek(rw, 0, RW_SEEK_END);
chewi@506
   121
	size = SDL_RWtell(rw) - offset;
chewi@506
   122
	SDL_RWseek(rw, offset, RW_SEEK_SET);
chewi@506
   123
slouken@561
   124
	if ((buffer = (char*) SDL_malloc(size))) {
chewi@506
   125
		if(SDL_RWread(rw, buffer, size, 1) == 1) {
chewi@506
   126
			if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) {
chewi@506
   127
				return 1;
chewi@506
   128
			} else {
chewi@506
   129
				Mix_SetError("FluidSynth failed to load in-memory song");
chewi@506
   130
			}
chewi@506
   131
		} else {
chewi@506
   132
			Mix_SetError("Failed to read in-memory song");
chewi@506
   133
		}
slouken@561
   134
		SDL_free(buffer);
chewi@506
   135
	} else {
chewi@506
   136
		Mix_SetError("Insufficient memory for song");
chewi@506
   137
	}
chewi@506
   138
	return 0;
chewi@506
   139
}
chewi@506
   140
slouken@521
   141
FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw, int freerw)
chewi@506
   142
{
slouken@521
   143
	FluidSynthMidiSong *song;
slouken@521
   144
slouken@521
   145
	song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) rw);
slouken@521
   146
	if (freerw) {
slouken@521
   147
		SDL_RWclose(rw);
slouken@521
   148
	}
slouken@521
   149
	return song;
chewi@506
   150
}
chewi@506
   151
chewi@506
   152
void fluidsynth_freesong(FluidSynthMidiSong *song)
chewi@506
   153
{
chewi@506
   154
	if (!song) return;
chewi@506
   155
	fluidsynth.delete_fluid_player(song->player);
chewi@506
   156
	fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth));
chewi@506
   157
	fluidsynth.delete_fluid_synth(song->synth);
slouken@561
   158
	SDL_free(song);
chewi@506
   159
}
chewi@506
   160
chewi@506
   161
void fluidsynth_start(FluidSynthMidiSong *song)
chewi@506
   162
{
chewi@506
   163
	fluidsynth.fluid_player_set_loop(song->player, 1);
chewi@506
   164
	fluidsynth.fluid_player_play(song->player);
chewi@506
   165
}
chewi@506
   166
chewi@506
   167
void fluidsynth_stop(FluidSynthMidiSong *song)
chewi@506
   168
{
chewi@506
   169
	fluidsynth.fluid_player_stop(song->player);
chewi@506
   170
}
chewi@506
   171
chewi@506
   172
int fluidsynth_active(FluidSynthMidiSong *song)
chewi@506
   173
{
chewi@506
   174
	return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? 1 : 0;
chewi@506
   175
}
chewi@506
   176
chewi@506
   177
void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume)
chewi@506
   178
{
chewi@506
   179
	/* FluidSynth's default is 0.2. Make 0.8 the maximum. */
chewi@506
   180
	fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 0.00625));
chewi@506
   181
}
chewi@506
   182
chewi@506
   183
int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len)
chewi@506
   184
{
chewi@506
   185
	int result = -1;
chewi@506
   186
	int frames = dest_len / channels / ((format & 0xFF) / 8);
chewi@506
   187
	int src_len = frames * 4; /* 16-bit stereo */
chewi@506
   188
	void *src = dest;
chewi@506
   189
chewi@506
   190
	if (dest_len < src_len) {
slouken@561
   191
		if (!(src = SDL_malloc(src_len))) {
chewi@506
   192
			Mix_SetError("Insufficient memory for audio conversion");
chewi@506
   193
			return result;
chewi@506
   194
		}
chewi@506
   195
	}
chewi@506
   196
chewi@506
   197
	if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
chewi@506
   198
		Mix_SetError("Error generating FluidSynth audio");
chewi@506
   199
		goto finish;
chewi@506
   200
	}
chewi@506
   201
chewi@506
   202
	song->convert.buf = src;
chewi@506
   203
	song->convert.len = src_len;
chewi@506
   204
chewi@506
   205
	if (SDL_ConvertAudio(&song->convert) < 0) {
chewi@506
   206
		Mix_SetError("Error during audio conversion");
chewi@506
   207
		goto finish;
chewi@506
   208
	}
chewi@506
   209
chewi@506
   210
	if (src != dest)
chewi@506
   211
		memcpy(dest, src, dest_len);
chewi@506
   212
chewi@506
   213
	result = 0;
chewi@506
   214
chewi@506
   215
finish:
chewi@506
   216
	if (src != dest)
slouken@561
   217
		SDL_free(src);
chewi@506
   218
chewi@506
   219
	return result;
chewi@506
   220
}
slouken@532
   221
slouken@532
   222
#endif /* USE_FLUIDSYNTH_MIDI */