fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 04 Jan 2012 00:16:03 -0500
changeset 542 3de4970b36d4
parent 532 b8e8ae4852b2
child 545 32e5ed415a34
permissions -rw-r--r--
Fixed bug 1252 - Added Mix_LoadMUSType_RW() so you can tell SDL_mixer what type the music is

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