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
32 #include "alcontext.h"
33 #include "alAuxEffectSlot.h"
35 #include "alListener.h"
38 #include "fpu_modes.h"
42 static void AddActiveEffectSlots(const ALuint
*slotids
, ALsizei count
, ALCcontext
*context
);
43 static void RemoveActiveEffectSlots(const ALuint
*slotids
, ALsizei count
, ALCcontext
*context
);
47 EffectStateFactory
* (*GetFactory
)(void);
49 { AL_EFFECT_NULL
, NullStateFactory_getFactory
},
50 { AL_EFFECT_EAXREVERB
, ReverbStateFactory_getFactory
},
51 { AL_EFFECT_REVERB
, ReverbStateFactory_getFactory
},
52 { AL_EFFECT_AUTOWAH
, AutowahStateFactory_getFactory
},
53 { AL_EFFECT_CHORUS
, ChorusStateFactory_getFactory
},
54 { AL_EFFECT_COMPRESSOR
, CompressorStateFactory_getFactory
},
55 { AL_EFFECT_DISTORTION
, DistortionStateFactory_getFactory
},
56 { AL_EFFECT_ECHO
, EchoStateFactory_getFactory
},
57 { AL_EFFECT_EQUALIZER
, EqualizerStateFactory_getFactory
},
58 { AL_EFFECT_FLANGER
, FlangerStateFactory_getFactory
},
59 { AL_EFFECT_FREQUENCY_SHIFTER
, FshifterStateFactory_getFactory
},
60 { AL_EFFECT_RING_MODULATOR
, ModulatorStateFactory_getFactory
},
61 { AL_EFFECT_PITCH_SHIFTER
, PshifterStateFactory_getFactory
},
62 { AL_EFFECT_DEDICATED_DIALOGUE
, DedicatedStateFactory_getFactory
},
63 { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT
, DedicatedStateFactory_getFactory
}
66 static inline EffectStateFactory
*getFactoryByType(ALenum type
)
69 for(i
= 0;i
< COUNTOF(FactoryList
);i
++)
71 if(FactoryList
[i
].Type
== type
)
72 return FactoryList
[i
].GetFactory();
77 static void ALeffectState_IncRef(ALeffectState
*state
);
80 static inline ALeffectslot
*LookupEffectSlot(ALCcontext
*context
, ALuint id
)
83 if(UNLIKELY(id
>= context
->EffectSlotList
.size()))
85 return context
->EffectSlotList
[id
];
88 static inline ALeffect
*LookupEffect(ALCdevice
*device
, ALuint id
)
90 ALuint lidx
= (id
-1) >> 6;
91 ALsizei slidx
= (id
-1) & 0x3f;
93 if(UNLIKELY(lidx
>= device
->EffectList
.size()))
95 EffectSubList
&sublist
= device
->EffectList
[lidx
];
96 if(UNLIKELY(sublist
.FreeMask
& (U64(1)<<slidx
)))
98 return sublist
.Effects
+ slidx
;
102 #define DO_UPDATEPROPS() do { \
103 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
104 UpdateEffectSlotProps(slot, context); \
106 ATOMIC_STORE(&slot->PropsClean, AL_FALSE, almemory_order_release); \
110 AL_API ALvoid AL_APIENTRY
alGenAuxiliaryEffectSlots(ALsizei n
, ALuint
*effectslots
)
116 context
= GetContextRef();
120 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Generating %d effect slots", n
);
121 if(n
== 0) goto done
;
123 LockEffectSlotList(context
);
124 device
= context
->Device
;
125 for(cur
= 0;cur
< n
;cur
++)
127 auto iter
= std::find_if(context
->EffectSlotList
.begin(), context
->EffectSlotList
.end(),
128 [](const ALeffectslotPtr
&entry
) noexcept
-> bool
131 if(iter
== context
->EffectSlotList
.end())
133 if(device
->AuxiliaryEffectSlotMax
== context
->EffectSlotList
.size())
135 UnlockEffectSlotList(context
);
136 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
137 SETERR_GOTO(context
, AL_OUT_OF_MEMORY
, done
,
138 "Exceeding %u auxiliary effect slot limit", device
->AuxiliaryEffectSlotMax
);
140 context
->EffectSlotList
.emplace_back(nullptr);
141 iter
= context
->EffectSlotList
.end() - 1;
144 auto slot
= new ALeffectslot
{};
145 ALenum err
{InitEffectSlot(slot
)};
146 if(err
!= AL_NO_ERROR
)
149 UnlockEffectSlotList(context
);
151 alDeleteAuxiliaryEffectSlots(cur
, effectslots
);
152 SETERR_GOTO(context
, err
, done
, "Effect slot object allocation failed");
154 aluInitEffectPanning(slot
);
156 slot
->id
= std::distance(context
->EffectSlotList
.begin(), iter
) + 1;
159 effectslots
[cur
] = slot
->id
;
161 AddActiveEffectSlots(effectslots
, n
, context
);
162 UnlockEffectSlotList(context
);
165 ALCcontext_DecRef(context
);
168 AL_API ALvoid AL_APIENTRY
alDeleteAuxiliaryEffectSlots(ALsizei n
, const ALuint
*effectslots
)
174 context
= GetContextRef();
177 LockEffectSlotList(context
);
179 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Deleting %d effect slots", n
);
180 if(n
== 0) goto done
;
184 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == nullptr)
185 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u",
187 if(ReadRef(&slot
->ref
) != 0)
188 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Deleting in-use effect slot %u",
192 // All effectslots are valid
193 RemoveActiveEffectSlots(effectslots
, n
, context
);
196 if((slot
=LookupEffectSlot(context
, effectslots
[i
])) == nullptr)
198 context
->EffectSlotList
[effectslots
[i
]-1] = nullptr;
204 UnlockEffectSlotList(context
);
205 ALCcontext_DecRef(context
);
208 AL_API ALboolean AL_APIENTRY
alIsAuxiliaryEffectSlot(ALuint effectslot
)
213 context
= GetContextRef();
214 if(!context
) return AL_FALSE
;
216 LockEffectSlotList(context
);
217 ret
= (LookupEffectSlot(context
, effectslot
) ? AL_TRUE
: AL_FALSE
);
218 UnlockEffectSlotList(context
);
220 ALCcontext_DecRef(context
);
225 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint value
)
230 ALeffect
*effect
= nullptr;
233 context
= GetContextRef();
236 almtx_lock(&context
->PropLock
);
237 LockEffectSlotList(context
);
238 if((slot
=LookupEffectSlot(context
, effectslot
)) == nullptr)
239 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
242 case AL_EFFECTSLOT_EFFECT
:
243 device
= context
->Device
;
245 LockEffectList(device
);
246 effect
= (value
? LookupEffect(device
, value
) : nullptr);
247 if(!(value
== 0 || effect
!= nullptr))
249 UnlockEffectList(device
);
250 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Invalid effect ID %u", value
);
252 err
= InitializeEffect(context
, slot
, effect
);
253 UnlockEffectList(device
);
255 if(err
!= AL_NO_ERROR
)
256 SETERR_GOTO(context
, err
, done
, "Effect initialization failed");
259 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
260 if(!(value
== AL_TRUE
|| value
== AL_FALSE
))
261 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
,
262 "Effect slot auxiliary send auto out of range");
263 slot
->AuxSendAuto
= value
;
267 SETERR_GOTO(context
, AL_INVALID_ENUM
, done
, "Invalid effect slot integer property 0x%04x",
273 UnlockEffectSlotList(context
);
274 almtx_unlock(&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 LockEffectSlotList(context
);
294 if(LookupEffectSlot(context
, effectslot
) == nullptr)
295 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
299 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
304 UnlockEffectSlotList(context
);
305 ALCcontext_DecRef(context
);
308 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat value
)
313 context
= GetContextRef();
316 almtx_lock(&context
->PropLock
);
317 LockEffectSlotList(context
);
318 if((slot
=LookupEffectSlot(context
, effectslot
)) == nullptr)
319 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
322 case AL_EFFECTSLOT_GAIN
:
323 if(!(value
>= 0.0f
&& value
<= 1.0f
))
324 SETERR_GOTO(context
, AL_INVALID_VALUE
, done
, "Effect slot gain out of range");
329 SETERR_GOTO(context
, AL_INVALID_ENUM
, done
, "Invalid effect slot float property 0x%04x",
335 UnlockEffectSlotList(context
);
336 almtx_unlock(&context
->PropLock
);
337 ALCcontext_DecRef(context
);
340 AL_API ALvoid AL_APIENTRY
alAuxiliaryEffectSlotfv(ALuint effectslot
, ALenum param
, const ALfloat
*values
)
346 case AL_EFFECTSLOT_GAIN
:
347 alAuxiliaryEffectSlotf(effectslot
, param
, values
[0]);
351 context
= GetContextRef();
354 LockEffectSlotList(context
);
355 if(LookupEffectSlot(context
, effectslot
) == nullptr)
356 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
360 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
365 UnlockEffectSlotList(context
);
366 ALCcontext_DecRef(context
);
369 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSloti(ALuint effectslot
, ALenum param
, ALint
*value
)
374 context
= GetContextRef();
377 LockEffectSlotList(context
);
378 if((slot
=LookupEffectSlot(context
, effectslot
)) == nullptr)
379 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
382 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
383 *value
= slot
->AuxSendAuto
;
387 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot integer property 0x%04x", param
);
391 UnlockEffectSlotList(context
);
392 ALCcontext_DecRef(context
);
395 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotiv(ALuint effectslot
, ALenum param
, ALint
*values
)
401 case AL_EFFECTSLOT_EFFECT
:
402 case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO
:
403 alGetAuxiliaryEffectSloti(effectslot
, param
, values
);
407 context
= GetContextRef();
410 LockEffectSlotList(context
);
411 if(LookupEffectSlot(context
, effectslot
) == nullptr)
412 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
416 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot integer-vector property 0x%04x",
421 UnlockEffectSlotList(context
);
422 ALCcontext_DecRef(context
);
425 AL_API ALvoid AL_APIENTRY
alGetAuxiliaryEffectSlotf(ALuint effectslot
, ALenum param
, ALfloat
*value
)
430 context
= GetContextRef();
433 LockEffectSlotList(context
);
434 if((slot
=LookupEffectSlot(context
, effectslot
)) == nullptr)
435 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
438 case AL_EFFECTSLOT_GAIN
:
443 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot float property 0x%04x", param
);
447 UnlockEffectSlotList(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 LockEffectSlotList(context
);
466 if(LookupEffectSlot(context
, effectslot
) == nullptr)
467 SETERR_GOTO(context
, AL_INVALID_NAME
, done
, "Invalid effect slot ID %u", effectslot
);
471 alSetError(context
, AL_INVALID_ENUM
, "Invalid effect slot float-vector property 0x%04x",
476 UnlockEffectSlotList(context
);
477 ALCcontext_DecRef(context
);
481 ALenum
InitializeEffect(ALCcontext
*Context
, ALeffectslot
*EffectSlot
, ALeffect
*effect
)
483 ALCdevice
*Device
= Context
->Device
;
484 ALenum newtype
= (effect
? effect
->type
: AL_EFFECT_NULL
);
485 struct ALeffectslotProps
*props
;
486 ALeffectState
*State
;
488 if(newtype
!= EffectSlot
->Effect
.Type
)
490 EffectStateFactory
*factory
;
492 factory
= getFactoryByType(newtype
);
495 ERR("Failed to find factory for effect type 0x%04x\n", newtype
);
496 return AL_INVALID_ENUM
;
498 State
= EffectStateFactory_create(factory
);
499 if(!State
) return AL_OUT_OF_MEMORY
;
502 almtx_lock(&Device
->BackendLock
);
503 State
->OutBuffer
= Device
->Dry
.Buffer
;
504 State
->OutChannels
= Device
->Dry
.NumChannels
;
505 if(V(State
,deviceUpdate
)(Device
) == AL_FALSE
)
507 almtx_unlock(&Device
->BackendLock
);
509 ALeffectState_DecRef(State
);
510 return AL_OUT_OF_MEMORY
;
512 almtx_unlock(&Device
->BackendLock
);
517 EffectSlot
->Effect
.Type
= AL_EFFECT_NULL
;
518 memset(&EffectSlot
->Effect
.Props
, 0, sizeof(EffectSlot
->Effect
.Props
));
522 EffectSlot
->Effect
.Type
= effect
->type
;
523 EffectSlot
->Effect
.Props
= effect
->Props
;
526 ALeffectState_DecRef(EffectSlot
->Effect
.State
);
527 EffectSlot
->Effect
.State
= State
;
530 EffectSlot
->Effect
.Props
= effect
->Props
;
532 /* Remove state references from old effect slot property updates. */
533 props
= ATOMIC_LOAD_SEQ(&Context
->FreeEffectslotProps
);
537 ALeffectState_DecRef(props
->State
);
538 props
->State
= nullptr;
539 props
= ATOMIC_LOAD(&props
->next
, almemory_order_relaxed
);
546 static void ALeffectState_IncRef(ALeffectState
*state
)
548 auto ref
= IncrementRef(&state
->Ref
);
549 TRACEREF("%p increasing refcount to %u\n", state
, ref
);
552 void ALeffectState_DecRef(ALeffectState
*state
)
554 auto ref
= DecrementRef(&state
->Ref
);
555 TRACEREF("%p decreasing refcount to %u\n", state
, ref
);
556 if(ref
== 0) DELETE_OBJ(state
);
560 void ALeffectState_Construct(ALeffectState
*state
)
562 InitRef(&state
->Ref
, 1);
564 state
->OutBuffer
= nullptr;
565 state
->OutChannels
= 0;
568 void ALeffectState_Destruct(ALeffectState
*UNUSED(state
))
573 static void AddActiveEffectSlots(const ALuint
*slotids
, ALsizei count
, ALCcontext
*context
)
575 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
,
576 almemory_order_acquire
);
577 struct ALeffectslotArray
*newarray
= nullptr;
578 ALsizei newcount
= curarray
->count
+ count
;
579 ALCdevice
*device
= context
->Device
;
582 /* Insert the new effect slots into the head of the array, followed by the
585 newarray
= static_cast<struct ALeffectslotArray
*>(al_calloc(DEF_ALIGN
,
586 FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
)));
587 newarray
->count
= newcount
;
588 for(i
= 0;i
< count
;i
++)
589 newarray
->slot
[i
] = LookupEffectSlot(context
, slotids
[i
]);
590 for(j
= 0;i
< newcount
;)
591 newarray
->slot
[i
++] = curarray
->slot
[j
++];
592 /* Remove any duplicates (first instance of each will be kept). */
593 for(i
= 1;i
< newcount
;i
++)
597 if(UNLIKELY(newarray
->slot
[i
] == newarray
->slot
[--j
]))
600 for(j
= i
;j
< newcount
;j
++)
601 newarray
->slot
[j
] = newarray
->slot
[j
+1];
608 /* Reallocate newarray if the new size ended up smaller from duplicate
611 if(UNLIKELY(newcount
< newarray
->count
))
613 struct ALeffectslotArray
*tmp
= static_cast<struct ALeffectslotArray
*>(al_calloc(DEF_ALIGN
,
614 FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
)));
615 memcpy(tmp
, newarray
, FAM_SIZE(struct ALeffectslotArray
, slot
, newcount
));
618 newarray
->count
= newcount
;
621 curarray
= context
->ActiveAuxSlots
.exchange(newarray
, std::memory_order_acq_rel
);
622 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
627 static void RemoveActiveEffectSlots(const ALuint
*slotids
, ALsizei count
, ALCcontext
*context
)
629 struct ALeffectslotArray
*curarray
= ATOMIC_LOAD(&context
->ActiveAuxSlots
,
630 almemory_order_acquire
);
631 struct ALeffectslotArray
*newarray
= nullptr;
632 ALCdevice
*device
= context
->Device
;
635 /* Don't shrink the allocated array size since we don't know how many (if
636 * any) of the effect slots to remove are in the array.
638 newarray
= static_cast<struct ALeffectslotArray
*>(al_calloc(DEF_ALIGN
,
639 FAM_SIZE(struct ALeffectslotArray
, slot
, curarray
->count
)));
641 for(i
= 0;i
< curarray
->count
;i
++)
643 /* Insert this slot into the new array only if it's not one to remove. */
644 ALeffectslot
*slot
= curarray
->slot
[i
];
645 for(j
= count
;j
!= 0;)
647 if(slot
->id
== slotids
[--j
])
650 newarray
->slot
[newarray
->count
++] = slot
;
654 /* TODO: Could reallocate newarray now that we know it's needed size. */
656 curarray
= context
->ActiveAuxSlots
.exchange(newarray
, std::memory_order_acq_rel
);
657 while((ATOMIC_LOAD(&device
->MixCount
, almemory_order_acquire
)&1))
663 ALenum
InitEffectSlot(ALeffectslot
*slot
)
665 EffectStateFactory
*factory
{getFactoryByType(slot
->Effect
.Type
)};
666 slot
->Effect
.State
= EffectStateFactory_create(factory
);
667 if(!slot
->Effect
.State
) return AL_OUT_OF_MEMORY
;
669 ALeffectState_IncRef(slot
->Effect
.State
);
670 slot
->Params
.EffectState
= slot
->Effect
.State
;
674 ALeffectslot::~ALeffectslot()
676 struct ALeffectslotProps
*props
{Update
.load()};
679 if(props
->State
) ALeffectState_DecRef(props
->State
);
680 TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", props
);
685 ALeffectState_DecRef(Effect
.State
);
686 if(Params
.EffectState
)
687 ALeffectState_DecRef(Params
.EffectState
);
690 void UpdateEffectSlotProps(ALeffectslot
*slot
, ALCcontext
*context
)
692 struct ALeffectslotProps
*props
;
693 ALeffectState
*oldstate
;
695 /* Get an unused property container, or allocate a new one as needed. */
696 props
= context
->FreeEffectslotProps
.load(std::memory_order_relaxed
);
698 props
= static_cast<ALeffectslotProps
*>(al_calloc(16, sizeof(*props
)));
701 struct ALeffectslotProps
*next
;
703 next
= props
->next
.load(std::memory_order_relaxed
);
704 } while(context
->FreeEffectslotProps
.compare_exchange_weak(props
, next
,
705 std::memory_order_seq_cst
, std::memory_order_acquire
) == 0);
708 /* Copy in current property values. */
709 props
->Gain
= slot
->Gain
;
710 props
->AuxSendAuto
= slot
->AuxSendAuto
;
712 props
->Type
= slot
->Effect
.Type
;
713 props
->Props
= slot
->Effect
.Props
;
714 /* Swap out any stale effect state object there may be in the container, to
717 ALeffectState_IncRef(slot
->Effect
.State
);
718 oldstate
= props
->State
;
719 props
->State
= slot
->Effect
.State
;
721 /* Set the new container for updating internal parameters. */
722 props
= slot
->Update
.exchange(props
, std::memory_order_acq_rel
);
725 /* If there was an unused update container, put it back in the
729 ALeffectState_DecRef(props
->State
);
730 props
->State
= nullptr;
731 AtomicReplaceHead(context
->FreeEffectslotProps
, props
);
735 ALeffectState_DecRef(oldstate
);
738 void UpdateAllEffectSlotProps(ALCcontext
*context
)
740 struct ALeffectslotArray
*auxslots
;
743 LockEffectSlotList(context
);
744 auxslots
= context
->ActiveAuxSlots
.load(std::memory_order_acquire
);
745 for(i
= 0;i
< auxslots
->count
;i
++)
747 ALeffectslot
*slot
= auxslots
->slot
[i
];
748 if(!slot
->PropsClean
.exchange(AL_TRUE
, std::memory_order_acq_rel
))
749 UpdateEffectSlotProps(slot
, context
);
751 UnlockEffectSlotList(context
);
754 ALvoid
ReleaseALAuxiliaryEffectSlots(ALCcontext
*context
)
757 for(auto &entry
: context
->EffectSlotList
)
766 WARN("(%p) Deleted " SZFMT
" AuxiliaryEffectSlot%s\n", context
, leftover
, (leftover
==1)?"":"s");