From 4932b4af90561f08b888807096bb8305b33b7b39 Mon Sep 17 00:00:00 2001 From: Marcus Weseloh Date: Wed, 4 Apr 2018 11:03:47 +0200 Subject: [PATCH] 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 --- src/sfloader/fluid_defsfont.c | 27 ++- src/sfloader/fluid_samplecache.c | 373 ++++++++++++++----------------- src/sfloader/fluid_samplecache.h | 15 +- src/sfloader/fluid_sffile.c | 99 ++++++++ src/sfloader/fluid_sffile.h | 2 + 5 files changed, 288 insertions(+), 228 deletions(-) diff --git a/src/sfloader/fluid_defsfont.c b/src/sfloader/fluid_defsfont.c index 7e13d1fc..02e30e4d 100644 --- a/src/sfloader/fluid_defsfont.c +++ b/src/sfloader/fluid_defsfont.c @@ -286,7 +286,7 @@ int delete_fluid_defsfont(fluid_defsfont_t* sfont) } if (sfont->sampledata != NULL) { - fluid_cached_sampledata_unload(sfont->sampledata); + fluid_samplecache_unload(sfont->sampledata); } 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; /* 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; + } /* Create all the sample headers */ p = sfdata->sample; @@ -443,11 +446,21 @@ int fluid_defsfont_add_preset(fluid_defsfont_t* sfont, fluid_defpreset_t* preset int fluid_defsfont_load_sampledata(fluid_defsfont_t* sfont, const fluid_file_callbacks_t* fcbs) { - return fluid_cached_sampledata_load(sfont->filename, - sfont->samplepos, sfont->samplesize, &sfont->sampledata, - sfont->sample24pos, sfont->sample24size, &sfont->sample24data, - sfont->mlock, - fcbs); + SFData *sfdata; + int ret; + + sfdata = fluid_sffile_load(sfont->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, sfont->mlock, + &sfont->sampledata, &sfont->sample24data); + + fluid_sffile_close (sfdata); + return ret; } /* diff --git a/src/sfloader/fluid_samplecache.c b/src/sfloader/fluid_samplecache.c index 415a36ad..48734ba0 100644 --- a/src/sfloader/fluid_samplecache.c +++ b/src/sfloader/fluid_samplecache.c @@ -21,287 +21,238 @@ * 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_sys.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; 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 mlock; - - short *sampledata; - unsigned int samplesize; - - char *sample24data; - unsigned int sample24size; + int mlocked; }; +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_mutex_t cached_sampledata_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 void delete_samplecache_entry(fluid_samplecache_entry_t *entry); static int fluid_get_file_modification_time(char *filename, time_t *modification_time); /* PUBLIC INTERFACE */ -int fluid_cached_sampledata_load(char *filename, - unsigned int samplepos, - unsigned int samplesize, - short **sampledata, - unsigned int sample24pos, - unsigned int sample24size, - char **sample24data, - int try_mlock, - const fluid_file_callbacks_t *fcbs) +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_count, + int try_mlock, short **sample_data, char **sample_data24) { - fluid_file fd = NULL; - short *loaded_sampledata = NULL; - char *loaded_sample24data = NULL; - fluid_cached_sampledata_t *cached_sampledata = NULL; - time_t modification_time; + fluid_samplecache_entry_t *entry; - 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."); - modification_time = 0; - } - - 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) + entry = new_samplecache_entry(sf, sample_start, sample_count); + if (entry == NULL) { - 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."); + return FLUID_FAILED; } - cached_sampledata->num_references++; - loaded_sampledata = cached_sampledata->sampledata; - loaded_sample24data = cached_sampledata->sample24data; - goto success_exit; + samplecache_list = fluid_list_prepend(samplecache_list, entry); } - fd = fcbs->fopen(filename); - if (fd == NULL) + if (try_mlock && !entry->mlocked) { - 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) + /* 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) { - perror("error"); - FLUID_LOG(FLUID_ERR, "Failed to seek position in data file"); - goto error_exit; + entry->mlocked &= (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); } - loaded_sample24data = (char *)FLUID_MALLOC(sample24size); - if (loaded_sample24data == NULL) + if (!entry->mlocked) { - 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; - } + entry->num_references++; + *sample_data = entry->sample_data; + *sample_data24 = entry->sample_data24; - 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; + fluid_mutex_unlock(samplecache_mutex); - 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; } -int fluid_cached_sampledata_unload(const short *sampledata) +int fluid_samplecache_unload(const short *sample_data) { - fluid_cached_sampledata_t *prev = NULL; - fluid_cached_sampledata_t *cached_sampledata; + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; - fluid_mutex_lock(cached_sampledata_mutex); - cached_sampledata = all_cached_sampledata; + fluid_mutex_lock(samplecache_mutex); - while (cached_sampledata != NULL) + entry_list = samplecache_list; + while (entry_list) { - if (sampledata == cached_sampledata->sampledata) + entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list); + + if (sample_data == entry->sample_data) { + entry->num_references--; - cached_sampledata->num_references--; - - if (cached_sampledata->num_references == 0) + if (entry->num_references == 0) { - if (cached_sampledata->mlock) + if (entry->mlocked) { - 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_munlock(entry->sample_data, entry->sample_count * 2); + fluid_munlock(entry->sample_data24, entry->sample_count); } - FLUID_FREE(cached_sampledata); + fluid_list_remove(samplecache_list, entry); + delete_samplecache_entry(entry); } goto success_exit; } - prev = cached_sampledata; - cached_sampledata = cached_sampledata->next; + entry_list = fluid_list_next(entry_list); } - FLUID_LOG(FLUID_ERR, "Trying to free sampledata not found in cache."); + FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); goto error_exit; success_exit: - fluid_mutex_unlock(cached_sampledata_mutex); + fluid_mutex_unlock(samplecache_mutex); return FLUID_OK; error_exit: - fluid_mutex_unlock(cached_sampledata_mutex); + fluid_mutex_unlock(samplecache_mutex); return FLUID_FAILED; } /* 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) { diff --git a/src/sfloader/fluid_samplecache.h b/src/sfloader/fluid_samplecache.h index 3fdeb8dc..704ddba1 100644 --- a/src/sfloader/fluid_samplecache.h +++ b/src/sfloader/fluid_samplecache.h @@ -23,17 +23,12 @@ #define _FLUID_SAMPLECACHE_H #include "fluid_sfont.h" +#include "fluid_sffile.h" -int fluid_cached_sampledata_load(char *filename, - unsigned int samplepos, - unsigned int samplesize, - short **sampledata, - unsigned int sample24pos, - unsigned int sample24size, - char **sample24data, - int try_mlock, - const fluid_file_callbacks_t *fcbs); +int fluid_samplecache_load(SFData *sf, + unsigned int sample_start, unsigned int sample_count, + int try_mlock, short **data, char **data24); -int fluid_cached_sampledata_unload(const short *sampledata); +int fluid_samplecache_unload(const short *sample_data); #endif /* _FLUID_SAMPLECACHE_H */ diff --git a/src/sfloader/fluid_sffile.c b/src/sfloader/fluid_sffile.c index c6474367..04b52960 100644 --- a/src/sfloader/fluid_sffile.c +++ b/src/sfloader/fluid_sffile.c @@ -353,6 +353,105 @@ error_exit: 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. * diff --git a/src/sfloader/fluid_sffile.h b/src/sfloader/fluid_sffile.h index ac6f6651..eca67394 100644 --- a/src/sfloader/fluid_sffile.h +++ b/src/sfloader/fluid_sffile.h @@ -206,5 +206,7 @@ struct _SFShdr /* Public functions */ SFData *fluid_sffile_load(const char *fname, const fluid_file_callbacks_t *fcbs); 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 */