fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 17 Jun 2015 00:11:41 -0700
changeset 705 fe757163b8f7
parent 625 1d489d8ec2e0
child 711 f40c5ac95b12
permissions -rw-r--r--
Fixed bug 3018 - Loading MIDI music using FluidSynth leaks memory.

Philipp Wiesemann

There is a memory leak in fluidsynth.c and fluidsynth_loadsong_RW_internal(). The allocated temporary buffer is not deleted if fluid_player_add_mem() returns FLUID_OK.
chewi@506
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@601
     3
  Copyright (C) 1997-2013 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@532
    25
#ifdef USE_FLUIDSYNTH_MIDI
slouken@532
    26
chewi@506
    27
#include <stdio.h>
chewi@506
    28
#include <sys/types.h>
chewi@506
    29
slouken@532
    30
#include "SDL_mixer.h"
slouken@532
    31
#include "fluidsynth.h"
slouken@532
    32
chewi@506
    33
static Uint16 format;
chewi@506
    34
static Uint8 channels;
chewi@506
    35
static int freq;
chewi@506
    36
chewi@506
    37
int fluidsynth_check_soundfont(const char *path, void *data)
chewi@506
    38
{
slouken@617
    39
    FILE *file = fopen(path, "r");
chewi@506
    40
slouken@617
    41
    if (file) {
slouken@617
    42
        fclose(file);
slouken@617
    43
        return 1;
slouken@617
    44
    } else {
slouken@617
    45
        Mix_SetError("Failed to access the SoundFont %s", path);
slouken@617
    46
        return 0;
slouken@617
    47
    }
chewi@506
    48
}
chewi@506
    49
chewi@506
    50
int fluidsynth_load_soundfont(const char *path, void *data)
chewi@506
    51
{
slouken@617
    52
    /* If this fails, it's too late to try Timidity so pray that at least one works. */
slouken@617
    53
    fluidsynth.fluid_synth_sfload((fluid_synth_t*) data, path, 1);
slouken@617
    54
    return 1;
chewi@506
    55
}
chewi@506
    56
chewi@506
    57
int fluidsynth_init(SDL_AudioSpec *mixer)
chewi@506
    58
{
slouken@617
    59
    if (!Mix_EachSoundFont(fluidsynth_check_soundfont, NULL))
slouken@617
    60
        return -1;
chewi@506
    61
slouken@617
    62
    format = mixer->format;
slouken@617
    63
    channels = mixer->channels;
slouken@617
    64
    freq = mixer->freq;
chewi@506
    65
slouken@617
    66
    return 0;
chewi@506
    67
}
chewi@506
    68
slouken@542
    69
static FluidSynthMidiSong *fluidsynth_loadsong_common(int (*function)(FluidSynthMidiSong*, void*), void *data)
chewi@506
    70
{
slouken@617
    71
    FluidSynthMidiSong *song;
slouken@617
    72
    fluid_settings_t *settings = NULL;
chewi@506
    73
slouken@617
    74
    if (!Mix_Init(MIX_INIT_FLUIDSYNTH)) {
slouken@617
    75
        return NULL;
slouken@617
    76
    }
slouken@545
    77
slouken@617
    78
    if ((song = SDL_malloc(sizeof(FluidSynthMidiSong)))) {
slouken@621
    79
        SDL_memset(song, 0, sizeof(FluidSynthMidiSong));
chewi@506
    80
slouken@617
    81
        if (SDL_BuildAudioCVT(&song->convert, AUDIO_S16, 2, freq, format, channels, freq) >= 0) {
slouken@617
    82
            if ((settings = fluidsynth.new_fluid_settings())) {
slouken@617
    83
                fluidsynth.fluid_settings_setnum(settings, "synth.sample-rate", (double) freq);
chewi@506
    84
slouken@617
    85
                if ((song->synth = fluidsynth.new_fluid_synth(settings))) {
slouken@617
    86
                    if (Mix_EachSoundFont(fluidsynth_load_soundfont, (void*) song->synth)) {
slouken@617
    87
                        if ((song->player = fluidsynth.new_fluid_player(song->synth))) {
slouken@617
    88
                            if (function(song, data)) return song;
slouken@617
    89
                            fluidsynth.delete_fluid_player(song->player);
slouken@617
    90
                        } else {
slouken@617
    91
                            Mix_SetError("Failed to create FluidSynth player");
slouken@617
    92
                        }
slouken@617
    93
                    }
slouken@617
    94
                    fluidsynth.delete_fluid_synth(song->synth);
slouken@617
    95
                } else {
slouken@617
    96
                    Mix_SetError("Failed to create FluidSynth synthesizer");
slouken@617
    97
                }
slouken@617
    98
                fluidsynth.delete_fluid_settings(settings);
slouken@617
    99
            } else {
slouken@617
   100
                Mix_SetError("Failed to create FluidSynth settings");
slouken@617
   101
            }
slouken@617
   102
        } else {
slouken@617
   103
            Mix_SetError("Failed to set up audio conversion");
slouken@617
   104
        }
slouken@617
   105
        SDL_free(song);
slouken@617
   106
    } else {
slouken@617
   107
        Mix_SetError("Insufficient memory for song");
slouken@617
   108
    }
slouken@617
   109
    return NULL;
chewi@506
   110
}
chewi@506
   111
slouken@542
   112
