Move atomic method definitions to a separate common source
[openal-soft.git] / OpenAL32 / alSoundfont.c
blob0952c412a6f04ee6e58fe2904dff4f41b803e544
2 #include "config.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
8 #include "alMain.h"
9 #include "alMidi.h"
10 #include "alThunk.h"
11 #include "alError.h"
13 #include "midi/base.h"
16 extern inline struct ALsoundfont *LookupSfont(ALCdevice *device, ALuint id);
17 extern inline struct ALsoundfont *RemoveSfont(ALCdevice *device, ALuint id);
19 void ALsoundfont_Construct(ALsoundfont *self);
20 void ALsoundfont_Destruct(ALsoundfont *self);
21 void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device);
22 ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context);
23 static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr);
26 AL_API void AL_APIENTRY alGenSoundfontsSOFT(ALsizei n, ALuint *ids)
28 ALCdevice *device;
29 ALCcontext *context;
30 ALsizei cur = 0;
31 ALenum err;
33 context = GetContextRef();
34 if(!context) return;
36 if(!(n >= 0))
37 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
39 device = context->Device;
40 for(cur = 0;cur < n;cur++)
42 ALsoundfont *sfont = calloc(1, sizeof(ALsoundfont));
43 if(!sfont)
45 alDeleteSoundfontsSOFT(cur, ids);
46 SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
48 ALsoundfont_Construct(sfont);
50 err = NewThunkEntry(&sfont->id);
51 if(err == AL_NO_ERROR)
52 err = InsertUIntMapEntry(&device->SfontMap, sfont->id, sfont);
53 if(err != AL_NO_ERROR)
55 ALsoundfont_Destruct(sfont);
56 memset(sfont, 0, sizeof(ALsoundfont));
57 free(sfont);
59 alDeleteSoundfontsSOFT(cur, ids);
60 SET_ERROR_AND_GOTO(context, err, done);
63 ids[cur] = sfont->id;
66 done:
67 ALCcontext_DecRef(context);
70 AL_API ALvoid AL_APIENTRY alDeleteSoundfontsSOFT(ALsizei n, const ALuint *ids)
72 ALCdevice *device;
73 ALCcontext *context;
74 ALsoundfont *sfont;
75 ALsizei i;
77 context = GetContextRef();
78 if(!context) return;
80 if(!(n >= 0))
81 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
83 device = context->Device;
84 for(i = 0;i < n;i++)
86 /* Check for valid soundfont ID */
87 if(ids[i] == 0)
89 if(!(sfont=device->DefaultSfont))
90 continue;
92 else if((sfont=LookupSfont(device, ids[i])) == NULL)
93 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
94 if(sfont->Mapped != AL_FALSE || sfont->ref != 0)
95 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
98 for(i = 0;i < n;i++)
100 if(ids[i] == 0)
102 MidiSynth *synth = device->Synth;
103 WriteLock(&synth->Lock);
104 if(device->DefaultSfont != NULL)
105 ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
106 device->DefaultSfont = NULL;
107 WriteUnlock(&synth->Lock);
108 continue;
110 else if((sfont=RemoveSfont(device, ids[i])) == NULL)
111 continue;
113 ALsoundfont_Destruct(sfont);
115 memset(sfont, 0, sizeof(*sfont));
116 free(sfont);
119 done:
120 ALCcontext_DecRef(context);
123 AL_API ALboolean AL_APIENTRY alIsSoundfontSOFT(ALuint id)
125 ALCcontext *context;
126 ALboolean ret;
128 context = GetContextRef();
129 if(!context) return AL_FALSE;
131 ret = ((!id || LookupSfont(context->Device, id)) ?
132 AL_TRUE : AL_FALSE);
134 ALCcontext_DecRef(context);
136 return ret;
139 AL_API ALvoid AL_APIENTRY alSoundfontSamplesSOFT(ALuint id, ALenum type, ALsizei count, const ALvoid *samples)
141 ALCdevice *device;
142 ALCcontext *context;
143 ALsoundfont *sfont;
144 void *ptr;
146 context = GetContextRef();
147 if(!context) return;
149 device = context->Device;
150 if(id == 0)
151 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
152 if(!(sfont=LookupSfont(device, id)))
153 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
154 if(type != AL_SHORT_SOFT)
155 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
156 if(count <= 0)
157 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
159 WriteLock(&sfont->Lock);
160 if(sfont->ref != 0)
161 alSetError(context, AL_INVALID_OPERATION);
162 else if(sfont->Mapped)
163 alSetError(context, AL_INVALID_OPERATION);
164 else if(!(ptr=realloc(sfont->Samples, count * sizeof(ALshort))))
165 alSetError(context, AL_OUT_OF_MEMORY);
166 else
168 sfont->Samples = ptr;
169 sfont->NumSamples = count;
170 if(samples != NULL)
171 memcpy(sfont->Samples, samples, count * sizeof(ALshort));
173 WriteUnlock(&sfont->Lock);
175 done:
176 ALCcontext_DecRef(context);
179 AL_API void AL_APIENTRY alGetSoundfontSamplesSOFT(ALuint id, ALsizei offset, ALsizei count, ALenum type, ALvoid *samples)
181 ALCdevice *device;
182 ALCcontext *context;
183 ALsoundfont *sfont;
185 context = GetContextRef();
186 if(!context) return;
188 device = context->Device;
189 if(id == 0)
190 sfont = ALsoundfont_getDefSoundfont(context);
191 else if(!(sfont=LookupSfont(device, id)))
192 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
193 if(type != AL_SHORT_SOFT)
194 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
195 if(offset < 0 || count <= 0)
196 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
198 ReadLock(&sfont->Lock);
199 if(offset >= sfont->NumSamples || count > (sfont->NumSamples-offset))
200 alSetError(context, AL_INVALID_VALUE);
201 else if(sfont->Mapped)
202 alSetError(context, AL_INVALID_OPERATION);
203 else
205 /* TODO: Allow conversion. */
206 memcpy(samples, sfont->Samples + offset*sizeof(ALshort), count * sizeof(ALshort));
208 ReadUnlock(&sfont->Lock);
210 done:
211 ALCcontext_DecRef(context);
214 AL_API ALvoid* AL_APIENTRY alSoundfontMapSamplesSOFT(ALuint id, ALsizei offset, ALsizei length)
216 ALCdevice *device;
217 ALCcontext *context;
218 ALsoundfont *sfont;
219 ALvoid *ptr = NULL;
221 context = GetContextRef();
222 if(!context) return NULL;
224 device = context->Device;
225 if(id == 0)
226 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
227 if(!(sfont=LookupSfont(device, id)))
228 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
229 if(offset < 0 || (ALuint)offset > sfont->NumSamples*sizeof(ALshort))
230 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
231 if(length <= 0 || (ALuint)length > (sfont->NumSamples*sizeof(ALshort) - offset))
232 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
234 ReadLock(&sfont->Lock);
235 if(sfont->ref != 0)
236 alSetError(context, AL_INVALID_OPERATION);
237 else if(ExchangeInt(&sfont->Mapped, AL_TRUE) == AL_TRUE)
238 alSetError(context, AL_INVALID_OPERATION);
239 else
240 ptr = (ALbyte*)sfont->Samples + offset;
241 ReadUnlock(&sfont->Lock);
243 done:
244 ALCcontext_DecRef(context);
246 return ptr;
249 AL_API ALvoid AL_APIENTRY alSoundfontUnmapSamplesSOFT(ALuint id)
251 ALCdevice *device;
252 ALCcontext *context;
253 ALsoundfont *sfont;
255 context = GetContextRef();
256 if(!context) return;
258 device = context->Device;
259 if(id == 0)
260 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
261 if(!(sfont=LookupSfont(device, id)))
262 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
263 if(ExchangeInt(&sfont->Mapped, AL_FALSE) == AL_FALSE)
264 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
266 done:
267 ALCcontext_DecRef(context);
270 AL_API void AL_APIENTRY alGetSoundfontivSOFT(ALuint id, ALenum param, ALint *values)
272 ALCdevice *device;
273 ALCcontext *context;
274 ALsoundfont *sfont;
275 ALsizei i;
277 context = GetContextRef();
278 if(!context) return;
280 device = context->Device;
281 if(id == 0)
282 sfont = ALsoundfont_getDefSoundfont(context);
283 else if(!(sfont=LookupSfont(device, id)))
284 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
285 switch(param)
287 case AL_PRESETS_SIZE_SOFT:
288 values[0] = sfont->NumPresets;
289 break;
291 case AL_PRESETS_SOFT:
292 for(i = 0;i < sfont->NumPresets;i++)
293 values[i] = sfont->Presets[i]->id;
294 break;
296 case AL_SAMPLE_LENGTH_SOFT:
297 values[0] = sfont->NumSamples;
298 break;
300 case AL_FORMAT_TYPE_SOFT:
301 values[0] = AL_SHORT_SOFT;
302 break;
304 default:
305 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
308 done:
309 ALCcontext_DecRef(context);
312 AL_API void AL_APIENTRY alSoundfontPresetsSOFT(ALuint id, ALsizei count, const ALuint *pids)
314 ALCdevice *device;
315 ALCcontext *context;
316 ALsoundfont *sfont;
317 ALsfpreset **presets;
318 ALsizei i;
320 context = GetContextRef();
321 if(!context) return;
323 device = context->Device;
324 if(id == 0)
325 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
326 if(!(sfont=LookupSfont(device, id)))
327 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
328 if(count < 0)
329 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
331 WriteLock(&sfont->Lock);
332 if(sfont->ref != 0)
334 WriteUnlock(&sfont->Lock);
335 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
338 if(count == 0)
339 presets = NULL;
340 else
342 presets = calloc(count, sizeof(presets[0]));
343 if(!presets)
345 WriteUnlock(&sfont->Lock);
346 SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
349 for(i = 0;i < count;i++)
351 if(!(presets[i]=LookupPreset(device, pids[i])))
353 free(presets);
354 WriteUnlock(&sfont->Lock);
355 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
360 for(i = 0;i < count;i++)
361 IncrementRef(&presets[i]->ref);
363 presets = ExchangePtr((XchgPtr*)&sfont->Presets, presets);
364 count = ExchangeInt(&sfont->NumPresets, count);
365 WriteUnlock(&sfont->Lock);
367 for(i = 0;i < count;i++)
368 DecrementRef(&presets[i]->ref);
369 free(presets);
371 done:
372 ALCcontext_DecRef(context);
376 AL_API void AL_APIENTRY alLoadSoundfontSOFT(ALuint id, size_t(*cb)(ALvoid*,size_t,ALvoid*), ALvoid *user)
378 ALCdevice *device;
379 ALCcontext *context;
380 ALsoundfont *sfont;
381 Reader reader;
383 context = GetContextRef();
384 if(!context) return;
386 device = context->Device;
387 if(id == 0)
388 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
389 if(!(sfont=LookupSfont(device, id)))
390 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
392 WriteLock(&sfont->Lock);
393 if(sfont->ref != 0)
395 WriteUnlock(&sfont->Lock);
396 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
398 if(sfont->Mapped)
400 WriteUnlock(&sfont->Lock);
401 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
403 if(sfont->NumPresets > 0)
405 WriteUnlock(&sfont->Lock);
406 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
409 reader.cb = cb;
410 reader.ptr = user;
411 reader.error = 0;
412 loadSf2(&reader, sfont, context);
413 WriteUnlock(&sfont->Lock);
415 done:
416 ALCcontext_DecRef(context);
420 void ALsoundfont_Construct(ALsoundfont *self)
422 self->ref = 0;
424 self->Presets = NULL;
425 self->NumPresets = 0;
427 self->Samples = NULL;
428 self->NumSamples = 0;
430 RWLockInit(&self->Lock);
431 self->Mapped = AL_FALSE;
433 self->id = 0;
436 void ALsoundfont_Destruct(ALsoundfont *self)
438 ALsizei i;
440 FreeThunkEntry(self->id);
441 self->id = 0;
443 for(i = 0;i < self->NumPresets;i++)
445 DecrementRef(&self->Presets[i]->ref);
446 self->Presets[i] = NULL;
448 free(self->Presets);
449 self->Presets = NULL;
450 self->NumPresets = 0;
452 free(self->Samples);
453 self->Samples = NULL;
454 self->NumSamples = 0;
457 ALsoundfont *ALsoundfont_getDefSoundfont(ALCcontext *context)
459 ALCdevice *device = context->Device;
460 const char *fname;
462 if(device->DefaultSfont)
463 return device->DefaultSfont;
465 device->DefaultSfont = calloc(1, sizeof(device->DefaultSfont[0]));
466 ALsoundfont_Construct(device->DefaultSfont);
468 fname = getenv("ALSOFT_SOUNDFONT");
469 if((fname && fname[0]) || ConfigValueStr("midi", "soundfont", &fname))
471 FILE *f;
473 f = OpenDataFile(fname, "openal/soundfonts");
474 if(f == NULL)
475 ERR("Failed to open %s\n", fname);
476 else
478 Reader reader;
479 reader.cb = ALsoundfont_read;
480 reader.ptr = f;
481 reader.error = 0;
482 TRACE("Loading %s\n", fname);
483 loadSf2(&reader, device->DefaultSfont, context);
484 fclose(f);
488 return device->DefaultSfont;
491 void ALsoundfont_deleteSoundfont(ALsoundfont *self, ALCdevice *device)
493 ALsfpreset **presets;
494 ALsizei num_presets;
495 ALsizei i;
497 presets = ExchangePtr((XchgPtr*)&self->Presets, NULL);
498 num_presets = ExchangeInt(&self->NumPresets, 0);
500 for(i = 0;i < num_presets;i++)
502 ALsfpreset *preset = presets[i];
503 ALfontsound **sounds;
504 ALsizei num_sounds;
505 ALboolean deleting;
506 ALsizei j;
508 sounds = ExchangePtr((XchgPtr*)&preset->Sounds, NULL);
509 num_sounds = ExchangeInt(&preset->NumSounds, 0);
510 DeletePreset(preset, device);
511 preset = NULL;
513 for(j = 0;j < num_sounds;j++)
514 DecrementRef(&sounds[j]->ref);
515 /* Some fontsounds may not be immediately deletable because they're
516 * linked to another fontsound. When those fontsounds are deleted
517 * they should become deletable, so use a loop until all fontsounds
518 * are deleted. */
519 do {
520 deleting = AL_FALSE;
521 for(j = 0;j < num_sounds;j++)
523 if(sounds[j] && sounds[j]->ref == 0)
525 deleting = AL_TRUE;
526 RemoveFontsound(device, sounds[j]->id);
527 ALfontsound_Destruct(sounds[j]);
528 free(sounds[j]);
529 sounds[j] = NULL;
532 } while(deleting);
533 free(sounds);
536 ALsoundfont_Destruct(self);
537 free(self);
541 static size_t ALsoundfont_read(ALvoid *buf, size_t bytes, ALvoid *ptr)
543 return fread(buf, 1, bytes, (FILE*)ptr);
547 /* ReleaseALSoundfonts
549 * Called to destroy any soundfonts that still exist on the device
551 void ReleaseALSoundfonts(ALCdevice *device)
553 ALsizei i;
554 for(i = 0;i < device->SfontMap.size;i++)
556 ALsoundfont *temp = device->SfontMap.array[i].value;
557 device->SfontMap.array[i].value = NULL;
559 ALsoundfont_Destruct(temp);
561 memset(temp, 0, sizeof(*temp));
562 free(temp);