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"
31 #include "alListener.h"
34 #include "fpu_modes.h"
38 extern inline void LockEffectSlotList(ALCcontext
*context
);
39 extern inline void UnlockEffectSlotList(ALCcontext
*context
);
43 ALeffectStateFactory
* (*GetFactory
)(void);
45 { AL_EFFECT_NULL
, ALnullStateFactory_getFactory
},
46 { AL_EFFECT_EAXREVERB
, ALreverbStateFactory_getFactory
},
47 { AL_EFFECT_REVERB
, ALreverbStateFactory_getFactory
},
48 { AL_EFFECT_CHORUS
, ALchorusStateFactory_getFactory
},
49 { AL_EFFECT_COMPRESSOR
, ALcompressorStateFactory_getFactory
},
50 { AL_EFFECT_DISTORTION
, ALdistortionStateFactory_getFactory
},
51 { AL_EFFECT_ECHO
, ALechoStateFactory_getFactory
},
52 { AL_EFFECT_EQUALIZER
, ALequalizerStateFactory_getFactory
},
53 { AL_EFFECT_FLANGER
, ALflangerStateFactory_getFactory
},
54 { AL_EFFECT_RING_MODULATOR
, ALmodulatorStateFactory_getFactory
},
55 { AL_EFFECT_DEDICATED_DIALOGUE
, ALdedicatedStateFactory_getFactory
},
56 { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
, ALdedicatedStateFactory_getFactory
}
59 static inline ALeffectStateFactory
*getFactoryByType(ALenum type
)
62 for(i
= 0;i
< COUNTOF(FactoryList
);i
++)
64 if(FactoryList
[i
].Type
== type
)
65 return FactoryList
[i
].GetFactory();
70 static void ALeffectState_IncRef(ALeffectState
*state
);
73 static inline ALeffectslot
*LookupEffectSlot(ALCcontext
*context
, ALuint id
)
76 if(UNLIKELY(id
>= VECTOR_SIZE(context
->EffectSlotList
)))
78 return VECTOR_ELEM(context
->EffectSlotList
, id
);
81 static inline ALeffect
*LookupEffect(ALCdevice
*device
, ALuint id
)
83 EffectSubList
*sublist
;
84 ALuint lidx
= (id
-1) >> 6;
85 ALsizei slidx
= (id
-1) & 0x3f;
87 if(UNLIKELY(lidx
>= VECTOR_SIZE(device
->EffectList
)))
89 sublist
= &VECTOR_ELEM(device
->EffectList
, lidx
);
90 if(UNLIKELY(sublist
->FreeMask
& (U64(1)<<slidx
)))
92 return sublist
->Effects
+ slidx
;
96 #define DO_UPDATEPROPS() do { \
97 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
98 UpdateEffectSlotProps(slot, context); \
100 ATOMIC_FLAG_CLEAR(&slot->PropsClean, almemory_order_release); \
104 AL_API ALvoid AL_APIENTRY
alGenAuxiliaryEffectSlots(ALsizei n
, ALuint
*effectslots
)
108 ALeffectslot
**tmpslots
= NULL
;
112 context
= GetContextRef();
116 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Generating %d effect slots", n
);
117 tmpslots
= al_malloc(DEF_ALIGN
, sizeof(ALeffectslot
*)*n
);
119 device
= context
->Device
;
120 LockEffectSlotList(context
);
121 if(device
->AuxiliaryEffectSlotMax
- VECTOR_SIZE(context
->EffectSlotList
) < (ALuint
)n
)
123 UnlockEffectSlotList(context
);
124 SETERR_GOTO(context
, AL_OUT_OF_MEMORY
, done
, "Exceeding %u auxiliary effect slot limit",
125 device
->AuxiliaryEffectSlotMax
);
127 for(cur
= 0;cur
< n
;cur
++)
129 ALeffectslotPtr
*iter
= VECTOR_BEGIN(context
->EffectSlotList
);
130 ALeffectslotPtr
*end
= VECTOR_END(context
->EffectSlotList
);
131 ALeffectslot
*slot
= NULL
;
133 for(;iter
!= end
;iter
++)
140 if(VECTOR_SIZE(context
->EffectSlotList
) >= INT_MAX
)
142 UnlockEffectSlotList(context
);
144 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
145 SETERR_GOTO(context
, err
, done
, "Too many effect slot objects");
147 VECTOR_PUSH_BACK(context
->EffectSlotList
, NULL
);
148 iter
= &VECTOR_BACK(context
->EffectSlotList
);
150 slot
= al_calloc(16, sizeof(ALeffectslot
));
151 err
= AL_OUT_OF_MEMORY
;
152 if(!slot
|| (err
=InitEffectSlot(slot
)) != AL_NO_ERROR
)
155 UnlockEffectSlotList(context
);
157 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
158 SETERR_GOTO(context
, err
, done
, "Effect slot object allocation failed");
160 aluInitEffectPanning(slot
);
162 slot
->id
= (iter
- VECTOR_BEGIN(context
->EffectSlotList
)) + 1;
165 tmpslots
[cur
] = slot
;
166 effectslots
[cur
] = slot
->id
;
170 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
171 struct ALeffectslotArray
*newarray
= NULL
;
172 ALsizei newcount
= curarray
->count
+ n
;
175 newarray
= al_calloc(DEF_ALIGN
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
176 newarray
->count
= newcount
;
177 memcpy(newarray
->slot
, tmpslots
, sizeof(ALeffectslot
*)*n
);
179 memcpy(newarray
->slot
+n
, curarray
->slot
, sizeof(ALeffectslot
*)*curarray
->count
);
181 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
182 almemory_order_acq_rel
);
183 device
= context
->Device
;
184 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
188 UnlockEffectSlotList(context
);
192 ALCcontext_DecRef(context
);
195 AL_API ALvoid AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
201 context
= GetContextRef();
204 LockEffectSlotList(context
);
206 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Deleting %d effect slots", n
);
209 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == NULL
)
210 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u",
212 if(ReadRef(&slot
->ref
) != 0)
213 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Deleting in-use effect slot %u",
217 // All effectslots are valid
220 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
221 struct ALeffectslotArray
*newarray
= NULL
;
222 ALsizei newcount
= curarray
->count
- n
;
226 assert(newcount
>= 0);
227 newarray
= al_calloc(DEF_ALIGN
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
228 newarray
->count
= newcount
;
229 for(i
= j
= 0;i
< newarray
->count
;)
231 slot
= curarray
->slot
[j
++];
234 if(slot
->id
== effectslots
[k
])
238 newarray
->slot
[i
++] = slot
;
241 newarray
= ATOMIC_EXCHANGE_PTR(&context
->ActiveAuxSlots
, newarray
,
242 almemory_order_acq_rel
);
243 device
= context
->Device
;
244 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
251 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == NULL
)
253 VECTOR_ELEM(context
->EffectSlotList
, effectslots
[i
]-1) = NULL
;
255 DeinitEffectSlot(slot
);
257 memset(slot
, 0, sizeof(*slot
));
262 UnlockEffectSlotList(context
);
263 ALCcontext_DecRef(context
);
266 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
271 context
= GetContextRef();
272 if(!context
) return AL_FALSE
;
274 LockEffectSlotList(context
);
275 ret
= (LookupEffectSlot(context
, effectslot
) ? AL_TRUE
: AL_FALSE
);
276 UnlockEffectSlotList(context
);
278 ALCcontext_DecRef(context
);
283 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
288 ALeffect
*effect
= NULL
;
291 context
= GetContextRef();
294 WriteLock(&context
->PropLock
);
295 LockEffectSlotList(context
);
296 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
297 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
300 case AL_EFFECTSLOT_EFFECT
:
301 device
= context
->Device
;
303 LockEffectList(device
);
304 effect
= (value
? LookupEffect(device
, value
) : NULL
);
305 if(!(value
== 0 || effect
!= NULL
))
307 UnlockEffectList(device
);
308 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Invalid effect ID %u", value
);
310 err
= InitializeEffect(context
, slot
, effect
);
311 UnlockEffectList(device
);
313 if(err
!= AL_NO_ERROR
)
314 SETERR_GOTO(context
, err
, done
, "Effect initialization failed");
317 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
318 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
319 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
,
320 "Effect slot auxiliary send auto out of range");
321 slot
->AuxSendAuto
= value
;
325 SETERR_GOTO(context
, AL_INVALID_ENUM
, done
, "Invalid effect slot integer property 0x%04x",
331 UnlockEffectSlotList(context
);
332 WriteUnlock(&context
->PropLock
);
333 ALCcontext_DecRef(context
);
336 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
342 case AL_EFFECTSLOT_EFFECT
:
343 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
344 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
348 context
= GetContextRef();
351 LockEffectSlotList(context
);
352 if(LookupEffectSlot(context
, effectslot
) == NULL
)
353 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
357 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
362 UnlockEffectSlotList(context
);
363 ALCcontext_DecRef(context
);
366 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
371 context
= GetContextRef();
374 WriteLock(&context
->PropLock
);
375 LockEffectSlotList(context
);
376 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
377 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
380 case AL_EFFECTSLOT_GAIN
:
381 if(!(value
>= 0.0f
&& value
<= 1.0f
))
382 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Effect slot gain out of range");
387 SETERR_GOTO(context
, AL_INVALID_ENUM
, done
, "Invalid effect slot float property 0x%04x",
393 UnlockEffectSlotList(context
);
394 WriteUnlock(&context
->PropLock
);
395 ALCcontext_DecRef(context
);
398 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
404 case AL_EFFECTSLOT_GAIN
:
405 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
409 context
= GetContextRef();
412 LockEffectSlotList(context
);
413 if(LookupEffectSlot(context
, effectslot
) == NULL
)
414 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
418 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
423 UnlockEffectSlotList(context
);
424 ALCcontext_DecRef(context
);
427 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
432 context
= GetContextRef();
435 LockEffectSlotList(context
);
436 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
437 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
440 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
441 *value
= slot
->AuxSendAuto
;
445 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
);
449 UnlockEffectSlotList(context
);
450 ALCcontext_DecRef(context
);
453 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
459 case AL_EFFECTSLOT_EFFECT
:
460 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
461 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
465 context
= GetContextRef();
468 LockEffectSlotList(context
);
469 if(LookupEffectSlot(context
, effectslot
) == NULL
)
470 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
474 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
479 UnlockEffectSlotList(context
);
480 ALCcontext_DecRef(context
);
483 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
488 context
= GetContextRef();
491 LockEffectSlotList(context
);
492 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
493 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
496 case AL_EFFECTSLOT_GAIN
:
501 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
);
505 UnlockEffectSlotList(context
);
506 ALCcontext_DecRef(context
);
509 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
515 case AL_EFFECTSLOT_GAIN
:
516 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
520 context
= GetContextRef();
523 LockEffectSlotList(context
);
524 if(LookupEffectSlot(context
, effectslot
) == NULL
)
525 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
529 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
534 UnlockEffectSlotList(context
);
535 ALCcontext_DecRef(context
);
539 ALenum
InitializeEffect(ALCcontext
*Context
, ALeffectslot
*EffectSlot
, ALeffect
*effect
)
541 ALCdevice
*Device
= Context
->Device
;
542 ALenum newtype
= (effect
? effect
->type
: AL_EFFECT_NULL
);
543 struct ALeffectslotProps
*props
;
544 ALeffectState
*State
;
546 if(newtype
!= EffectSlot
->Effect
.Type
)
548 ALeffectStateFactory
*factory
;
550 factory
= getFactoryByType(newtype
);
553 ERR("Failed to find factory for effect type 0x%04x\n", newtype
);
554 return AL_INVALID_ENUM
;
556 State
= V0(factory
,create
)();
557 if(!State
) return AL_OUT_OF_MEMORY
;
560 almtx_lock(&Device
->BackendLock
);
561 State
->OutBuffer
= Device
->Dry
.Buffer
;
562 State
->OutChannels
= Device
->Dry
.NumChannels
;
563 if(V(State
,deviceUpdate
)(Device
) == AL_FALSE
)
565 almtx_unlock(&Device
->BackendLock
);
567 ALeffectState_DecRef(State
);
568 return AL_OUT_OF_MEMORY
;
570 almtx_unlock(&Device
->BackendLock
);
575 EffectSlot
->Effect
.Type
= AL_EFFECT_NULL
;
576 memset(&EffectSlot
->Effect
.Props
, 0, sizeof(EffectSlot
->Effect
.Props
));
580 EffectSlot
->Effect
.Type
= effect
->type
;
581 EffectSlot
->Effect
.Props
= effect
->Props
;
584 ALeffectState_DecRef(EffectSlot
->Effect
.State
);
585 EffectSlot
->Effect
.State
= State
;
588 EffectSlot
->Effect
.Props
= effect
->Props
;
590 /* Remove state references from old effect slot property updates. */
591 props
= ATOMIC_LOAD_SEQ(&Context
->FreeEffectslotProps
);
595 ALeffectState_DecRef(props
->State
);
597 props
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
604 static void ALeffectState_IncRef(ALeffectState
*state
)
607 ref
= IncrementRef(&state
->Ref
);
608 TRACEREF("%p increasing refcount to %u\n", state
, ref
);
611 void ALeffectState_DecRef(ALeffectState
*state
)
614 ref
= DecrementRef(&state
->Ref
);
615 TRACEREF("%p decreasing refcount to %u\n", state
, ref
);
616 if(ref
== 0) DELETE_OBJ(state
);
620 void ALeffectState_Construct(ALeffectState
*state
)
622 InitRef(&state
->Ref
, 1);
624 state
->OutBuffer
= NULL
;
625 state
->OutChannels
= 0;
628 void ALeffectState_Destruct(ALeffectState
*UNUSED(state
))
633 ALenum
InitEffectSlot(ALeffectslot
*slot
)
635 ALeffectStateFactory
*factory
;
637 slot
->Effect
.Type
= AL_EFFECT_NULL
;
639 factory
= getFactoryByType(AL_EFFECT_NULL
);
640 if(!(slot
->Effect
.State
=V0(factory
,create
)()))
641 return AL_OUT_OF_MEMORY
;
644 slot
->AuxSendAuto
= AL_TRUE
;
645 ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_relaxed
);
646 InitRef(&slot
->ref
, 0);
648 ATOMIC_INIT(&slot
->Update
, NULL
);
650 slot
->Params
.Gain
= 1.0f
;
651 slot
->Params
.AuxSendAuto
= AL_TRUE
;
652 ALeffectState_IncRef(slot
->Effect
.State
);
653 slot
->Params
.EffectState
= slot
->Effect
.State
;
654 slot
->Params
.RoomRolloff
= 0.0f
;
655 slot
->Params
.DecayTime
= 0.0f
;
656 slot
->Params
.DecayHFRatio
= 0.0f
;
657 slot
->Params
.DecayHFLimit
= AL_FALSE
;
658 slot
->Params
.AirAbsorptionGainHF
= 1.0f
;
663 void DeinitEffectSlot(ALeffectslot
*slot
)
665 struct ALeffectslotProps
*props
;
667 props
= ATOMIC_LOAD_SEQ(&slot
->Update
);
670 if(props
->State
) ALeffectState_DecRef(props
->State
);
671 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props
);
675 ALeffectState_DecRef(slot
->Effect
.State
);
676 if(slot
->Params
.EffectState
)
677 ALeffectState_DecRef(slot
->Params
.EffectState
);
680 void UpdateEffectSlotProps(ALeffectslot
*slot
, ALCcontext
*context
)
682 struct ALeffectslotProps
*props
;
683 ALeffectState
*oldstate
;
685 /* Get an unused property container, or allocate a new one as needed. */
686 props
= ATOMIC_LOAD(&context
->FreeEffectslotProps
, almemory_order_relaxed
);
688 props
= al_calloc(16, sizeof(*props
));
691 struct ALeffectslotProps
*next
;
693 next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
694 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context
->FreeEffectslotProps
, &props
, next
,
695 almemory_order_seq_cst
, almemory_order_acquire
) == 0);
698 /* Copy in current property values. */
699 props
->Gain
= slot
->Gain
;
700 props
->AuxSendAuto
= slot
->AuxSendAuto
;
702 props
->Type
= slot
->Effect
.Type
;
703 props
->Props
= slot
->Effect
.Props
;
704 /* Swap out any stale effect state object there may be in the container, to
707 ALeffectState_IncRef(slot
->Effect
.State
);
708 oldstate
= props
->State
;
709 props
->State
= slot
->Effect
.State
;
711 /* Set the new container for updating internal parameters. */
712 props
= ATOMIC_EXCHANGE_PTR(&slot
->Update
, props
, almemory_order_acq_rel
);
715 /* If there was an unused update container, put it back in the
718 ATOMIC_REPLACE_HEAD(struct ALeffectslotProps
*, &context
->FreeEffectslotProps
, props
);
722 ALeffectState_DecRef(oldstate
);
725 void UpdateAllEffectSlotProps(ALCcontext
*context
)
727 struct ALeffectslotArray
*auxslots
;
730 LockEffectSlotList(context
);
731 auxslots
= ATOMIC_LOAD(&context
->ActiveAuxSlots
, almemory_order_acquire
);
732 for(i
= 0;i
< auxslots
->count
;i
++)
734 ALeffectslot
*slot
= auxslots
->slot
[i
];
735 if(!ATOMIC_FLAG_TEST_AND_SET(&slot
->PropsClean
, almemory_order_acq_rel
))
736 UpdateEffectSlotProps(slot
, context
);
738 UnlockEffectSlotList(context
);
741 ALvoid
ReleaseALAuxiliaryEffectSlots(ALCcontext
*context
)
743 ALeffectslotPtr
*iter
= VECTOR_BEGIN(context
->EffectSlotList
);
744 ALeffectslotPtr
*end
= VECTOR_END(context
->EffectSlotList
);
747 for(;iter
!= end
;iter
++)
749 ALeffectslot
*slot
= *iter
;
753 DeinitEffectSlot(slot
);
755 memset(slot
, 0, sizeof(*slot
));
760 WARN("(%p) Deleted "SZFMT
" AuxiliaryEffectSlot%s\n", context
, leftover
, (leftover
==1)?"":"s");