music_fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 20 Oct 2017 23:39:04 -0700
changeset 797 b4b6adff699a
parent 786 12a659c8b902
child 798 9b6d7d1b3a23
permissions -rw-r--r--
Switched to using SDL_AudioStream in SDL 2.0.7 for better streaming resampling support
Also made the following changes:
* Moved looping support into the music interfaces for better seamless looping when resampling
* Music interfaces always start play from the beginning of the song
* Implemented 24-bit and surround sound support for FLAC audio files
* Implemented sample dithering in MAD music interface using GPL code
* Improved error checking in music interfaces
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 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 
    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 
    82         FUNCTION_LOADER(delete_fluid_player, int (*)(fluid_player_t*))
    83         FUNCTION_LOADER(delete_fluid_settings, void (*)(fluid_settings_t*))
    84         FUNCTION_LOADER(delete_fluid_synth, int (*)(fluid_synth_t*))
    85         FUNCTION_LOADER(fluid_player_add, int (*)(fluid_player_t*, const char*))
    86         FUNCTION_LOADER(fluid_player_add_mem, int (*)(fluid_player_t*, const void*, size_t))
    87         FUNCTION_LOADER(fluid_player_get_status, int (*)(fluid_player_t*))
    88         FUNCTION_LOADER(fluid_player_play, int (*)(fluid_player_t*))
    89         FUNCTION_LOADER(fluid_player_set_loop, int (*)(fluid_player_t*, int))
    90         FUNCTION_LOADER(fluid_player_stop, int (*)(fluid_player_t*))
    91         FUNCTION_LOADER(fluid_settings_setnum, int (*)(fluid_settings_t*, const char*, double))
    92         FUNCTION_LOADER(fluid_synth_get_settings, fluid_settings_t* (*)(fluid_synth_t*))
    93         FUNCTION_LOADER(fluid_synth_set_gain, void (*)(fluid_synth_t*, float))
    94         FUNCTION_LOADER(fluid_synth_sfload, int(*)(fluid_synth_t*, const char*, int))
    95         FUNCTION_LOADER(fluid_synth_write_s16, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int))
    96         FUNCTION_LOADER(new_fluid_player, fluid_player_t* (*)(fluid_synth_t*))
    97         FUNCTION_LOADER(new_fluid_settings, fluid_settings_t* (*)(void))
    98         FUNCTION_LOADER(new_fluid_synth, fluid_synth_t* (*)(fluid_settings_t*))
    99     }
   100     ++fluidsynth.loaded;
   101 
   102     return 0;
   103 }
   104 
   105 static void FLUIDSYNTH_Unload()
   106 {
   107     if (fluidsynth.loaded == 0) {
   108         return;
   109     }
   110     if (fluidsynth.loaded == 1) {
   111 #ifdef FLUIDSYNTH_DYNAMIC
   112         SDL_UnloadObject(fluidsynth.handle);
   113 #endif
   114     }
   115     --fluidsynth.loaded;
   116 }
   117 
   118 
   119 typedef struct {
   120     fluid_synth_t *synth;
   121     fluid_player_t *player;
   122     SDL_AudioStream *stream;
   123     void *buffer;
   124     int buffer_size;
   125 } FLUIDSYNTH_Music;
   126 
   127 
   128 static int fluidsynth_check_soundfont(const char *path, void *data)
   129 {
   130     FILE *file = fopen(path, "r");
   131 
   132     if (file) {
   133         fclose(file);
   134         return 1;
   135     } else {
   136         Mix_SetError("Failed to access the SoundFont %s", path);
   137         return 0;
   138     }
   139 }
   140 
   141 static int fluidsynth_load_soundfont(const char *path, void *data)
   142 {
   143     /* If this fails, it's too late to try Timidity so pray that at least one works. */
   144     fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1);
   145     return 1;
   146 }
   147 
   148 static int FLUIDSYNTH_Open(const SDL_AudioSpec *spec)
   149 {
   150     if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) {
   151         return -1;
   152     }
   153     return 0;
   154 }
   155 
   156 static FLUIDSYNTH_Music *FLUIDSYNTH_LoadMusic(void *data)
   157 {
   158     SDL_RWops *src = (SDL_RWops *)data;
   159     FLUIDSYNTH_Music *music;
   160     fluid_settings_t *settings;
   161 
   162     if ((music = SDL_calloc(1, sizeof(FLUIDSYNTH_Music)))) {
   163         int channels = 2;
   164         if ((music->stream = SDL_NewAudioStream(AUDIO_S16SYS, channels, music_spec.freq, music_spec.format, music_spec.channels, music_spec.freq))) {
   165             music->buffer_size = music_spec.samples * sizeof(Sint16) * channels;
   166             if ((music->buffer = SDL_malloc(music->buffer_size))) {
   167                 if ((settings = fluidsynth.new_fluid_settings())) {
   168                     fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) music_spec.freq);
   169 
   170                     if ((music->synth = fluidsynth.new_fluid_synth(settings))) {
   171                         if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) music->synth)) {
   172                             if ((music->player = fluidsynth.new_fluid_player(music->synth))) {
   173                                 void *buffer;
   174                                 size_t size;
   175 
   176                                 buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE);
   177                                 if (buffer) {
   178                                     if (fluidsynth.fluid_player_add_mem(music->player, buffer, size) == FLUID_OK) {
   179                                         SDL_free(buffer);
   180                                         return music;
   181                                     } else {
   182                                         Mix_SetError("FluidSynth failed to load in-memory song");
   183                                     }
   184                                     SDL_free(buffer);
   185                                 } else {
   186                                     SDL_OutOfMemory();
   187                                 }
   188                                 fluidsynth.delete_fluid_player(music->player);
   189                             } else {
   190                                 Mix_SetError("Failed to create FluidSynth player");
   191                             }
   192                         }
   193                         fluidsynth.delete_fluid_synth(music->synth);
   194                     } else {
   195                         Mix_SetError("Failed to create FluidSynth synthesizer");
   196                     }
   197                     fluidsynth.delete_fluid_settings(settings);
   198                 } else {
   199                     Mix_SetError("Failed to create FluidSynth settings");
   200                 }
   201             } else {
   202                 SDL_OutOfMemory();
   203             }
   204         }
   205         SDL_free(music);
   206     } else {
   207         SDL_OutOfMemory();
   208     }
   209     return NULL;
   210 }
   211 
   212 static void *FLUIDSYNTH_CreateFromRW(SDL_RWops *src, int freesrc)
   213 {
   214     FLUIDSYNTH_Music *music;
   215 
   216     music = FLUIDSYNTH_LoadMusic(src);
   217     if (music && freesrc) {
   218         SDL_RWclose(src);
   219     }
   220     return music;
   221 }
   222 
   223 static void FLUIDSYNTH_SetVolume(void *context, int volume)
   224 {
   225     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
   226     /* FluidSynth's default is 0.2. Make 1.2 the maximum. */
   227     fluidsynth.fluid_synth_set_gain(music->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME));
   228 }
   229 
   230 static int FLUIDSYNTH_Play(void *context, int play_count)
   231 {
   232     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
   233     fluidsynth.fluid_player_set_loop(music->player, play_count);
   234     fluidsynth.fluid_player_play(music->player);
   235     return 0;
   236 }
   237 
   238 static SDL_bool FLUIDSYNTH_IsPlaying(void *context)
   239 {
   240     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
   241     return fluidsynth.fluid_player_get_status(music->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE;
   242 }
   243 
   244 static int FLUIDSYNTH_GetSome(void *context, void *data, int bytes, SDL_bool *done)
   245 {
   246     int filled;
   247 
   248     filled = SDL_AudioStreamGet(music->stream, data, bytes);
   249     if (filled != 0) {
   250         return filled;
   251     }
   252 
   253     /* FIXME: What happens at end of song? */
   254     if (fluidsynth.fluid_synth_write_s16(music->synth, mixer_spec.samples, music->buffer, 0, 2, music->buffer, 1, 2) != FLUID_OK) {
   255         Mix_SetError("Error generating FluidSynth audio");
   256         return -1;
   257     }
   258     if (SDL_AudioStreamPut(music->stream, music->buffer, music->buffer_size) < 0) {
   259         return -1;
   260     }
   261     return 0;
   262 }
   263 static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes)
   264 {
   265     return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, FLUIDSYNTH_GetSome);
   266 }
   267 
   268 static void FLUIDSYNTH_Stop(void *context)
   269 {
   270     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
   271     fluidsynth.fluid_player_stop(music->player);
   272 }
   273 
   274 static void FLUIDSYNTH_Delete(void *context)
   275 {
   276     FLUIDSYNTH_Music *music = (FLUIDSYNTH_Music *)context;
   277     fluidsynth.delete_fluid_player(music->player);
   278     fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(music->synth));
   279     fluidsynth.delete_fluid_synth(music->synth);
   280     SDL_free(music);
   281 }
   282 
   283 Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH =
   284 {
   285     "FLUIDSYNTH",
   286     MIX_MUSIC_FLUIDSYNTH,
   287     MUS_MID,
   288     SDL_FALSE,
   289     SDL_FALSE,
   290 
   291     FLUIDSYNTH_Load,
   292     FLUIDSYNTH_Open,
   293     FLUIDSYNTH_CreateFromRW,
   294     NULL,   /* CreateFromFile */
   295     FLUIDSYNTH_SetVolume,
   296     FLUIDSYNTH_Play,
   297     FLUIDSYNTH_IsPlaying,
   298     FLUIDSYNTH_GetAudio,
   299     NULL,   /* Seek */
   300     NULL,   /* Pause */
   301     NULL,   /* Resume */
   302     FLUIDSYNTH_Stop,
   303     FLUIDSYNTH_Delete,
   304     NULL,   /* Close */
   305     FLUIDSYNTH_Unload,
   306 };
   307 
   308 #endif /* MUSIC_MID_FLUIDSYNTH */
   309 
   310 /* vi: set ts=4 sw=4 expandtab: */