fluidsynth.c
author James Le Cuirot <chewi@aura-online.co.uk>
Sun, 20 Mar 2011 14:34:18 +0000
changeset 506 586ae09f8176
child 518 8bc9b5fd2aae
permissions -rw-r--r--
Add FluidSynth backend and generic SoundFont functions. No FluidSynth DLL or headers for MSVC in this commit.
chewi@506
     1
/*
chewi@506
     2
    SDL_mixer:  An audio mixer library based on the SDL library
chewi@506
     3
    Copyright (C) 1997-2011 Sam Lantinga
chewi@506
     4
chewi@506
     5
    This library is free software; you can redistribute it and/or
chewi@506
     6
    modify it under the terms of the GNU Library General Public
chewi@506
     7
    License as published by the Free Software Foundation; either
chewi@506
     8
    version 2 of the License, or (at your option) any later version.
chewi@506
     9
chewi@506
    10
    This library is distributed in the hope that it will be useful,
chewi@506
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
chewi@506
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
chewi@506
    13
    Library General Public License for more details.
chewi@506
    14
chewi@506
    15
    You should have received a copy of the GNU Library General Public
chewi@506
    16
    License along with this library; if not, write to the Free
chewi@506
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
chewi@506
    18
chewi@506
    19
    James Le Cuirot
chewi@506
    20
    chewi@aura-online.co.uk
chewi@506
    21
*/
chewi@506
    22
chewi@506
    23
#include "fluidsynth.h"
chewi@506
    24
#include "SDL_mixer.h"
chewi@506
    25
#include <stdio.h>
chewi@506
    26
#include <sys/types.h>
chewi@506
    27
chewi@506
    28
static Uint16 format;
chewi@506
    29
static Uint8 channels;
chewi@506
    30
static int freq;
chewi@506
    31
chewi@506
    32
int fluidsynth_check_soundfont(const char *path, void *data)
chewi@506
    33
{
chewi@506
    34
	FILE *file = fopen(path, "r");
chewi@506
    35
chewi@506
    36
	if (file) {
chewi@506
    37
		fclose(file);
chewi@506
    38
		return 1;
chewi@506
    39
	} else {
chewi@506
    40
		Mix_SetError("Failed to access the SoundFont %s", path);
chewi@506
    41
		return 0;
chewi@506
    42
	}
chewi@506
    43
}
chewi@506
    44
chewi@506
    45
int fluidsynth_load_soundfont(const char *path, void *data)
chewi@506
    46
{
chewi@506
    47
	/* If this fails, it's too late to try Timidity so pray that at least one works. */
chewi@506
    48
	fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1);
chewi@506
    49
	return 1;
chewi@506
    50
}
chewi@506
    51
chewi@506
    52
int fluidsynth_init(SDL_AudioSpec *mixer)
chewi@506
    53
{
chewi@506
    54
	if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL))
chewi@506
    55
		return -1;
chewi@506
    56
chewi@506
    57
	format = mixer->format;
chewi@506
    58
	channels = mixer->channels;
chewi@506
    59
	freq = mixer->freq;
chewi@506
    60
chewi@506
    61
	return 0;
chewi@506
    62
}
chewi@506
    63
chewi@506
    64
FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data)
chewi@506
    65
{
chewi@506
    66
	FluidSynthMidiSong *song;
chewi@506
    67
	fluid_settings_t *settings = NULL;
chewi@506
    68
chewi@506
    69
	if ((song = malloc(sizeof(FluidSynthMidiSong)))) {
chewi@506
    70
		memset(song, 0, sizeof(FluidSynthMidiSong));
chewi@506
    71
chewi@506
    72
		if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) {
chewi@506
    73
			if ((settings = fluidsynth.new_fluid_settings())) {
chewi@506
    74
				fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq);
chewi@506
    75
chewi@506
    76
				if ((song->synth = fluidsynth.new_fluid_synth(settings))) {
chewi@506
    77
					if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) {
chewi@506
    78
						if ((song->player = fluidsynth.new_fluid_player(song->synth))) {
chewi@506
    79
							if (function(song, data)) return song;
chewi@506
    80
							fluidsynth.delete_fluid_player(song->player);
chewi@506
    81
						} else {
chewi@506
    82
							Mix_SetError("Failed to create FluidSynth player");
chewi@506
    83
						}
chewi@506
    84
					}
chewi@506
    85
					fluidsynth.delete_fluid_synth(song->synth);
chewi@506
    86
				} else {
chewi@506
    87
					Mix_SetError("Failed to create FluidSynth synthesizer");
chewi@506
    88
				}
chewi@506
    89
				fluidsynth.delete_fluid_settings(settings);
chewi@506
    90
			} else {
chewi@506
    91
				Mix_SetError("Failed to create FluidSynth settings");
chewi@506
    92
			}
chewi@506
    93
		} else {
chewi@506
    94
			Mix_SetError("Failed to set up audio conversion");
chewi@506
    95
		}
chewi@506
    96
		free(song);
chewi@506
    97
	} else {
chewi@506
    98
		Mix_SetError("Insufficient memory for song");
chewi@506
    99
	}
chewi@506
   100
	return NULL;
chewi@506
   101
}
chewi@506
   102
chewi@506
   103
