Update Fluidsynth to 2.0.1
[ardour.git] / libs / fluidsynth / src / fluid_samplecache.c
1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  *
5  * SoundFont file loading code borrowed from Smurf SoundFont Editor
6  * Copyright (C) 1999-2001 Josh Green
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301, USA
22  */
23
24 /* CACHED SAMPLE DATA LOADER
25  *
26  * This is a wrapper around fluid_sffile_read_sample_data that attempts to cache the read
27  * data across all FluidSynth instances in a global (process-wide) list.
28  */
29
30 #include "fluid_samplecache.h"
31 #include "fluid_sys.h"
32 #include "fluidsynth.h"
33 #include "fluid_list.h"
34
35
36 typedef struct _fluid_samplecache_entry_t fluid_samplecache_entry_t;
37
38 struct _fluid_samplecache_entry_t
39 {
40     /* The follwing members all form the cache key */
41     char *filename;
42     time_t modification_time;
43     unsigned int sf_samplepos;
44     unsigned int sf_samplesize;
45     unsigned int sf_sample24pos;
46     unsigned int sf_sample24size;
47     unsigned int sample_start;
48     unsigned int sample_end;
49     int sample_type;
50     /*  End of cache key members */
51
52     short *sample_data;
53     char *sample_data24;
54     int sample_count;
55
56     int num_references;
57     int mlocked;
58 };
59
60 static fluid_list_t *samplecache_list = NULL;
61 static fluid_mutex_t samplecache_mutex = FLUID_MUTEX_INIT;
62
63 static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
64 static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf, unsigned int sample_start, unsigned int sample_end, int sample_type);
65 static void delete_samplecache_entry(fluid_samplecache_entry_t *entry);
66
67 static int fluid_get_file_modification_time(char *filename, time_t *modification_time);
68
69
70 /* PUBLIC INTERFACE */
71
72 int fluid_samplecache_load(SFData *sf,
73                            unsigned int sample_start, unsigned int sample_end, int sample_type,
74                            int try_mlock, short **sample_data, char **sample_data24)
75 {
76     fluid_samplecache_entry_t *entry;
77     int ret;
78
79     fluid_mutex_lock(samplecache_mutex);
80
81     entry = get_samplecache_entry(sf, sample_start, sample_end, sample_type);
82
83     if(entry == NULL)
84     {
85         entry = new_samplecache_entry(sf, sample_start, sample_end, sample_type);
86
87         if(entry == NULL)
88         {
89             ret = -1;
90             goto unlock_exit;
91         }
92
93         samplecache_list = fluid_list_prepend(samplecache_list, entry);
94     }
95
96     if(try_mlock && !entry->mlocked)
97     {
98         /* Lock the memory to disable paging. It's okay if this fails. It
99          * probably means that the user doesn't have the required permission. */
100         if(fluid_mlock(entry->sample_data, entry->sample_count * sizeof(short)) == 0)
101         {
102             if(entry->sample_data24 != NULL)
103             {
104                 entry->mlocked = (fluid_mlock(entry->sample_data24, entry->sample_count) == 0);
105             }
106             else
107             {
108                 entry->mlocked = TRUE;
109             }
110
111             if(!entry->mlocked)
112             {
113                 fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));
114                 FLUID_LOG(FLUID_WARN, "Failed to pin the sample data to RAM; swapping is possible.");
115             }
116         }
117     }
118
119     entry->num_references++;
120     *sample_data = entry->sample_data;
121     *sample_data24 = entry->sample_data24;
122     ret = entry->sample_count;
123
124 unlock_exit:
125     fluid_mutex_unlock(samplecache_mutex);
126     return ret;
127 }
128
129 int fluid_samplecache_unload(const short *sample_data)
130 {
131     fluid_list_t *entry_list;
132     fluid_samplecache_entry_t *entry;
133     int ret;
134
135     fluid_mutex_lock(samplecache_mutex);
136
137     entry_list = samplecache_list;
138
139     while(entry_list)
140     {
141         entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);
142
143         if(sample_data == entry->sample_data)
144         {
145             entry->num_references--;
146
147             if(entry->num_references == 0)
148             {
149                 if(entry->mlocked)
150                 {
151                     fluid_munlock(entry->sample_data, entry->sample_count * sizeof(short));
152
153                     if(entry->sample_data24 != NULL)
154                     {
155                         fluid_munlock(entry->sample_data24, entry->sample_count);
156                     }
157                 }
158
159                 samplecache_list = fluid_list_remove(samplecache_list, entry);
160                 delete_samplecache_entry(entry);
161             }
162
163             ret = FLUID_OK;
164             goto unlock_exit;
165         }
166
167         entry_list = fluid_list_next(entry_list);
168     }
169
170     FLUID_LOG(FLUID_ERR, "Trying to free sample data not found in cache.");
171     ret = FLUID_FAILED;
172
173 unlock_exit:
174     fluid_mutex_unlock(samplecache_mutex);
175     return ret;
176 }
177
178
179 /* Private functions */
180 static fluid_samplecache_entry_t *new_samplecache_entry(SFData *sf,
181         unsigned int sample_start,
182         unsigned int sample_end,
183         int sample_type)
184 {
185     fluid_samplecache_entry_t *entry;
186
187     entry = FLUID_NEW(fluid_samplecache_entry_t);
188
189     if(entry == NULL)
190     {
191         FLUID_LOG(FLUID_ERR, "Out of memory");
192         return NULL;
193     }
194
195     FLUID_MEMSET(entry, 0, sizeof(*entry));
196
197     entry->filename = FLUID_STRDUP(sf->fname);
198
199     if(entry->filename == NULL)
200     {
201         FLUID_LOG(FLUID_ERR, "Out of memory");
202         goto error_exit;
203     }
204
205     if(fluid_get_file_modification_time(entry->filename, &entry->modification_time) == FLUID_FAILED)
206     {
207         FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
208         entry->modification_time = 0;
209     }
210
211     entry->sf_samplepos = sf->samplepos;
212     entry->sf_samplesize = sf->samplesize;
213     entry->sf_sample24pos = sf->sample24pos;
214     entry->sf_sample24size = sf->sample24size;
215     entry->sample_start = sample_start;
216     entry->sample_end = sample_end;
217     entry->sample_type = sample_type;
218
219     entry->sample_count = fluid_sffile_read_sample_data(sf, sample_start, sample_end, sample_type,
220                           &entry->sample_data, &entry->sample_data24);
221
222     if(entry->sample_count < 0)
223     {
224         goto error_exit;
225     }
226
227     return entry;
228
229 error_exit:
230     delete_samplecache_entry(entry);
231     return NULL;
232 }
233
234 static void delete_samplecache_entry(fluid_samplecache_entry_t *entry)
235 {
236     fluid_return_if_fail(entry != NULL);
237
238     FLUID_FREE(entry->filename);
239     FLUID_FREE(entry->sample_data);
240     FLUID_FREE(entry->sample_data24);
241     FLUID_FREE(entry);
242 }
243
244 static fluid_samplecache_entry_t *get_samplecache_entry(SFData *sf,
245         unsigned int sample_start,
246         unsigned int sample_end,
247         int sample_type)
248 {
249     time_t mtime;
250     fluid_list_t *entry_list;
251     fluid_samplecache_entry_t *entry;
252
253     if(fluid_get_file_modification_time(sf->fname, &mtime) == FLUID_FAILED)
254     {
255         FLUID_LOG(FLUID_WARN, "Unable to read modificaton time of soundfont file.");
256         mtime = 0;
257     }
258
259     entry_list = samplecache_list;
260
261     while(entry_list)
262     {
263         entry = (fluid_samplecache_entry_t *)fluid_list_get(entry_list);
264
265         if((FLUID_STRCMP(sf->fname, entry->filename) == 0) &&
266                 (mtime == entry->modification_time) &&
267                 (sf->samplepos == entry->sf_samplepos) &&
268                 (sf->samplesize == entry->sf_samplesize) &&
269                 (sf->sample24pos == entry->sf_sample24pos) &&
270                 (sf->sample24size == entry->sf_sample24size) &&
271                 (sample_start == entry->sample_start) &&
272                 (sample_end == entry->sample_end) &&
273                 (sample_type == entry->sample_type))
274         {
275             return entry;
276         }
277
278         entry_list = fluid_list_next(entry_list);
279     }
280
281     return NULL;
282 }
283
284 static int fluid_get_file_modification_time(char *filename, time_t *modification_time)
285 {
286     fluid_stat_buf_t buf;
287
288     if(fluid_stat(filename, &buf))
289     {
290         return FLUID_FAILED;
291     }
292
293     *modification_time = buf.st_mtime;
294     return FLUID_OK;
295 }