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
29 #include "alAuxEffectSlot.h"
32 #include "alListener.h"
38 extern inline void LockEffectSlotsRead(ALCcontext
*context
);
39 extern inline void UnlockEffectSlotsRead(ALCcontext
*context
);
40 extern inline void LockEffectSlotsWrite(ALCcontext
*context
);
41 extern inline void UnlockEffectSlotsWrite(ALCcontext
*context
);
42 extern inline struct ALeffectslot
*LookupEffectSlot(ALCcontext
*context
, ALuint id
);
43 extern inline struct ALeffectslot
*RemoveEffectSlot(ALCcontext
*context
, ALuint id
);
45 static UIntMap EffectStateFactoryMap
;
46 static inline ALeffectStateFactory
*getFactoryByType(ALenum type
)
48 ALeffectStateFactory
* (*getFactory
)(void) = LookupUIntMapKey(&EffectStateFactoryMap
, type
);
49 if(getFactory
!= NULL
)
54 static void ALeffectState_IncRef(ALeffectState
*state
);
55 static void ALeffectState_DecRef(ALeffectState
*state
);
57 #define DO_UPDATEPROPS() do { \
58 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
59 UpdateEffectSlotProps(slot); \
61 ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \
65 AL_API ALvoid AL_APIENTRY
alGenAuxiliaryEffectSlots(ALsizei n
, ALuint
*effectslots
)
68 ALeffectslot
**tmpslots
= NULL
;
72 context
= GetContextRef();
76 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
77 tmpslots
= al_malloc(DEF_ALIGN
, sizeof(ALeffectslot
*)*n
);
79 LockEffectSlotsWrite(context
);
80 for(cur
= 0;cur
< n
;cur
++)
82 ALeffectslot
*slot
= al_calloc(16, sizeof(ALeffectslot
));
83 err
= AL_OUT_OF_MEMORY
;
84 if(!slot
|| (err
=InitEffectSlot(slot
)) != AL_NO_ERROR
)
87 UnlockEffectSlotsWrite(context
);
89 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
90 SET_ERROR_AND_GOTO(context
, err
, done
);
93 err
= NewThunkEntry(&slot
->id
);
94 if(err
== AL_NO_ERROR
)
95 err
= InsertUIntMapEntryNoLock(&context
->EffectSlotMap
, slot
->id
, slot
);
96 if(err
!= AL_NO_ERROR
)
98 FreeThunkEntry(slot
->id
);
99 ALeffectState_DecRef(slot
->Effect
.State
);
100 if(slot
->Params
.EffectState
)
101 ALeffectState_DecRef(slot
->Params
.EffectState
);
103 UnlockEffectSlotsWrite(context
);
105 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
106 SET_ERROR_AND_GOTO(context
, err
, done
);
109 aluInitEffectPanning(slot
);
111 tmpslots
[cur
] = slot
;
112 effectslots
[cur
] = slot
->id
;
116 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
117 struct ALeffectslotArray
*newarray
= NULL
;
118 ALsizei newcount
= curarray
->count
+ n
;
121 newarray
= al_calloc(DEF_ALIGN
,
122 offsetof(struct ALeffectslotArray
, slot
[newcount
])
124 newarray
->count
= newcount
;
125 memcpy(newarray
->slot
, tmpslots
, sizeof(ALeffectslot
*)*n
);
127 memcpy(newarray
->slot
+n
, curarray
->slot
, sizeof(ALeffectslot
*)*curarray
->count
);
129 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
130 almemory_order_acq_rel
);
131 device
= context
->Device
;
132 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
136 UnlockEffectSlotsWrite(context
);
140 ALCcontext_DecRef(context
);
143 AL_API ALvoid AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
149 context
= GetContextRef();
152 LockEffectSlotsWrite(context
);
154 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
157 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == NULL
)
158 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
159 if(ReadRef(&slot
->ref
) != 0)
160 SET_ERROR_AND_GOTO(context
, AL_INVALID_OPERATION
, done
);
163 // All effectslots are valid
166 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
167 struct ALeffectslotArray
*newarray
= NULL
;
168 ALsizei newcount
= curarray
->count
- n
;
172 assert(newcount
>= 0);
173 newarray
= al_calloc(DEF_ALIGN
,
174 offsetof(struct ALeffectslotArray
, slot
[newcount
])
176 newarray
->count
= newcount
;
177 for(i
= j
= 0;i
< newarray
->count
;)
179 slot
= curarray
->slot
[j
++];
182 if(slot
->id
== effectslots
[k
])
186 newarray
->slot
[i
++] = slot
;
189 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
190 almemory_order_acq_rel
);
191 device
= context
->Device
;
192 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
199 if((slot
=RemoveEffectSlot(context
, effectslots
[i
])) == NULL
)
201 FreeThunkEntry(slot
->id
);
203 DeinitEffectSlot(slot
);
205 memset(slot
, 0, sizeof(*slot
));
210 UnlockEffectSlotsWrite(context
);
211 ALCcontext_DecRef(context
);
214 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
219 context
= GetContextRef();
220 if(!context
) return AL_FALSE
;
222 LockEffectSlotsRead(context
);
223 ret
= (LookupEffectSlot(context
, effectslot
) ? AL_TRUE
: AL_FALSE
);
224 UnlockEffectSlotsRead(context
);
226 ALCcontext_DecRef(context
);
231 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
236 ALeffect
*effect
= NULL
;
239 context
= GetContextRef();
242 WriteLock(&context
->PropLock
);
243 LockEffectSlotsRead(context
);
244 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
245 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
248 case AL_EFFECTSLOT_EFFECT
:
249 device
= context
->Device
;
251 LockEffectsRead(device
);
252 effect
= (value
? LookupEffect(device
, value
) : NULL
);
253 if(!(value
== 0 || effect
!= NULL
))
255 UnlockEffectsRead(device
);
256 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
258 err
= InitializeEffect(device
, slot
, effect
);
259 UnlockEffectsRead(device
);
261 if(err
!= AL_NO_ERROR
)
262 SET_ERROR_AND_GOTO(context
, err
, done
);
265 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
266 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
267 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
268 slot
->AuxSendAuto
= value
;
272 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
277 UnlockEffectSlotsRead(context
);
278 WriteUnlock(&context
->PropLock
);
279 ALCcontext_DecRef(context
);
282 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
288 case AL_EFFECTSLOT_EFFECT
:
289 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
290 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
294 context
= GetContextRef();
297 LockEffectSlotsRead(context
);
298 if(LookupEffectSlot(context
, effectslot
) == NULL
)
299 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
303 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
307 UnlockEffectSlotsRead(context
);
308 ALCcontext_DecRef(context
);
311 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
316 context
= GetContextRef();
319 WriteLock(&context
->PropLock
);
320 LockEffectSlotsRead(context
);
321 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
322 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
325 case AL_EFFECTSLOT_GAIN
:
326 if(!(value
>= 0.0f
&& value
<= 1.0f
))
327 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
332 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
337 UnlockEffectSlotsRead(context
);
338 WriteUnlock(&context
->PropLock
);
339 ALCcontext_DecRef(context
);
342 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
348 case AL_EFFECTSLOT_GAIN
:
349 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
353 context
= GetContextRef();
356 LockEffectSlotsRead(context
);
357 if(LookupEffectSlot(context
, effectslot
) == NULL
)
358 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
362 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
366 UnlockEffectSlotsRead(context
);
367 ALCcontext_DecRef(context
);
370 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
375 context
= GetContextRef();
378 LockEffectSlotsRead(context
);
379 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
380 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
383 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
384 *value
= slot
->AuxSendAuto
;
388 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
392 UnlockEffectSlotsRead(context
);
393 ALCcontext_DecRef(context
);
396 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
402 case AL_EFFECTSLOT_EFFECT
:
403 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
404 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
408 context
= GetContextRef();
411 LockEffectSlotsRead(context
);
412 if(LookupEffectSlot(context
, effectslot
) == NULL
)
413 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
417 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
421 UnlockEffectSlotsRead(context
);
422 ALCcontext_DecRef(context
);
425 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
430 context
= GetContextRef();
433 LockEffectSlotsRead(context
);
434 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
435 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
438 case AL_EFFECTSLOT_GAIN
:
443 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
447 UnlockEffectSlotsRead(context
);
448 ALCcontext_DecRef(context
);
451 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
457 case AL_EFFECTSLOT_GAIN
:
458 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
462 context
= GetContextRef();
465 LockEffectSlotsRead(context
);
466 if(LookupEffectSlot(context
, effectslot
) == NULL
)
467 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
471 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
475 UnlockEffectSlotsRead(context
);
476 ALCcontext_DecRef(context
);
480 void InitEffectFactoryMap(void)
482 InitUIntMap(&EffectStateFactoryMap
, ~0);
484 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_NULL
, ALnullStateFactory_getFactory
);
485 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EAXREVERB
, ALreverbStateFactory_getFactory
);
486 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_REVERB
, ALreverbStateFactory_getFactory
);
487 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_CHORUS
, ALchorusStateFactory_getFactory
);
488 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_COMPRESSOR
, ALcompressorStateFactory_getFactory
);
489 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DISTORTION
, ALdistortionStateFactory_getFactory
);
490 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_ECHO
, ALechoStateFactory_getFactory
);
491 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EQUALIZER
, ALequalizerStateFactory_getFactory
);
492 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_FLANGER
, ALflangerStateFactory_getFactory
);
493 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_RING_MODULATOR
, ALmodulatorStateFactory_getFactory
);
494 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_DIALOGUE
, ALdedicatedStateFactory_getFactory
);
495 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
, ALdedicatedStateFactory_getFactory
);
498 void DeinitEffectFactoryMap(void)
500 ResetUIntMap(&EffectStateFactoryMap
);
504 ALenum
InitializeEffect(ALCdevice
*Device
, ALeffectslot
*EffectSlot
, ALeffect
*effect
)
506 ALenum newtype
= (effect
? effect
->type
: AL_EFFECT_NULL
);
507 struct ALeffectslotProps
*props
;
508 ALeffectState
*State
;
510 if(newtype
!= EffectSlot
->Effect
.Type
)
512 ALeffectStateFactory
*factory
;
515 factory
= getFactoryByType(newtype
);
518 ERR("Failed to find factory for effect type 0x%04x\n", newtype
);
519 return AL_INVALID_ENUM
;
521 State
= V0(factory
,create
)();
522 if(!State
) return AL_OUT_OF_MEMORY
;
524 SetMixerFPUMode(&oldMode
);
525 almtx_lock(&Device
->BackendLock
);
526 State
->OutBuffer
= Device
->Dry
.Buffer
;
527 State
->OutChannels
= Device
->Dry
.NumChannels
;
528 if(V(State
,deviceUpdate
)(Device
) == AL_FALSE
)
530 almtx_unlock(&Device
->BackendLock
);
531 RestoreFPUMode(&oldMode
);
532 ALeffectState_DecRef(State
);
533 return AL_OUT_OF_MEMORY
;
535 almtx_unlock(&Device
->BackendLock
);
536 RestoreFPUMode(&oldMode
);
540 EffectSlot
->Effect
.Type
= AL_EFFECT_NULL
;
541 memset(&EffectSlot
->Effect
.Props
, 0, sizeof(EffectSlot
->Effect
.Props
));
545 EffectSlot
->Effect
.Type
= effect
->type
;
546 EffectSlot
->Effect
.Props
= effect
->Props
;
549 ALeffectState_DecRef(EffectSlot
->Effect
.State
);
550 EffectSlot
->Effect
.State
= State
;
553 EffectSlot
->Effect
.Props
= effect
->Props
;
555 /* Remove state references from old effect slot property updates. */
556 props
= ATOMIC_LOAD_SEQ(&EffectSlot
->FreeList
);
560 ALeffectState_DecRef(props
->State
);
562 props
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
569 static void ALeffectState_IncRef(ALeffectState
*state
)
572 ref
= IncrementRef(&state
->Ref
);
573 TRACEREF("%p increasing refcount to %u\n", state
, ref
);
576 static void ALeffectState_DecRef(ALeffectState
*state
)
579 ref
= DecrementRef(&state
->Ref
);
580 TRACEREF("%p decreasing refcount to %u\n", state
, ref
);
581 if(ref
== 0) DELETE_OBJ(state
);
585 void ALeffectState_Construct(ALeffectState
*state
)
587 InitRef(&state
->Ref
, 1);
589 state
->OutBuffer
= NULL
;
590 state
->OutChannels
= 0;
593 void ALeffectState_Destruct(ALeffectState
*UNUSED(state
))
598 ALenum
InitEffectSlot(ALeffectslot
*slot
)
600 ALeffectStateFactory
*factory
;
602 slot
->Effect
.Type
= AL_EFFECT_NULL
;
604 factory
= getFactoryByType(AL_EFFECT_NULL
);
605 if(!(slot
->Effect
.State
=V0(factory
,create
)()))
606 return AL_OUT_OF_MEMORY
;
609 slot
->AuxSendAuto
= AL_TRUE
;
610 ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_relaxed
);
611 InitRef(&slot
->ref
, 0);
613 ATOMIC_INIT(&slot
->Update
, NULL
);
614 ATOMIC_INIT(&slot
->FreeList
, NULL
);
616 slot
->Params
.Gain
= 1.0f
;
617 slot
->Params
.AuxSendAuto
= AL_TRUE
;
618 ALeffectState_IncRef(slot
->Effect
.State
);
619 slot
->Params
.EffectState
= slot
->Effect
.State
;
620 slot
->Params
.RoomRolloff
= 0.0f
;
621 slot
->Params
.DecayTime
= 0.0f
;
622 slot
->Params
.AirAbsorptionGainHF
= 1.0f
;
627 void DeinitEffectSlot(ALeffectslot
*slot
)
629 struct ALeffectslotProps
*props
;
632 props
= ATOMIC_LOAD_SEQ(&slot
->Update
);
635 if(props
->State
) ALeffectState_DecRef(props
->State
);
636 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props
);
639 props
= ATOMIC_LOAD(&slot
->FreeList
, almemory_order_relaxed
);
642 struct ALeffectslotProps
*next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
643 if(props
->State
) ALeffectState_DecRef(props
->State
);
648 TRACE("Freed "SZFMT
" AuxiliaryEffectSlot property object%s\n", count
, (count
==1)?"":"s");
650 ALeffectState_DecRef(slot
->Effect
.State
);
651 if(slot
->Params
.EffectState
)
652 ALeffectState_DecRef(slot
->Params
.EffectState
);
655 void UpdateEffectSlotProps(ALeffectslot
*slot
)
657 struct ALeffectslotProps
*props
;
658 ALeffectState
*oldstate
;
660 /* Get an unused property container, or allocate a new one as needed. */
661 props
= ATOMIC_LOAD(&slot
->FreeList
, almemory_order_relaxed
);
663 props
= al_calloc(16, sizeof(*props
));
666 struct ALeffectslotProps
*next
;
668 next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
669 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&slot
->FreeList
, &props
, next
,
670 almemory_order_seq_cst
, almemory_order_acquire
) == 0);
673 /* Copy in current property values. */
674 props
->Gain
= slot
->Gain
;
675 props
->AuxSendAuto
= slot
->AuxSendAuto
;
677 props
->Type
= slot
->Effect
.Type
;
678 props
->Props
= slot
->Effect
.Props
;
679 /* Swap out any stale effect state object there may be in the container, to
682 ALeffectState_IncRef(slot
->Effect
.State
);
683 oldstate
= props
->State
;
684 props
->State
= slot
->Effect
.State
;
686 /* Set the new container for updating internal parameters. */
687 props
= ATOMIC_EXCHANGE_PTR(&slot
->Update
, props
, almemory_order_acq_rel
);
690 /* If there was an unused update container, put it back in the
693 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps
*, &slot
->FreeList
, props
);
697 ALeffectState_DecRef(oldstate
);
700 void UpdateAllEffectSlotProps(ALCcontext
*context
)
702 struct ALeffectslotArray
*auxslots
;
705 LockEffectSlotsRead(context
);
706 auxslots
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
707 for(i
= 0;i
< auxslots
->count
;i
++)
709 ALeffectslot
*slot
= auxslots
->slot
[i
];
710 if(!ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_acq_rel
))
711 UpdateEffectSlotProps(slot
);
713 UnlockEffectSlotsRead(context
);
716 ALvoid
ReleaseALAuxiliaryEffectSlots(ALCcontext
*Context
)
719 for(pos
= 0;pos
< Context
->EffectSlotMap
.size
;pos
++)
721 ALeffectslot
*temp
= Context
->EffectSlotMap
.values
[pos
];
722 Context
->EffectSlotMap
.values
[pos
] = NULL
;
724 DeinitEffectSlot(temp
);
726 FreeThunkEntry(temp
->id
);
727 memset(temp
, 0, sizeof(ALeffectslot
));