fluidsynth.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 04 Jan 2012 00:16:03 -0500
changeset 542 3de4970b36d4
parent 532 b8e8ae4852b2
child 545 32e5ed415a34
permissions -rw-r--r--
Fixed bug 1252 - Added Mix_LoadMUSType_RW() so you can tell SDL_mixer what type the music is

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