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