music_fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 22:55:54 -0700
changeset 786 12a659c8b902
parent 777 92882ef2ab81
child 797 b4b6adff699a
permissions -rw-r--r--
Minor cleanup
     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     SDL_AudioCVT convert;
   121     fluid_synth_t *synth;
   122     fluid_player_t* player;
   123 } FluidSynthMidiSong;
   124 
   125 static Uint16 format;
   126 static Uint8 channels;
   127 static int freq;
   128 
   129 static int fluidsynth_check_soundfont(const char *path, void *data)
   130 {
   131     FILE *file = fopen(path, "r");
   132 
   133     if (file) {
   134         fclose(file);
   135         return 1;
   136     } else {
   137         Mix_SetError("Failed to access the SoundFont %s", path);
   138         return 0;
   139     }
   140 }
   141 
   142 static int fluidsynth_load_soundfont(const char *path, void *data)
   143 {
   144     /* If this fails, it's too late to try Timidity so pray that at least one works. */
   145     fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1);
   146     return 1;
   147 }
   148 
   149 static int FLUIDSYNTH_Open(const SDL_AudioSpec *spec)
   150 {
   151     if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) {
   152         return -1;
   153     }
   154 
   155     format = spec->format;
   156     channels = spec->channels;
   157     freq = spec->freq;
   158 
   159     return 0;
   160 }
   161 
   162 static FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data)
   163 {
   164     FluidSynthMidiSong *song;
   165     fluid_settings_t *settings = NULL;
   166 
   167     if ((song = SDL_calloc(1, sizeof(FluidSynthMidiSong)))) {
   168         if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) {
   169             if ((settings = fluidsynth.new_fluid_settings())) {
   170                 fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq);
   171 
   172                 if ((song->synth = fluidsynth.new_fluid_synth(settings))) {
   173                     if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) {
   174                         if ((song->player = fluidsynth.new_fluid_player(song->synth))) {
   175                             if (function(song, data)) return song;
   176                             fluidsynth.delete_fluid_player(song->player);
   177                         } else {
   178                             Mix_SetError("Failed to create FluidSynth player");
   179                         }
   180                     }
   181                     fluidsynth.delete_fluid_synth(song->synth);
   182                 } else {
   183                     Mix_SetError("Failed to create FluidSynth synthesizer");
   184                 }
   185                 fluidsynth.delete_fluid_settings(settings);
   186             } else {
   187                 Mix_SetError("Failed to create FluidSynth settings");
   188             }
   189         } else {
   190             Mix_SetError("Failed to set up audio conversion");
   191         }
   192         SDL_free(song);
   193     } else {
   194         Mix_SetError("Insufficient memory for song");
   195     }
   196     return NULL;
   197 }
   198 
   199 static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data)
   200 {
   201     Sint64 offset;
   202     size_t size;
   203     char *buffer;
   204     SDL_RWops *src = (SDL_RWops*) data;
   205 
   206     offset = SDL_RWtell(src);
   207     SDL_RWseek(src, 0, RW_SEEK_END);
   208     size = (size_t)(SDL_RWtell(src) - offset);
   209     SDL_RWseek(src, offset, RW_SEEK_SET);
   210 
   211     if ((buffer = (char*) SDL_malloc(size))) {
   212         if(SDL_RWread(src, buffer, size, 1) == 1) {
   213             if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) {
   214                 SDL_free(buffer);
   215                 return 1;
   216             } else {
   217                 Mix_SetError("FluidSynth failed to load in-memory song");
   218             }
   219         } else {
   220             Mix_SetError("Failed to read in-memory song");
   221         }
   222         SDL_free(buffer);
   223     } else {
   224         Mix_SetError("Insufficient memory for song");
   225     }
   226     return 0;
   227 }
   228 
   229 static void *FLUIDSYNTH_CreateFromRW(SDL_RWops *src, int freesrc)
   230 {
   231     FluidSynthMidiSong *song;
   232 
   233     song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) src);
   234     if (song && freesrc) {
   235         SDL_RWclose(src);
   236     }
   237     return song;
   238 }
   239 
   240 static void FLUIDSYNTH_SetVolume(void *context, int volume)
   241 {
   242     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   243     /* FluidSynth's default is 0.2. Make 1.2 the maximum. */
   244     fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME));
   245 }
   246 
   247 static int FLUIDSYNTH_Play(void *context)
   248 {
   249     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   250     fluidsynth.fluid_player_set_loop(song->player, 1);
   251     fluidsynth.fluid_player_play(song->player);
   252     return 0;
   253 }
   254 
   255 static SDL_bool FLUIDSYNTH_IsPlaying(void *context)
   256 {
   257     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   258     return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE;
   259 }
   260 
   261 static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes)
   262 {
   263     int result = -1;
   264     int frames = bytes / channels / ((format & 0xFF) / 8);
   265     int src_len = frames * 4; /* 16-bit stereo */
   266     void *src = dest;
   267 
   268     if (bytes < src_len) {
   269         if (!(src = SDL_malloc(src_len))) {
   270             Mix_SetError("Insufficient memory for audio conversion");
   271             return result;
   272         }
   273     }
   274 
   275     if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
   276         Mix_SetError("Error generating FluidSynth audio");
   277         goto finish;
   278     }
   279 
   280     song->convert.buf = src;
   281     song->convert.len = src_len;
   282 
   283     if (SDL_ConvertAudio(&song->convert) < 0) {
   284         Mix_SetError("Error during audio conversion");
   285         goto finish;
   286     }
   287 
   288     if (src != dest)
   289         SDL_memcpy(dest, src, bytes);
   290 
   291     result = 0;
   292 
   293 finish:
   294     if (src != dest)
   295         SDL_free(src);
   296 
   297     return result;
   298 }
   299 
   300 static void FLUIDSYNTH_Stop(void *context)
   301 {
   302     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   303     fluidsynth.fluid_player_stop(song->player);
   304 }
   305 
   306 static void FLUIDSYNTH_Delete(void *context)
   307 {
   308     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   309     fluidsynth.delete_fluid_player(song->player);
   310     fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth));
   311     fluidsynth.delete_fluid_synth(song->synth);
   312     SDL_free(song);
   313 }
   314 
   315 Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH =
   316 {
   317     "FLUIDSYNTH",
   318     MIX_MUSIC_FLUIDSYNTH,
   319     MUS_MID,
   320     SDL_FALSE,
   321     SDL_FALSE,
   322 
   323     FLUIDSYNTH_Load,
   324     FLUIDSYNTH_Open,
   325     FLUIDSYNTH_CreateFromRW,
   326     NULL,   /* CreateFromFile */
   327     FLUIDSYNTH_SetVolume,
   328     FLUIDSYNTH_Play,
   329     FLUIDSYNTH_IsPlaying,
   330     FLUIDSYNTH_GetAudio,
   331     NULL,   /* Seek */
   332     NULL,   /* Pause */
   333     NULL,   /* Resume */
   334     FLUIDSYNTH_Stop,
   335     FLUIDSYNTH_Delete,
   336     NULL,   /* Close */
   337     FLUIDSYNTH_Unload,
   338 };
   339 
   340 #endif /* MUSIC_MID_FLUIDSYNTH */
   341 
   342 /* vi: set ts=4 sw=4 expandtab: */