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 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
)
55 AL_API ALvoid AL_APIENTRY
alGenAuxiliaryEffectSlots(ALsizei n
, ALuint
*effectslots
)
58 VECTOR(ALeffectslot
*) slotvec
;
62 context
= GetContextRef();
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
)
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
);
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
);
110 VECTOR_DEINIT(slotvec
);
112 ALCcontext_DecRef(context
);
115 AL_API ALvoid AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
121 context
= GetContextRef();
125 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
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
137 if((slot
=RemoveEffectSlot(context
, effectslots
[i
])) == NULL
)
139 FreeThunkEntry(slot
->id
);
141 RemoveEffectSlotArray(context
, slot
);
142 DeinitEffectSlot(slot
);
144 memset(slot
, 0, sizeof(*slot
));
149 ALCcontext_DecRef(context
);
152 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
157 context
= GetContextRef();
158 if(!context
) return AL_FALSE
;
160 ret
= (LookupEffectSlot(context
, effectslot
) ? AL_TRUE
: AL_FALSE
);
162 ALCcontext_DecRef(context
);
167 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
172 ALeffect
*effect
= NULL
;
175 context
= GetContextRef();
178 WriteLock(&context
->PropLock
);
179 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
180 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
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
);
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
);
205 if(!ATOMIC_LOAD(&context
->DeferUpdates
, almemory_order_acquire
))
206 UpdateAllSourceProps(context
);
210 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
214 WriteUnlock(&context
->PropLock
);
215 ALCcontext_DecRef(context
);
218 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, const ALint
*values
)
224 case AL_EFFECTSLOT_EFFECT
:
225 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
226 alAuxiliaryEffectSloti(effectslot
, param
, values
[0]);
230 context
= GetContextRef();
233 if(LookupEffectSlot(context
, effectslot
) == NULL
)
234 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
238 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
242 ALCcontext_DecRef(context
);
245 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
250 context
= GetContextRef();
253 WriteLock(&context
->PropLock
);
254 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
255 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
258 case AL_EFFECTSLOT_GAIN
:
259 if(!(value
>= 0.0f
&& value
<= 1.0f
))
260 SET_ERROR_AND_GOTO(context
, AL_INVALID_VALUE
, done
);
265 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
267 UpdateEffectSlotProps(slot
);
270 WriteUnlock(&context
->PropLock
);
271 ALCcontext_DecRef(context
);
274 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
280 case AL_EFFECTSLOT_GAIN
:
281 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
285 context
= GetContextRef();
288 if(LookupEffectSlot(context
, effectslot
) == NULL
)
289 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
293 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
297 ALCcontext_DecRef(context
);
300 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
305 context
= GetContextRef();
308 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
309 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
312 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
313 *value
= slot
->AuxSendAuto
;
317 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
321 ALCcontext_DecRef(context
);
324 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
330 case AL_EFFECTSLOT_EFFECT
:
331 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
332 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
336 context
= GetContextRef();
339 if(LookupEffectSlot(context
, effectslot
) == NULL
)
340 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
344 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
348 ALCcontext_DecRef(context
);
351 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
356 context
= GetContextRef();
359 if((slot
=LookupEffectSlot(context
, effectslot
)) == NULL
)
360 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
363 case AL_EFFECTSLOT_GAIN
:
368 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
372 ALCcontext_DecRef(context
);
375 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, ALfloat
*values
)
381 case AL_EFFECTSLOT_GAIN
:
382 alGetAuxiliaryEffectSlotf(effectslot
, param
, values
);
386 context
= GetContextRef();
389 if(LookupEffectSlot(context
, effectslot
) == NULL
)
390 SET_ERROR_AND_GOTO(context
, AL_INVALID_NAME
, done
);
394 SET_ERROR_AND_GOTO(context
, AL_INVALID_ENUM
, done
);
398 ALCcontext_DecRef(context
);
402 static ALenum
AddEffectSlotArray(ALCcontext
*context
, ALeffectslot
**start
, ALsizei count
)
404 ALenum err
= AL_NO_ERROR
;
406 LockContext(context
);
407 if(!VECTOR_INSERT(context
->ActiveAuxSlots
, VECTOR_END(context
->ActiveAuxSlots
), start
, start
+count
))
408 err
= AL_OUT_OF_MEMORY
;
409 UnlockContext(context
);
414 static void RemoveEffectSlotArray(ALCcontext
*context
, const ALeffectslot
*slot
)
418 LockContext(context
);
419 #define MATCH_SLOT(_i) (slot == *(_i))
420 VECTOR_FIND_IF(iter
, ALeffectslot
*, context
->ActiveAuxSlots
, MATCH_SLOT
);
421 if(iter
!= VECTOR_END(context
->ActiveAuxSlots
))
423 *iter
= VECTOR_BACK(context
->ActiveAuxSlots
);
424 VECTOR_POP_BACK(context
->ActiveAuxSlots
);
427 UnlockContext(context
);
431 void InitEffectFactoryMap(void)
433 InitUIntMap(&EffectStateFactoryMap
, ~0);
435 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_NULL
, ALnullStateFactory_getFactory
);
436 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EAXREVERB
, ALreverbStateFactory_getFactory
);
437 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_REVERB
, ALreverbStateFactory_getFactory
);
438 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_AUTOWAH
, ALautowahStateFactory_getFactory
);
439 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_CHORUS
, ALchorusStateFactory_getFactory
);
440 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_COMPRESSOR
, ALcompressorStateFactory_getFactory
);
441 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DISTORTION
, ALdistortionStateFactory_getFactory
);
442 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_ECHO
, ALechoStateFactory_getFactory
);
443 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_EQUALIZER
, ALequalizerStateFactory_getFactory
);
444 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_FLANGER
, ALflangerStateFactory_getFactory
);
445 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_RING_MODULATOR
, ALmodulatorStateFactory_getFactory
);
446 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_DIALOGUE
, ALdedicatedStateFactory_getFactory
);
447 InsertUIntMapEntry(&EffectStateFactoryMap
, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
, ALdedicatedStateFactory_getFactory
);
450 void DeinitEffectFactoryMap(void)
452 ResetUIntMap(&EffectStateFactoryMap
);
456 ALenum
InitializeEffect(ALCdevice
*Device
, ALeffectslot
*EffectSlot
, ALeffect
*effect
)
458 ALenum newtype
= (effect
? effect
->type
: AL_EFFECT_NULL
);
459 ALeffectStateFactory
*factory
;
461 if(newtype
!= EffectSlot
->Effect
.Type
)
463 ALeffectState
*State
;
466 factory
= getFactoryByType(newtype
);
469 ERR("Failed to find factory for effect type 0x%04x\n", newtype
);
470 return AL_INVALID_ENUM
;
472 State
= V0(factory
,create
)();
474 return AL_OUT_OF_MEMORY
;
476 SetMixerFPUMode(&oldMode
);
477 /* FIXME: This just needs to prevent the device from being reset during
478 * the state's device update, so the list lock in ALc.c should do here.
480 ALCdevice_Lock(Device
);
481 State
->OutBuffer
= Device
->Dry
.Buffer
;
482 State
->OutChannels
= Device
->Dry
.NumChannels
;
483 if(V(State
,deviceUpdate
)(Device
) == AL_FALSE
)
485 ALCdevice_Unlock(Device
);
486 RestoreFPUMode(&oldMode
);
488 return AL_OUT_OF_MEMORY
;
490 ALCdevice_Unlock(Device
);
491 RestoreFPUMode(&oldMode
);
495 EffectSlot
->Effect
.Type
= AL_EFFECT_NULL
;
496 memset(&EffectSlot
->Effect
.Props
, 0, sizeof(EffectSlot
->Effect
.Props
));
500 EffectSlot
->Effect
.Type
= effect
->type
;
501 memcpy(&EffectSlot
->Effect
.Props
, &effect
->Props
, sizeof(EffectSlot
->Effect
.Props
));
504 EffectSlot
->Effect
.State
= State
;
505 UpdateEffectSlotProps(EffectSlot
);
509 memcpy(&EffectSlot
->Effect
.Props
, &effect
->Props
, sizeof(EffectSlot
->Effect
.Props
));
510 UpdateEffectSlotProps(EffectSlot
);
517 void ALeffectState_Destruct(ALeffectState
*UNUSED(state
))
522 ALenum
InitEffectSlot(ALeffectslot
*slot
)
524 ALeffectStateFactory
*factory
;
526 slot
->Effect
.Type
= AL_EFFECT_NULL
;
528 factory
= getFactoryByType(AL_EFFECT_NULL
);
529 if(!(slot
->Effect
.State
=V0(factory
,create
)()))
530 return AL_OUT_OF_MEMORY
;
533 slot
->AuxSendAuto
= AL_TRUE
;
534 InitRef(&slot
->ref
, 0);
536 ATOMIC_INIT(&slot
->Update
, NULL
);
537 ATOMIC_INIT(&slot
->FreeList
, NULL
);
539 slot
->Params
.Gain
= 1.0f
;
540 slot
->Params
.AuxSendAuto
= AL_TRUE
;
541 slot
->Params
.EffectState
= slot
->Effect
.State
;
542 slot
->Params
.RoomRolloff
= 0.0f
;
543 slot
->Params
.DecayTime
= 0.0f
;
544 slot
->Params
.AirAbsorptionGainHF
= 1.0f
;
549 void DeinitEffectSlot(ALeffectslot
*slot
)
551 struct ALeffectslotProps
*props
;
552 ALeffectState
*state
;
555 props
= ATOMIC_LOAD(&slot
->Update
);
558 state
= ATOMIC_LOAD(&props
->State
, almemory_order_relaxed
);
559 if(state
!= slot
->Params
.EffectState
)
561 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props
);
564 props
= ATOMIC_LOAD(&slot
->FreeList
, almemory_order_relaxed
);
567 struct ALeffectslotProps
*next
;
568 state
= ATOMIC_LOAD(&props
->State
, almemory_order_relaxed
);
569 next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
575 TRACE("Freed "SZFMT
" AuxiliaryEffectSlot property object%s\n", count
, (count
==1)?"":"s");
577 DELETE_OBJ(slot
->Params
.EffectState
);
580 void UpdateEffectSlotProps(ALeffectslot
*slot
)
582 struct ALeffectslotProps
*props
;
583 ALeffectState
*oldstate
;
585 props
= ATOMIC_EXCHANGE(struct ALeffectslotProps
*, &slot
->Update
, NULL
);
588 /* If there was an unapplied update, check if its state object is the
589 * same as the current in-use one, or the one that will be set. If
590 * neither, delete it.
592 oldstate
= ATOMIC_EXCHANGE(ALeffectState
*, &props
->State
, NULL
, almemory_order_relaxed
);
593 if(oldstate
!= slot
->Params
.EffectState
&& oldstate
!= slot
->Effect
.State
)
594 DELETE_OBJ(oldstate
);
598 /* Get an unused property container, or allocate a new one as needed. */
599 props
= ATOMIC_LOAD(&slot
->FreeList
, almemory_order_relaxed
);
601 props
= al_calloc(16, sizeof(*props
));
604 struct ALeffectslotProps
*next
;
606 next
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
607 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps
*,
608 &slot
->FreeList
, &props
, next
, almemory_order_seq_cst
,
609 almemory_order_consume
) == 0);
613 /* Copy in current property values. */
614 ATOMIC_STORE(&props
->Gain
, slot
->Gain
, almemory_order_relaxed
);
615 ATOMIC_STORE(&props
->AuxSendAuto
, slot
->AuxSendAuto
, almemory_order_relaxed
);
617 ATOMIC_STORE(&props
->Type
, slot
->Effect
.Type
, almemory_order_relaxed
);
618 memcpy(&props
->Props
, &slot
->Effect
.Props
, sizeof(props
->Props
));
619 /* Swap out any stale effect state object there may be in the container, to
622 oldstate
= ATOMIC_EXCHANGE(ALeffectState
*, &props
->State
, slot
->Effect
.State
,
623 almemory_order_relaxed
);
625 /* Set the new container for updating internal parameters. */
626 props
= ATOMIC_EXCHANGE(struct ALeffectslotProps
*, &slot
->Update
, props
,
627 almemory_order_acq_rel
);
630 /* If there was an unused update container, put it back in the
633 struct ALeffectslotProps
*first
= ATOMIC_LOAD(&slot
->FreeList
);
635 ATOMIC_STORE(&props
->next
, first
, almemory_order_relaxed
);
636 } while(ATOMIC_COMPARE_EXCHANGE_WEAK(struct ALeffectslotProps
*,
637 &slot
->FreeList
, &first
, props
) == 0);
640 DELETE_OBJ(oldstate
);
643 ALvoid
ReleaseALAuxiliaryEffectSlots(ALCcontext
*Context
)
646 for(pos
= 0;pos
< Context
->EffectSlotMap
.size
;pos
++)
648 ALeffectslot
*temp
= Context
->EffectSlotMap
.array
[pos
].value
;
649 Context
->EffectSlotMap
.array
[pos
].value
= NULL
;
651 DeinitEffectSlot(temp
);
653 FreeThunkEntry(temp
->id
);
654 memset(temp
, 0, sizeof(ALeffectslot
));