summaryrefslogtreecommitdiff
path: root/libs/fluidsynth/src/fluid_samplecache.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/fluidsynth/src/fluid_samplecache.c')
-rw-r--r--libs/fluidsynth/src/fluid_samplecache.c295
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;
+}