Make the fontsound's buffer and link fields atomic
[openal-soft.git] / OpenAL32 / alSoundfont.c
blob44f5c35cf6c0dd1b9087856595f1707a113ee041
2 #include "config.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <ctype.h>
9 #include "alMain.h"
10 #include "alMidi.h"
11 #include "alThunk.h"
12 #include "alError.h"
13 #include <alBuffer.h>
15 #include "midi/base.h"
18 extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id);
19 extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id);
21 static void ALsoundfont_Construct(ALsoundfont *self);
22 static void ALsoundfont_Destruct(ALsoundfont *self);
23 void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
24 ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
25 static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr);
28 AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids)
30 ALCdevice *device;
31 ALCcontext *context;
32 ALsizei cur = 0;
33 ALenum err;
35 context = GetContextRef();
36 if(!context) return;
38 if(!(n >= 0))
39 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
41 device = context->Device;
42 for(cur = 0;cur < n;cur++)
44 ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont));
45 if(!sfont)
47 alDeleteSoundfontsSOFT(cur, ids);
48 SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
50 ALsoundfont_Construct(sfont);
52 err = NewThunkEntry(&sfont->id);
53 if(err == AL_NO_ERROR)
54 err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont);
55 if(err != AL_NO_ERROR)
57 ALsoundfont_Destruct(sfont);
58 memset(sfont, 0, sizeof(ALsoundfont));
59 free(sfont);
61 alDeleteSoundfontsSOFT(cur, ids);
62 SET_ERROR_AND_GOTO(context, err, done);
65 ids[cur] = sfont->id;
68 done:
69 ALCcontext_DecRef(context);
72 AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids)
74 ALCdevice *device;
75 ALCcontext *context;
76 ALsoundfont *sfont;
77 ALsizei i;
79 context = GetContextRef();
80 if(!context) return;
82 if(!(n >= 0))
83 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
85 device = context->Device;
86 for(i = 0;i < n;i++)
88 /* Check for valid soundfont ID */
89 if(ids[i] == 0)
91 if(!(sfont=device->DefaultSfont))
92 continue;
94 else if((sfont=LookupSfont(device, ids[i])) == NULL)
95 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
96 if(ReadRef(&sfont->ref) != 0)
97 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
100 for(i = 0;i < n;i++)
102 if(ids[i] == 0)
104 MidiSynth *synth = device->Synth;
105 WriteLock(&synth->Lock);
106 if(device->DefaultSfont != NULL)
107 ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
108 device->DefaultSfont = NULL;
109 WriteUnlock(&synth->Lock);
110 continue;
112 else if((sfont=RemoveSfont(device, ids[i])) == NULL)
113 continue;
115 ALsoundfont_Destruct(sfont);
117 memset(sfont, 0, sizeof(*sfont));
118 free(sfont);
121 done:
122 ALCcontext_DecRef(context);
125 AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id)
127 ALCcontext *context;
128 ALboolean ret;
130 context = GetContextRef();
131 if(!context) return AL_FALSE;
133 ret = ((!id || LookupSfont(context->Device, id)) ?
134 AL_TRUE : AL_FALSE);
136 ALCcontext_DecRef(context);
138 return ret;
141 AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values)
143 ALCdevice *device;
144 ALCcontext *context;
145 ALsoundfont *sfont;
146 ALsizei i;
148 context = GetContextRef();
149 if(!context) return;
151 device = context->Device;
152 if(id == 0)
153 sfont = ALsoundfont_getDefSoundfont(context);
154 else if(!(sfont=LookupSfont(device, id)))
155 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
156 switch(param)
158 case AL_PRESETS_SIZE_SOFT:
159 values[0] = sfont->NumPresets;
160 break;
162 case AL_PRESETS_SOFT:
163 for(i = 0;i < sfont->NumPresets;i++)
164 values[i] = sfont->Presets[i]->id;
165 break;
167 default:
168 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
171 done:
172 ALCcontext_DecRef(context);
175 AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids)
177 ALCdevice *device;
178 ALCcontext *context;
179 ALsoundfont *sfont;
180 ALsfpreset **presets;
181 ALsizei i;
183 context = GetContextRef();
184 if(!context) return;
186 device = context->Device;
187 if(id == 0)
188 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
189 if(!(sfont=LookupSfont(device, id)))
190 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
191 if(count < 0)
192 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
194 WriteLock(&sfont->Lock);
195 if(ReadRef(&sfont->ref) != 0)
197 WriteUnlock(&sfont->Lock);
198 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
201 if(count == 0)
202 presets = NULL;
203 else
205 presets = calloc(count, sizeof(presets[0]));
206 if(!presets)
208 WriteUnlock(&sfont->Lock);
209 SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
212 for(i = 0;i < count;i++)
214 if(!(presets[i]=LookupPreset(device, pids[i])))
216 free(presets);
217 WriteUnlock(&sfont->Lock);
218 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
223 for(i = 0;i < count;i++)
224 IncrementRef(&presets[i]->ref);
226 presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets);
227 count = ExchangeInt(&sfont->NumPresets, count);
228 WriteUnlock(&sfont->Lock);
230 for(i = 0;i < count;i++)
231 DecrementRef(&presets[i]->ref);
232 free(presets);
234 done:
235 ALCcontext_DecRef(context);
239 AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user)
241 ALCdevice *device;
242 ALCcontext *context;
243 ALsoundfont *sfont;
244 Reader reader;
246 context = GetContextRef();
247 if(!context) return;
249 device = context->Device;
250 if(id == 0)
251 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
252 if(!(sfont=LookupSfont(device, id)))
253 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
255 WriteLock(&sfont->Lock);
256 if(ReadRef(&sfont->ref) != 0)
258 WriteUnlock(&sfont->Lock);
259 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
261 if(sfont->NumPresets > 0)
263 WriteUnlock(&sfont->Lock);
264 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
267 reader.cb = cb;
268 reader.ptr = user;
269 reader.error = 0;
270 loadSf2(&reader, sfont, context);
271 WriteUnlock(&sfont->Lock);
273 done:
274 ALCcontext_DecRef(context);
278 static void ALsoundfont_Construct(ALsoundfont *self)
280 InitRef(&self->ref, 0);
282 self->Presets = NULL;
283 self->NumPresets = 0;
285 RWLockInit(&self->Lock);
287 self->id = 0;
290 static void ALsoundfont_Destruct(ALsoundfont *self)
292 ALsizei i;
294 FreeThunkEntry(self->id);
295 self->id = 0;
297 for(i = 0;i < self->NumPresets;i++)
299 DecrementRef(&self->Presets[i]->ref);
300 self->Presets[i] = NULL;
302 free(self->Presets);
303 self->Presets = NULL;
304 self->NumPresets = 0;
307 ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context)
309 ALCdevice *device = context->Device;
310 al_string fname = AL_STRING_INIT_STATIC();
311 const char *namelist;
313 if(device->DefaultSfont)
314 return device->DefaultSfont;
316 device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0]));
317 ALsoundfont_Construct(device->DefaultSfont);
319 namelist = getenv("ALSOFT_SOUNDFONT");
320 if(!namelist || !namelist[0])
321 ConfigValueStr("midi", "soundfont", &namelist);
322 while(namelist && namelist[0])
324 const char *next, *end;
325 FILE *f;
327 while(*namelist && (isspace(*namelist) || *namelist == ','))
328 namelist++;
329 if(!*namelist)
330 break;
331 next = strchr(namelist, ',');
332 end = next ? next++ : (namelist+strlen(namelist));
333 while(--end != namelist && isspace(*end)) {
335 if(end == namelist)
336 continue;
337 al_string_append_range(&fname, namelist, end+1);
338 namelist = next;
340 f = OpenDataFile(al_string_get_cstr(fname), "openal/soundfonts");
341 if(f == NULL)
342 ERR("Failed to open %s\n", al_string_get_cstr(fname));
343 else
345 Reader reader;
346 reader.cb = ALsoundfont_read;
347 reader.ptr = f;
348 reader.error = 0;
349 TRACE("Loading %s\n", al_string_get_cstr(fname));
350 loadSf2(&reader, device->DefaultSfont, context);
351 fclose(f);
354 al_string_clear(&fname);
356 AL_STRING_DEINIT(fname);
358 return device->DefaultSfont;
361 void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device)
363 ALsfpreset **presets;
364 ALsizei num_presets;
365 VECTOR(ALbuffer*) buffers;
366 ALsizei i;
368 VECTOR_INIT(buffers);
369 presets = ExchangePtr((XchgPtr*)&self->Presets, NULL);
370 num_presets = ExchangeInt(&self->NumPresets, 0);
372 for(i = 0;i < num_presets;i++)
374 ALsfpreset *preset = presets[i];
375 ALfontsound **sounds;
376 ALsizei num_sounds;
377 ALboolean deleting;
378 ALsizei j;
380 sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL);
381 num_sounds = ExchangeInt(&preset->NumSounds, 0);
383 DeletePreset(device, preset);
384 preset = NULL;
386 for(j = 0;j < num_sounds;j++)
387 DecrementRef(&sounds[j]->ref);
388 /* Some fontsounds may not be immediately deletable because they're
389 * linked to another fontsound. When those fontsounds are deleted
390 * they should become deletable, so use a loop until all fontsounds
391 * are deleted. */
392 do {
393 deleting = AL_FALSE;
394 for(j = 0;j < num_sounds;j++)
396 if(sounds[j] && ReadRef(&sounds[j]->ref) == 0)
398 ALbuffer *buffer;
400 deleting = AL_TRUE;
401 if((buffer=ATOMIC_LOAD(&sounds[j]->Buffer)) != NULL)
403 ALbuffer **iter;
405 #define MATCH_BUFFER(_i) (buffer == *(_i))
406 VECTOR_FIND_IF(iter, ALbuffer*, buffers, MATCH_BUFFER);
407 if(iter == VECTOR_ITER_END(buffers))
408 VECTOR_PUSH_BACK(buffers, buffer);
409 #undef MATCH_BUFFER
411 DeleteFontsound(device, sounds[j]);
412 sounds[j] = NULL;
415 } while(deleting);
416 free(sounds);
419 ALsoundfont_Destruct(self);
420 free(self);
422 #define DELETE_BUFFER(iter) do { \
423 assert(ReadRef(&(*(iter))->ref) == 0); \
424 DeleteBuffer(device, *(iter)); \
425 } while(0)
426 VECTOR_FOR_EACH(ALbuffer*, buffers, DELETE_BUFFER);
427 #undef DELETE_BUFFER
428 VECTOR_DEINIT(buffers);
432 static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr)
434 return fread(buf, 1, bytes, (FILE*)ptr);
438 /* ReleaseALSoundfonts
440 * Called to destroy any soundfonts that still exist on the device
442 void ReleaseALSoundfonts(ALCdevice *device)
444 ALsizei i;
445 for(i = 0;i < device->SfontMap.size;i++)
447 ALsoundfont *temp = device->SfontMap.array[i].value;
448 device->SfontMap.array[i].value = NULL;
450 ALsoundfont_Destruct(temp);
452 memset(temp, 0, sizeof(*temp));
453 free(temp);