mirror of
https://github.com/ZDoom/fluidsynth.git
synced 2025-04-08 08:41:21 +00:00
Enable decompressed Ogg Vorbis samples to be stored in sample cache
This change moves the Ogg Vorbis decompression to fluid_sffile, so that this is the only place where we have to deal with compressed audio. It also changes the way the samples are loaded for SF3 files: previously, the compressed data was copied into memory, then the individual samples were decompressed (resulting in both compressed and decompressed data to stay in memory). Also, decompressed data wasn't cached, so previous loads of the same file ran the decompressed again for each sample. After this change, the vorbis decompression is changed so that it reads the compressed data directly from the Soundfont file. And the resulting WAV data is stored in the sample cache.
This commit is contained in:
parent
f52bbf53a4
commit
a985c68a13
8 changed files with 432 additions and 299 deletions
|
@ -252,6 +252,99 @@ const char* fluid_defsfont_get_name(fluid_defsfont_t* defsfont)
|
|||
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;
|
||||
|
||||
num_samples = fluid_samplecache_load(
|
||||
sfdata, sample->start, sample->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->loopstart - sample->start;
|
||||
sample->loopend = sample->loopend - sample->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
|
||||
|
@ -290,14 +383,7 @@ int fluid_defsfont_load(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t
|
|||
defsfont->sample24pos = sfdata->sample24pos;
|
||||
defsfont->sample24size = sfdata->sample24size;
|
||||
|
||||
/* load sample data in one block */
|
||||
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 */
|
||||
/* Create all samples from sample headers */
|
||||
p = sfdata->sample;
|
||||
while (p != NULL) {
|
||||
sfsample = (SFSample *)fluid_list_get(p);
|
||||
|
@ -311,15 +397,22 @@ int fluid_defsfont_load(fluid_defsfont_t* defsfont, const fluid_file_callbacks_t
|
|||
sfsample->fluid_sample = sample;
|
||||
|
||||
fluid_defsfont_add_sample(defsfont, sample);
|
||||
fluid_voice_optimize_sample(sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete_fluid_sample(sample);
|
||||
}
|
||||
|
||||
p = fluid_list_next(p);
|
||||
}
|
||||
|
||||
/* Load all sample data */
|
||||
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 */
|
||||
p = sfdata->preset;
|
||||
while (p != NULL) {
|
||||
|
@ -382,29 +475,6 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* defsfont, fluid_defpreset_t* def
|
|||
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_open(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
|
||||
*/
|
||||
|
@ -1522,6 +1592,7 @@ fluid_sample_in_rom(fluid_sample_t* sample)
|
|||
return (sample->sampletype & FLUID_SAMPLETYPE_ROM);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* fluid_sample_import_sfont
|
||||
*/
|
||||
|
@ -1529,8 +1600,6 @@ int
|
|||
fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defsfont_t* defsfont)
|
||||
{
|
||||
FLUID_STRCPY(sample->name, sfsample->name);
|
||||
sample->data = defsfont->sampledata;
|
||||
sample->data24 = defsfont->sample24data;
|
||||
sample->start = sfsample->start;
|
||||
sample->end = (sfsample->end > 0) ? sfsample->end - 1 : 0; /* marks last sample, contrary to SF spec. */
|
||||
sample->loopstart = sfsample->loopstart;
|
||||
|
@ -1545,13 +1614,5 @@ fluid_sample_import_sfont(fluid_sample_t* sample, SFSample* sfsample, fluid_defs
|
|||
return FLUID_FAILED;
|
||||
}
|
||||
|
||||
if ((sample->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS)
|
||||
&& fluid_sample_decompress_vorbis(sample) == FLUID_FAILED)
|
||||
{
|
||||
return FLUID_FAILED;
|
||||
}
|
||||
|
||||
fluid_sample_sanitize_loop(sample, defsfont->samplesize);
|
||||
|
||||
return FLUID_OK;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,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);
|
||||
void fluid_defsfont_iteration_start(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_preset(fluid_defsfont_t* defsfont, fluid_defpreset_t* defpreset);
|
||||
|
||||
|
|
|
@ -45,11 +45,13 @@ struct _fluid_samplecache_entry_t
|
|||
unsigned int sf_sample24pos;
|
||||
unsigned int sf_sample24size;
|
||||
unsigned int sample_start;
|
||||
unsigned int sample_count;
|
||||
unsigned int sample_end;
|
||||
int sample_type;
|
||||
/* End of cache key members */
|
||||
|
||||
short *sample_data;
|
||||
char *sample_data24;
|
||||
int sample_count;
|
||||
|
||||
int num_references;
|
||||
int mlocked;
|
||||
|
@ -58,8 +60,8 @@ struct _fluid_samplecache_entry_t
|
|||
static fluid_list_t *samplecache_list = NULL;
|
||||
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 *get_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_end, int sample_type);
|
||||
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
|
||||
|
||||
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 */
|
||||
|
||||
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)
|
||||
{
|
||||
fluid_samplecache_entry_t *entry;
|
||||
|
@ -76,13 +78,13 @@ int fluid_samplecache_load(SFData *sf,
|
|||
|
||||
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)
|
||||
{
|
||||
entry = new_samplecache_entry(sf, sample_start, sample_count);
|
||||
entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type);
|
||||
if (entry == NULL)
|
||||
{
|
||||
ret = FLUID_FAILED;
|
||||
ret = -1;
|
||||
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
|
||||
* 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)
|
||||
{
|
||||
|
@ -106,7 +108,7 @@ int fluid_samplecache_load(SFData *sf,
|
|||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +117,7 @@ int fluid_samplecache_load(SFData *sf,
|
|||
entry->num_references++;
|
||||
*sample_data = entry->sample_data;
|
||||
*sample_data24 = entry->sample_data24;
|
||||
ret = FLUID_OK;
|
||||
ret = entry->sample_count;
|
||||
|
||||
unlock_exit:
|
||||
fluid_mutex_unlock(samplecache_mutex);
|
||||
|
@ -143,7 +145,7 @@ int fluid_samplecache_unload(const short *sample_data)
|
|||
{
|
||||
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)
|
||||
{
|
||||
fluid_munlock(entry->sample_data24, entry->sample_count);
|
||||
|
@ -173,7 +175,8 @@ unlock_exit:
|
|||
/* Private functions */
|
||||
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
|
||||
unsigned int sample_start,
|
||||
unsigned int sample_count)
|
||||
unsigned int sample_end,
|
||||
int sample_type)
|
||||
{
|
||||
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_sample24size = sf->sample24size;
|
||||
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_data, &entry->sample_data24) == FLUID_FAILED)
|
||||
entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
|
||||
&entry->sample_data, &entry->sample_data24);
|
||||
if (entry->sample_count < 0)
|
||||
{
|
||||
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,
|
||||
unsigned int sample_start,
|
||||
unsigned int sample_count)
|
||||
unsigned int sample_end,
|
||||
int sample_type)
|
||||
{
|
||||
time_t mtime;
|
||||
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->sample24size == entry->sf_sample24size) &&
|
||||
(sample_start == entry->sample_start) &&
|
||||
(sample_count == entry->sample_count))
|
||||
(sample_end == entry->sample_end) &&
|
||||
(sample_type == entry->sample_type))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "fluid_sffile.h"
|
||||
|
||||
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 fluid_samplecache_unload(const short *sample_data);
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#include "fluid_sfont.h"
|
||||
#include "fluid_sys.h"
|
||||
|
||||
#if LIBSNDFILE_SUPPORT
|
||||
#include <sndfile.h>
|
||||
#endif
|
||||
|
||||
/*=================================sfload.c========================
|
||||
Borrowed from Smurf SoundFont Editor by Josh Green
|
||||
=================================================================*/
|
||||
|
@ -289,6 +293,8 @@ static void delete_preset(SFPreset *preset);
|
|||
static void delete_inst(SFInst *inst);
|
||||
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.
|
||||
|
@ -372,108 +378,34 @@ int fluid_sffile_parse_presets(SFData *sf)
|
|||
|
||||
/* Load sample data from the soundfont file
|
||||
*
|
||||
* @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
|
||||
* 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.
|
||||
*
|
||||
* @return FLUID_OK on success, otherwise FLUID_FAILED
|
||||
* @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 start, unsigned int count,
|
||||
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)
|
||||
{
|
||||
unsigned int end;
|
||||
short *loaded_data = NULL;
|
||||
char *loaded_data24 = NULL;
|
||||
int num_samples;
|
||||
|
||||
fluid_return_val_if_fail(count != 0, FLUID_FAILED);
|
||||
|
||||
end = start + count;
|
||||
|
||||
if (((start * 2) > sf->samplesize) || ((end * 2) > sf->samplesize))
|
||||
if (sample_type & FLUID_SAMPLETYPE_OGG_VORBIS)
|
||||
{
|
||||
FLUID_LOG(FLUID_ERR, "Sample offsets exceed sample data chunk");
|
||||
goto error_exit;
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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 num_samples;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1933,3 +1865,275 @@ static int valid_preset_genid(unsigned short genid)
|
|||
i++;
|
||||
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
|
||||
|
|
|
@ -212,7 +212,7 @@ struct _SFShdr
|
|||
SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs);
|
||||
void fluid_sffile_close(SFData *sf);
|
||||
int fluid_sffile_parse_presets(SFData *sf);
|
||||
int fluid_sffile_read_sample_data(SFData *sf, unsigned int start, unsigned int count,
|
||||
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 */
|
||||
|
|
|
@ -21,11 +21,6 @@
|
|||
#include "fluid_sfont.h"
|
||||
#include "fluid_sys.h"
|
||||
|
||||
#if LIBSNDFILE_SUPPORT
|
||||
#include <sndfile.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void * default_fopen(const char * path)
|
||||
{
|
||||
|
@ -605,16 +600,6 @@ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
|
|||
* to the data word after the last sample. FIXME: why? */
|
||||
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)
|
||||
{
|
||||
/* Some SoundFonts disable loops by setting loopstart = loopend. While
|
||||
|
@ -670,128 +655,3 @@ int fluid_sample_sanitize_loop(fluid_sample_t *sample, unsigned int buffer_size)
|
|||
|
||||
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_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
|
||||
|
|
Loading…
Reference in a new issue