Path: blob/master/libs/fluidsynth/src/sfloader/fluid_samplecache.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* SoundFont file loading code borrowed from Smurf SoundFont Editor5* Copyright (C) 1999-2001 Josh Green6*7* This library is free software; you can redistribute it and/or8* modify it under the terms of the GNU Lesser General Public License9* as published by the Free Software Foundation; either version 2.1 of10* the License, or (at your option) any later version.11*12* This library is distributed in the hope that it will be useful, but13* WITHOUT ANY WARRANTY; without even the implied warranty of14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU15* Lesser General Public License for more details.16*17* You should have received a copy of the GNU Lesser General Public18* License along with this library; if not, write to the Free19* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA20* 02110-1301, USA21*/2223/* CACHED SAMPLE DATA LOADER24*25* This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read26* data across all FluidSynth instances in a global (process-wide) list.27*/2829#include "fluid_samplecache.h"30#include "fluid_sys.h"31#include "fluid_list.h"323334typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t;3536struct _fluid_samplecache_entry_t37{38/* The following members all form the cache key */39char *filename;40time_t modification_time;41unsigned int sf_samplepos;42unsigned int sf_samplesize;43unsigned int sf_sample24pos;44unsigned int sf_sample24size;45unsigned int sample_start;46unsigned int sample_end;47int sample_type;48/* End of cache key members */4950short *sample_data;51char *sample_data24;52int sample_count;5354int num_references;55int mlocked;56};5758static fluid_list_t *samplecache_list = NULL;59static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;6061static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start,62unsigned int sample_end, int sample_type, time_t mtime);63static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start,64unsigned int sample_end, int sample_type, time_t mtime);65static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);6667static int fluid_get_file_modification_time(char *filename, time_t *modification_time);686970/* PUBLIC INTERFACE */7172int fluid_samplecache_load(SFData *sf,73unsigned int sample_start, unsigned int sample_end, int sample_type,74int try_mlock, short **sample_data, char **sample_data24)75{76fluid_samplecache_entry_t *entry;77int ret;78time_t mtime;7980fluid_mutex_lock(samplecache_mutex);8182if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)83{84mtime = 0;85}8687entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);8889if(entry == NULL)90{91fluid_mutex_unlock(samplecache_mutex);92entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);9394if(entry == NULL)95{96ret = -1;97goto unlock_exit;98}99100fluid_mutex_lock(samplecache_mutex);101samplecache_list = fluid_list_prepend(samplecache_list, entry);102}103fluid_mutex_unlock(samplecache_mutex);104105if(try_mlock && !entry->mlocked)106{107/* Lock the memory to disable paging. It's okay if this fails. It108* probably means that the user doesn't have the required permission. */109if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0)110{111if(entry->sample_data24 != NULL)112{113entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0);114}115else116{117entry->mlocked = TRUE;118}119120if(!entry->mlocked)121{122fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));123FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");124}125}126}127128entry->num_references++;129*sample_data = entry->sample_data;130*sample_data24 = entry->sample_data24;131ret = entry->sample_count;132133unlock_exit:134return ret;135}136137int fluid_samplecache_unload(const short *sample_data)138{139fluid_list_t *entry_list;140fluid_samplecache_entry_t *entry;141int ret;142143fluid_mutex_lock(samplecache_mutex);144145entry_list = samplecache_list;146147while(entry_list)148{149entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);150151if(sample_data == entry->sample_data)152{153entry->num_references--;154155if(entry->num_references == 0)156{157if(entry->mlocked)158{159fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));160161if(entry->sample_data24 != NULL)162{163fluid_munlock(entry->sample_data24, entry->sample_count);164}165}166167samplecache_list = fluid_list_remove(samplecache_list, entry);168delete_samplecache_entry(entry);169}170171ret = FLUID_OK;172goto unlock_exit;173}174175entry_list = fluid_list_next(entry_list);176}177178FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache.");179ret = FLUID_FAILED;180181unlock_exit:182fluid_mutex_unlock(samplecache_mutex);183return ret;184}185186187/* Private functions */188static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,189unsigned int sample_start,190unsigned int sample_end,191int sample_type,192time_t mtime)193{194fluid_samplecache_entry_t *entry;195196entry = FLUID_NEW(fluid_samplecache_entry_t);197198if(entry == NULL)199{200FLUID_LOG(FLUID_ERR, "Out of memory");201return NULL;202}203204FLUID_MEMSET(entry, 0, sizeof(*entry));205206entry->filename = FLUID_STRDUP(sf->fname);207208if(entry->filename == NULL)209{210FLUID_LOG(FLUID_ERR, "Out of memory");211goto error_exit;212}213214entry->sf_samplepos = sf->samplepos;215entry->sf_samplesize = sf->samplesize;216entry->sf_sample24pos = sf->sample24pos;217entry->sf_sample24size = sf->sample24size;218entry->sample_start = sample_start;219entry->sample_end = sample_end;220entry->sample_type = sample_type;221entry->modification_time = mtime;222223entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,224&entry->sample_data, &entry->sample_data24);225226if(entry->sample_count < 0)227{228goto error_exit;229}230231return entry;232233error_exit:234delete_samplecache_entry(entry);235return NULL;236}237238static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)239{240fluid_return_if_fail(entry != NULL);241242FLUID_FREE(entry->filename);243FLUID_FREE(entry->sample_data);244FLUID_FREE(entry->sample_data24);245FLUID_FREE(entry);246}247248static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,249unsigned int sample_start,250unsigned int sample_end,251int sample_type,252time_t mtime)253{254fluid_list_t *entry_list;255fluid_samplecache_entry_t *entry;256257entry_list = samplecache_list;258259while(entry_list)260{261entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);262263if((FLUID_STRCMP(sf->fname, entry->filename) == 0) &&264(mtime == entry->modification_time) &&265(sf->samplepos == entry->sf_samplepos) &&266(sf->samplesize == entry->sf_samplesize) &&267(sf->sample24pos == entry->sf_sample24pos) &&268(sf->sample24size == entry->sf_sample24size) &&269(sample_start == entry->sample_start) &&270(sample_end == entry->sample_end) &&271(sample_type == entry->sample_type))272{273return entry;274}275276entry_list = fluid_list_next(entry_list);277}278279return NULL;280}281282static int fluid_get_file_modification_time(char *filename, time_t *modification_time)283{284fluid_stat_buf_t buf;285286if(fluid_stat(filename, &buf))287{288return FLUID_FAILED;289}290291*modification_time = buf.st_mtime;292return FLUID_OK;293}294295296/* Only used for tests */297int fluid_samplecache_count_entries(void)298{299fluid_list_t *entry;300int count = 0;301302fluid_mutex_lock(samplecache_mutex);303304for(entry = samplecache_list; entry != NULL; entry = fluid_list_next(entry))305{306count++;307}308309fluid_mutex_unlock(samplecache_mutex);310311return count;312}313314315