Properly load the effect state pointer from the property container
[openal-soft.git] / OpenAL32 / alAuxEffectSlot.c
blob98ee932861ca4e7ea8b51c8ff6dd12b7b30b944e
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, AL_FALSE);
205 break;
207 default:
208 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
211 done:
212 WriteUnlock(&context->PropLock);
213 ALCcontext_DecRef(context);
216 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *values)
218 ALCcontext *context;
220 switch(param)
222 case AL_EFFECTSLOT_EFFECT:
223 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
224 alAuxiliaryEffectSloti(effectslot, param, values[0]);
225 return;
228 context = GetContextRef();
229 if(!context) return;
231 if(LookupEffectSlot(context, effectslot) == NULL)
232 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
233 switch(param)
235 default:
236 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
239 done:
240 ALCcontext_DecRef(context);
243 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat value)
245 ALCcontext *context;
246 ALeffectslot *slot;
248 context = GetContextRef();
249 if(!context) return;
251 WriteLock(&context->PropLock);
252 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
253 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
254 switch(param)
256 case AL_EFFECTSLOT_GAIN:
257 if(!(value >= 0.0f && value <= 1.0f))
258 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
259 slot->Gain = value;
260 break;
262 default:
263 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
265 UpdateEffectSlotProps(slot, AL_FALSE);
267 done:
268 WriteUnlock(&context->PropLock);
269 ALCcontext_DecRef(context);
272 AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *values)
274 ALCcontext *context;
276 switch(param)
278 case AL_EFFECTSLOT_GAIN:
279 alAuxiliaryEffectSlotf(effectslot, param, values[0]);
280 return;
283 context = GetContextRef();
284 if(!context) return;
286 if(LookupEffectSlot(context, effectslot) == NULL)
287 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
288 switch(param)
290 default:
291 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
294 done:
295 ALCcontext_DecRef(context);
298 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *value)
300 ALCcontext *context;
301 ALeffectslot *slot;
303 context = GetContextRef();
304 if(!context) return;
306 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
307 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
308 switch(param)
310 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
311 *value = slot->AuxSendAuto;
312 break;
314 default:
315 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
318 done:
319 ALCcontext_DecRef(context);
322 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *values)
324 ALCcontext *context;
326 switch(param)
328 case AL_EFFECTSLOT_EFFECT:
329 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO:
330 alGetAuxiliaryEffectSloti(effectslot, param, values);
331 return;
334 context = GetContextRef();
335 if(!context) return;
337 if(LookupEffectSlot(context, effectslot) == NULL)
338 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
339 switch(param)
341 default:
342 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
345 done:
346 ALCcontext_DecRef(context);
349 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *value)
351 ALCcontext *context;
352 ALeffectslot *slot;
354 context = GetContextRef();
355 if(!context) return;
357 if((slot=LookupEffectSlot(context, effectslot)) == NULL)
358 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
359 switch(param)
361 case AL_EFFECTSLOT_GAIN:
362 *value = slot->Gain;
363 break;
365 default:
366 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
369 done:
370 ALCcontext_DecRef(context);
373 AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *values)
375 ALCcontext *context;
377 switch(param)
379 case AL_EFFECTSLOT_GAIN:
380 alGetAuxiliaryEffectSlotf(effectslot, param, values);
381 return;
384 context = GetContextRef();
385 if(!context) return;
387 if(LookupEffectSlot(context, effectslot) == NULL)
388 SET_ERROR_AND_GOTO(context, AL_INVALID_NAME, done);
389 switch(param)
391 default:
392 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
395 done:
396 ALCcontext_DecRef(context);
400 static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count)
402 ALenum err = AL_NO_ERROR;
404 LockContext(context);
405 if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_END(context->ActiveAuxSlots), start, start+count))
406 err = AL_OUT_OF_MEMORY;
407 UnlockContext(context);
409 return err;
412 static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot)
414 ALeffectslot **iter;
416 LockContext(context);
417 #define MATCH_SLOT(_i) (slot == *(_i))
418 VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT);
419 if(iter != VECTOR_END(context->ActiveAuxSlots))
421 *iter = VECTOR_BACK(context->ActiveAuxSlots);
422 VECTOR_POP_BACK(context->ActiveAuxSlots);
424 #undef MATCH_SLOT
425 UnlockContext(context);
429 void InitEffectFactoryMap(void)
431 InitUIntMap(&EffectStateFactoryMap, ~0);
433 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_NULL, ALnullStateFactory_getFactory);
434 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EAXREVERB, ALreverbStateFactory_getFactory);
435 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_REVERB, ALreverbStateFactory_getFactory);
436 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_AUTOWAH, ALautowahStateFactory_getFactory);
437 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_CHORUS, ALchorusStateFactory_getFactory);
438 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_COMPRESSOR, ALcompressorStateFactory_getFactory);
439 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DISTORTION, ALdistortionStateFactory_getFactory);
440 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_ECHO, ALechoStateFactory_getFactory);
441 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_EQUALIZER, ALequalizerStateFactory_getFactory);
442 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_FLANGER, ALflangerStateFactory_getFactory);
443 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_RING_MODULATOR, ALmodulatorStateFactory_getFactory);
444 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_DIALOGUE, ALdedicatedStateFactory_getFactory);
445 InsertUIntMapEntry(&EffectStateFactoryMap, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, ALdedicatedStateFactory_getFactory);
448 void DeinitEffectFactoryMap(void)
450 ResetUIntMap(&EffectStateFactoryMap);
454 ALenum InitializeEffect(ALCdevice *Device, ALeffectslot *EffectSlot, ALeffect *effect)
456 ALenum newtype = (effect ? effect->type : AL_EFFECT_NULL);
457 ALeffectStateFactory *factory;
459 if(newtype != EffectSlot->Effect.Type)
461 ALeffectState *State;
462 FPUCtl oldMode;
464 factory = getFactoryByType(newtype);
465 if(!factory)
467 ERR("Failed to find factory for effect type 0x%04x\n", newtype);
468 return AL_INVALID_ENUM;
470 State = V0(factory,create)();
471 if(!State)
472 return AL_OUT_OF_MEMORY;
474 SetMixerFPUMode(&oldMode);
475 /* FIXME: This just needs to prevent the device from being reset during
476 * the state's device update, so the list lock in ALc.c should do here.
478 ALCdevice_Lock(Device);
479 State->OutBuffer = Device->Dry.Buffer;
480 State->OutChannels = Device->Dry.NumChannels;
481 if(V(State,deviceUpdate)(Device) == AL_FALSE)
483 ALCdevice_Unlock(Device);
484 RestoreFPUMode(&oldMode);
485 DELETE_OBJ(State);
486 return AL_OUT_OF_MEMORY;
488 ALCdevice_Unlock(Device);
489 RestoreFPUMode(&oldMode);
491 if(!effect)
493 EffectSlot->Effect.Type = AL_EFFECT_NULL;
494 memset(&EffectSlot->Effect.Props, 0, sizeof(EffectSlot->Effect.Props));
496 else
498 EffectSlot->Effect.Type = effect->type;
499 memcpy(&EffectSlot->Effect.Props, &effect->Props, sizeof(EffectSlot->Effect.Props));
502 EffectSlot->Effect.State = State;
503 UpdateEffectSlotProps(EffectSlot, AL_TRUE);
505 else if(effect)
507 memcpy(&EffectSlot->Effect.Props, &effect->Props, sizeof(EffectSlot->Effect.Props));
508 UpdateEffectSlotProps(EffectSlot, AL_FALSE);
511 return AL_NO_ERROR;
515 void ALeffectState_Destruct(ALeffectState *UNUSED(state))
520 ALenum InitEffectSlot(ALeffectslot *slot)
522 ALeffectStateFactory *factory;
524 slot->Effect.Type = AL_EFFECT_NULL;
526 factory = getFactoryByType(AL_EFFECT_NULL);
527 if(!(slot->Effect.State=V0(factory,create)()))
528 return AL_OUT_OF_MEMORY;
530 slot->Gain = 1.0;
531 slot->AuxSendAuto = AL_TRUE;
532 InitRef(&slot->ref, 0);
534 ATOMIC_INIT(&slot->Update, NULL);
535 ATOMIC_INIT(&slot->FreeList, NULL);
537 slot->Params.Gain = 1.0f;
538 slot->Params.AuxSendAuto = AL_TRUE;
539 slot->Params.EffectState = slot->Effect.State;
540 slot->Params.RoomRolloff = 0.0f;
541 slot->Params.DecayTime = 0.0f;
542 slot->Params.AirAbsorptionGainHF = 1.0f;
544 return AL_NO_ERROR;
547 void DeinitEffectSlot(ALeffectslot *slot)
549 struct ALeffectslotProps *props;
550 size_t count = 0;
552 props = ATOMIC_LOAD(&slot->Update);
553 if(props)
555 ALeffectState *state;
556 state = ATOMIC_LOAD(&props->State, almemory_order_relaxed);
557 DELETE_OBJ(state);
558 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props);
559 al_free(props);
561 props = ATOMIC_LOAD(&slot->FreeList, almemory_order_relaxed);
562 while(props)
564 struct ALeffectslotProps *next;
565 ALeffectState *state;
566 state = ATOMIC_LOAD(&props->State, almemory_order_relaxed);
567 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
568 DELETE_OBJ(state);
569 al_free(props);
570 props = next;
571 ++count;
573 TRACE("Freed "SZFMT" AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s");
575 DELETE_OBJ(slot->Params.EffectState);
578 void UpdateEffectSlotProps(ALeffectslot *slot, ALboolean withstate)
580 struct ALeffectslotProps *props;
581 ALeffectState *oldstate;
583 /* Get an unused property container, or allocate a new one as needed. */
584 props = ATOMIC_LOAD(&slot->FreeList, almemory_order_acquire);
585 if(!props)
586 props = al_calloc(16, sizeof(*props));
587 else
589 struct ALeffectslotProps *next;
590 do {
591 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
592 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
593 &slot->FreeList, &props, next, almemory_order_seq_cst,
594 almemory_order_consume) == 0);
597 /* Copy in current property values. */
598 ATOMIC_STORE(&props->Gain, slot->Gain, almemory_order_relaxed);
599 ATOMIC_STORE(&props->AuxSendAuto, slot->AuxSendAuto, almemory_order_relaxed);
601 ATOMIC_STORE(&props->Type, slot->Effect.Type, almemory_order_relaxed);
602 memcpy(&props->Props, &slot->Effect.Props, sizeof(props->Props));
603 /* Swap out any stale effect state object there may be in the container, to
604 * delete it.
606 ATOMIC_STORE(&props->UpdateState, withstate, almemory_order_relaxed);
607 oldstate = ATOMIC_EXCHANGE(ALeffectState*, &props->State,
608 withstate ? slot->Effect.State : NULL, almemory_order_relaxed
611 /* Set the new container for updating internal parameters. */
612 props = ATOMIC_EXCHANGE(struct ALeffectslotProps*, &slot->Update, props,
613 almemory_order_acq_rel);
614 if(props)
616 /* If there was an unused update container, put it back in the
617 * freelist.
619 struct ALeffectslotProps *first = ATOMIC_LOAD(&slot->FreeList);
620 do {
621 ATOMIC_STORE(&props->next, first, almemory_order_relaxed);
622 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps*,
623 &slot->FreeList, &first, props) == 0);
626 DELETE_OBJ(oldstate);
629 ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *Context)
631 ALsizei pos;
632 for(pos = 0;pos < Context->EffectSlotMap.size;pos++)
634 ALeffectslot *temp = Context->EffectSlotMap.array[pos].value;
635 Context->EffectSlotMap.array[pos].value = NULL;
637 DeinitEffectSlot(temp);
639 FreeThunkEntry(temp->id);
640 memset(temp, 0, sizeof(ALeffectslot));
641 al_free(temp);