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