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
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
122 newarray
->count
= newcount
;
123 memcpy(newarray
->slot
, tmpslots
, sizeof(ALeffectslot
*)*n
);
125 memcpy(newarray
->slot
+n
, curarray
->slot
, sizeof(ALeffectslot
*)*curarray
->count
);
127 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
128 almemory_order_acq_rel
);
129 device
= context
->Device
;
130 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
134 UnlockEffectSlotsWrite(context
);
138 ALCcontext_DecRef(context
);
141 AL_API ALvoid AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
147 context
= GetContextRef();
150 LockEffectSlotsWrite(context
);
152 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
155 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == NULL
)
156 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
157 if(ReadRef(&slot
->ref
) != 0)
158 SET_ERROR_AND_GOTO(context
, AL_INVALID_OPERATION
, done
);
161 // All effectslots are valid
164 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
165 struct ALeffectslotArray
*newarray
= NULL
;
166 ALsizei newcount
= curarray
->count
- n
;
170 assert(newcount
>= 0);
171 newarray
= al_calloc(DEF_ALIGN
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
172 newarray
->count
= newcount
;
173 for(i
= j
= 0;i
< newarray
->count
;)
175 slot
= curarray
->slot
[j
++];
178 if(slot
->id
== effectslots
[k
])
182 newarray
->slot
[i
++] = slot
;
185 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
186 almemory_order_acq_rel
);
187 device
= context
->Device
;
188 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
195 if((slot
=RemoveEffectSlot(context
, effectslots
[i
])) == NULL
)
197 FreeThunkEntry(slot
->id
);
199 DeinitEffectSlot(slot
);
201 memset(slot
, 0, sizeof(*slot
));
206 UnlockEffectSlotsWrite(context
);
207 ALCcontext_DecRef(context
);
210 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
215 context
= GetContextRef();
216 if(!context
) return AL_FALSE
;
218 LockEffectSlotsRead(context
);
219 ret
= (LookupEffectSlot(context
, effectslot
) ? AL_TRUE
: AL_FALSE
);
220 UnlockEffectSlotsRead(context
);
222 ALCcontext_DecRef(context
);
227 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
232 ALeffect
*effect
= NULL
;
235 context
= GetContextRef();
238 WriteLock(&context
->PropLock
);
239 LockEffectSlotsRead(context
);
240 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
241 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
244 case AL_EFFECTSLOT_EFFECT
:
245 device
= context
->Device
;
247 LockEffectsRead(device
);
248 effect
= (value
? LookupEffect(device
, value
) : NULL
);
249 if(!(value
== 0 || effect
!= NULL
))
251 UnlockEffectsRead(device
);
252 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
254 err
= InitializeEffect(device
, slot
, effect
);
255 UnlockEffectsRead(device
);
257 if(err
!= AL_NO_ERROR
)
258 SET_ERROR_AND_GOTO(context
, err
, done
);
261 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
262 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
263 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
264 slot
->AuxSendAuto
= value
;
268 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
273 UnlockEffectSlotsRead(context
);
274 WriteUnlock(&context
->PropLock
);
275 ALCcontext_DecRef(context
);
278 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
284 case AL_EFFECTSLOT_EFFECT
:
285 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
286 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
290 context
= GetContextRef();
293 LockEffectSlotsRead(context
);
294 if(LookupEffectSlot(context
, effectslot
) == NULL
)
295 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
299 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
303 UnlockEffectSlotsRead(context
);
304 ALCcontext_DecRef(context
);
307 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
312 context
= GetContextRef();
315 WriteLock(&context
->PropLock
);
316 LockEffectSlotsRead(context
);
317 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
318 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
321 case AL_EFFECTSLOT_GAIN
:
322 if(!(value
>= 0.0f
&& value
<= 1.0f
))
323 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
328 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
333 UnlockEffectSlotsRead(context
);
334 WriteUnlock(&context
->PropLock
);
335 ALCcontext_DecRef(context
);
338 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
344 case AL_EFFECTSLOT_GAIN
:
345 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
349 context
= GetContextRef();
352 LockEffectSlotsRead(context
);
353 if(LookupEffectSlot(context
, effectslot
) == NULL
)
354 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
358 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
362 UnlockEffectSlotsRead(context
);
363 ALCcontext_DecRef(context
);
366 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
371 context
= GetContextRef();
374 LockEffectSlotsRead(context
);
375 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
376 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
379 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
380 *value
= slot
->AuxSendAuto
;
384 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
388 UnlockEffectSlotsRead(context
);
389 ALCcontext_DecRef(context
);
392 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
398 case AL_EFFECTSLOT_EFFECT
:
399 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
400 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
404 context
= GetContextRef();
407 LockEffectSlotsRead(context
);
408 if(LookupEffectSlot(context
, effectslot
) == NULL
)
409 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
413 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
417 UnlockEffectSlotsRead(context
);
418 ALCcontext_DecRef(context
);
421 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
426 context
= GetContextRef();
429 LockEffectSlotsRead(context
);
430 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
431 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
434 case AL_EFFECTSLOT_GAIN
:
439 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
443 UnlockEffectSlotsRead(context
);
444 ALCcontext_DecRef(context
);
447 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
453 case AL_EFFECTSLOT_GAIN
:
454 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
458 context
= GetContextRef();
461 LockEffectSlotsRead(context
);
462 if(LookupEffectSlot(context
, effectslot
) == NULL
)
463 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
467 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
471 UnlockEffectSlotsRead(context
);
472 ALCcontext_DecRef(context
);
476 void InitEffectFactoryMap(void)
478 InitUIntMap(&EffectStateFactoryMap
, INT_MAX
);
480 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_NULL
, ALnullStateFactory_getFactory
);
481 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EAXREVERB
, ALreverbStateFactory_getFactory
);
482 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_REVERB
, ALreverbStateFactory_getFactory
);
483 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_CHORUS
, ALchorusStateFactory_getFactory
);
484 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_COMPRESSOR
, ALcompressorStateFactory_getFactory
);
485 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DISTORTION
, ALdistortionStateFactory_getFactory
);
486 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_ECHO
, ALechoStateFactory_getFactory
);
487 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EQUALIZER
, ALequalizerStateFactory_getFactory
);
488 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_FLANGER
, ALflangerStateFactory_getFactory
);
489 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_RING_MODULATOR
, ALmodulatorStateFactory_getFactory
);
490 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_DIALOGUE
, ALdedicatedStateFactory_getFactory
);
491 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
, ALdedicatedStateFactory_getFactory
);
494 void DeinitEffectFactoryMap(void)
496 ResetUIntMap(&EffectStateFactoryMap
);
500 ALenum
InitializeEffect(ALCdevice
*Device
, ALeffectslot
*EffectSlot
, ALeffect
*effect
)
502 ALenum newtype
= (effect
? effect
->type
: AL_EFFECT_NULL
);
503 struct ALeffectslotProps
*props
;
504 ALeffectState
*State
;
506 if(newtype
!= EffectSlot
->Effect
.Type
)
508 ALeffectStateFactory
*factory
;
511 factory
= getFactoryByType(newtype
);
514 ERR("Failed to find factory for effect type 0x%04x\n", newtype
);
515 return AL_INVALID_ENUM
;
517 State
= V0(factory
,create
)();
518 if(!State
) return AL_OUT_OF_MEMORY
;
520 SetMixerFPUMode(&oldMode
);
521 almtx_lock(&Device
->BackendLock
);
522 State
->OutBuffer
= Device
->Dry
.Buffer
;
523 State
->OutChannels
= Device
->Dry
.NumChannels
;
524 if(V(State
,deviceUpdate
)(Device
) == AL_FALSE
)
526 almtx_unlock(&Device
->BackendLock
);
527 RestoreFPUMode(&oldMode
);
528 ALeffectState_DecRef(State
);
529 return AL_OUT_OF_MEMORY
;
531 almtx_unlock(&Device
->BackendLock
);
532 RestoreFPUMode(&oldMode
);
536 EffectSlot
->Effect
.Type
= AL_EFFECT_NULL
;
537 memset(&EffectSlot
->Effect
.Props
, 0, sizeof(EffectSlot
->Effect
.Props
));
541 EffectSlot
->Effect
.Type
= effect
->type
;
542 EffectSlot
->Effect
.Props
= effect
->Props
;
545 ALeffectState_DecRef(EffectSlot
->Effect
.State
);
546 EffectSlot
->Effect
.State
= State
;
549 EffectSlot
->Effect
.Props
= effect
->Props
;
551 /* Remove state references from old effect slot property updates. */
552 props
= ATOMIC_LOAD_SEQ(&EffectSlot
->FreeList
);
556 ALeffectState_DecRef(props
->State
);
558 props
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
565 static void ALeffectState_IncRef(ALeffectState
*state
)
568 ref
= IncrementRef(&state
->Ref
);
569 TRACEREF("%p increasing refcount to %u\n", state
, ref
);
572 static void ALeffectState_DecRef(ALeffectState
*state
)
575 ref
= DecrementRef(&state
->Ref
);
576 TRACEREF("%p decreasing refcount to %u\n", state
, ref
);
577 if(ref
== 0) DELETE_OBJ(state
);
581 void ALeffectState_Construct(ALeffectState
*state
)
583 InitRef(&state
->Ref
, 1);
585 state
->OutBuffer
= NULL
;
586 state
->OutChannels
= 0;
589 void ALeffectState_Destruct(ALeffectState
*UNUSED(state
))
594 ALenum
InitEffectSlot(ALeffectslot
*slot
)
596 ALeffectStateFactory
*factory
;
598 slot
->Effect
.Type
= AL_EFFECT_NULL
;
600 factory
= getFactoryByType(AL_EFFECT_NULL
);
601 if(!(slot
->Effect
.State
=V0(factory
,create
)()))
602 return AL_OUT_OF_MEMORY
;
605 slot
->AuxSendAuto
= AL_TRUE
;
606 ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_relaxed
);
607 InitRef(&slot
->ref
, 0);
609 ATOMIC_INIT(&slot
->Update
, NULL
);
610 ATOMIC_INIT(&slot
->FreeList
, NULL
);
612 slot
->Params
.Gain
= 1.0f
;
613 slot
->Params
.AuxSendAuto
= AL_TRUE
;
614 ALeffectState_IncRef(slot
->Effect
.State
);
615 slot
->Params
.EffectState
= slot
->Effect
.State
;
616 slot
->Params
.RoomRolloff
= 0.0f
;
617 slot
->Params
.DecayTime
= 0.0f
;
618 slot
->Params
.DecayHFRatio
= 0.0f
;
619 slot
->Params
.DecayHFLimit
= AL_FALSE
;
620 slot
->Params
.AirAbsorptionGainHF
= 1.0f
;
625 void DeinitEffectSlot(ALeffectslot
*slot
)
627 struct ALeffectslotProps
*props
;
630 props
= ATOMIC_LOAD_SEQ(&slot
->Update
);
633 if(props
->State
) ALeffectState_DecRef(props
->State
);
634 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props
);
637 props
= ATOMIC_LOAD(&slot
->FreeList
, almemory_order_relaxed
);
640 struct ALeffectslotProps
*next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
641 if(props
->State
) ALeffectState_DecRef(props
->State
);
646 TRACE("Freed "SZFMT
" AuxiliaryEffectSlot property object%s\n", count
, (count
==1)?"":"s");
648 ALeffectState_DecRef(slot
->Effect
.State
);
649 if(slot
->Params
.EffectState
)
650 ALeffectState_DecRef(slot
->Params
.EffectState
);
653 void UpdateEffectSlotProps(ALeffectslot
*slot
)
655 struct ALeffectslotProps
*props
;
656 ALeffectState
*oldstate
;
658 /* Get an unused property container, or allocate a new one as needed. */
659 props
= ATOMIC_LOAD(&slot
->FreeList
, almemory_order_relaxed
);
661 props
= al_calloc(16, sizeof(*props
));
664 struct ALeffectslotProps
*next
;
666 next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
667 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&slot
->FreeList
, &props
, next
,
668 almemory_order_seq_cst
, almemory_order_acquire
) == 0);
671 /* Copy in current property values. */
672 props
->Gain
= slot
->Gain
;
673 props
->AuxSendAuto
= slot
->AuxSendAuto
;
675 props
->Type
= slot
->Effect
.Type
;
676 props
->Props
= slot
->Effect
.Props
;
677 /* Swap out any stale effect state object there may be in the container, to
680 ALeffectState_IncRef(slot
->Effect
.State
);
681 oldstate
= props
->State
;
682 props
->State
= slot
->Effect
.State
;
684 /* Set the new container for updating internal parameters. */
685 props
= ATOMIC_EXCHANGE_PTR(&slot
->Update
, props
, almemory_order_acq_rel
);
688 /* If there was an unused update container, put it back in the
691 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps
*, &slot
->FreeList
, props
);
695 ALeffectState_DecRef(oldstate
);
698 void UpdateAllEffectSlotProps(ALCcontext
*context
)
700 struct ALeffectslotArray
*auxslots
;
703 LockEffectSlotsRead(context
);
704 auxslots
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
705 for(i
= 0;i
< auxslots
->count
;i
++)
707 ALeffectslot
*slot
= auxslots
->slot
[i
];
708 if(!ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_acq_rel
))
709 UpdateEffectSlotProps(slot
);
711 UnlockEffectSlotsRead(context
);
714 ALvoid
ReleaseALAuxiliaryEffectSlots(ALCcontext
*Context
)
717 for(pos
= 0;pos
< Context
->EffectSlotMap
.size
;pos
++)
719 ALeffectslot
*temp
= Context
->EffectSlotMap
.values
[pos
];
720 Context
->EffectSlotMap
.values
[pos
] = NULL
;
722 DeinitEffectSlot(temp
);
724 FreeThunkEntry(temp
->id
);
725 memset(temp
, 0, sizeof(ALeffectslot
));