1 /*
2   SDL_mixer:  An audio mixer library based on the SDL library
3   Copyright (C) 1997-2018 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 MUSIC_MID_FLUIDSYNTH
26 
27 #include <stdio.h>
28 
29 #include "SDL_loadso.h"
30 
31 #include "music_fluidsynth.h"
32 
33 #include <fluidsynth.h>
34 
35 
36 typedef struct {
37     int loaded;
38     void *handle;
39 
40     int (*delete_fluid_player)(fluid_player_t*);
41     void (*delete_fluid_settings)(fluid_settings_t*);
42     int (*delete_fluid_synth)(fluid_synth_t*);
43     int (*fluid_player_add)(fluid_player_t*, const char*);
44     int (*fluid_player_add_mem)(fluid_player_t*, const void*, size_t);
45     int (*fluid_player_get_status)(fluid_player_t*);
46     int (*fluid_player_play)(fluid_player_t*);
47     int (*fluid_player_set_loop)(fluid_player_t*, int);
48     int (*fluid_player_stop)(fluid_player_t*);
49     int (*fluid_settings_setnum)(fluid_settings_t*, const char*, double);
50     fluid_settings_t* (*fluid_synth_get_settings)(fluid_synth_t*);
51     void (*fluid_synth_set_gain)(fluid_synth_t*, float);
52     int (*fluid_synth_sfload)(fluid_synth_t*, const char*, int);
53     int (*fluid_synth_write_s16)(fluid_synth_t*, int, void*, int, int, void*, int, int);
54     fluid_player_t* (*new_fluid_player)(fluid_synth_t*);
55     fluid_settings_t* (*new_fluid_settings)(void);
56     fluid_synth_t* (*new_fluid_synth)(fluid_settings_t*);
57 } fluidsynth_loader;
58 
59 static fluidsynth_loader fluidsynth = {
60     0, NULL
61 };
62 
63 #ifdef FLUIDSYNTH_DYNAMIC
64 #define FUNCTION_LOADER(FUNC, SIG) \
65     fluidsynth.FUNC = (SIG) SDL_LoadFunction(fluidsynth.handle, #FUNC); \
66     if (fluidsynth.FUNC == NULL) { SDL_UnloadObject(fluidsynth.handle); return -1; }
67 #else
68 #define FUNCTION_LOADER(FUNC, SIG) \
69     fluidsynth.FUNC = FUNC;
70 #endif
71 
FLUIDSYNTH_Load()72 static int FLUIDSYNTH_Load()
73 {
74     if (fluidsynth.loaded == 0) {
75 #ifdef FLUIDSYNTH_DYNAMIC
76         fluidsynth.handle = SDL_LoadObject(FLUIDSYNTH_DYNAMIC);
77         if (fluidsynth.handle == NULL) {
78             return -1;
79         }
80 #endif
81         FUNCTION_LOADER(delete_fluid_player, int (*)(fluid_player_t*))
82         FUNCTION_LOADER(delete_fluid_settings, void (*)(fluid_settings_t*))
83         FUNCTION_LOADER(delete_fluid_synth, int (*)(fluid_synth_t*))
84         FUNCTION_LOADER(fluid_player_add, int (*)(fluid_player_t*, const char*))
85         FUNCTION_LOADER(fluid_player_add_mem, int (*)(fluid_player_t*, const void*, size_t))
86         FUNCTION_LOADER(fluid_player_get_status, int (*)(fluid_player_t*))
87         FUNCTION_LOADER(fluid_player_play, int (*)(fluid_player_t*))
88         FUNCTION_LOADER(fluid_player_set_loop, int (*)(fluid_player_t*, int))
89         FUNCTION_LOADER(fluid_player_stop, int (*)(fluid_player_t*))
90         FUNCTION_LOADER(fluid_settings_setnum, int (*)(fluid_settings_t*, const char*, double))
91         FUNCTION_LOADER(fluid_synth_get_settings, fluid_settings_t* (*)(fluid_synth_t*))
92         FUNCTION_LOADER(fluid_synth_set_gain, void (*)(fluid_synth_t*, float))
93         FUNCTION_LOADER(fluid_synth_sfload, int(*)(fluid_synth_t*, const char*, int))
94         FUNCTION_LOADER(fluid_synth_write_s16, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int))
95         FUNCTION_LOADER(new_fluid_player, fluid_player_t* (*)(fluid_synth_t*))
96         FUNCTION_LOADER(new_fluid_settings, fluid_settings_t* (*)(void))
97         FUNCTION_LOADER(new_fluid_synth, fluid_synth_t* (*)(fluid_settings_t*))
98     }
99     ++fluidsynth.loaded;
100 
101     return 0;
102 }
103 
FLUIDSYNTH_Unload()104 static void FLUIDSYNTH_Unload()
105 {
106     if (fluidsynth.loaded == 0) {
107         return;
108     }
109     if (fluidsynth.loaded == 1) {
110 #ifdef FLUIDSYNTH_DYNAMIC
111         SDL_UnloadObject(fluidsynth.handle);
112 #endif
113     }
114     --fluidsynth.loaded;
115 }
116 
117 
118 typedef struct {
119     fluid_synth_t *synth;
120     fluid_player_t *player;
121     SDL_AudioStream *stream;
122     void *buffer;
123     int buffer_size;
124 } FLUIDSYNTH_Music;
125 
126 
fluidsynth_check_soundfont(const char * path,void * data)127 static int SDLCALL fluidsynth_check_soundfont(const char *path, void *data)
128 {
129     FILE *file = fopen(path, "r");
130 
131     if (file) {
132         fclose(file);
133         return 1;
134     } else {
135         Mix_SetError("Failed to access the SoundFont %s", path);
136         return 0;
137     }
138 }
139 
fluidsynth_load_soundfont(const char * path,void * data)140 static int SDLCALL fluidsynth_load_soundfont(const char *path, void *data)
141 {
142     /* If this fails, it's too late to try Timidity so pray that at least one works. */
143     fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1);
144     return 1;
145 }
146 
FLUIDSYNTH_Open(const SDL_AudioSpec * spec)147 static int FLUIDSYNTH_Open(const SDL_AudioSpec *spec)
148 {
149     if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) {
150         return -1;
151     }
152     return 0;
153 }
154 
FLUIDSYNTH_LoadMusic(void * data)155 static FLUIDSYNTH_Music *FLUIDSYNTH_LoadMusic(void *data)
156 {
157     SDL_RWops *src = (SDL_RWops *)data;
158     FLUIDSYNTH_Music *music;
159     fluid_settings_t *settings;
160 
161     if ((music = SDL_calloc(1, sizeof(FLUIDSYNTH_Music)))) {
162         int channels = 2;
163         if ((music->stream = SDL_NewAudioStream(AUDIO_S16SYS, channels, music_spec.freq, music_spec.format, music_spec.channels, music_spec.freq))) {
164             music->buffer_size = music_spec.samples * sizeof(Sint16) * channels;
165             if ((music->buffer = SDL_malloc(music->buffer_size))) {
166                 if ((settings = fluidsynth.new_fluid_settings())) {
167                     fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) music_spec.freq);
168 
169                     if ((music->synth = fluidsynth.new_fluid_synth(settings))) {
170                         if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) music->synth)) {
171                             if ((music->player = fluidsynth.new_fluid_player(music->synth))) {
172                                 void *buffer;
173                                 size_t size;
174 
175                                 buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE);
176                                 if (buffer) {
177                                     if (fluidsynth.fluid_player_add_mem(music->player, buffer, size) == FLUID_OK) {
178                                         SDL_free(buffer);
179                                         return music;
180                                     } else {
181                                         Mix_SetError("FluidSynth failed to load in-memory song");
182                                     }
183                                     SDL_free(buffer);
184                                 } else {
185                                     SDL_OutOfMemory();
186                                 }
187                                 fluidsynth.delete_fluid_player(music->player);
188                             } else {
189                                 Mix_SetError("Failed to create FluidSynth player");
190                             }
191                         }
192                         fluidsynth.delete_fluid_synth(music->synth);
193                     } else {
194                         Mix_SetError("Failed to create FluidSynth synthesizer");
195                     }
196                     fluidsynth.delete_fluid_settings(settings);
197                 } else {
198                     Mix_SetError("Failed to create FluidSynth settings");
199                 }
200             } else {
201                 SDL_OutOfMemory();
202             }
203         }
204         SDL_free(music);
205     } else {
206         SDL_OutOfMemory();
207     }
208     return NULL;
209 }
210 
FLUIDSYNTH_CreateFromRW(SDL_RWops * src,int freesrc)211 static void *FLUIDSYNTH_CreateFromRW(SDL_RWops *src, int freesrc)
212 {
213     FLUIDSYNTH_Music *music;
214 
215     music = FLUIDSYNTH_LoadMusic(src);
216     if (music && freesrc) {
217         SDL_RWclose(src);
218     }
219     return music;
220 }
221 
FLUIDSYNTH_SetVolume(void * context,int volume)222 static void FLUIDSYNTH_SetVolume(void *context, int volume)
223 {
224     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
225     /* FluidSynth's default is 0.2. Make 1.2 the maximum. */
226     fluidsynth.fluid_synth_set_gain(music->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME));
227 }
228 
FLUIDSYNTH_Play(void * context,int play_count)229 static int FLUIDSYNTH_Play(void *context, int play_count)
230 {
231     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
232     fluidsynth.fluid_player_set_loop(music->player, play_count);
233     fluidsynth.fluid_player_play(music->player);
234     return 0;
235 }
236 
FLUIDSYNTH_IsPlaying(void * context)237 static SDL_bool FLUIDSYNTH_IsPlaying(void *context)
238 {
239     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
240     return fluidsynth.fluid_player_get_status(music->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE;
241 }
242 
FLUIDSYNTH_GetSome(void * context,void * data,int bytes,SDL_bool * done)243 static int FLUIDSYNTH_GetSome(void *context, void *data, int bytes, SDL_bool *done)
244 {
245     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
246     int filled;
247 
248     filled = SDL_AudioStreamGet(music->stream, data, bytes);
249     if (filled != 0) {
250         return filled;
251     }
252 
253     if (fluidsynth.fluid_synth_write_s16(music->synth, music_spec.samples, music->buffer, 0, 2, music->buffer, 1, 2) != FLUID_OK) {
254         Mix_SetError("Error generating FluidSynth audio");
255         return -1;
256     }
257     if (SDL_AudioStreamPut(music->stream, music->buffer, music->buffer_size) < 0) {
258         return -1;
259     }
260     return 0;
261 }
FLUIDSYNTH_GetAudio(void * context,void * data,int bytes)262 static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes)
263 {
264     return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, FLUIDSYNTH_GetSome);
265 }
266 
FLUIDSYNTH_Stop(void * context)267 static void FLUIDSYNTH_Stop(void *context)
268 {
269     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
270     fluidsynth.fluid_player_stop(music->player);
271 }
272 
FLUIDSYNTH_Delete(void * context)273 static void FLUIDSYNTH_Delete(void *context)
274 {
275     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
276     fluidsynth.delete_fluid_player(music->player);
277     fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(music->synth));
278     fluidsynth.delete_fluid_synth(music->synth);
279     SDL_free(music);
280 }
281 
282 Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH =
283 {
284     "FLUIDSYNTH",
285     MIX_MUSIC_FLUIDSYNTH,
286     MUS_MID,
287     SDL_FALSE,
288     SDL_FALSE,
289 
290     FLUIDSYNTH_Load,
291     FLUIDSYNTH_Open,
292     FLUIDSYNTH_CreateFromRW,
293     NULL,   /* CreateFromFile */
294     FLUIDSYNTH_SetVolume,
295     FLUIDSYNTH_Play,
296     FLUIDSYNTH_IsPlaying,
297     FLUIDSYNTH_GetAudio,
298     NULL,   /* Seek */
299     NULL,   /* Pause */
300     NULL,   /* Resume */
301     FLUIDSYNTH_Stop,
302     FLUIDSYNTH_Delete,
303     NULL,   /* Close */
304     FLUIDSYNTH_Unload,
305 };
306 
307 #endif /* MUSIC_MID_FLUIDSYNTH */
308 
309 /* vi: set ts=4 sw=4 expandtab: */
310