music_fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:33:47 -0700
changeset 777 92882ef2ab81
parent 725 fluidsynth.c@bdf7b8d20566
child 786 12a659c8b902
permissions -rw-r--r--
Rewrote music.c to support any number of decode libraries using a compiled-in plugin interface
Mix_LoadWAV_RW() can now load sound formats that were previously available only as music.

This is still work in progress. Testing and project updates need to happen on other platforms.
     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 FLUIDSYNTH_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 FLUIDSYNTH_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) return -1;
    78 #endif
    79 
    80         FLUIDSYNTH_LOADER(delete_fluid_player, int (*)(fluid_player_t*));
    81         FLUIDSYNTH_LOADER(delete_fluid_settings, void (*)(fluid_settings_t*));
    82         FLUIDSYNTH_LOADER(delete_fluid_synth, int (*)(fluid_synth_t*));
    83         FLUIDSYNTH_LOADER(fluid_player_add, int (*)(fluid_player_t*, const char*));
    84         FLUIDSYNTH_LOADER(fluid_player_add_mem, int (*)(fluid_player_t*, const void*, size_t));
    85         FLUIDSYNTH_LOADER(fluid_player_get_status, int (*)(fluid_player_t*));
    86         FLUIDSYNTH_LOADER(fluid_player_play, int (*)(fluid_player_t*));
    87         FLUIDSYNTH_LOADER(fluid_player_set_loop, int (*)(fluid_player_t*, int));
    88         FLUIDSYNTH_LOADER(fluid_player_stop, int (*)(fluid_player_t*));
    89         FLUIDSYNTH_LOADER(fluid_settings_setnum, int (*)(fluid_settings_t*, const char*, double));
    90         FLUIDSYNTH_LOADER(fluid_synth_get_settings, fluid_settings_t* (*)(fluid_synth_t*));
    91         FLUIDSYNTH_LOADER(fluid_synth_set_gain, void (*)(fluid_synth_t*, float));
    92         FLUIDSYNTH_LOADER(fluid_synth_sfload, int(*)(fluid_synth_t*, const char*, int));
    93         FLUIDSYNTH_LOADER(fluid_synth_write_s16, int(*)(fluid_synth_t*, int, void*, int, int, void*, int, int));
    94         FLUIDSYNTH_LOADER(new_fluid_player, fluid_player_t* (*)(fluid_synth_t*));
    95         FLUIDSYNTH_LOADER(new_fluid_settings, fluid_settings_t* (*)(void));
    96         FLUIDSYNTH_LOADER(new_fluid_synth, fluid_synth_t* (*)(fluid_settings_t*));
    97     }
    98     ++fluidsynth.loaded;
    99 
   100     return 0;
   101 }
   102 
   103 static void FLUIDSYNTH_Unload()
   104 {
   105     if (fluidsynth.loaded == 0) {
   106         return;
   107     }
   108     if (fluidsynth.loaded == 1) {
   109 #ifdef FLUIDSYNTH_DYNAMIC
   110         SDL_UnloadObject(fluidsynth.handle);
   111 #endif
   112     }
   113     --fluidsynth.loaded;
   114 }
   115 
   116 
   117 typedef struct {
   118     SDL_AudioCVT convert;
   119     fluid_synth_t *synth;
   120     fluid_player_t* player;
   121 } FluidSynthMidiSong;
   122 
   123 static Uint16 format;
   124 static Uint8 channels;
   125 static int freq;
   126 
   127 static int 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 
   140 static int 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 
   147 static int FLUIDSYNTH_Open(const SDL_AudioSpec *spec)
   148 {
   149     if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL)) {
   150         return -1;
   151     }
   152 
   153     format = spec->format;
   154     channels = spec->channels;
   155     freq = spec->freq;
   156 
   157     return 0;
   158 }
   159 
   160 static FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data)
   161 {
   162     FluidSynthMidiSong *song;
   163     fluid_settings_t *settings = NULL;
   164 
   165     if ((song = SDL_calloc(1, sizeof(FluidSynthMidiSong)))) {
   166         if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) {
   167             if ((settings = fluidsynth.new_fluid_settings())) {
   168                 fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq);
   169 
   170                 if ((song->synth = fluidsynth.new_fluid_synth(settings))) {
   171                     if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) {
   172                         if ((song->player = fluidsynth.new_fluid_player(song->synth))) {
   173                             if (function(song, data)) return song;
   174                             fluidsynth.delete_fluid_player(song->player);
   175                         } else {
   176                             Mix_SetError("Failed to create FluidSynth player");
   177                         }
   178                     }
   179                     fluidsynth.delete_fluid_synth(song->synth);
   180                 } else {
   181                     Mix_SetError("Failed to create FluidSynth synthesizer");
   182                 }
   183                 fluidsynth.delete_fluid_settings(settings);
   184             } else {
   185                 Mix_SetError("Failed to create FluidSynth settings");
   186             }
   187         } else {
   188             Mix_SetError("Failed to set up audio conversion");
   189         }
   190         SDL_free(song);
   191     } else {
   192         Mix_SetError("Insufficient memory for song");
   193     }
   194     return NULL;
   195 }
   196 
   197 static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data)
   198 {
   199     Sint64 offset;
   200     size_t size;
   201     char *buffer;
   202     SDL_RWops *src = (SDL_RWops*) data;
   203 
   204     offset = SDL_RWtell(src);
   205     SDL_RWseek(src, 0, RW_SEEK_END);
   206     size = (size_t)(SDL_RWtell(src) - offset);
   207     SDL_RWseek(src, offset, RW_SEEK_SET);
   208 
   209     if ((buffer = (char*) SDL_malloc(size))) {
   210         if(SDL_RWread(src, buffer, size, 1) == 1) {
   211             if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) {
   212                 SDL_free(buffer);
   213                 return 1;
   214             } else {
   215                 Mix_SetError("FluidSynth failed to load in-memory song");
   216             }
   217         } else {
   218             Mix_SetError("Failed to read in-memory song");
   219         }
   220         SDL_free(buffer);
   221     } else {
   222         Mix_SetError("Insufficient memory for song");
   223     }
   224     return 0;
   225 }
   226 
   227 static void *FLUIDSYNTH_CreateFromRW(SDL_RWops *src, int freesrc)
   228 {
   229     FluidSynthMidiSong *song;
   230 
   231     song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) src);
   232     if (song && freesrc) {
   233         SDL_RWclose(src);
   234     }
   235     return song;
   236 }
   237 
   238 static void FLUIDSYNTH_SetVolume(void *context, int volume)
   239 {
   240     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   241     /* FluidSynth's default is 0.2. Make 1.2 the maximum. */
   242     fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME));
   243 }
   244 
   245 static int FLUIDSYNTH_Play(void *context)
   246 {
   247     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   248     fluidsynth.fluid_player_set_loop(song->player, 1);
   249     fluidsynth.fluid_player_play(song->player);
   250     return 0;
   251 }
   252 
   253 static SDL_bool FLUIDSYNTH_IsPlaying(void *context)
   254 {
   255     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   256     return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? SDL_TRUE : SDL_FALSE;
   257 }
   258 
   259 static int FLUIDSYNTH_GetAudio(void *context, void *data, int bytes)
   260 {
   261     int result = -1;
   262     int frames = bytes / channels / ((format & 0xFF) / 8);
   263     int src_len = frames * 4; /* 16-bit stereo */
   264     void *src = dest;
   265 
   266     if (bytes < src_len) {
   267         if (!(src = SDL_malloc(src_len))) {
   268             Mix_SetError("Insufficient memory for audio conversion");
   269             return result;
   270         }
   271     }
   272 
   273     if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
   274         Mix_SetError("Error generating FluidSynth audio");
   275         goto finish;
   276     }
   277 
   278     song->convert.buf = src;
   279     song->convert.len = src_len;
   280 
   281     if (SDL_ConvertAudio(&song->convert) < 0) {
   282         Mix_SetError("Error during audio conversion");
   283         goto finish;
   284     }
   285 
   286     if (src != dest)
   287         SDL_memcpy(dest, src, bytes);
   288 
   289     result = 0;
   290 
   291 finish:
   292     if (src != dest)
   293         SDL_free(src);
   294 
   295     return result;
   296 }
   297 
   298 static void FLUIDSYNTH_Stop(void *context)
   299 {
   300     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   301     fluidsynth.fluid_player_stop(song->player);
   302 }
   303 
   304 static void FLUIDSYNTH_Delete(void *context)
   305 {
   306     FluidSynthMidiSong *song = (FluidSynthMidiSong *)context;
   307     fluidsynth.delete_fluid_player(song->player);
   308     fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth));
   309     fluidsynth.delete_fluid_synth(song->synth);
   310     SDL_free(song);
   311 }
   312 
   313 Mix_MusicInterface Mix_MusicInterface_FLUIDSYNTH =
   314 {
   315     "FLUIDSYNTH",
   316     MIX_MUSIC_FLUIDSYNTH,
   317     MUS_MID,
   318     SDL_FALSE,
   319     SDL_FALSE,
   320 
   321     FLUIDSYNTH_Load,
   322     FLUIDSYNTH_Open,
   323     FLUIDSYNTH_CreateFromRW,
   324     NULL,   /* CreateFromFile */
   325     FLUIDSYNTH_SetVolume,
   326     FLUIDSYNTH_Play,
   327     FLUIDSYNTH_IsPlaying,
   328     FLUIDSYNTH_GetAudio,
   329     NULL,   /* Seek */
   330     NULL,   /* Pause */
   331     NULL,   /* Resume */
   332     FLUIDSYNTH_Stop,
   333     FLUIDSYNTH_Delete,
   334     NULL,   /* Close */
   335     FLUIDSYNTH_Unload,
   336 };
   337 
   338 #endif /* MUSIC_MID_FLUIDSYNTH */
   339 
   340 /* vi: set ts=4 sw=4 expandtab: */