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