static int fluidsynth_loadsong_RW_internal(FluidSynthMidiSong *song, void *data)
chewi@506
   113
{
slouken@625
   114
    Sint64 offset;
slouken@617
   115
    size_t size;
slouken@617
   116
    char *buffer;
slouken@625
   117
    SDL_RWops *src = (SDL_RWops*) data;
chewi@506
   118
slouken@625
   119
    offset = SDL_RWtell(src);
slouken@625
   120
    SDL_RWseek(src, 0, RW_SEEK_END);
slouken@625
   121
    size = (size_t)(SDL_RWtell(src) - offset);
slouken@625
   122
    SDL_RWseek(src, offset, RW_SEEK_SET);
chewi@506
   123
slouken@617
   124
    if ((buffer = (char*) SDL_malloc(size))) {
slouken@625
   125
        if(SDL_RWread(src, buffer, size, 1) == 1) {
slouken@617
   126
            if (fluidsynth.fluid_player_add_mem(song->player, buffer, size) == FLUID_OK) {
slouken@705
   127
                SDL_free(buffer);
slouken@617
   128
                return 1;
slouken@617
   129
            } else {
slouken@617
   130
                Mix_SetError("FluidSynth failed to load in-memory song");
slouken@617
   131
            }
slouken@617
   132
        } else {
slouken@617
   133
            Mix_SetError("Failed to read in-memory song");
slouken@617
   134
        }
slouken@617
   135
        SDL_free(buffer);
slouken@617
   136
    } else {
slouken@617
   137
        Mix_SetError("Insufficient memory for song");
slouken@617
   138
    }
slouken@617
   139
    return 0;
chewi@506
   140
}
chewi@506
   141
slouken@625
   142
FluidSynthMidiSong *fluidsynth_loadsong_RW(SDL_RWops *src, int freesrc)
chewi@506
   143
{
slouken@617
   144
    FluidSynthMidiSong *song;
slouken@521
   145
slouken@625
   146
    song = fluidsynth_loadsong_common(fluidsynth_loadsong_RW_internal, (void*) src);
slouken@625
   147
    if (song && freesrc) {
slouken@625
   148
        SDL_RWclose(src);
slouken@617
   149
    }
slouken@617
   150
    return song;
chewi@506
   151
}
chewi@506
   152
chewi@506
   153
void fluidsynth_freesong(FluidSynthMidiSong *song)
chewi@506
   154
{
slouken@617
   155
    if (!song) return;
slouken@617
   156
    fluidsynth.delete_fluid_player(song->player);
slouken@617
   157
    fluidsynth.delete_fluid_settings(fluidsynth.fluid_synth_get_settings(song->synth));
slouken@617
   158
    fluidsynth.delete_fluid_synth(song->synth);
slouken@617
   159
    SDL_free(song);
chewi@506
   160
}
chewi@506
   161
chewi@506
   162
void fluidsynth_start(FluidSynthMidiSong *song)
chewi@506
   163
{
slouken@617
   164
    fluidsynth.fluid_player_set_loop(song->player, 1);
slouken@617
   165
    fluidsynth.fluid_player_play(song->player);
chewi@506
   166
}
chewi@506
   167
chewi@506
   168
void fluidsynth_stop(FluidSynthMidiSong *song)
chewi@506
   169
{
slouken@617
   170
    fluidsynth.fluid_player_stop(song->player);
chewi@506
   171
}
chewi@506
   172
chewi@506
   173
int fluidsynth_active(FluidSynthMidiSong *song)
chewi@506
   174
{
slouken@617
   175
    return fluidsynth.fluid_player_get_status(song->player) == FLUID_PLAYER_PLAYING ? 1 : 0;
chewi@506
   176
}
chewi@506
   177
chewi@506
   178
void fluidsynth_setvolume(FluidSynthMidiSong *song, int volume)
chewi@506
   179
{
slouken@617
   180
    /* FluidSynth's default is 0.2. Make 1.2 the maximum. */
slouken@617
   181
    fluidsynth.fluid_synth_set_gain(song->synth, (float) (volume * 1.2 / MIX_MAX_VOLUME));
chewi@506
   182
}
chewi@506
   183
chewi@506
   184
int fluidsynth_playsome(FluidSynthMidiSong *song, void *dest, int dest_len)
chewi@506
   185
{
slouken@617
   186
    int result = -1;
slouken@617
   187
    int frames = dest_len / channels / ((format & 0xFF) / 8);
slouken@617
   188
    int src_len = frames * 4; /* 16-bit stereo */
slouken@617
   189
    void *src = dest;
chewi@506
   190
slouken@617
   191
    if (dest_len < src_len) {
slouken@617
   192
        if (!(src = SDL_malloc(src_len))) {
slouken@617
   193
            Mix_SetError("Insufficient memory for audio conversion");
slouken@617
   194
            return result;
slouken@617
   195
        }
slouken@617
   196
    }
chewi@506
   197
slouken@617
   198
    if (fluidsynth.fluid_synth_write_s16(song->synth, frames, src, 0, 2, src, 1, 2) != FLUID_OK) {
slouken@617
   199
        Mix_SetError("Error generating FluidSynth audio");
slouken@617
   200
        goto finish;
slouken@617
   201
    }
chewi@506
   202
slouken@617
   203
    song->convert.buf = src;
slouken@617
   204
    song->convert.len = src_len;
chewi@506
   205
slouken@617
   206
    if (SDL_ConvertAudio(&song->convert) < 0) {
slouken@617
   207
        Mix_SetError("Error during audio conversion");
slouken@617
   208
        goto finish;
slouken@617
   209
    }
chewi@506
   210
slouken@617
   211
    if (src != dest)
slouken@621
   212
        SDL_memcpy(dest, src, dest_len);
chewi@506
   213
slouken@617
   214
    result = 0;
chewi@506
   215
chewi@506
   216
finish:
slouken@617
   217
    if (src != dest)
slouken@617
   218
        SDL_free(src);
chewi@506
   219
slouken@617
   220
    return result;
chewi@506
   221
}
slouken@532
   222
slouken@532
   223
#endif /* USE_FLUIDSYNTH_MIDI */