293 lines
8.2 KiB
C
293 lines
8.2 KiB
C
/* 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 "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, time_t mtime);
|
|
static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start,
|
|
unsigned int sample_end, int sample_type, time_t mtime);
|
|
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;
|
|
time_t mtime;
|
|
|
|
fluid_mutex_lock(samplecache_mutex);
|
|
|
|
if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
|
|
{
|
|
mtime = 0;
|
|
}
|
|
|
|
entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
|
|
|
|
if(entry == NULL)
|
|
{
|
|
entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type, mtime);
|
|
|
|
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,
|
|
time_t mtime)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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->modification_time = mtime;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|