int fluidsynth_loadsong_internal(FluidSynthMidiSong *song, void *data)
chewi@506
   104
{
chewi@506
   105
	const char* path = (const char*) data;
chewi@506
   106
chewi@506
   107
	if (fluidsynth.fluid_player_add(song->player, path) == FLUID_OK) {
chewi@506
   108
		return 1;
chewi@506
   109
	} else {
chewi@506
   110
		Mix_SetError("FluidSynth failed to load %s", path);
chewi@506
   111
		return 0;
chewi@506
   112
	}
chewi@506
   113
}
chewi@506
   114
chewi@506
   115
int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data)
chewi@506
   116
{
chewi@506
   117
	off_t offset;
chewi@506
   118
	size_t size;
chewi@506
   119
	char *buffer;
chewi@506
   120
	SDL_RWops *rw = (SDL_RWops*) data;
chewi@506
   121
chewi@506
   122
	offset = SDL_RWtell(rw);
chewi@506
   123
	SDL_RWseek(rw, 0, RW_SEEK_END);
chewi@506
   124
	size = SDL_RWtell(rw) - offset;
chewi@506
   125
	SDL_RWseek(rw, offset, RW_SEEK_SET);
chewi@506
   126
chewi@506
   127
	if ((buffer = (char*) malloc(size))) {
chewi@506
   128
		if(SDL_RWread(rw, buffer, size, 1) == 1) {
chewi@506
   129
			if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) {
chewi@506
   130
				return 1;
chewi@506
   131
			} else {
chewi@506
   132
				Mix_SetError("FluidSynth failed to load in-memory song");
chewi@506
   133
			}
chewi@506
   134
		} else {
chewi@506
   135
			Mix_SetError("Failed to read in-memory song");
chewi@506
   136
		}
chewi@506
   137
		free(buffer);
chewi@506
   138
	} else {
chewi@506
   139
		Mix_SetError("Insufficient memory for song");
chewi@506
   140
	}
chewi@506
   141
	return 0;
chewi@506
   142
}
chewi@506
   143
chewi@506
   144
FluidSynthMidiSong *fluidsynth_loadsong(const char *midifile)
chewi@506
   145
{
chewi@506
   146
	return fluidsynth_loadsong_common(fluidsynth_loadsong_internal, (void*) midifile);
chewi@506
   147
}
chewi@506
   148
chewi@506
   149
FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw)
chewi@506
   150
{
chewi@506
   151
	return fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) rw);
chewi@506
   152
}
chewi@506
   153
chewi@506
   154
void fluidsynth_freesong(FluidSynthMidiSong *song)
chewi@506
   155
{
chewi@506
   156
	if (!song) return;
chewi@506
   157
	fluidsynth.delete_fluid_player(song->player);
chewi@506
   158
	fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth));
chewi@506
   159
	fluidsynth.delete_fluid_synth(song->synth);
chewi@506
   160
	free(song);
chewi@506
   161
}
chewi@506
   162
chewi@506
   163
void fluidsynth_start(FluidSynthMidiSong *song)
chewi@506
   164
{
chewi@506
   165
	fluidsynth.fluid_player_set_loop(song->player, 1);
chewi@506
   166
	fluidsynth.fluid_player_play(song->player);
chewi@506
   167
}
chewi@506
   168
chewi@506
   169
void fluidsynth_stop(FluidSynthMidiSong *song)
chewi@506
   170
{
chewi@506
   171
	fluidsynth.fluid_player_stop(song->player);
chewi@506
   172
}
chewi@506
   173
chewi@506
   174
int fluidsynth_active(FluidSynthMidiSong *song)
chewi@506
   175
{
chewi@506
   176
	return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? 1 : 0;
chewi@506
   177
}
chewi@506
   178
chewi@506
   179
void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume)
chewi@506
   180
{
chewi@506
   181
	/* FluidSynth's default is 0.2. Make 0.8 the maximum. */
chewi@506
   182
	fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 0.00625));
chewi@506
   183
}
chewi@506
   184
chewi@506
   185
int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len)
chewi@506
   186
{
chewi@506
   187
	int result = -1;
chewi@506
   188
	int frames = dest_len / channels / ((format & 0xFF) / 8);
chewi@506
   189
	int src_len = frames * 4; /* 16-bit stereo */
chewi@506
   190
	void *src = dest;
chewi@506
   191
chewi@506
   192
	if (dest_len < src_len) {
chewi@506
   193
		if (!(src = malloc(src_len))) {
chewi@506
   194
			Mix_SetError("Insufficient memory for audio conversion");
chewi@506
   195
			return result;
chewi@506
   196
		}
chewi@506
   197
	}
chewi@506
   198
chewi@506
   199
	if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
chewi@506
   200
		Mix_SetError("Error generating FluidSynth audio");
chewi@506
   201
		goto finish;
chewi@506
   202
	}
chewi@506
   203
chewi@506
   204
	song->convert.buf = src;
chewi@506
   205
	song->convert.len = src_len;
chewi@506
   206
chewi@506
   207
	if (SDL_ConvertAudio(&song->convert) < 0) {
chewi@506
   208
		Mix_SetError("Error during audio conversion");
chewi@506
   209
		goto finish;
chewi@506
   210
	}
chewi@506
   211
chewi@506
   212
	if (src != dest)
chewi@506
   213
		memcpy(dest, src, dest_len);
chewi@506
   214
chewi@506
   215
	result = 0;
chewi@506
   216
chewi@506
   217
finish:
chewi@506
   218
	if (src != dest)
chewi@506
   219
		free(src);
chewi@506
   220
chewi@506
   221
	return result;
chewi@506
   222
}