diff options
Diffstat (limited to 'libs/fluidsynth/src/fluid_samplecache.c')
-rw-r--r-- | libs/fluidsynth/src/fluid_samplecache.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/libs/fluidsynth/src/fluid_samplecache.c b/libs/fluidsynth/src/fluid_samplecache.c new file mode 100644 index 0000000000..773e19feea --- /dev/null +++ b/libs/fluidsynth/src/fluid_samplecache.c @@ -0,0 +1,295 @@ +/* FluidSynth - A Software Synthesizer + * + * Copyright (C) 2003 Peter Hanappe and others. + * + * SoundFont file loading code borrowed from Smurf SoundFont Editor + * Copyright (C) 1999-2001 Josh Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 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" + + +typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t; + +struct _fluid_samplecache_entry_t +{ + /* 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_end; + int sample_type; + /* End of cache key members */ + + short *sample_data; + char *sample_data24; + int sample_count; + + int num_references; + int mlocked; +}; + +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_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); + + +/* PUBLIC INTERFACE */ + +int fluid_samplecache_load(SFData *sf, + 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; + int ret; + + fluid_mutex_lock(samplecache_mutex); + + entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type); + + if(entry == NULL) + { + entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type); + + if(entry == NULL) + { + ret = -1; + goto unlock_exit; + } + + samplecache_list = fluid_list_prepend(samplecache_list, entry); + } + + 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. */ + if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0) + { + if(entry->sample_data24 != NULL) + { + entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0); + } + else + { + entry->mlocked = TRUE; + } + + if(!entry->mlocked) + { + 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."); + } + } + } + + entry->num_references++; + *sample_data = entry->sample_data; + *sample_data24 = entry->sample_data24; + ret = entry->sample_count; + +unlock_exit: + fluid_mutex_unlock(samplecache_mutex); + return ret; +} + +int fluid_samplecache_unload(const short *sample_data) +{ + fluid_list_t *entry_list; + fluid_samplecache_entry_t *entry; + int ret; + + 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 * sizeof(short)); + + if(entry->sample_data24 != NULL) + { + fluid_munlock(entry->sample_data24, entry->sample_count); + } + } + + samplecache_list = fluid_list_remove(samplecache_list, entry); + delete_samplecache_entry(entry); + } + + ret = FLUID_OK; + goto unlock_exit; + } + + entry_list = fluid_list_next(entry_list); + } + + FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache."); + ret = FLUID_FAILED; + +unlock_exit: + fluid_mutex_unlock(samplecache_mutex); + return ret; +} + + +/* Private functions */ +static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, + unsigned int sample_start, + unsigned int sample_end, + int sample_type) +{ + 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_end = sample_end; + entry->sample_type = sample_type; + + 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; + } + + 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_end, + int sample_type) +{ + 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_end == entry->sample_end) && + (sample_type == entry->sample_type)) + { + return entry; + } + + entry_list = fluid_list_next(entry_list); + } + + return NULL; +} + +static int fluid_get_file_modification_time(char *filename, time_t *modification_time) +{ + fluid_stat_buf_t buf; + + if(fluid_stat(filename, &buf)) + { + return FLUID_FAILED; + } + + *modification_time = buf.st_mtime; + return FLUID_OK; +} |