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