Refactor sample cache loader

- move sample reading to fluid_sffile
- refactor sample cache to use fluid_list and separate long functions into
  smaller ones
- include sample start and count in cache key, in preparation for lazy loading
- make defsfont use new sample cache loader interface
This commit is contained in:
Marcus Weseloh 2018-04-04 11:03:47 +02:00
parent 5bc2d33bb9
commit 4932b4af90
5 changed files with 288 additions and 228 deletions

View file

@ -286,7 +286,7 @@ int delete_fluid_defsfont(fluid_defsfont_t* sfont)
} }
if (sfont->sampledata != NULL) { if (sfont->sampledata != NULL) {
fluid_cached_sampledata_unload(sfont->sampledata); fluid_samplecache_unload(sfont->sampledata);
} }
while (sfont->preset_stack_size > 0) while (sfont->preset_stack_size > 0)
@ -347,8 +347,11 @@ int fluid_defsfont_load(fluid_defsfont_t* sfont, const fluid_file_callbacks_t* f
sfont->sample24size = sfdata->sample24size; sfont->sample24size = sfdata->sample24size;
/* load sample data in one block */ /* load sample data in one block */
if (fluid_defsfont_load_sampledata(sfont, fcbs) != FLUID_OK) if (fluid_samplecache_load(sfdata, 0, sfdata->samplesize / 2, sfont->mlock,
&sfont->sampledata, &sfont->sample24data) == FLUID_FAILED)
{
goto err_exit; goto err_exit;
}
/* Create all the sample headers */ /* Create all the sample headers */
p = sfdata->sample; p = sfdata->sample;
@ -443,11 +446,21 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset
int int
fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont, const fluid_file_callbacks_t* fcbs) fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont, const fluid_file_callbacks_t* fcbs)
{ {
return fluid_cached_sampledata_load(sfont->filename, SFData *sfdata;
sfont->samplepos, sfont->samplesize, &sfont->sampledata, int ret;
sfont->sample24pos, sfont->sample24size, &sfont->sample24data,
sfont->mlock, sfdata = fluid_sffile_load(sfont->filename, fcbs);
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, sfont->mlock,
&sfont->sampledata, &sfont->sample24data);
fluid_sffile_close (sfdata);
return ret;
} }
/* /*

View file

@ -21,287 +21,238 @@
* 02110-1301, USA * 02110-1301, USA
*/ */
/* CACHED SAMPLE DATA LOADER
*
* This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read
* data across all FluidSynth instances in a global (process-wide) list.
*/
#include "fluid_samplecache.h" #include "fluid_samplecache.h"
#include "fluid_sys.h" #include "fluid_sys.h"
#include "fluidsynth.h" #include "fluidsynth.h"
#include "fluid_list.h"
/***************************************************************
*
* CACHED SAMPLEDATA LOADER
*/
typedef struct _fluid_cached_sampledata_t fluid_cached_sampledata_t; typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t;
struct _fluid_cached_sampledata_t struct _fluid_samplecache_entry_t
{ {
fluid_cached_sampledata_t *next; /* The follwing members all form the cache key */
char *filename; char *filename;
time_t modification_time; time_t modification_time;
unsigned int sf_samplepos;
unsigned int sf_samplesize;
unsigned int sf_sample24pos;
unsigned int sf_sample24size;
unsigned int sample_start;
unsigned int sample_count;
/* End of cache key members */
short *sample_data;
char *sample_data24;
int num_references; int num_references;
int mlock; int mlocked;
short *sampledata;
unsigned int samplesize;
char *sample24data;
unsigned int sample24size;
}; };
static fluid_list_t *samplecache_list = NULL;
static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
static fluid_cached_sampledata_t *all_cached_sampledata = NULL; static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_count);
static fluid_mutex_t cached_sampledata_mutex = FLUID_MUTEX_INIT; static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_count);
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);
/* PUBLIC INTERFACE */ /* PUBLIC INTERFACE */
int fluid_cached_sampledata_load(char *filename, int fluid_samplecache_load(SFData *sf,
unsigned int samplepos, unsigned int sample_start, unsigned int sample_count,
unsigned int samplesize, int try_mlock, short **sample_data, char **sample_data24)
short **sampledata,
unsigned int sample24pos,
unsigned int sample24size,
char **sample24data,
int try_mlock,
const fluid_file_callbacks_t *fcbs)
{ {
fluid_file fd = NULL; fluid_samplecache_entry_t *entry;
short *loaded_sampledata = NULL;
char *loaded_sample24data = NULL;
fluid_cached_sampledata_t *cached_sampledata = NULL;
time_t modification_time;
fluid_mutex_lock(cached_sampledata_mutex); fluid_mutex_lock(samplecache_mutex);
if (fluid_get_file_modification_time(filename, &modification_time) == FLUID_FAILED) entry = get_samplecache_entry(sf, sample_start, sample_count);
if (entry == NULL)
{ {
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file."); entry = new_samplecache_entry(sf, sample_start, sample_count);
modification_time = 0; if (entry == NULL)
}
for (cached_sampledata = all_cached_sampledata; cached_sampledata;
cached_sampledata = cached_sampledata->next)
{ {
if (FLUID_STRCMP(filename, cached_sampledata->filename))
continue;
if (cached_sampledata->modification_time != modification_time)
continue;
if (cached_sampledata->samplesize != samplesize || cached_sampledata->sample24size != sample24size)
continue;
if (try_mlock && !cached_sampledata->mlock)
{
if (fluid_mlock(cached_sampledata->sampledata, samplesize) != 0)
FLUID_LOG(FLUID_WARN,
"Failed to pin the sample data to RAM; swapping is possible.");
else
cached_sampledata->mlock = try_mlock;
if (cached_sampledata->sample24data != NULL)
if (fluid_mlock(cached_sampledata->sample24data, sample24size) != 0)
FLUID_LOG(FLUID_WARN,
"Failed to pin the sample24 data to RAM; swapping is possible.");
}
cached_sampledata->num_references++;
loaded_sampledata = cached_sampledata->sampledata;
loaded_sample24data = cached_sampledata->sample24data;
goto success_exit;
}
fd = fcbs->fopen(filename);
if (fd == NULL)
{
FLUID_LOG(FLUID_ERR, "Can't open soundfont file");
goto error_exit;
}
if (fcbs->fseek(fd, samplepos, SEEK_SET) == FLUID_FAILED)
{
perror("error");
FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
goto error_exit;
}
loaded_sampledata = (short *)FLUID_MALLOC(samplesize);
if (loaded_sampledata == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_exit;
}
if (fcbs->fread(loaded_sampledata, samplesize, fd) == FLUID_FAILED)
{
FLUID_LOG(FLUID_ERR, "Failed to read sample data");
goto error_exit;
}
if (sample24pos > 0)
{
if (fcbs->fseek(fd, sample24pos, SEEK_SET) == FLUID_FAILED)
{
perror("error");
FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
goto error_exit;
}
loaded_sample24data = (char *)FLUID_MALLOC(sample24size);
if (loaded_sample24data == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory when allocating 24bit sample, ignoring");
}
else if (fcbs->fread(loaded_sample24data, sample24size, fd) == FLUID_FAILED)
{
FLUID_LOG(FLUID_ERR, "Failed to read sample24 data");
FLUID_FREE(loaded_sample24data);
loaded_sample24data = NULL;
}
}
fcbs->fclose(fd);
fd = NULL;
cached_sampledata = (fluid_cached_sampledata_t *)FLUID_MALLOC(sizeof(fluid_cached_sampledata_t));
if (cached_sampledata == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
goto error_exit;
}
/* Lock the memory to disable paging. It's okay if this fails. It
probably means that the user doesn't have the required permission. */
cached_sampledata->mlock = 0;
if (try_mlock)
{
if (fluid_mlock(loaded_sampledata, samplesize) != 0)
FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
else
cached_sampledata->mlock = try_mlock;
}
/* If this machine is big endian, the sample have to byte swapped */
if (FLUID_IS_BIG_ENDIAN)
{
unsigned char *cbuf;
unsigned char hi, lo;
unsigned int i, j;
short s;
cbuf = (unsigned char *)loaded_sampledata;
for (i = 0, j = 0; j < samplesize; i++)
{
lo = cbuf[j++];
hi = cbuf[j++];
s = (hi << 8) | lo;
loaded_sampledata[i] = s;
}
}
cached_sampledata->filename = FLUID_STRDUP(filename);
if (cached_sampledata->filename == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
goto error_exit;
}
cached_sampledata->modification_time = modification_time;
cached_sampledata->num_references = 1;
cached_sampledata->sampledata = loaded_sampledata;
cached_sampledata->samplesize = samplesize;
cached_sampledata->sample24data = loaded_sample24data;
cached_sampledata->sample24size = sample24size;
cached_sampledata->next = all_cached_sampledata;
all_cached_sampledata = cached_sampledata;
success_exit:
fluid_mutex_unlock(cached_sampledata_mutex);
*sampledata = loaded_sampledata;
*sample24data = loaded_sample24data;
return FLUID_OK;
error_exit:
if (fd != NULL)
{
fcbs->fclose(fd);
}
FLUID_FREE(loaded_sampledata);
FLUID_FREE(loaded_sample24data);
if (cached_sampledata != NULL)
{
FLUID_FREE(cached_sampledata->filename);
}
FLUID_FREE(cached_sampledata);
fluid_mutex_unlock(cached_sampledata_mutex);
*sampledata = NULL;
*sample24data = NULL;
return FLUID_FAILED; return FLUID_FAILED;
} }
int fluid_cached_sampledata_unload(const short *sampledata) samplecache_list = fluid_list_prepend(samplecache_list, entry);
{
fluid_cached_sampledata_t *prev = NULL;
fluid_cached_sampledata_t *cached_sampledata;
fluid_mutex_lock(cached_sampledata_mutex);
cached_sampledata = all_cached_sampledata;
while (cached_sampledata != NULL)
{
if (sampledata == cached_sampledata->sampledata)
{
cached_sampledata->num_references--;
if (cached_sampledata->num_references == 0)
{
if (cached_sampledata->mlock)
{
fluid_munlock(cached_sampledata->sampledata, cached_sampledata->samplesize);
fluid_munlock(cached_sampledata->sample24data, cached_sampledata->sample24size);
}
FLUID_FREE(cached_sampledata->sampledata);
FLUID_FREE(cached_sampledata->sample24data);
FLUID_FREE(cached_sampledata->filename);
if (prev != NULL)
{
prev->next = cached_sampledata->next;
}
else
{
all_cached_sampledata = cached_sampledata->next;
} }
FLUID_FREE(cached_sampledata); if (try_mlock && !entry->mlocked)
{
/* Lock the memory to disable paging. It's okay if this fails. It
* probably means that the user doesn't have the required permission. */
entry->mlocked = (fluid_mlock(entry->sample_data, entry->sample_count * 2) == 0);
if (entry->sample_data24 != NULL)
{
entry->mlocked &= (fluid_mlock(entry->sample_data24, entry->sample_count) == 0);
}
if (!entry->mlocked)
{
FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
}
}
entry->num_references++;
*sample_data = entry->sample_data;
*sample_data24 = entry->sample_data24;
fluid_mutex_unlock(samplecache_mutex);
return FLUID_OK;
}
int fluid_samplecache_unload(const short *sample_data)
{
fluid_list_t *entry_list;
fluid_samplecache_entry_t *entry;
fluid_mutex_lock(samplecache_mutex);
entry_list = samplecache_list;
while (entry_list)
{
entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);
if (sample_data == entry->sample_data)
{
entry->num_references--;
if (entry->num_references == 0)
{
if (entry->mlocked)
{
fluid_munlock(entry->sample_data, entry->sample_count * 2);
fluid_munlock(entry->sample_data24, entry->sample_count);
}
fluid_list_remove(samplecache_list, entry);
delete_samplecache_entry(entry);
} }
goto success_exit; goto success_exit;
} }
prev = cached_sampledata; entry_list = fluid_list_next(entry_list);
cached_sampledata = cached_sampledata->next;
} }
FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache.");
goto error_exit; goto error_exit;
success_exit: success_exit:
fluid_mutex_unlock(cached_sampledata_mutex); fluid_mutex_unlock(samplecache_mutex);
return FLUID_OK; return FLUID_OK;
error_exit: error_exit:
fluid_mutex_unlock(cached_sampledata_mutex); fluid_mutex_unlock(samplecache_mutex);
return FLUID_FAILED; return FLUID_FAILED;
} }
/* Private functions */ /* Private functions */
static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
unsigned int sample_start,
unsigned int sample_count)
{
fluid_samplecache_entry_t *entry;
entry = FLUID_NEW(fluid_samplecache_entry_t);
if (entry == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
FLUID_MEMSET(entry, 0, sizeof(*entry));
entry->filename = FLUID_STRDUP(sf->fname);
if (entry->filename == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_exit;
}
if (fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED)
{
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
entry->modification_time = 0;
}
entry->sf_samplepos = sf->samplepos;
entry->sf_samplesize = sf->samplesize;
entry->sf_sample24pos = sf->sample24pos;
entry->sf_sample24size = sf->sample24size;
entry->sample_start = sample_start;
entry->sample_count = sample_count;
if (fluid_sffile_read_sample_data(sf, sample_start, sample_count,
&entry->sample_data, &entry->sample_data24) == FLUID_FAILED)
{
goto error_exit;
}
return entry;
error_exit:
delete_samplecache_entry(entry);
return NULL;
}
static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
{
fluid_return_if_fail(entry != NULL);
FLUID_FREE(entry->filename);
FLUID_FREE(entry->sample_data);
FLUID_FREE(entry->sample_data24);
FLUID_FREE(entry);
}
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
unsigned int sample_start,
unsigned int sample_count)
{
time_t mtime;
fluid_list_t *entry_list;
fluid_samplecache_entry_t *entry;
if (fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
{
FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
mtime = 0;
}
entry_list = samplecache_list;
while (entry_list)
{
entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);
if ((FLUID_STRCMP(sf->fname, entry->filename) == 0) &&
(mtime == entry->modification_time) &&
(sf->samplepos == entry->sf_samplepos) &&
(sf->samplesize == entry->sf_samplesize) &&
(sf->sample24pos == entry->sf_sample24pos) &&
(sf->sample24size == entry->sf_sample24size) &&
(sample_start == entry->sample_start) &&
(sample_count == entry->sample_count))
{
return entry;
}
entry_list = fluid_list_next(entry_list);
}
return NULL;
}
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)
{ {

View file

@ -23,17 +23,12 @@
#define _FLUID_SAMPLECACHE_H #define _FLUID_SAMPLECACHE_H
#include "fluid_sfont.h" #include "fluid_sfont.h"
#include "fluid_sffile.h"
int fluid_cached_sampledata_load(char *filename, int fluid_samplecache_load(SFData *sf,
unsigned int samplepos, unsigned int sample_start, unsigned int sample_count,
unsigned int samplesize, int try_mlock, short **data, char **data24);
short **sampledata,
unsigned int sample24pos,
unsigned int sample24size,
char **sample24data,
int try_mlock,
const fluid_file_callbacks_t *fcbs);
int fluid_cached_sampledata_unload(const short *sampledata); int fluid_samplecache_unload(const short *sample_data);
#endif /* _FLUID_SAMPLECACHE_H */ #endif /* _FLUID_SAMPLECACHE_H */

View file

@ -353,6 +353,105 @@ error_exit:
return NULL; return NULL;
} }
/* 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
*
* @return FLUID_OK on success, otherwise FLUID_FAILED
*/
int fluid_sffile_read_sample_data(SFData *sf, unsigned int start, unsigned int count,
short **data, char **data24)
{
unsigned int end;
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;
}
if (sf->sample24pos && ((start > sf->sample24size) || (end > sf->sample24size)))
{
FLUID_LOG(FLUID_ERR, "Sample offsets exceed 24-bit 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)
{
perror("error");
FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
goto error_exit;
}
loaded_data = (short *)FLUID_MALLOC(count * 2);
if (loaded_data == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
goto error_exit;
}
if (sf->fcbs->fread(loaded_data, count * 2, 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]);
}
}
/* Optionally load additional 8 bit sample data for 24-bit support */
if (sf->sample24pos)
{
if (sf->fcbs->fseek(sf->sffd, sf->sample24pos + start, SEEK_SET) == FLUID_FAILED)
{
perror("error");
FLUID_LOG(FLUID_ERR, "Failed to seek position in data file");
goto error_exit;
}
loaded_data24 = (char *)FLUID_MALLOC(count);
if (loaded_data24 == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return FLUID_FAILED;
}
if (sf->fcbs->fread(loaded_data24, count, sf->sffd) == FLUID_FAILED)
{
FLUID_LOG(FLUID_ERR, "Failed to read 24-bit sample data");
goto error_exit;
}
}
*data = loaded_data;
*data24 = loaded_data24;
return FLUID_OK;
error_exit:
FLUID_FREE(loaded_data);
FLUID_FREE(loaded_data24);
return FLUID_FAILED;
}
/* /*
* Close a SoundFont file and free the SFData structure. * Close a SoundFont file and free the SFData structure.
* *

View file

@ -206,5 +206,7 @@ struct _SFShdr
/* Public functions */ /* Public functions */
SFData *fluid_sffile_load(const char *fname, const fluid_file_callbacks_t *fcbs); SFData *fluid_sffile_load(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,
short **data, char **data24);
#endif /* _FLUID_SFFILE_H */ #endif /* _FLUID_SFFILE_H */