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
fluidsynth_check_soundfont(const char * path,void * data)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
fluidsynth_load_soundfont(const char * path,void * data)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
fluidsynth_init(SDL_AudioSpec * mixer)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
fluidsynth_loadsong_common(int (* function)(FluidSynthMidiSong *,void *),void * data)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 (!Mix_Init(MIX_INIT_FLUIDSYNTH)) {
75 return NULL;
76 }
77
78 if ((song = SDL_malloc(sizeof(FluidSynthMidiSong)))) {
79 memset(song, 0, sizeof(FluidSynthMidiSong));
80
81 if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) {
82 if ((settings = fluidsynth.new_fluid_settings())) {
83 fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq);
84
85 if ((song->synth = fluidsynth.new_fluid_synth(settings))) {
86 if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) {
87 if ((song->player = fluidsynth.new_fluid_player(song->synth))) {
88 if (function(song, data)) return song;
89 fluidsynth.delete_fluid_player(song->player);
90 } else {
91 Mix_SetError("Failed to create FluidSynth player");
92 }
93 }
94 fluidsynth.delete_fluid_synth(song->synth);
95 } else {
96 Mix_SetError("Failed to create FluidSynth synthesizer");
97 }
98 fluidsynth.delete_fluid_settings(settings);
99 } else {
100 Mix_SetError("Failed to create FluidSynth settings");
101 }
102 } else {
103 Mix_SetError("Failed to set up audio conversion");
104 }
105 SDL_free(song);
106 } else {
107 Mix_SetError("Insufficient memory for song");
108 }
109 return NULL;
110 }
111
fluidsynth_loadsong_RW_internal(FluidSynthMidiSong * song,void * data)112 static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data)
113 {
114 off_t offset;
115 size_t size;
116 char *buffer;
117 SDL_RWops *rw = (SDL_RWops*) data;
118
119 offset = SDL_RWtell(rw);
120 SDL_RWseek(rw, 0, RW_SEEK_END);
121 size = SDL_RWtell(rw) - offset;
122 SDL_RWseek(rw, offset, RW_SEEK_SET);
123
124 if ((buffer = (char*) SDL_malloc(size))) {
125 if(SDL_RWread(rw, buffer, size, 1) == 1) {
126 if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) {
127 return 1;
128 } else {
129 Mix_SetError("FluidSynth failed to load in-memory song");
130 }
131 } else {
132 Mix_SetError("Failed to read in-memory song");
133 }
134 SDL_free(buffer);
135 } else {
136 Mix_SetError("Insufficient memory for song");
137 }
138 return 0;
139 }
140
fluidsynth_loadsong_RW(SDL_RWops * rw,int freerw)141 FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *rw, int freerw)
142 {
143 FluidSynthMidiSong *song;
144
145 song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) rw);
146 if (freerw) {
147 SDL_RWclose(rw);
148 }
149 return song;
150 }
151
fluidsynth_freesong(FluidSynthMidiSong * song)152 void fluidsynth_freesong(FluidSynthMidiSong *song)
153 {
154 if (!song) return;
155 fluidsynth.delete_fluid_player(song->player);
156 fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth));
157 fluidsynth.delete_fluid_synth(song->synth);
158 SDL_free(song);
159 }
160
fluidsynth_start(FluidSynthMidiSong * song)161 void fluidsynth_start(FluidSynthMidiSong *song)
162 {
163 fluidsynth.fluid_player_set_loop(song->player, 1);
164 fluidsynth.fluid_player_play(song->player);
165 }
166
fluidsynth_stop(FluidSynthMidiSong * song)167 void fluidsynth_stop(FluidSynthMidiSong *song)
168 {
169 fluidsynth.fluid_player_stop(song->player);
170 }
171
fluidsynth_active(FluidSynthMidiSong * song)172 int fluidsynth_active(FluidSynthMidiSong *song)
173 {
174 return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? 1 : 0;
175 }
176
fluidsynth_setvolume(FluidSynthMidiSong * song,int volume)177 void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume)
178 {
179 /* FluidSynth's default is 0.2. Make 0.8 the maximum. */
180 fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 0.00625));
181 }
182
fluidsynth_playsome(FluidSynthMidiSong * song,void * dest,int dest_len)183 int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len)
184 {
185 int result = -1;
186 int frames = dest_len / channels / ((format & 0xFF) / 8);
187 int src_len = frames * 4; /* 16-bit stereo */
188 void *src = dest;
189
190 if (dest_len < src_len) {
191 if (!(src = SDL_malloc(src_len))) {
192 Mix_SetError("Insufficient memory for audio conversion");
193 return result;
194 }
195 }
196
197 if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
198 Mix_SetError("Error generating FluidSynth audio");
199 goto finish;
200 }
201
202 song->convert.buf = src;
203 song->convert.len = src_len;
204
205 if (SDL_ConvertAudio(&song->convert) < 0) {
206 Mix_SetError("Error during audio conversion");
207 goto finish;
208 }
209
210 if (src != dest)
211 memcpy(dest, src, dest_len);
212
213 result = 0;
214
215 finish:
216 if (src != dest)
217 SDL_free(src);
218
219 return result;
220 }
221
222 #endif /* USE_FLUIDSYNTH_MIDI */
223