Use the backend lock for the effectstate's deviceUpdate call
[openal-soft.git] / OpenAL32 / alAuxEffectSlot.c
blobc37ecd58bd8f82d20bb170999c1b4e057bbefc0c
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdlib.h>
24 #include <math.h>
26 #include "AL/al.h"
27 #include "AL/alc.h"
28 #include "alMain.h"
29 #include "alAuxEffectSlot.h"
30 #include "alThunk.h"
31 #include "alError.h"
32 #include "alListener.h"
33 #include "alSource.h"
35 #include "almalloc.h"
38 extern inline struct ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id);
39 extern inline struct ALeffectslot *RemoveEffectSlot(ALCcontext *context, ALuint id);
41 static ALenum AddEffectSlotArray(ALCcontext *Context, ALeffectslot **start, ALsizei count);
42 static void RemoveEffectSlotArray(ALCcontext *Context, const ALeffectslot *slot);
45 static UIntMap EffectStateFactoryMap;
46 static inline ALeffectStateFactory *getFactoryByType(ALenum type)
48 ALeffectStateFactory* (*getFactory)(void) = LookupUIntMapKey(&EffectStateFactoryMap, type);
49 if(getFactory != NULL)
50 return getFactory();
51 return NULL;
55 AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
57 ALCcontext *context;
58 VECTOR(ALeffectslot*) slotvec;
59 ALsizei cur;
60 ALenum err;
62 context = GetContextRef();
63 if(!context) return;
65 VECTOR_INIT(slotvec);
67 if(!(n >= 0))
68 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
69 if(!VECTOR_RESERVE(slotvec, n))
70 SET_ERROR_AND_GOTO(context, AL_OUT_OF_MEMORY, done);
72 for(cur = 0;cur < n;cur++)
74 ALeffectslot *slot = al_calloc(16, sizeof(ALeffectslot));
75 err = AL_OUT_OF_MEMORY;
76 if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
78 al_free(slot);
79 alDeleteAuxiliaryEffectSlots(cur, effectslots);
80 SET_ERROR_AND_GOTO(context, err, done);
83 err = NewThunkEntry(&slot->id);
84 if(err == AL_NO_ERROR)
85 err = InsertUIntMapEntry(&context->EffectSlotMap, slot->id, slot);
86 if(err != AL_NO_ERROR)
88 FreeThunkEntry(slot->id);
89 DELETE_OBJ(slot->Params.EffectState);
90 al_free(slot);
92 alDeleteAuxiliaryEffectSlots(cur, effectslots);
93 SET_ERROR_AND_GOTO(context, err, done);
96 aluInitEffectPanning(slot);
98 VECTOR_PUSH_BACK(slotvec, slot);
100 effectslots[cur] = slot->id;
102 err = AddEffectSlotArray(context, VECTOR_BEGIN(slotvec), n);
103 if(err != AL_NO_ERROR)
105 alDeleteAuxiliaryEffectSlots(cur, effectslots);
106 SET_ERROR_AND_GOTO(context, err, done);
109 done:
110 VECTOR_DEINIT(slotvec);
112 ALCcontext_DecRef(context);
115 AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots)
117 ALCcontext *context;
118 ALeffectslot *slot;
119 ALsizei i;
121 context = GetContextRef();
122 if(!context) return;
124 if(!(n >= 0))
125 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
126 for(i = 0;i < n;i++)
128 if((slot=LookupEffectSlot(context, effectslots[i])) == NULL)
129 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
130 if(ReadRef(&slot->ref) != 0)
131 SET_ERROR_AND_GOTO(context, AL_INVALID_OPERATION, done);
134 // All effectslots are valid
135 for(i = 0;i < n;i++)
137 if((slot=RemoveEffectSlot(context, effectslots[i])) == NULL)
138 continue;
139 FreeThunkEntry(slot->id);
141 RemoveEffectSlotArray(context, slot);
142 DeinitEffectSlot(slot);
144 memset(slot, 0, sizeof(*slot));
145 al_free(slot);
148 done:
149 ALCcontext_DecRef(context);
152 AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot)
154 ALCcontext *context;
155 ALboolean ret;
157 context = GetContextRef();
158 if(!context) return AL_FALSE;
160 ret = (LookupEffectSlot(context, effectslot) ? AL_TRUE : AL_FALSE);
162 ALCcontext_DecRef(context);
164 return ret;
167 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint value)
169 ALCdevice *device;
170 ALCcontext *context;
171 ALeffectslot *slot;
172 ALeffect *effect = NULL;
173 ALenum err;
175 context = GetContextRef();
176 if(!context) return;
178 WriteLock(&context->PropLock);
179 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
180 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
181 switch(param)
183 case AL_EFFECTSLOT_EFFECT:
184 device = context->Device;
186 LockEffectsRead(device);
187 effect = (value ? LookupEffect(device, value) : NULL);
188 if(!(value == 0 || effect != NULL))
190 UnlockEffectsRead(device);
191 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
193 err = InitializeEffect(device, slot, effect);
194 UnlockEffectsRead(device);
196 if(err != AL_NO_ERROR)
197 SET_ERROR_AND_GOTO(context, err, done);
198 break;
200 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
201 if(!(value == AL_TRUE || value == AL_FALSE))
202 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
203 slot->AuxSendAuto = value;
204 UpdateEffectSlotProps(slot);
205 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
206 UpdateAllSourceProps(context);
207 break;
209 default:
210 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
213 done:
214 WriteUnlock(&context->PropLock);
215 ALCcontext_DecRef(context);
218 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
220 ALCcontext *context;
222 switch(param)
224 case AL_EFFECTSLOT_EFFECT:
225 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
226 alAuxiliaryEffectSloti(effectslot, param, values[0]);
227 return;
230 context = GetContextRef();
231 if(!context) return;
233 if(LookupEffectSlot(context, effectslot) == NULL)
234 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
235 switch(param)
237 default:
238 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
241 done:
242 ALCcontext_DecRef(context);
245 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
247 ALCcontext *context;
248 ALeffectslot *slot;
250 context = GetContextRef();
251 if(!context) return;
253 WriteLock(&context->PropLock);
254 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
255 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
256 switch(param)
258 case AL_EFFECTSLOT_GAIN:
259 if(!(value >= 0.0f && value <= 1.0f))
260 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
261 slot->Gain = value;
262 break;
264 default:
265 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
267 UpdateEffectSlotProps(slot);
269 done:
270 WriteUnlock(&context->PropLock);
271 ALCcontext_DecRef(context);
274 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
276 ALCcontext *context;
278 switch(param)
280 case AL_EFFECTSLOT_GAIN:
281 alAuxiliaryEffectSlotf(effectslot, param, values[0]);
282 return;
285 context = GetContextRef();
286 if(!context) return;
288 if(LookupEffectSlot(context, effectslot) == NULL)
289 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
290 switch(param)
292 default:
293 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
296 done:
297 ALCcontext_DecRef(context);
300 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
302 ALCcontext *context;
303 ALeffectslot *slot;
305 context = GetContextRef();
306 if(!context) return;
308 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
309 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
310 switch(param)
312 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
313 *value = slot->AuxSendAuto;
314 break;
316 default:
317 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
320 done:
321 ALCcontext_DecRef(context);
324 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
326 ALCcontext *context;
328 switch(param)
330 case AL_EFFECTSLOT_EFFECT:
331 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
332 alGetAuxiliaryEffectSloti(effectslot, param, values);
333 return;
336 context = GetContextRef();
337 if(!context) return;
339 if(LookupEffectSlot(context, effectslot) == NULL)
340 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
341 switch(param)
343 default:
344 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
347 done:
348 ALCcontext_DecRef(context);
351 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
353 ALCcontext *context;
354 ALeffectslot *slot;
356 context = GetContextRef();
357 if(!context) return;
359 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
360 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
361 switch(param)
363 case AL_EFFECTSLOT_GAIN:
364 *value = slot->Gain;
365 break;
367 default:
368 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
371 done:
372 ALCcontext_DecRef(context);
375 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
377 ALCcontext *context;
379 switch(param)
381 case AL_EFFECTSLOT_GAIN:
382 alGetAuxiliaryEffectSlotf(effectslot, param, values);
383 return;
386 context = GetContextRef();
387 if(!context) return;
389 if(LookupEffectSlot(context, effectslot) == NULL)
390 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
391 switch(param)
393 default:
394 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
397 done:
398 ALCcontext_DecRef(context);
402 static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count)
404 ALenum err = AL_NO_ERROR;
406 LockContext(context);
407 if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_END(context->ActiveAuxSlots), start, start+count))
408 err = AL_OUT_OF_MEMORY;
409 UnlockContext(context);
411 return err;
414 static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot)
416 ALeffectslot **iter;
418 LockContext(context);
419 #define MATCH_SLOT(_i) (slot == *(_i))
420 VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT);
421 if(iter != VECTOR_END(context->ActiveAuxSlots))
423 *iter = VECTOR_BACK(context->ActiveAuxSlots);
424 VECTOR_POP_BACK(context->ActiveAuxSlots);
426 #undef MATCH_SLOT
427 UnlockContext(context);
431 void InitEffectFactoryMap(void)
433 InitUIntMap(&EffectStateFactoryMap, ~0);
435 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
436 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
437 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
438 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory);
439 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
440 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
441 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
442 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
443 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
444 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
445 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
446 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
447 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
450 void DeinitEffectFactoryMap(void)
452 ResetUIntMap(&EffectStateFactoryMap);
456 ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
458 ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
459 ALeffectStateFactory *factory;
461 if(newtype != EffectSlot->Effect.Type)
463 ALeffectState *State;
464 FPUCtl oldMode;
466 factory = getFactoryByType(newtype);
467 if(!factory)
469 ERR("Failed to find factory for effect type 0x%04x\n", newtype);
470 return AL_INVALID_ENUM;
472 State = V0(factory,create)();
473 if(!State) return AL_OUT_OF_MEMORY;
475 SetMixerFPUMode(&oldMode);
476 almtx_lock(&Device->BackendLock);
477 State->OutBuffer = Device->Dry.Buffer;
478 State->OutChannels = Device->Dry.NumChannels;
479 if(V(State,deviceUpdate)(Device) == AL_FALSE)
481 almtx_unlock(&Device->BackendLock);
482 RestoreFPUMode(&oldMode);
483 DELETE_OBJ(State);
484 return AL_OUT_OF_MEMORY;
486 almtx_unlock(&Device->BackendLock);
487 RestoreFPUMode(&oldMode);
489 if(!effect)
491 EffectSlot->Effect.Type = AL_EFFECT_NULL;
492 memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props));
494 else
496 EffectSlot->Effect.Type = effect->type;
497 memcpy(&EffectSlot->Effect.Props, &effect->Props, sizeof(EffectSlot->Effect.Props));
500 EffectSlot->Effect.State = State;
501 UpdateEffectSlotProps(EffectSlot);
503 else if(effect)
505 memcpy(&EffectSlot->Effect.Props, &effect->Props, sizeof(EffectSlot->Effect.Props));
506 UpdateEffectSlotProps(EffectSlot);
509 return AL_NO_ERROR;
513 void ALeffectState_Destruct(ALeffectState *UNUSED(state))
518 ALenum InitEffectSlot(ALeffectslot *slot)
520 ALeffectStateFactory *factory;
522 slot->Effect.Type = AL_EFFECT_NULL;
524 factory = getFactoryByType(AL_EFFECT_NULL);
525 if(!(slot->Effect.State=V0(factory,create)()))
526 return AL_OUT_OF_MEMORY;
528 slot->Gain = 1.0;
529 slot->AuxSendAuto = AL_TRUE;
530 InitRef(&slot->ref, 0);
532 ATOMIC_INIT(&slot->Update, NULL);
533 ATOMIC_INIT(&slot->FreeList, NULL);
535 slot->Params.Gain = 1.0f;
536 slot->Params.AuxSendAuto = AL_TRUE;
537 slot->Params.EffectState = slot->Effect.State;
538 slot->Params.RoomRolloff = 0.0f;
539 slot->Params.DecayTime = 0.0f;
540 slot->Params.AirAbsorptionGainHF = 1.0f;
542 return AL_NO_ERROR;
545 void DeinitEffectSlot(ALeffectslot *slot)
547 struct ALeffectslotProps *props;
548 ALeffectState *state;
549 size_t count = 0;
551 props = ATOMIC_LOAD(&slot->Update);
552 if(props)
554 state = ATOMIC_LOAD(&props->State, almemory_order_relaxed);
555 if(state != slot->Params.EffectState)
556 DELETE_OBJ(state);
557 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
558 al_free(props);
560 props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
561 while(props)
563 struct ALeffectslotProps *next;
564 state = ATOMIC_LOAD(&props->State, almemory_order_relaxed);
565 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
566 DELETE_OBJ(state);
567 al_free(props);
568 props = next;
569 ++count;
571 TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
573 DELETE_OBJ(slot->Params.EffectState);
576 void UpdateEffectSlotProps(ALeffectslot *slot)
578 struct ALeffectslotProps *props;
579 ALeffectState *oldstate;
581 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, NULL);
582 if(props)
584 /* If there was an unapplied update, check if its state object is the
585 * same as the current in-use one, or the one that will be set. If
586 * neither, delete it.
588 oldstate = ATOMIC_EXCHANGE(ALeffectState*, &props->State, NULL, almemory_order_relaxed);
589 if(oldstate != slot->Params.EffectState && oldstate != slot->Effect.State)
590 DELETE_OBJ(oldstate);
592 else
594 /* Get an unused property container, or allocate a new one as needed. */
595 props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
596 if(!props)
597 props = al_calloc(16, sizeof(*props));
598 else
600 struct ALeffectslotProps *next;
601 do {
602 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
603 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
604 &slot->FreeList, &props, next, almemory_order_seq_cst,
605 almemory_order_consume) == 0);
609 /* Copy in current property values. */
610 ATOMIC_STORE(&props->Gain, slot->Gain, almemory_order_relaxed);
611 ATOMIC_STORE(&props->AuxSendAuto, slot->AuxSendAuto, almemory_order_relaxed);
613 ATOMIC_STORE(&props->Type, slot->Effect.Type, almemory_order_relaxed);
614 memcpy(&props->Props, &slot->Effect.Props, sizeof(props->Props));
615 /* Swap out any stale effect state object there may be in the container, to
616 * delete it.
618 oldstate = ATOMIC_EXCHANGE(ALeffectState*, &props->State, slot->Effect.State,
619 almemory_order_relaxed);
621 /* Set the new container for updating internal parameters. */
622 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, props,
623 almemory_order_acq_rel);
624 if(props)
626 /* If there was an unused update container, put it back in the
627 * freelist.
629 struct ALeffectslotProps *first = ATOMIC_LOAD(&slot->FreeList);
630 do {
631 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
632 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
633 &slot->FreeList, &first, props) == 0);
636 DELETE_OBJ(oldstate);
639 ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
641 ALsizei pos;
642 for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
644 ALeffectslot *temp = Context->EffectSlotMap.array[pos].value;
645 Context->EffectSlotMap.array[pos].value = NULL;
647 DeinitEffectSlot(temp);
649 FreeThunkEntry(temp->id);
650 memset(temp, 0, sizeof(ALeffectslot));
651 al_free(temp);