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
);
56 #define DO_UPDATEPROPS() do { \
57 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
58 UpdateEffectSlotProps(slot, context); \
60 ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \
64 AL_API ALvoid AL_APIENTRY
alGenAuxiliaryEffectSlots(ALsizei n
, ALuint
*effectslots
)
67 ALeffectslot
**tmpslots
= NULL
;
71 context
= GetContextRef();
75 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
76 tmpslots
= al_malloc(DEF_ALIGN
, sizeof(ALeffectslot
*)*n
);
78 LockEffectSlotsWrite(context
);
79 for(cur
= 0;cur
< n
;cur
++)
81 ALeffectslot
*slot
= al_calloc(16, sizeof(ALeffectslot
));
82 err
= AL_OUT_OF_MEMORY
;
83 if(!slot
|| (err
=InitEffectSlot(slot
)) != AL_NO_ERROR
)
86 UnlockEffectSlotsWrite(context
);
88 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
89 SET_ERROR_AND_GOTO(context
, err
, done
);
92 err
= NewThunkEntry(&slot
->id
);
93 if(err
== AL_NO_ERROR
)
94 err
= InsertUIntMapEntryNoLock(&context
->EffectSlotMap
, slot
->id
, slot
);
95 if(err
!= AL_NO_ERROR
)
97 FreeThunkEntry(slot
->id
);
98 ALeffectState_DecRef(slot
->Effect
.State
);
99 if(slot
->Params
.EffectState
)
100 ALeffectState_DecRef(slot
->Params
.EffectState
);
102 UnlockEffectSlotsWrite(context
);
104 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
105 SET_ERROR_AND_GOTO(context
, err
, done
);
108 aluInitEffectPanning(slot
);
110 tmpslots
[cur
] = slot
;
111 effectslots
[cur
] = slot
->id
;
115 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
116 struct ALeffectslotArray
*newarray
= NULL
;
117 ALsizei newcount
= curarray
->count
+ n
;
120 newarray
= al_calloc(DEF_ALIGN
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
121 newarray
->count
= newcount
;
122 memcpy(newarray
->slot
, tmpslots
, sizeof(ALeffectslot
*)*n
);
124 memcpy(newarray
->slot
+n
, curarray
->slot
, sizeof(ALeffectslot
*)*curarray
->count
);
126 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
127 almemory_order_acq_rel
);
128 device
= context
->Device
;
129 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
133 UnlockEffectSlotsWrite(context
);
137 ALCcontext_DecRef(context
);
140 AL_API ALvoid AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
146 context
= GetContextRef();
149 LockEffectSlotsWrite(context
);
151 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
154 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == NULL
)
155 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
156 if(ReadRef(&slot
->ref
) != 0)
157 SET_ERROR_AND_GOTO(context
, AL_INVALID_OPERATION
, done
);
160 // All effectslots are valid
163 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
164 struct ALeffectslotArray
*newarray
= NULL
;
165 ALsizei newcount
= curarray
->count
- n
;
169 assert(newcount
>= 0);
170 newarray
= al_calloc(DEF_ALIGN
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
171 newarray
->count
= newcount
;
172 for(i
= j
= 0;i
< newarray
->count
;)
174 slot
= curarray
->slot
[j
++];
177 if(slot
->id
== effectslots
[k
])
181 newarray
->slot
[i
++] = slot
;
184 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
185 almemory_order_acq_rel
);
186 device
= context
->Device
;
187 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
194 if((slot
=RemoveEffectSlot(context
, effectslots
[i
])) == NULL
)
196 FreeThunkEntry(slot
->id
);
198 DeinitEffectSlot(slot
);
200 memset(slot
, 0, sizeof(*slot
));
205 UnlockEffectSlotsWrite(context
);
206 ALCcontext_DecRef(context
);
209 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
214 context
= GetContextRef();
215 if(!context
) return AL_FALSE
;
217 LockEffectSlotsRead(context
);
218 ret
= (LookupEffectSlot(context
, effectslot
) ? AL_TRUE
: AL_FALSE
);
219 UnlockEffectSlotsRead(context
);
221 ALCcontext_DecRef(context
);
226 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
231 ALeffect
*effect
= NULL
;
234 context
= GetContextRef();
237 WriteLock(&context
->PropLock
);
238 LockEffectSlotsRead(context
);
239 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
240 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
243 case AL_EFFECTSLOT_EFFECT
:
244 device
= context
->Device
;
246 LockEffectsRead(device
);
247 effect
= (value
? LookupEffect(device
, value
) : NULL
);
248 if(!(value
== 0 || effect
!= NULL
))
250 UnlockEffectsRead(device
);
251 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
253 err
= InitializeEffect(context
, slot
, effect
);
254 UnlockEffectsRead(device
);
256 if(err
!= AL_NO_ERROR
)
257 SET_ERROR_AND_GOTO(context
, err
, done
);
260 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
261 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
262 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
263 slot
->AuxSendAuto
= value
;
267 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
272 UnlockEffectSlotsRead(context
);
273 WriteUnlock(&context
->PropLock
);
274 ALCcontext_DecRef(context
);
277 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
283 case AL_EFFECTSLOT_EFFECT
:
284 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
285 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
289 context
= GetContextRef();
292 LockEffectSlotsRead(context
);
293 if(LookupEffectSlot(context
, effectslot
) == NULL
)
294 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
298 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
302 UnlockEffectSlotsRead(context
);
303 ALCcontext_DecRef(context
);
306 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
311 context
= GetContextRef();
314 WriteLock(&context
->PropLock
);
315 LockEffectSlotsRead(context
);
316 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
317 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
320 case AL_EFFECTSLOT_GAIN
:
321 if(!(value
>= 0.0f
&& value
<= 1.0f
))
322 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
327 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
332 UnlockEffectSlotsRead(context
);
333 WriteUnlock(&context
->PropLock
);
334 ALCcontext_DecRef(context
);
337 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
343 case AL_EFFECTSLOT_GAIN
:
344 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
348 context
= GetContextRef();
351 LockEffectSlotsRead(context
);
352 if(LookupEffectSlot(context
, effectslot
) == NULL
)
353 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
357 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
361 UnlockEffectSlotsRead(context
);
362 ALCcontext_DecRef(context
);
365 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
370 context
= GetContextRef();
373 LockEffectSlotsRead(context
);
374 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
375 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
378 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
379 *value
= slot
->AuxSendAuto
;
383 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
387 UnlockEffectSlotsRead(context
);
388 ALCcontext_DecRef(context
);
391 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
397 case AL_EFFECTSLOT_EFFECT
:
398 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
399 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
403 context
= GetContextRef();
406 LockEffectSlotsRead(context
);
407 if(LookupEffectSlot(context
, effectslot
) == NULL
)
408 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
412 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
416 UnlockEffectSlotsRead(context
);
417 ALCcontext_DecRef(context
);
420 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
425 context
= GetContextRef();
428 LockEffectSlotsRead(context
);
429 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
430 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
433 case AL_EFFECTSLOT_GAIN
:
438 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
442 UnlockEffectSlotsRead(context
);
443 ALCcontext_DecRef(context
);
446 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
452 case AL_EFFECTSLOT_GAIN
:
453 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
457 context
= GetContextRef();
460 LockEffectSlotsRead(context
);
461 if(LookupEffectSlot(context
, effectslot
) == NULL
)
462 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
466 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
470 UnlockEffectSlotsRead(context
);
471 ALCcontext_DecRef(context
);
475 void InitEffectFactoryMap(void)
477 InitUIntMap(&EffectStateFactoryMap
, INT_MAX
);
479 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_NULL
, ALnullStateFactory_getFactory
);
480 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EAXREVERB
, ALreverbStateFactory_getFactory
);
481 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_REVERB
, ALreverbStateFactory_getFactory
);
482 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_CHORUS
, ALchorusStateFactory_getFactory
);
483 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_COMPRESSOR
, ALcompressorStateFactory_getFactory
);
484 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DISTORTION
, ALdistortionStateFactory_getFactory
);
485 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_ECHO
, ALechoStateFactory_getFactory
);
486 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EQUALIZER
, ALequalizerStateFactory_getFactory
);
487 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_FLANGER
, ALflangerStateFactory_getFactory
);
488 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_RING_MODULATOR
, ALmodulatorStateFactory_getFactory
);
489 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_DIALOGUE
, ALdedicatedStateFactory_getFactory
);
490 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
, ALdedicatedStateFactory_getFactory
);
493 void DeinitEffectFactoryMap(void)
495 ResetUIntMap(&EffectStateFactoryMap
);
499 ALenum
InitializeEffect(ALCcontext
*Context
, ALeffectslot
*EffectSlot
, ALeffect
*effect
)
501 ALCdevice
*Device
= Context
->Device
;
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
;
510 factory
= getFactoryByType(newtype
);
513 ERR("Failed to find factory for effect type 0x%04x\n", newtype
);
514 return AL_INVALID_ENUM
;
516 State
= V0(factory
,create
)();
517 if(!State
) return AL_OUT_OF_MEMORY
;
520 almtx_lock(&Device
->BackendLock
);
521 State
->OutBuffer
= Device
->Dry
.Buffer
;
522 State
->OutChannels
= Device
->Dry
.NumChannels
;
523 if(V(State
,deviceUpdate
)(Device
) == AL_FALSE
)
525 almtx_unlock(&Device
->BackendLock
);
527 ALeffectState_DecRef(State
);
528 return AL_OUT_OF_MEMORY
;
530 almtx_unlock(&Device
->BackendLock
);
535 EffectSlot
->Effect
.Type
= AL_EFFECT_NULL
;
536 memset(&EffectSlot
->Effect
.Props
, 0, sizeof(EffectSlot
->Effect
.Props
));
540 EffectSlot
->Effect
.Type
= effect
->type
;
541 EffectSlot
->Effect
.Props
= effect
->Props
;
544 ALeffectState_DecRef(EffectSlot
->Effect
.State
);
545 EffectSlot
->Effect
.State
= State
;
548 EffectSlot
->Effect
.Props
= effect
->Props
;
550 /* Remove state references from old effect slot property updates. */
551 props
= ATOMIC_LOAD_SEQ(&Context
->FreeEffectslotProps
);
555 ALeffectState_DecRef(props
->State
);
557 props
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
564 static void ALeffectState_IncRef(ALeffectState
*state
)
567 ref
= IncrementRef(&state
->Ref
);
568 TRACEREF("%p increasing refcount to %u\n", state
, ref
);
571 void ALeffectState_DecRef(ALeffectState
*state
)
574 ref
= DecrementRef(&state
->Ref
);
575 TRACEREF("%p decreasing refcount to %u\n", state
, ref
);
576 if(ref
== 0) DELETE_OBJ(state
);
580 void ALeffectState_Construct(ALeffectState
*state
)
582 InitRef(&state
->Ref
, 1);
584 state
->OutBuffer
= NULL
;
585 state
->OutChannels
= 0;
588 void ALeffectState_Destruct(ALeffectState
*UNUSED(state
))
593 ALenum
InitEffectSlot(ALeffectslot
*slot
)
595 ALeffectStateFactory
*factory
;
597 slot
->Effect
.Type
= AL_EFFECT_NULL
;
599 factory
= getFactoryByType(AL_EFFECT_NULL
);
600 if(!(slot
->Effect
.State
=V0(factory
,create
)()))
601 return AL_OUT_OF_MEMORY
;
604 slot
->AuxSendAuto
= AL_TRUE
;
605 ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_relaxed
);
606 InitRef(&slot
->ref
, 0);
608 ATOMIC_INIT(&slot
->Update
, NULL
);
610 slot
->Params
.Gain
= 1.0f
;
611 slot
->Params
.AuxSendAuto
= AL_TRUE
;
612 ALeffectState_IncRef(slot
->Effect
.State
);
613 slot
->Params
.EffectState
= slot
->Effect
.State
;
614 slot
->Params
.RoomRolloff
= 0.0f
;
615 slot
->Params
.DecayTime
= 0.0f
;
616 slot
->Params
.DecayHFRatio
= 0.0f
;
617 slot
->Params
.DecayHFLimit
= AL_FALSE
;
618 slot
->Params
.AirAbsorptionGainHF
= 1.0f
;
623 void DeinitEffectSlot(ALeffectslot
*slot
)
625 struct ALeffectslotProps
*props
;
627 props
= ATOMIC_LOAD_SEQ(&slot
->Update
);
630 if(props
->State
) ALeffectState_DecRef(props
->State
);
631 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props
);
635 ALeffectState_DecRef(slot
->Effect
.State
);
636 if(slot
->Params
.EffectState
)
637 ALeffectState_DecRef(slot
->Params
.EffectState
);
640 void UpdateEffectSlotProps(ALeffectslot
*slot
, ALCcontext
*context
)
642 struct ALeffectslotProps
*props
;
643 ALeffectState
*oldstate
;
645 /* Get an unused property container, or allocate a new one as needed. */
646 props
= ATOMIC_LOAD(&context
->FreeEffectslotProps
, almemory_order_relaxed
);
648 props
= al_calloc(16, sizeof(*props
));
651 struct ALeffectslotProps
*next
;
653 next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
654 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context
->FreeEffectslotProps
, &props
, next
,
655 almemory_order_seq_cst
, almemory_order_acquire
) == 0);
658 /* Copy in current property values. */
659 props
->Gain
= slot
->Gain
;
660 props
->AuxSendAuto
= slot
->AuxSendAuto
;
662 props
->Type
= slot
->Effect
.Type
;
663 props
->Props
= slot
->Effect
.Props
;
664 /* Swap out any stale effect state object there may be in the container, to
667 ALeffectState_IncRef(slot
->Effect
.State
);
668 oldstate
= props
->State
;
669 props
->State
= slot
->Effect
.State
;
671 /* Set the new container for updating internal parameters. */
672 props
= ATOMIC_EXCHANGE_PTR(&slot
->Update
, props
, almemory_order_acq_rel
);
675 /* If there was an unused update container, put it back in the
678 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps
*, &context
->FreeEffectslotProps
, props
);
682 ALeffectState_DecRef(oldstate
);
685 void UpdateAllEffectSlotProps(ALCcontext
*context
)
687 struct ALeffectslotArray
*auxslots
;
690 LockEffectSlotsRead(context
);
691 auxslots
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
692 for(i
= 0;i
< auxslots
->count
;i
++)
694 ALeffectslot
*slot
= auxslots
->slot
[i
];
695 if(!ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_acq_rel
))
696 UpdateEffectSlotProps(slot
, context
);
698 UnlockEffectSlotsRead(context
);
701 ALvoid
ReleaseALAuxiliaryEffectSlots(ALCcontext
*Context
)
704 for(pos
= 0;pos
< Context
->EffectSlotMap
.size
;pos
++)
706 ALeffectslot
*temp
= Context
->EffectSlotMap
.values
[pos
];
707 Context
->EffectSlotMap
.values
[pos
] = NULL
;
709 DeinitEffectSlot(temp
);
711 FreeThunkEntry(temp
->id
);
712 memset(temp
, 0, sizeof(ALeffectslot
));