mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2024-12-11 05:11:33 +00:00
Merge pull request #366 from FluidSynth/dynamic-sample-loading
Dynamic sample loading
This commit is contained in:
commit
a95a4864fd
17 changed files with 874 additions and 321 deletions
|
@ -504,6 +504,7 @@ endif ( enable-readline )
|
||||||
if(enable-tests)
|
if(enable-tests)
|
||||||
# manipulate some variables to setup a proper test env
|
# manipulate some variables to setup a proper test env
|
||||||
set(TEST_SOUNDFONT "${CMAKE_SOURCE_DIR}/sf2/VintageDreamsWaves-v2.sf2")
|
set(TEST_SOUNDFONT "${CMAKE_SOURCE_DIR}/sf2/VintageDreamsWaves-v2.sf2")
|
||||||
|
set(TEST_SOUNDFONT_SF3 "${CMAKE_SOURCE_DIR}/sf2/VintageDreamsWaves-v2.sf3")
|
||||||
|
|
||||||
# force to build a static lib, in order to bypass the visibility control, allowing us
|
# force to build a static lib, in order to bypass the visibility control, allowing us
|
||||||
# to test fluidsynths private/internal functions
|
# to test fluidsynths private/internal functions
|
||||||
|
|
|
@ -11,5 +11,10 @@ macro ( ADD_FLUID_TEST _test )
|
||||||
$<TARGET_PROPERTY:libfluidsynth,INCLUDE_DIRECTORIES> # include all other header search paths needed by libfluidsynth (esp. glib)
|
$<TARGET_PROPERTY:libfluidsynth,INCLUDE_DIRECTORIES> # include all other header search paths needed by libfluidsynth (esp. glib)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# add the test to ctest
|
||||||
ADD_TEST(NAME ${_test} COMMAND ${_test})
|
ADD_TEST(NAME ${_test} COMMAND ${_test})
|
||||||
|
|
||||||
|
# append the current unit test to check-target as dependency
|
||||||
|
add_dependencies(check ${_test})
|
||||||
|
|
||||||
endmacro ( ADD_FLUID_TEST )
|
endmacro ( ADD_FLUID_TEST )
|
||||||
|
|
|
@ -66,6 +66,15 @@ https://stackoverflow.com/a/6251757
|
||||||
<desc>
|
<desc>
|
||||||
Device identifier used for SYSEX commands, such as MIDI Tuning Standard commands. Only those SYSEX commands destined for this ID or to all devices will be acted upon.</desc>
|
Device identifier used for SYSEX commands, such as MIDI Tuning Standard commands. Only those SYSEX commands destined for this ID or to all devices will be acted upon.</desc>
|
||||||
</setting>
|
</setting>
|
||||||
|
<setting>
|
||||||
|
<name>dynamic-sample-loading</name>
|
||||||
|
<type>bool</type>
|
||||||
|
<def>0 (FALSE)</def>
|
||||||
|
<desc>
|
||||||
|
When set to 1 (TRUE), samples are loaded to and unloaded from memory
|
||||||
|
on demand.
|
||||||
|
</desc>
|
||||||
|
</setting>
|
||||||
<setting>
|
<setting>
|
||||||
<name>effects-channels</name>
|
<name>effects-channels</name>
|
||||||
<type>int</type>
|
<type>int</type>
|
||||||
|
|
BIN
sf2/VintageDreamsWaves-v2.sf3
Normal file
BIN
sf2/VintageDreamsWaves-v2.sf3
Normal file
Binary file not shown.
|
@ -193,6 +193,9 @@
|
||||||
/* Soundfont to load for unit testing */
|
/* Soundfont to load for unit testing */
|
||||||
#cmakedefine TEST_SOUNDFONT "@TEST_SOUNDFONT@"
|
#cmakedefine TEST_SOUNDFONT "@TEST_SOUNDFONT@"
|
||||||
|
|
||||||
|
/* SF3 Soundfont to load for unit testing */
|
||||||
|
#cmakedefine TEST_SOUNDFONT_SF3 "@TEST_SOUNDFONT_SF3@"
|
||||||
|
|
||||||
/* Define to enable SIGFPE assertions */
|
/* Define to enable SIGFPE assertions */
|
||||||
#cmakedefine TRAP_ON_FPE @TRAP_ON_FPE@
|
#cmakedefine TRAP_ON_FPE @TRAP_ON_FPE@
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,13 @@
|
||||||
* compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */
|
* compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */
|
||||||
#define EMU_ATTENUATION_FACTOR (0.4f)
|
#define EMU_ATTENUATION_FACTOR (0.4f)
|
||||||
|
|
||||||
|
/* Dynamic sample loading functions */
|
||||||
|
static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset);
|
||||||
|
static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset);
|
||||||
|
static void unload_sample(fluid_sample_t *sample);
|
||||||
|
static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan);
|
||||||
|
static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason);
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
*
|
*
|
||||||
|
@ -195,6 +202,7 @@ fluid_defsfont_t* new_fluid_defsfont(fluid_settings_t* settings)
|
||||||
FLUID_MEMSET(defsfont, 0, sizeof(*defsfont));
|
FLUID_MEMSET(defsfont, 0, sizeof(*defsfont));
|
||||||
|
|
||||||
fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock);
|
fluid_settings_getint(settings, "synth.lock-memory", &defsfont->mlock);
|
||||||
|
fluid_settings_getint(settings, "synth.dynamic-sample-loading", &defsfont->dynamic_samples);
|
||||||
|
|
||||||
return defsfont;
|
return defsfont;
|
||||||
}
|
}
|
||||||
|
@ -252,6 +260,115 @@ const char* fluid_defsfont_get_name(fluid_defsfont_t* defsfont)
|
||||||
return defsfont->filename;
|
return defsfont->filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Load sample data for a single sample from the Soundfont file.
|
||||||
|
* Returns FLUID_OK on error, otherwise FLUID_FAILED
|
||||||
|
*/
|
||||||
|
int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample)
|
||||||
|
{
|
||||||
|
int num_samples;
|
||||||
|
int source_end = sample->source_end;
|
||||||
|
|
||||||
|
/* For uncompressed samples we want to include the 46 zero sample word area following each sample
|
||||||
|
* in the Soundfont. Otherwise samples with loopend > end, which we have decided not to correct, would
|
||||||
|
* be corrected after all in fluid_sample_sanitize_loop */
|
||||||
|
if (!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
|
||||||
|
{
|
||||||
|
source_end += 46; /* Length of zero sample word after each sample, according to SF specs */
|
||||||
|
|
||||||
|
/* Safeguard against Soundfonts that are not quite valid and don't include 46 sample words after the
|
||||||
|
* last sample */
|
||||||
|
if (source_end >= (defsfont->samplesize / sizeof(short)))
|
||||||
|
{
|
||||||
|
source_end = defsfont->samplesize / sizeof(short);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
num_samples = fluid_samplecache_load(
|
||||||
|
sfdata, sample->source_start, source_end, sample->sampletype,
|
||||||
|
defsfont->mlock, &sample->data, &sample->data24);
|
||||||
|
|
||||||
|
if (num_samples < 0)
|
||||||
|
{
|
||||||
|
return FLUID_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_samples == 0)
|
||||||
|
{
|
||||||
|
sample->start = sample->end = 0;
|
||||||
|
sample->loopstart= sample->loopend = 0;
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ogg Vorbis samples already have loop pointers relative to the invididual decompressed sample,
|
||||||
|
* but SF2 samples are relative to sample chunk start, so they need to be adjusted */
|
||||||
|
if (!(sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS))
|
||||||
|
{
|
||||||
|
sample->loopstart = sample->source_loopstart - sample->source_start;
|
||||||
|
sample->loopend = sample->source_loopend - sample->source_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* As we've just loaded an individual sample into it's own buffer, we need to adjust the start
|
||||||
|
* and end pointers */
|
||||||
|
sample->start = 0;
|
||||||
|
sample->end = num_samples - 1;
|
||||||
|
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loads the sample data for all samples from the Soundfont file. For SF2 files, it loads the data in
|
||||||
|
* one large block. For SF3 files, each compressed sample gets loaded individually.
|
||||||
|
* Returns FLUID_OK on success, otherwise FLUID_FAILED
|
||||||
|
*/
|
||||||
|
int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata)
|
||||||
|
{
|
||||||
|
fluid_list_t *list;
|
||||||
|
fluid_sample_t *sample;
|
||||||
|
int sf3_file = (sfdata->version.major == 3);
|
||||||
|
|
||||||
|
/* For SF2 files, we load the sample data in one large block */
|
||||||
|
if (!sf3_file)
|
||||||
|
{
|
||||||
|
int read_samples;
|
||||||
|
int num_samples = sfdata->samplesize / sizeof(short);
|
||||||
|
|
||||||
|
read_samples = fluid_samplecache_load(sfdata, 0, num_samples - 1, 0, defsfont->mlock,
|
||||||
|
&defsfont->sampledata, &defsfont->sample24data);
|
||||||
|
if (read_samples != num_samples)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Attempted to read %d words of sample data, but got %d instead",
|
||||||
|
num_samples, read_samples);
|
||||||
|
return FLUID_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (list = defsfont->sample; list; list = fluid_list_next(list))
|
||||||
|
{
|
||||||
|
sample = fluid_list_get(list);
|
||||||
|
|
||||||
|
if (sf3_file)
|
||||||
|
{
|
||||||
|
/* SF3 samples get loaded individually, as most (or all) of them are in Ogg Vorbis format
|
||||||
|
* anyway */
|
||||||
|
if (fluid_defsfont_load_sampledata(defsfont, sfdata, sample) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to load sample '%s'", sample->name);
|
||||||
|
return FLUID_FAILED;
|
||||||
|
}
|
||||||
|
fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Data pointers of SF2 samples point to large sample data block loaded above */
|
||||||
|
sample->data = defsfont->sampledata;
|
||||||
|
sample->data24 = defsfont->sample24data;
|
||||||
|
fluid_sample_sanitize_loop(sample, defsfont->samplesize);
|
||||||
|
}
|
||||||
|
|
||||||
|
fluid_voice_optimize_sample(sample);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fluid_defsfont_load
|
* fluid_defsfont_load
|
||||||
|
@ -271,13 +388,20 @@ int fluid_defsfont_load(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t
|
||||||
return FLUID_FAILED;
|
return FLUID_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defsfont->fcbs = fcbs;
|
||||||
|
|
||||||
/* The actual loading is done in the sfont and sffile files */
|
/* The actual loading is done in the sfont and sffile files */
|
||||||
sfdata = fluid_sffile_load(file, fcbs);
|
sfdata = fluid_sffile_open(file, fcbs);
|
||||||
if (sfdata == NULL) {
|
if (sfdata == NULL) {
|
||||||
FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
|
FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
|
||||||
return FLUID_FAILED;
|
return FLUID_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fluid_sffile_parse_presets(sfdata) == FLUID_FAILED) {
|
||||||
|
FLUID_LOG(FLUID_ERR, "Couldn't parse presets from soundfont file");
|
||||||
|
goto err_exit;
|
||||||
|
}
|
||||||
|
|
||||||
/* Keep track of the position and size of the sample data because
|
/* Keep track of the position and size of the sample data because
|
||||||
it's loaded separately (and might be unoaded/reloaded in future) */
|
it's loaded separately (and might be unoaded/reloaded in future) */
|
||||||
defsfont->samplepos = sfdata->samplepos;
|
defsfont->samplepos = sfdata->samplepos;
|
||||||
|
@ -285,14 +409,7 @@ int fluid_defsfont_load(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t
|
||||||
defsfont->sample24pos = sfdata->sample24pos;
|
defsfont->sample24pos = sfdata->sample24pos;
|
||||||
defsfont->sample24size = sfdata->sample24size;
|
defsfont->sample24size = sfdata->sample24size;
|
||||||
|
|
||||||
/* load sample data in one block */
|
/* Create all samples from sample headers */
|
||||||
if (fluid_samplecache_load(sfdata, 0, sfdata->samplesize / 2, defsfont->mlock,
|
|
||||||
&defsfont->sampledata, &defsfont->sample24data) == FLUID_FAILED)
|
|
||||||
{
|
|
||||||
goto err_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create all the sample headers */
|
|
||||||
p = sfdata->sample;
|
p = sfdata->sample;
|
||||||
while (p != NULL) {
|
while (p != NULL) {
|
||||||
sfsample = (SFSample *)fluid_list_get(p);
|
sfsample = (SFSample *)fluid_list_get(p);
|
||||||
|
@ -303,7 +420,6 @@ int fluid_defsfont_load(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t
|
||||||
if (fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK)
|
if (fluid_sample_import_sfont(sample, sfsample, defsfont) == FLUID_OK)
|
||||||
{
|
{
|
||||||
fluid_defsfont_add_sample(defsfont, sample);
|
fluid_defsfont_add_sample(defsfont, sample);
|
||||||
fluid_voice_optimize_sample(sample);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -313,9 +429,20 @@ int fluid_defsfont_load(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t
|
||||||
|
|
||||||
/* Store reference to FluidSynth sample in SFSample for later IZone fixups */
|
/* Store reference to FluidSynth sample in SFSample for later IZone fixups */
|
||||||
sfsample->fluid_sample = sample;
|
sfsample->fluid_sample = sample;
|
||||||
|
|
||||||
p = fluid_list_next(p);
|
p = fluid_list_next(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If dynamic sample loading is disabled, load all samples in the Soundfont */
|
||||||
|
if (!defsfont->dynamic_samples)
|
||||||
|
{
|
||||||
|
if (fluid_defsfont_load_all_sampledata(defsfont, sfdata) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Unable to load all sample data");
|
||||||
|
goto err_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Load all the presets */
|
/* Load all the presets */
|
||||||
p = sfdata->preset;
|
p = sfdata->preset;
|
||||||
while (p != NULL) {
|
while (p != NULL) {
|
||||||
|
@ -367,6 +494,11 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* defsfont, fluid_defpreset_t* def
|
||||||
fluid_defpreset_preset_noteon,
|
fluid_defpreset_preset_noteon,
|
||||||
fluid_defpreset_preset_delete);
|
fluid_defpreset_preset_delete);
|
||||||
|
|
||||||
|
if (defsfont->dynamic_samples)
|
||||||
|
{
|
||||||
|
preset->notify = dynamic_samples_preset_notify;
|
||||||
|
}
|
||||||
|
|
||||||
if (preset == NULL) {
|
if (preset == NULL) {
|
||||||
return FLUID_FAILED;
|
return FLUID_FAILED;
|
||||||
}
|
}
|
||||||
|
@ -378,29 +510,6 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* defsfont, fluid_defpreset_t* def
|
||||||
return FLUID_OK;
|
return FLUID_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* fluid_defsfont_load_sampledata
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
fluid_defsfont_load_sampledata(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t* fcbs)
|
|
||||||
{
|
|
||||||
SFData *sfdata;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
sfdata = fluid_sffile_load(defsfont->filename, fcbs);
|
|
||||||
if (sfdata == NULL) {
|
|
||||||
FLUID_LOG(FLUID_ERR, "Couldn't load soundfont file");
|
|
||||||
return FLUID_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* load sample data in one block */
|
|
||||||
ret = fluid_samplecache_load(sfdata, 0, sfdata->samplesize / 2, defsfont->mlock,
|
|
||||||
&defsfont->sampledata, &defsfont->sample24data);
|
|
||||||
|
|
||||||
fluid_sffile_close (sfdata);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fluid_defsfont_get_preset
|
* fluid_defsfont_get_preset
|
||||||
*/
|
*/
|
||||||
|
@ -1518,6 +1627,7 @@ fluid_sample_in_rom(fluid_sample_t* sample)
|
||||||
return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
|
return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fluid_sample_import_sfont
|
* fluid_sample_import_sfont
|
||||||
*/
|
*/
|
||||||
|
@ -1525,29 +1635,197 @@ int
|
||||||
fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* defsfont)
|
fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* defsfont)
|
||||||
{
|
{
|
||||||
FLUID_STRCPY(sample->name, sfsample->name);
|
FLUID_STRCPY(sample->name, sfsample->name);
|
||||||
sample->data = defsfont->sampledata;
|
|
||||||
sample->data24 = defsfont->sample24data;
|
sample->source_start = sfsample->start;
|
||||||
sample->start = sfsample->start;
|
sample->source_end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */
|
||||||
sample->end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */
|
sample->source_loopstart = sfsample->loopstart;
|
||||||
sample->loopstart = sfsample->loopstart;
|
sample->source_loopend = sfsample->loopend;
|
||||||
sample->loopend = sfsample->loopend;
|
|
||||||
|
sample->start = sample->source_start;
|
||||||
|
sample->end = sample->source_end;
|
||||||
|
sample->loopstart = sample->source_loopstart;
|
||||||
|
sample->loopend = sample->source_loopend;
|
||||||
sample->samplerate = sfsample->samplerate;
|
sample->samplerate = sfsample->samplerate;
|
||||||
sample->origpitch = sfsample->origpitch;
|
sample->origpitch = sfsample->origpitch;
|
||||||
sample->pitchadj = sfsample->pitchadj;
|
sample->pitchadj = sfsample->pitchadj;
|
||||||
sample->sampletype = sfsample->sampletype;
|
sample->sampletype = sfsample->sampletype;
|
||||||
|
|
||||||
|
if (defsfont->dynamic_samples)
|
||||||
|
{
|
||||||
|
sample->notify = dynamic_samples_sample_notify;
|
||||||
|
}
|
||||||
|
|
||||||
if (fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED)
|
if (fluid_sample_validate(sample, defsfont->samplesize) == FLUID_FAILED)
|
||||||
{
|
{
|
||||||
return FLUID_FAILED;
|
return FLUID_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)
|
return FLUID_OK;
|
||||||
&& fluid_sample_decompress_vorbis(sample) == FLUID_FAILED)
|
}
|
||||||
{
|
|
||||||
return FLUID_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
fluid_sample_sanitize_loop(sample, defsfont->samplesize);
|
/* Called if a sample is no longer used by a voice. Used by dynamic sample loading
|
||||||
|
* to unload a sample that is not used by any loaded presets anymore but couldn't
|
||||||
|
* be unloaded straight away because it was still in use by a voice. */
|
||||||
|
static int dynamic_samples_sample_notify(fluid_sample_t *sample, int reason)
|
||||||
|
{
|
||||||
|
if (reason == FLUID_SAMPLE_DONE && sample->preset_count == 0)
|
||||||
|
{
|
||||||
|
unload_sample(sample);
|
||||||
|
}
|
||||||
|
|
||||||
return FLUID_OK;
|
return FLUID_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Called if a preset has been selected for or unselected from a channel. Used by
|
||||||
|
* dynamic sample loading to load and unload samples on demand. */
|
||||||
|
static int dynamic_samples_preset_notify(fluid_preset_t *preset, int reason, int chan)
|
||||||
|
{
|
||||||
|
fluid_defsfont_t *defsfont;
|
||||||
|
|
||||||
|
if (reason == FLUID_PRESET_SELECTED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_DBG, "Selected preset '%s' on channel %d", fluid_preset_get_name(preset), chan);
|
||||||
|
defsfont = fluid_sfont_get_data(preset->sfont);
|
||||||
|
load_preset_samples(defsfont, preset);
|
||||||
|
}
|
||||||
|
else if (reason == FLUID_PRESET_UNSELECTED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_DBG, "Deselected preset '%s' from channel %d", fluid_preset_get_name(preset), chan);
|
||||||
|
defsfont = fluid_sfont_get_data(preset->sfont);
|
||||||
|
unload_preset_samples(defsfont, preset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Walk through all samples used by the passed in preset and make sure that the
|
||||||
|
* sample data is loaded for each sample. Used by dynamic sample loading. */
|
||||||
|
static int load_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset)
|
||||||
|
{
|
||||||
|
fluid_defpreset_t *defpreset;
|
||||||
|
fluid_preset_zone_t *preset_zone;
|
||||||
|
fluid_inst_t *inst;
|
||||||
|
fluid_inst_zone_t *inst_zone;
|
||||||
|
fluid_sample_t *sample;
|
||||||
|
SFData *sffile = NULL;
|
||||||
|
|
||||||
|
defpreset = fluid_preset_get_data(preset);
|
||||||
|
preset_zone = fluid_defpreset_get_zone(defpreset);
|
||||||
|
while (preset_zone != NULL)
|
||||||
|
{
|
||||||
|
inst = fluid_preset_zone_get_inst(preset_zone);
|
||||||
|
inst_zone = fluid_inst_get_zone(inst);
|
||||||
|
while (inst_zone != NULL) {
|
||||||
|
sample = fluid_inst_zone_get_sample(inst_zone);
|
||||||
|
|
||||||
|
if ((sample != NULL) && (sample->start != sample->end))
|
||||||
|
{
|
||||||
|
sample->preset_count++;
|
||||||
|
|
||||||
|
/* If this is the first time this sample has been selected,
|
||||||
|
* load the sampledata */
|
||||||
|
if (sample->preset_count == 1)
|
||||||
|
{
|
||||||
|
/* Make sure we have an open Soundfont file. Do this here
|
||||||
|
* to avoid having to open the file if no loading is necessary
|
||||||
|
* for a preset */
|
||||||
|
if (sffile == NULL)
|
||||||
|
{
|
||||||
|
sffile = fluid_sffile_open(defsfont->filename, defsfont->fcbs);
|
||||||
|
if (sffile == NULL)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Unable to open Soundfont file");
|
||||||
|
return FLUID_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fluid_defsfont_load_sampledata(defsfont, sffile, sample) == FLUID_OK)
|
||||||
|
{
|
||||||
|
fluid_sample_sanitize_loop(sample, (sample->end + 1) * sizeof(short));
|
||||||
|
fluid_voice_optimize_sample(sample);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Unable to load sample '%s', disabling", sample->name);
|
||||||
|
sample->start = sample->end = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inst_zone = fluid_inst_zone_next(inst_zone);
|
||||||
|
}
|
||||||
|
preset_zone = fluid_preset_zone_next(preset_zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sffile != NULL)
|
||||||
|
{
|
||||||
|
fluid_sffile_close(sffile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk through all samples used by the passed in preset and unload the sample data
|
||||||
|
* of each sample that is not used by any selected preset anymore. Used by dynamic
|
||||||
|
* sample loading. */
|
||||||
|
static int unload_preset_samples(fluid_defsfont_t *defsfont, fluid_preset_t *preset)
|
||||||
|
{
|
||||||
|
fluid_defpreset_t *defpreset;
|
||||||
|
fluid_preset_zone_t *preset_zone;
|
||||||
|
fluid_inst_t *inst;
|
||||||
|
fluid_inst_zone_t *inst_zone;
|
||||||
|
fluid_sample_t *sample;
|
||||||
|
|
||||||
|
defpreset = fluid_preset_get_data(preset);
|
||||||
|
preset_zone = fluid_defpreset_get_zone(defpreset);
|
||||||
|
while (preset_zone != NULL)
|
||||||
|
{
|
||||||
|
inst = fluid_preset_zone_get_inst(preset_zone);
|
||||||
|
inst_zone = fluid_inst_get_zone(inst);
|
||||||
|
while (inst_zone != NULL) {
|
||||||
|
sample = fluid_inst_zone_get_sample(inst_zone);
|
||||||
|
|
||||||
|
if ((sample != NULL) && (sample->preset_count > 0))
|
||||||
|
{
|
||||||
|
sample->preset_count--;
|
||||||
|
|
||||||
|
/* If the sample is not used by any preset or used by a
|
||||||
|
* sounding voice, unload it from the sample cache. If it's
|
||||||
|
* still in use by a voice, dynamic_samples_sample_notify will
|
||||||
|
* take care of unloading the sample as soon as the voice is
|
||||||
|
* finished with it (but only on the next API call). */
|
||||||
|
if (sample->preset_count == 0 && sample->refcount == 0)
|
||||||
|
{
|
||||||
|
unload_sample(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inst_zone = fluid_inst_zone_next(inst_zone);
|
||||||
|
}
|
||||||
|
preset_zone = fluid_preset_zone_next(preset_zone);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unload an unused sample from the samplecache */
|
||||||
|
static void unload_sample(fluid_sample_t *sample)
|
||||||
|
{
|
||||||
|
fluid_return_if_fail(sample != NULL);
|
||||||
|
fluid_return_if_fail(sample->data != NULL);
|
||||||
|
fluid_return_if_fail(sample->preset_count == 0);
|
||||||
|
fluid_return_if_fail(sample->refcount == 0);
|
||||||
|
|
||||||
|
FLUID_LOG(FLUID_DBG, "Unloading sample '%s'", sample->name);
|
||||||
|
|
||||||
|
if (fluid_samplecache_unload(sample->data) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Unable to unload sample '%s'", sample->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sample->data = NULL;
|
||||||
|
sample->data24 = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ int fluid_zone_inside_range(fluid_zone_range_t* zone_range, int key, int vel);
|
||||||
*/
|
*/
|
||||||
struct _fluid_defsfont_t
|
struct _fluid_defsfont_t
|
||||||
{
|
{
|
||||||
|
const fluid_file_callbacks_t* fcbs; /* the file callbacks used to load this Soundfont */
|
||||||
char* filename; /* the filename of this soundfont */
|
char* filename; /* the filename of this soundfont */
|
||||||
unsigned int samplepos; /* the position in the file at which the sample data starts */
|
unsigned int samplepos; /* the position in the file at which the sample data starts */
|
||||||
unsigned int samplesize; /* the size of the sample data in bytes */
|
unsigned int samplesize; /* the size of the sample data in bytes */
|
||||||
|
@ -107,6 +108,7 @@ struct _fluid_defsfont_t
|
||||||
fluid_list_t* sample; /* the samples in this soundfont */
|
fluid_list_t* sample; /* the samples in this soundfont */
|
||||||
fluid_list_t* preset; /* the presets of this soundfont */
|
fluid_list_t* preset; /* the presets of this soundfont */
|
||||||
int mlock; /* Should we try memlock (avoid swapping)? */
|
int mlock; /* Should we try memlock (avoid swapping)? */
|
||||||
|
int dynamic_samples; /* Enables dynamic sample loading if set */
|
||||||
|
|
||||||
fluid_list_t *preset_iter_cur; /* the current preset in the iteration */
|
fluid_list_t *preset_iter_cur; /* the current preset in the iteration */
|
||||||
};
|
};
|
||||||
|
@ -119,7 +121,9 @@ const char* fluid_defsfont_get_name(fluid_defsfont_t* defsfont);
|
||||||
fluid_preset_t* fluid_defsfont_get_preset(fluid_defsfont_t* defsfont, unsigned int bank, unsigned int prenum);
|
fluid_preset_t* fluid_defsfont_get_preset(fluid_defsfont_t* defsfont, unsigned int bank, unsigned int prenum);
|
||||||
void fluid_defsfont_iteration_start(fluid_defsfont_t* defsfont);
|
void fluid_defsfont_iteration_start(fluid_defsfont_t* defsfont);
|
||||||
fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t* defsfont);
|
fluid_preset_t *fluid_defsfont_iteration_next(fluid_defsfont_t* defsfont);
|
||||||
int fluid_defsfont_load_sampledata(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t* file_callbacks);
|
int fluid_defsfont_load_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata, fluid_sample_t *sample);
|
||||||
|
int fluid_defsfont_load_all_sampledata(fluid_defsfont_t *defsfont, SFData *sfdata);
|
||||||
|
|
||||||
int fluid_defsfont_add_sample(fluid_defsfont_t* defsfont, fluid_sample_t* sample);
|
int fluid_defsfont_add_sample(fluid_defsfont_t* defsfont, fluid_sample_t* sample);
|
||||||
int fluid_defsfont_add_preset(fluid_defsfont_t* defsfont, fluid_defpreset_t* defpreset);
|
int fluid_defsfont_add_preset(fluid_defsfont_t* defsfont, fluid_defpreset_t* defpreset);
|
||||||
|
|
||||||
|
@ -211,7 +215,6 @@ int fluid_inst_zone_import_sfont(fluid_preset_zone_t* preset_zone,
|
||||||
fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone);
|
fluid_sample_t* fluid_inst_zone_get_sample(fluid_inst_zone_t* zone);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* defsfont);
|
int fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* defsfont);
|
||||||
int fluid_sample_in_rom(fluid_sample_t* sample);
|
int fluid_sample_in_rom(fluid_sample_t* sample);
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,13 @@ struct _fluid_samplecache_entry_t
|
||||||
unsigned int sf_sample24pos;
|
unsigned int sf_sample24pos;
|
||||||
unsigned int sf_sample24size;
|
unsigned int sf_sample24size;
|
||||||
unsigned int sample_start;
|
unsigned int sample_start;
|
||||||
unsigned int sample_count;
|
unsigned int sample_end;
|
||||||
|
int sample_type;
|
||||||
/* End of cache key members */
|
/* End of cache key members */
|
||||||
|
|
||||||
short *sample_data;
|
short *sample_data;
|
||||||
char *sample_data24;
|
char *sample_data24;
|
||||||
|
int sample_count;
|
||||||
|
|
||||||
int num_references;
|
int num_references;
|
||||||
int mlocked;
|
int mlocked;
|
||||||
|
@ -58,8 +60,8 @@ struct _fluid_samplecache_entry_t
|
||||||
static fluid_list_t *samplecache_list = NULL;
|
static fluid_list_t *samplecache_list = NULL;
|
||||||
static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
|
static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
|
||||||
|
|
||||||
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_count);
|
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
|
||||||
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_count);
|
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
|
||||||
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
|
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
|
||||||
|
|
||||||
static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
|
static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
|
||||||
|
@ -68,7 +70,7 @@ static int fluid_get_file_modification_time(char *filename, time_t *modification
|
||||||
/* PUBLIC INTERFACE */
|
/* PUBLIC INTERFACE */
|
||||||
|
|
||||||
int fluid_samplecache_load(SFData *sf,
|
int fluid_samplecache_load(SFData *sf,
|
||||||
unsigned int sample_start, unsigned int sample_count,
|
unsigned int sample_start, unsigned int sample_end, int sample_type,
|
||||||
int try_mlock, short **sample_data, char **sample_data24)
|
int try_mlock, short **sample_data, char **sample_data24)
|
||||||
{
|
{
|
||||||
fluid_samplecache_entry_t *entry;
|
fluid_samplecache_entry_t *entry;
|
||||||
|
@ -76,13 +78,13 @@ int fluid_samplecache_load(SFData *sf,
|
||||||
|
|
||||||
fluid_mutex_lock(samplecache_mutex);
|
fluid_mutex_lock(samplecache_mutex);
|
||||||
|
|
||||||
entry = get_samplecache_entry(sf, sample_start, sample_count);
|
entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type);
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
{
|
{
|
||||||
entry = new_samplecache_entry(sf, sample_start, sample_count);
|
entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type);
|
||||||
if (entry == NULL)
|
if (entry == NULL)
|
||||||
{
|
{
|
||||||
ret = FLUID_FAILED;
|
ret = -1;
|
||||||
goto unlock_exit;
|
goto unlock_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +95,7 @@ int fluid_samplecache_load(SFData *sf,
|
||||||
{
|
{
|
||||||
/* Lock the memory to disable paging. It's okay if this fails. It
|
/* Lock the memory to disable paging. It's okay if this fails. It
|
||||||
* probably means that the user doesn't have the required permission. */
|
* probably means that the user doesn't have the required permission. */
|
||||||
if (fluid_mlock(entry->sample_data, entry->sample_count * 2) == 0)
|
if (fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0)
|
||||||
{
|
{
|
||||||
if (entry->sample_data24 != NULL)
|
if (entry->sample_data24 != NULL)
|
||||||
{
|
{
|
||||||
|
@ -106,7 +108,7 @@ int fluid_samplecache_load(SFData *sf,
|
||||||
|
|
||||||
if (!entry->mlocked)
|
if (!entry->mlocked)
|
||||||
{
|
{
|
||||||
fluid_munlock(entry->sample_data, entry->sample_count * 2);
|
fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));
|
||||||
FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
|
FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,7 +117,7 @@ int fluid_samplecache_load(SFData *sf,
|
||||||
entry->num_references++;
|
entry->num_references++;
|
||||||
*sample_data = entry->sample_data;
|
*sample_data = entry->sample_data;
|
||||||
*sample_data24 = entry->sample_data24;
|
*sample_data24 = entry->sample_data24;
|
||||||
ret = FLUID_OK;
|
ret = entry->sample_count;
|
||||||
|
|
||||||
unlock_exit:
|
unlock_exit:
|
||||||
fluid_mutex_unlock(samplecache_mutex);
|
fluid_mutex_unlock(samplecache_mutex);
|
||||||
|
@ -143,7 +145,7 @@ int fluid_samplecache_unload(const short *sample_data)
|
||||||
{
|
{
|
||||||
if (entry->mlocked)
|
if (entry->mlocked)
|
||||||
{
|
{
|
||||||
fluid_munlock(entry->sample_data, entry->sample_count * 2);
|
fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));
|
||||||
if (entry->sample_data24 != NULL)
|
if (entry->sample_data24 != NULL)
|
||||||
{
|
{
|
||||||
fluid_munlock(entry->sample_data24, entry->sample_count);
|
fluid_munlock(entry->sample_data24, entry->sample_count);
|
||||||
|
@ -173,7 +175,8 @@ unlock_exit:
|
||||||
/* Private functions */
|
/* Private functions */
|
||||||
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
|
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
|
||||||
unsigned int sample_start,
|
unsigned int sample_start,
|
||||||
unsigned int sample_count)
|
unsigned int sample_end,
|
||||||
|
int sample_type)
|
||||||
{
|
{
|
||||||
fluid_samplecache_entry_t *entry;
|
fluid_samplecache_entry_t *entry;
|
||||||
|
|
||||||
|
@ -203,10 +206,12 @@ static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
|
||||||
entry->sf_sample24pos = sf->sample24pos;
|
entry->sf_sample24pos = sf->sample24pos;
|
||||||
entry->sf_sample24size = sf->sample24size;
|
entry->sf_sample24size = sf->sample24size;
|
||||||
entry->sample_start = sample_start;
|
entry->sample_start = sample_start;
|
||||||
entry->sample_count = sample_count;
|
entry->sample_end = sample_end;
|
||||||
|
entry->sample_type = sample_type;
|
||||||
|
|
||||||
if (fluid_sffile_read_sample_data(sf, sample_start, sample_count,
|
entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
|
||||||
&entry->sample_data, &entry->sample_data24) == FLUID_FAILED)
|
&entry->sample_data, &entry->sample_data24);
|
||||||
|
if (entry->sample_count < 0)
|
||||||
{
|
{
|
||||||
goto error_exit;
|
goto error_exit;
|
||||||
}
|
}
|
||||||
|
@ -230,7 +235,8 @@ static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
|
||||||
|
|
||||||
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
|
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
|
||||||
unsigned int sample_start,
|
unsigned int sample_start,
|
||||||
unsigned int sample_count)
|
unsigned int sample_end,
|
||||||
|
int sample_type)
|
||||||
{
|
{
|
||||||
time_t mtime;
|
time_t mtime;
|
||||||
fluid_list_t *entry_list;
|
fluid_list_t *entry_list;
|
||||||
|
@ -254,7 +260,8 @@ static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
|
||||||
(sf->sample24pos == entry->sf_sample24pos) &&
|
(sf->sample24pos == entry->sf_sample24pos) &&
|
||||||
(sf->sample24size == entry->sf_sample24size) &&
|
(sf->sample24size == entry->sf_sample24size) &&
|
||||||
(sample_start == entry->sample_start) &&
|
(sample_start == entry->sample_start) &&
|
||||||
(sample_count == entry->sample_count))
|
(sample_end == entry->sample_end) &&
|
||||||
|
(sample_type == entry->sample_type))
|
||||||
{
|
{
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include "fluid_sffile.h"
|
#include "fluid_sffile.h"
|
||||||
|
|
||||||
int fluid_samplecache_load(SFData *sf,
|
int fluid_samplecache_load(SFData *sf,
|
||||||
unsigned int sample_start, unsigned int sample_count,
|
unsigned int sample_start, unsigned int sample_end, int sample_type,
|
||||||
int try_mlock, short **data, char **data24);
|
int try_mlock, short **data, char **data24);
|
||||||
|
|
||||||
int fluid_samplecache_unload(const short *sample_data);
|
int fluid_samplecache_unload(const short *sample_data);
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
#include "fluid_sfont.h"
|
#include "fluid_sfont.h"
|
||||||
#include "fluid_sys.h"
|
#include "fluid_sys.h"
|
||||||
|
|
||||||
|
#if LIBSNDFILE_SUPPORT
|
||||||
|
#include <sndfile.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/*=================================sfload.c========================
|
/*=================================sfload.c========================
|
||||||
Borrowed from Smurf SoundFont Editor by Josh Green
|
Borrowed from Smurf SoundFont Editor by Josh Green
|
||||||
=================================================================*/
|
=================================================================*/
|
||||||
|
@ -259,7 +263,8 @@ static const unsigned short invalid_preset_gen[] = {
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
static int load_body(SFData *sf, unsigned int size);
|
static int load_header(SFData *sf);
|
||||||
|
static int load_body(SFData *sf);
|
||||||
static int process_info(SFData *sf, int size);
|
static int process_info(SFData *sf, int size);
|
||||||
static int process_sdta(SFData *sf, unsigned int size);
|
static int process_sdta(SFData *sf, unsigned int size);
|
||||||
static int process_pdta(SFData *sf, int size);
|
static int process_pdta(SFData *sf, int size);
|
||||||
|
@ -288,15 +293,17 @@ static void delete_preset(SFPreset *preset);
|
||||||
static void delete_inst(SFInst *inst);
|
static void delete_inst(SFInst *inst);
|
||||||
static void delete_zone(SFZone *zone);
|
static void delete_zone(SFZone *zone);
|
||||||
|
|
||||||
|
static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data);
|
||||||
|
static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open a SoundFont file and parse it's contents into a SFData structure.
|
* Open a SoundFont file and parse it's contents into a SFData structure.
|
||||||
*
|
*
|
||||||
* @param fname filename
|
* @param fname filename
|
||||||
* @param fcbs file callback structure
|
* @param fcbs file callback structure
|
||||||
* @return the parsed SoundFont as SFData structure or NULL on error
|
* @return the partially parsed SoundFont as SFData structure or NULL on error
|
||||||
*/
|
*/
|
||||||
SFData *fluid_sffile_load(const char *fname, const fluid_file_callbacks_t *fcbs)
|
SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs)
|
||||||
{
|
{
|
||||||
SFData *sf;
|
SFData *sf;
|
||||||
int fsize = 0;
|
int fsize = 0;
|
||||||
|
@ -334,6 +341,7 @@ SFData *fluid_sffile_load(const char *fname, const fluid_file_callbacks_t *fcbs)
|
||||||
FLUID_LOG(FLUID_ERR, "Get end of file position failed");
|
FLUID_LOG(FLUID_ERR, "Get end of file position failed");
|
||||||
goto error_exit;
|
goto error_exit;
|
||||||
}
|
}
|
||||||
|
sf->filesize = fsize;
|
||||||
|
|
||||||
if (fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED)
|
if (fcbs->fseek(sf->sffd, 0, SEEK_SET) == FLUID_FAILED)
|
||||||
{
|
{
|
||||||
|
@ -341,7 +349,7 @@ SFData *fluid_sffile_load(const char *fname, const fluid_file_callbacks_t *fcbs)
|
||||||
goto error_exit;
|
goto error_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!load_body(sf, fsize))
|
if (!load_header(sf))
|
||||||
{
|
{
|
||||||
goto error_exit;
|
goto error_exit;
|
||||||
}
|
}
|
||||||
|
@ -353,110 +361,51 @@ error_exit:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load sample data from the soundfont file
|
/*
|
||||||
*
|
* Parse all preset information from the soundfont
|
||||||
* @param sf SFFile instance
|
|
||||||
* @param start start offset of sample data (in sample words from start of sample data chunk)
|
|
||||||
* @param count number of samples to read (in sample words)
|
|
||||||
* @param data pointer to sample data pointer, set on success
|
|
||||||
* @param data24 pointer to 24-bit sample data pointer if 24-bit data present, set on success
|
|
||||||
*
|
*
|
||||||
* @return FLUID_OK on success, otherwise FLUID_FAILED
|
* @return FLUID_OK on success, otherwise FLUID_FAILED
|
||||||
*/
|
*/
|
||||||
int fluid_sffile_read_sample_data(SFData *sf, unsigned int start, unsigned int count,
|
int fluid_sffile_parse_presets(SFData *sf)
|
||||||
short **data, char **data24)
|
|
||||||
{
|
{
|
||||||
unsigned int end;
|
if (!load_body(sf))
|
||||||
short *loaded_data = NULL;
|
|
||||||
char *loaded_data24 = NULL;
|
|
||||||
|
|
||||||
fluid_return_val_if_fail(count != 0, FLUID_FAILED);
|
|
||||||
|
|
||||||
end = start + count;
|
|
||||||
|
|
||||||
if (((start * 2) > sf->samplesize) || ((end * 2) > sf->samplesize))
|
|
||||||
{
|
{
|
||||||
FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk");
|
|
||||||
goto error_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load 16-bit sample data */
|
|
||||||
if (sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * 2), SEEK_SET) == FLUID_FAILED)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
|
|
||||||
goto error_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
loaded_data = FLUID_ARRAY(short, count);
|
|
||||||
if (loaded_data == NULL)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
||||||
goto error_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf->fcbs->fread(loaded_data, count * sizeof(short), sf->sffd) == FLUID_FAILED)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Failed to read sample data");
|
|
||||||
goto error_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this machine is big endian, byte swap the 16 bit samples */
|
|
||||||
if (FLUID_IS_BIG_ENDIAN)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
loaded_data[i] = FLUID_LE16TOH(loaded_data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*data = loaded_data;
|
|
||||||
|
|
||||||
/* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading
|
|
||||||
* the 24-bit sample data will be logged as errors but won't prevent the sample reading to
|
|
||||||
* fail, as sound output is still possible with the 16-bit sample data. */
|
|
||||||
if (sf->sample24pos)
|
|
||||||
{
|
|
||||||
if ((start > sf->sample24size) || (end > sf->sample24size))
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk");
|
|
||||||
goto error24_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file");
|
|
||||||
goto error24_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
loaded_data24 = FLUID_ARRAY(char, count);
|
|
||||||
if (loaded_data24 == NULL)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data");
|
|
||||||
goto error24_exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf->fcbs->fread(loaded_data24, count, sf->sffd) == FLUID_FAILED)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data");
|
|
||||||
goto error24_exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*data24 = loaded_data24;
|
|
||||||
|
|
||||||
return FLUID_OK;
|
|
||||||
|
|
||||||
error24_exit:
|
|
||||||
FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer");
|
|
||||||
FLUID_FREE(loaded_data24);
|
|
||||||
*data24 = NULL;
|
|
||||||
return FLUID_OK;
|
|
||||||
|
|
||||||
error_exit:
|
|
||||||
FLUID_FREE(loaded_data);
|
|
||||||
FLUID_FREE(loaded_data24);
|
|
||||||
return FLUID_FAILED;
|
return FLUID_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FLUID_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load sample data from the soundfont file
|
||||||
|
*
|
||||||
|
* This function will always return the sample data in WAV format. If the sample_type specifies an
|
||||||
|
* Ogg Vorbis compressed sample, it will be decompressed automatically before returning.
|
||||||
|
*
|
||||||
|
* @param sf SFData instance
|
||||||
|
* @param sample_start index of first sample point in Soundfont sample chunk
|
||||||
|
* @param sample_end index of last sample point in Soundfont sample chunk
|
||||||
|
* @param sample_type type of the sample in Soundfont
|
||||||
|
* @param data pointer to sample data pointer, will point to loaded sample data on success
|
||||||
|
* @param data24 pointer to 24-bit sample data pointer if 24-bit data present, will point to loaded
|
||||||
|
* 24-bit sample data on success or NULL if no 24-bit data is present in file
|
||||||
|
*
|
||||||
|
* @return The number of sample words in returned buffers or -1 on failure
|
||||||
|
*/
|
||||||
|
int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end,
|
||||||
|
int sample_type, short **data, char **data24)
|
||||||
|
{
|
||||||
|
int num_samples;
|
||||||
|
|
||||||
|
if (sample_type & FLUID_SAMPLETYPE_OGG_VORBIS)
|
||||||
|
{
|
||||||
|
num_samples = fluid_sffile_read_vorbis(sf, sample_start, sample_end, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
num_samples = fluid_sffile_read_wav(sf, sample_start, sample_end, data, data24);
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -534,7 +483,7 @@ static int chunkid(unsigned int id)
|
||||||
return UNKN_ID;
|
return UNKN_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_body(SFData *sf, unsigned int size)
|
static int load_header(SFData *sf)
|
||||||
{
|
{
|
||||||
SFChunk chunk;
|
SFChunk chunk;
|
||||||
|
|
||||||
|
@ -552,7 +501,7 @@ static int load_body(SFData *sf, unsigned int size)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunk.size != size - 8)
|
if (chunk.size != sf->filesize - 8)
|
||||||
{
|
{
|
||||||
FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch");
|
FLUID_LOG(FLUID_ERR, "SoundFont file size mismatch");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -588,11 +537,27 @@ static int load_body(SFData *sf, unsigned int size)
|
||||||
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk");
|
FLUID_LOG(FLUID_ERR, "Invalid ID found when expecting HYDRA chunk");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (!process_pdta(sf, chunk.size))
|
|
||||||
|
sf->hydrapos = sf->fcbs->ftell(sf->sffd);
|
||||||
|
sf->hydrasize = chunk.size;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_body(SFData *sf)
|
||||||
|
{
|
||||||
|
if (sf->fcbs->fseek(sf->sffd, sf->hydrapos, SEEK_SET) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to seek to HYDRA position");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process_pdta(sf, sf->hydrasize))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!fixup_pgen(sf))
|
if (!fixup_pgen(sf))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (!fixup_igen(sf))
|
if (!fixup_igen(sf))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
@ -1900,3 +1865,275 @@ static int valid_preset_genid(unsigned short genid)
|
||||||
i++;
|
i++;
|
||||||
return (invalid_preset_gen[i] == 0);
|
return (invalid_preset_gen[i] == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int fluid_sffile_read_wav(SFData *sf, unsigned int start, unsigned int end, short **data, char **data24)
|
||||||
|
{
|
||||||
|
short *loaded_data = NULL;
|
||||||
|
char *loaded_data24 = NULL;
|
||||||
|
|
||||||
|
int num_samples = (end + 1) - start;
|
||||||
|
fluid_return_val_if_fail(num_samples > 0, -1);
|
||||||
|
|
||||||
|
if ((start * sizeof(short) > sf->samplesize) || (end * sizeof(short) > sf->samplesize))
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load 16-bit sample data */
|
||||||
|
if (sf->fcbs->fseek(sf->sffd, sf->samplepos + (start * sizeof(short)), SEEK_SET) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to seek to sample position");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded_data = FLUID_ARRAY(short, num_samples);
|
||||||
|
if (loaded_data == NULL)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf->fcbs->fread(loaded_data, num_samples * sizeof(short), sf->sffd) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to read sample data");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this machine is big endian, byte swap the 16 bit samples */
|
||||||
|
if (FLUID_IS_BIG_ENDIAN)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < num_samples; i++)
|
||||||
|
{
|
||||||
|
loaded_data[i] = FLUID_LE16TOH(loaded_data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = loaded_data;
|
||||||
|
|
||||||
|
/* Optionally load additional 8 bit sample data for 24-bit support. Any failures while loading
|
||||||
|
* the 24-bit sample data will be logged as errors but won't prevent the sample reading to
|
||||||
|
* fail, as sound output is still possible with the 16-bit sample data. */
|
||||||
|
if (sf->sample24pos)
|
||||||
|
{
|
||||||
|
if ((start > sf->sample24size) || (end > sf->sample24size))
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit sample data chunk");
|
||||||
|
goto error24_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to seek position for 24-bit sample data in data file");
|
||||||
|
goto error24_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded_data24 = FLUID_ARRAY(char, num_samples);
|
||||||
|
if (loaded_data24 == NULL)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Out of memory reading 24-bit sample data");
|
||||||
|
goto error24_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf->fcbs->fread(loaded_data24, num_samples, sf->sffd) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data");
|
||||||
|
goto error24_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*data24 = loaded_data24;
|
||||||
|
|
||||||
|
return num_samples;
|
||||||
|
|
||||||
|
error24_exit:
|
||||||
|
FLUID_LOG(FLUID_WARN, "Ignoring 24-bit sample data, sound quality might suffer");
|
||||||
|
FLUID_FREE(loaded_data24);
|
||||||
|
*data24 = NULL;
|
||||||
|
return num_samples;
|
||||||
|
|
||||||
|
error_exit:
|
||||||
|
FLUID_FREE(loaded_data);
|
||||||
|
FLUID_FREE(loaded_data24);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Ogg Vorbis loading and decompression */
|
||||||
|
#if LIBSNDFILE_SUPPORT
|
||||||
|
|
||||||
|
/* Virtual file access rountines to allow loading individually compressed
|
||||||
|
* samples from the Soundfont sample data chunk using the file callbacks
|
||||||
|
* passing in during opening of the file */
|
||||||
|
typedef struct _sfvio_data_t
|
||||||
|
{
|
||||||
|
SFData *sffile;
|
||||||
|
sf_count_t start; /* start byte offset of compressed data */
|
||||||
|
sf_count_t end; /* end byte offset of compressed data */
|
||||||
|
sf_count_t offset; /* current virtual file offset from start byte offset */
|
||||||
|
|
||||||
|
} sfvio_data_t;
|
||||||
|
|
||||||
|
static sf_count_t sfvio_get_filelen(void *user_data)
|
||||||
|
{
|
||||||
|
sfvio_data_t *data = user_data;
|
||||||
|
|
||||||
|
return (data->end + 1) - data->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data)
|
||||||
|
{
|
||||||
|
sfvio_data_t *data = user_data;
|
||||||
|
SFData *sf = data->sffile;
|
||||||
|
sf_count_t new_offset;
|
||||||
|
|
||||||
|
switch (whence)
|
||||||
|
{
|
||||||
|
case SEEK_SET:
|
||||||
|
new_offset = offset;
|
||||||
|
break;
|
||||||
|
case SEEK_CUR:
|
||||||
|
new_offset = data->offset + offset;
|
||||||
|
break;
|
||||||
|
case SEEK_END:
|
||||||
|
new_offset = sfvio_get_filelen(user_data) + offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf->fcbs->fseek(sf->sffd, sf->samplepos + data->start + new_offset, SEEK_SET) != FLUID_FAILED) {
|
||||||
|
data->offset = new_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static sf_count_t sfvio_read(void* ptr, sf_count_t count, void* user_data)
|
||||||
|
{
|
||||||
|
sfvio_data_t *data = user_data;
|
||||||
|
SFData *sf = data->sffile;
|
||||||
|
sf_count_t remain;
|
||||||
|
|
||||||
|
remain = sfvio_get_filelen(user_data) - data->offset;
|
||||||
|
if (count > remain)
|
||||||
|
count = remain;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf->fcbs->fread(ptr, count, sf->sffd) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to read compressed sample data");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->offset += count;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static sf_count_t sfvio_tell(void* user_data)
|
||||||
|
{
|
||||||
|
sfvio_data_t *data = user_data;
|
||||||
|
|
||||||
|
return data->offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read Ogg Vorbis compressed data from the Soundfont and decompress it, returning the number of samples
|
||||||
|
* in the decompressed WAV. Only 16-bit mono samples are supported.
|
||||||
|
*
|
||||||
|
* Note that this function takes byte indices for start and end source data. The sample headers in SF3
|
||||||
|
* files use byte indices, so those pointers can be passed directly to this function.
|
||||||
|
*
|
||||||
|
* This function uses a virtual file structure in order to read the Ogg Vorbis
|
||||||
|
* data from arbitrary locations in the source file.
|
||||||
|
*/
|
||||||
|
static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data)
|
||||||
|
{
|
||||||
|
SNDFILE *sndfile;
|
||||||
|
SF_INFO sfinfo;
|
||||||
|
SF_VIRTUAL_IO sfvio = {
|
||||||
|
sfvio_get_filelen,
|
||||||
|
sfvio_seek,
|
||||||
|
sfvio_read,
|
||||||
|
NULL,
|
||||||
|
sfvio_tell
|
||||||
|
};
|
||||||
|
sfvio_data_t sfdata;
|
||||||
|
short *wav_data = NULL;
|
||||||
|
|
||||||
|
if ((start_byte > sf->samplesize) || (end_byte > sf->samplesize))
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Ogg Vorbis data offsets exceed sample data chunk");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize file position indicator and SF_INFO structure
|
||||||
|
sfdata.sffile = sf;
|
||||||
|
sfdata.start = start_byte;
|
||||||
|
sfdata.end = end_byte;
|
||||||
|
sfdata.offset = 0;
|
||||||
|
|
||||||
|
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||||
|
|
||||||
|
/* Seek to beginning of Ogg Vorbis data in Soundfont */
|
||||||
|
if (sf->fcbs->fseek(sf->sffd, sf->samplepos + start_byte, SEEK_SET) == FLUID_FAILED)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Failed to seek to compressd sample position");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open sample as a virtual file
|
||||||
|
sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, &sfdata);
|
||||||
|
if (!sndfile)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty sample
|
||||||
|
if (!sfinfo.frames || !sfinfo.channels)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_DBG, "Empty decompressed sample");
|
||||||
|
*data = NULL;
|
||||||
|
sf_close(sndfile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: ensure that the decompressed WAV data is 16-bit mono? */
|
||||||
|
|
||||||
|
wav_data = FLUID_ARRAY(short, sfinfo.frames * sfinfo.channels);
|
||||||
|
if (!wav_data)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatically decompresses the Ogg Vorbis data to 16-bit WAV */
|
||||||
|
if (sf_readf_short(sndfile, wav_data, sfinfo.frames) < sfinfo.frames)
|
||||||
|
{
|
||||||
|
FLUID_LOG(FLUID_DBG, "Decompression failed!");
|
||||||
|
FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
sf_close(sndfile);
|
||||||
|
|
||||||
|
*data = wav_data;
|
||||||
|
|
||||||
|
return sfinfo.frames;
|
||||||
|
|
||||||
|
error_exit:
|
||||||
|
FLUID_FREE(wav_data);
|
||||||
|
sf_close(sndfile);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int fluid_sffile_read_vorbis(SFData *sf, unsigned int start_byte, unsigned int end_byte, short **data)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -134,6 +134,8 @@ struct _SFData
|
||||||
SFVersion version; /* sound font version */
|
SFVersion version; /* sound font version */
|
||||||
SFVersion romver; /* ROM version */
|
SFVersion romver; /* ROM version */
|
||||||
|
|
||||||
|
unsigned int filesize;
|
||||||
|
|
||||||
unsigned int samplepos; /* position within sffd of the sample chunk */
|
unsigned int samplepos; /* position within sffd of the sample chunk */
|
||||||
unsigned int samplesize; /* length within sffd of the sample chunk */
|
unsigned int samplesize; /* length within sffd of the sample chunk */
|
||||||
|
|
||||||
|
@ -141,6 +143,9 @@ struct _SFData
|
||||||
sample support */
|
sample support */
|
||||||
unsigned int sample24size; /* length within sffd of the sm24 chunk */
|
unsigned int sample24size; /* length within sffd of the sm24 chunk */
|
||||||
|
|
||||||
|
unsigned int hydrapos;
|
||||||
|
unsigned int hydrasize;
|
||||||
|
|
||||||
char *fname; /* file name */
|
char *fname; /* file name */
|
||||||
FILE *sffd; /* loaded sfont file descriptor */
|
FILE *sffd; /* loaded sfont file descriptor */
|
||||||
const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */
|
const fluid_file_callbacks_t *fcbs; /* file callbacks used to read this file */
|
||||||
|
@ -204,9 +209,10 @@ struct _SFShdr
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Public functions */
|
/* Public functions */
|
||||||
SFData *fluid_sffile_load(const char *fname, const fluid_file_callbacks_t *fcbs);
|
SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs);
|
||||||
void fluid_sffile_close(SFData *sf);
|
void fluid_sffile_close(SFData *sf);
|
||||||
int fluid_sffile_read_sample_data(SFData *sf, unsigned int start, unsigned int count,
|
int fluid_sffile_parse_presets(SFData *sf);
|
||||||
short **data, char **data24);
|
int fluid_sffile_read_sample_data(SFData *sf, unsigned int sample_start, unsigned int sample_end,
|
||||||
|
int sample_type, short **data, char **data24);
|
||||||
|
|
||||||
#endif /* _FLUID_SFFILE_H */
|
#endif /* _FLUID_SFFILE_H */
|
||||||
|
|
|
@ -21,11 +21,6 @@
|
||||||
#include "fluid_sfont.h"
|
#include "fluid_sfont.h"
|
||||||
#include "fluid_sys.h"
|
#include "fluid_sys.h"
|
||||||
|
|
||||||
#if LIBSNDFILE_SUPPORT
|
|
||||||
#include <sndfile.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void * default_fopen(const char * path)
|
void * default_fopen(const char * path)
|
||||||
{
|
{
|
||||||
|
@ -607,16 +602,6 @@ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
|
||||||
* to the data word after the last sample. FIXME: why? */
|
* to the data word after the last sample. FIXME: why? */
|
||||||
unsigned int sample_end = sample->end + 1;
|
unsigned int sample_end = sample->end + 1;
|
||||||
|
|
||||||
/* Checking loops on compressed samples makes no sense at all and is really
|
|
||||||
* a programming error. Disable the loop to be on the safe side. */
|
|
||||||
if (sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Sample '%s': checking loop on compressed sample, disabling loop",
|
|
||||||
sample->name);
|
|
||||||
sample->loopstart = sample->loopend = 0;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sample->loopstart == sample->loopend)
|
if (sample->loopstart == sample->loopend)
|
||||||
{
|
{
|
||||||
/* Some SoundFonts disable loops by setting loopstart = loopend. While
|
/* Some SoundFonts disable loops by setting loopstart = loopend. While
|
||||||
|
@ -672,128 +657,3 @@ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
|
||||||
|
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LIBSNDFILE_SUPPORT
|
|
||||||
|
|
||||||
// virtual file access rountines to allow for handling
|
|
||||||
// samples as virtual files in memory
|
|
||||||
static sf_count_t
|
|
||||||
sfvio_get_filelen(void* user_data)
|
|
||||||
{
|
|
||||||
fluid_sample_t *sample = (fluid_sample_t *)user_data;
|
|
||||||
|
|
||||||
return (sf_count_t)(sample->end + 1 - sample->start);
|
|
||||||
}
|
|
||||||
|
|
||||||
static sf_count_t
|
|
||||||
sfvio_seek(sf_count_t offset, int whence, void* user_data)
|
|
||||||
{
|
|
||||||
fluid_sample_t *sample = (fluid_sample_t *)user_data;
|
|
||||||
|
|
||||||
switch (whence)
|
|
||||||
{
|
|
||||||
case SEEK_SET:
|
|
||||||
sample->userdata = (void *)offset;
|
|
||||||
break;
|
|
||||||
case SEEK_CUR:
|
|
||||||
sample->userdata = (void *)((sf_count_t)sample->userdata + offset);
|
|
||||||
break;
|
|
||||||
case SEEK_END:
|
|
||||||
sample->userdata = (void *)(sfvio_get_filelen(user_data) + offset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (sf_count_t)sample->userdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sf_count_t
|
|
||||||
sfvio_read(void* ptr, sf_count_t count, void* user_data)
|
|
||||||
{
|
|
||||||
fluid_sample_t *sample = (fluid_sample_t *)user_data;
|
|
||||||
sf_count_t remain = sfvio_get_filelen(user_data) - (sf_count_t)sample->userdata;
|
|
||||||
|
|
||||||
if (count > remain)
|
|
||||||
count = remain;
|
|
||||||
|
|
||||||
memcpy(ptr, (char *)sample->data + sample->start + (sf_count_t)sample->userdata, count);
|
|
||||||
sample->userdata = (void *)((sf_count_t)sample->userdata + count);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static sf_count_t
|
|
||||||
sfvio_tell (void* user_data)
|
|
||||||
{
|
|
||||||
fluid_sample_t *sample = (fluid_sample_t *)user_data;
|
|
||||||
|
|
||||||
return (sf_count_t)sample->userdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fluid_sample_decompress_vorbis(fluid_sample_t *sample)
|
|
||||||
{
|
|
||||||
SNDFILE *sndfile;
|
|
||||||
SF_INFO sfinfo;
|
|
||||||
SF_VIRTUAL_IO sfvio = {
|
|
||||||
sfvio_get_filelen,
|
|
||||||
sfvio_seek,
|
|
||||||
sfvio_read,
|
|
||||||
NULL,
|
|
||||||
sfvio_tell
|
|
||||||
};
|
|
||||||
short *sampledata_ogg;
|
|
||||||
|
|
||||||
// initialize file position indicator and SF_INFO structure
|
|
||||||
g_assert(sample->userdata == NULL);
|
|
||||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
|
||||||
|
|
||||||
// open sample as a virtual file in memory
|
|
||||||
sndfile = sf_open_virtual(&sfvio, SFM_READ, &sfinfo, sample);
|
|
||||||
if (!sndfile)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
|
|
||||||
return FLUID_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty sample
|
|
||||||
if (!sfinfo.frames || !sfinfo.channels)
|
|
||||||
{
|
|
||||||
sample->start = sample->end = 0;
|
|
||||||
sample->loopstart = sample->loopend = 0;
|
|
||||||
sample->data = NULL;
|
|
||||||
sf_close(sndfile);
|
|
||||||
return FLUID_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allocate memory for uncompressed sample data stream
|
|
||||||
sampledata_ogg = (short *)FLUID_MALLOC(sfinfo.frames * sfinfo.channels * sizeof(short));
|
|
||||||
if (!sampledata_ogg)
|
|
||||||
{
|
|
||||||
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
||||||
sf_close(sndfile);
|
|
||||||
return FLUID_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// uncompress sample data stream
|
|
||||||
if (sf_readf_short(sndfile, sampledata_ogg, sfinfo.frames) < sfinfo.frames)
|
|
||||||
{
|
|
||||||
FLUID_FREE(sampledata_ogg);
|
|
||||||
FLUID_LOG(FLUID_ERR, sf_strerror(sndfile));
|
|
||||||
sf_close(sndfile);
|
|
||||||
return FLUID_FAILED;
|
|
||||||
}
|
|
||||||
sf_close(sndfile);
|
|
||||||
|
|
||||||
// point sample data to uncompressed data stream
|
|
||||||
sample->data = sampledata_ogg;
|
|
||||||
sample->auto_free = TRUE;
|
|
||||||
sample->start = 0;
|
|
||||||
sample->end = sfinfo.frames - 1;
|
|
||||||
|
|
||||||
return FLUID_OK;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int fluid_sample_decompress_vorbis(fluid_sample_t *sample)
|
|
||||||
{
|
|
||||||
return FLUID_FAILED;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end);
|
int fluid_sample_validate(fluid_sample_t *sample, unsigned int max_end);
|
||||||
int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end);
|
int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int max_end);
|
||||||
int fluid_sample_decompress_vorbis(fluid_sample_t *sample);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Utility macros to access soundfonts, presets, and samples
|
* Utility macros to access soundfonts, presets, and samples
|
||||||
|
@ -177,10 +176,22 @@ struct _fluid_preset_t {
|
||||||
struct _fluid_sample_t
|
struct _fluid_sample_t
|
||||||
{
|
{
|
||||||
char name[21]; /**< Sample name */
|
char name[21]; /**< Sample name */
|
||||||
|
|
||||||
|
/* The following for sample pointers store the original pointers from the Soundfont
|
||||||
|
* file. They are never changed after loading and are used to re-create the
|
||||||
|
* actual sample pointers after a sample has been unloaded and loaded again. The
|
||||||
|
* actual sample pointers get modified during loading for SF3 (compressed) samples
|
||||||
|
* and individually loaded SF2 samples. */
|
||||||
|
unsigned int source_start;
|
||||||
|
unsigned int source_end;
|
||||||
|
unsigned int source_loopstart;
|
||||||
|
unsigned int source_loopend;
|
||||||
|
|
||||||
unsigned int start; /**< Start index */
|
unsigned int start; /**< Start index */
|
||||||
unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */
|
unsigned int end; /**< End index, index of last valid sample point (contrary to SF spec) */
|
||||||
unsigned int loopstart; /**< Loop start index */
|
unsigned int loopstart; /**< Loop start index */
|
||||||
unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */
|
unsigned int loopend; /**< Loop end index, first point following the loop (superimposed on loopstart) */
|
||||||
|
|
||||||
unsigned int samplerate; /**< Sample rate */
|
unsigned int samplerate; /**< Sample rate */
|
||||||
int origpitch; /**< Original pitch (MIDI note number, 0-127) */
|
int origpitch; /**< Original pitch (MIDI note number, 0-127) */
|
||||||
int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */
|
int pitchadj; /**< Fine pitch adjustment (+/- 99 cents) */
|
||||||
|
@ -193,6 +204,7 @@ struct _fluid_sample_t
|
||||||
double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */
|
double amplitude_that_reaches_noise_floor; /**< The amplitude at which the sample's loop will be below the noise floor. For voice off optimization, calculated automatically. */
|
||||||
|
|
||||||
unsigned int refcount; /**< Count of voices using this sample */
|
unsigned int refcount; /**< Count of voices using this sample */
|
||||||
|
int preset_count; /**< Count of selected presets using this sample (used for dynamic sample loading) */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this function to receive notification when sample is no longer used.
|
* Implement this function to receive notification when sample is no longer used.
|
||||||
|
|
|
@ -228,6 +228,8 @@ void fluid_synth_settings(fluid_settings_t* settings)
|
||||||
fluid_settings_add_option(settings, "synth.midi-bank-select", "gs");
|
fluid_settings_add_option(settings, "synth.midi-bank-select", "gs");
|
||||||
fluid_settings_add_option(settings, "synth.midi-bank-select", "xg");
|
fluid_settings_add_option(settings, "synth.midi-bank-select", "xg");
|
||||||
fluid_settings_add_option(settings, "synth.midi-bank-select", "mma");
|
fluid_settings_add_option(settings, "synth.midi-bank-select", "mma");
|
||||||
|
|
||||||
|
fluid_settings_register_int(settings, "synth.dynamic-sample-loading", 0, 0, 1, FLUID_HINT_TOGGLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,18 +4,17 @@ project(fluidsynth-test)
|
||||||
ENABLE_TESTING()
|
ENABLE_TESTING()
|
||||||
include ( FluidUnitTest )
|
include ( FluidUnitTest )
|
||||||
|
|
||||||
|
# first define the test target, used by the macros below
|
||||||
|
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --output-on-failure)
|
||||||
|
|
||||||
|
|
||||||
## add unit tests here ##
|
## add unit tests here ##
|
||||||
ADD_FLUID_TEST(test_sample_cache)
|
ADD_FLUID_TEST(test_sample_cache)
|
||||||
ADD_FLUID_TEST(test_sfont_loading)
|
ADD_FLUID_TEST(test_sfont_loading)
|
||||||
ADD_FLUID_TEST(test_defsfont_preset_iteration)
|
ADD_FLUID_TEST(test_defsfont_preset_iteration)
|
||||||
ADD_FLUID_TEST(test_sample_rate_change)
|
ADD_FLUID_TEST(test_sample_rate_change)
|
||||||
|
|
||||||
|
if ( LIBSNDFILE_HASVORBIS )
|
||||||
add_custom_target(check
|
ADD_FLUID_TEST(test_sf3_sfont_loading)
|
||||||
COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --output-on-failure
|
ADD_FLUID_TEST(test_sf3_defsfont_preset_iteration)
|
||||||
DEPENDS
|
endif ( LIBSNDFILE_HASVORBIS )
|
||||||
test_sample_cache
|
|
||||||
test_sfont_loading
|
|
||||||
test_defsfont_preset_iteration
|
|
||||||
test_sample_rate_change
|
|
||||||
)
|
|
||||||
|
|
61
test/test_sf3_defsfont_preset_iteration.c
Normal file
61
test/test_sf3_defsfont_preset_iteration.c
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include "test.h"
|
||||||
|
#include "fluidsynth.h"
|
||||||
|
#include "sfloader/fluid_sfont.h"
|
||||||
|
#include "sfloader/fluid_defsfont.h"
|
||||||
|
#include "utils/fluidsynth_priv.h"
|
||||||
|
#include "utils/fluid_list.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
fluid_sfont_t *sfont;
|
||||||
|
fluid_list_t *list;
|
||||||
|
fluid_preset_t *preset;
|
||||||
|
fluid_preset_t *prev_preset = NULL;
|
||||||
|
fluid_defsfont_t *defsfont;
|
||||||
|
fluid_sample_t *sample;
|
||||||
|
fluid_sample_t *prev_sample = NULL;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/* setup */
|
||||||
|
fluid_settings_t *settings = new_fluid_settings();
|
||||||
|
fluid_synth_t *synth = new_fluid_synth(settings);
|
||||||
|
id = fluid_synth_sfload(synth, TEST_SOUNDFONT_SF3, 1);
|
||||||
|
sfont = fluid_synth_get_sfont_by_id(synth, id);
|
||||||
|
defsfont = fluid_sfont_get_data(sfont);
|
||||||
|
|
||||||
|
/* Make sure we have the right number of presets */
|
||||||
|
fluid_sfont_iteration_start(sfont);
|
||||||
|
while ((preset = fluid_sfont_iteration_next(sfont)) != NULL) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
/* make sure we actually got a different preset */
|
||||||
|
TEST_ASSERT(preset != prev_preset);
|
||||||
|
prev_preset = preset;
|
||||||
|
}
|
||||||
|
/* VintageDreams has 136 presets */
|
||||||
|
TEST_ASSERT(count == 136);
|
||||||
|
|
||||||
|
/* Make sure we have the right number of samples */
|
||||||
|
count = 0;
|
||||||
|
for (list = defsfont->sample; list; list = fluid_list_next(list))
|
||||||
|
{
|
||||||
|
sample = fluid_list_get(list);
|
||||||
|
if (sample->data != NULL)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we actually got a different sample */
|
||||||
|
TEST_ASSERT(sample != prev_sample);
|
||||||
|
prev_sample = sample;
|
||||||
|
}
|
||||||
|
/* VintageDreams has 123 valid samples (one is a ROM sample and ignored) */
|
||||||
|
TEST_ASSERT(count == 123);
|
||||||
|
|
||||||
|
/* teardown */
|
||||||
|
delete_fluid_synth(synth);
|
||||||
|
delete_fluid_settings(settings);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
70
test/test_sf3_sfont_loading.c
Normal file
70
test/test_sf3_sfont_loading.c
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#include "test.h"
|
||||||
|
#include "fluidsynth.h"
|
||||||
|
#include "sfloader/fluid_sfont.h"
|
||||||
|
#include "utils/fluidsynth_priv.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
fluid_sfont_t *sfont;
|
||||||
|
|
||||||
|
fluid_settings_t *settings = new_fluid_settings();
|
||||||
|
fluid_synth_t *synth = new_fluid_synth(settings);
|
||||||
|
|
||||||
|
TEST_ASSERT(settings != NULL);
|
||||||
|
TEST_ASSERT(synth != NULL);
|
||||||
|
|
||||||
|
// no sfont loaded
|
||||||
|
TEST_ASSERT(fluid_synth_sfcount(synth) == 0);
|
||||||
|
|
||||||
|
FLUID_LOG(FLUID_INFO, "Attempt to open %s", TEST_SOUNDFONT_SF3);
|
||||||
|
|
||||||
|
// load a sfont to synth
|
||||||
|
TEST_SUCCESS(id = fluid_synth_sfload(synth, TEST_SOUNDFONT_SF3, 1));
|
||||||
|
|
||||||
|
// one sfont loaded
|
||||||
|
TEST_ASSERT(fluid_synth_sfcount(synth) == 1);
|
||||||
|
|
||||||
|
sfont = fluid_synth_get_sfont_by_id(synth, id);
|
||||||
|
TEST_ASSERT(sfont != NULL);
|
||||||
|
|
||||||
|
// this is still the same filename as we've put in
|
||||||
|
TEST_ASSERT(FLUID_STRCMP(TEST_SOUNDFONT_SF3, fluid_sfont_get_name(sfont)) == 0);
|
||||||
|
TEST_ASSERT(fluid_sfont_get_id(sfont) == id);
|
||||||
|
|
||||||
|
// still the same id?
|
||||||
|
TEST_ASSERT(fluid_synth_sfreload(synth, id) == id);
|
||||||
|
// one sfont loaded
|
||||||
|
TEST_ASSERT(fluid_synth_sfcount(synth) == 1);
|
||||||
|
|
||||||
|
sfont = fluid_synth_get_sfont_by_id(synth, id);
|
||||||
|
TEST_ASSERT(sfont != NULL);
|
||||||
|
|
||||||
|
// still the same filename?
|
||||||
|
TEST_ASSERT(FLUID_STRCMP(TEST_SOUNDFONT_SF3, fluid_sfont_get_name(sfont)) == 0);
|
||||||
|
// correct id stored?
|
||||||
|
TEST_ASSERT(fluid_sfont_get_id(sfont) == id);
|
||||||
|
|
||||||
|
// remove the sfont without deleting
|
||||||
|
TEST_SUCCESS(fluid_synth_remove_sfont(synth, sfont));
|
||||||
|
|
||||||
|
// no sfont loaded
|
||||||
|
TEST_ASSERT(fluid_synth_sfcount(synth) == 0);
|
||||||
|
|
||||||
|
// re-add the sfont without deleting
|
||||||
|
TEST_SUCCESS(id = fluid_synth_add_sfont(synth, sfont));
|
||||||
|
|
||||||
|
// one sfont loaded
|
||||||
|
TEST_ASSERT(fluid_synth_sfcount(synth) == 1);
|
||||||
|
|
||||||
|
// destroy the sfont
|
||||||
|
TEST_SUCCESS(fluid_synth_sfunload(synth, id, 0));
|
||||||
|
// no sfont loaded
|
||||||
|
TEST_ASSERT(fluid_synth_sfcount(synth) == 0);
|
||||||
|
|
||||||
|
delete_fluid_synth(synth);
|
||||||
|
delete_fluid_settings(settings);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in a new issue