Skip to content

Commit

Permalink
feat(core): Amplimix optimizations.
Browse files Browse the repository at this point in the history
  • Loading branch information
na2axl committed Oct 27, 2024
1 parent f9de20d commit ce6296d
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 51 deletions.
3 changes: 3 additions & 0 deletions src/Core/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,9 @@ namespace SparkyStudios::Audio::Amplitude
// Stop all sounds
StopAll();

// Process one last frame to update stopped channel states
AdvanceFrame(0);

// Release channels
EraseFinishedSounds(_state);

Expand Down
30 changes: 24 additions & 6 deletions src/Core/Playback/ChannelInternalState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,8 +697,8 @@ namespace SparkyStudios::Audio::Amplitude
if (!Valid())
return;

_realChannel.Halt();
_channelState = eChannelPlaybackState_Stopped;
if (_realChannel.Halt())
_channelState = eChannelPlaybackState_Stopped;
}

bool ChannelInternalState::PlaySwitchContainerStateUpdate(
Expand Down Expand Up @@ -754,10 +754,15 @@ namespace SparkyStudios::Audio::Amplitude
settings.m_effectID = definition->effect();

instances.push_back(ampoolnew(
eMemoryPoolKind_Engine, SoundInstance, sound, settings, static_cast<const EffectImpl*>(_switchContainer->GetEffect())));
eMemoryPoolKind_Amplimix, SoundInstance, sound, settings, static_cast<const EffectImpl*>(_switchContainer->GetEffect())));
}

return _realChannel.Play(instances);
const bool success = _realChannel.Play(instances);
if (!success)
for (SoundInstance* instance : instances)
SoundImpl::DestroyInstance(instance);

return success;
}

bool ChannelInternalState::PlaySwitchContainer()
Expand Down Expand Up @@ -804,7 +809,13 @@ namespace SparkyStudios::Audio::Amplitude
if (_channelState != eChannelPlaybackState_FadingIn && _channelState != eChannelPlaybackState_FadingOut)
_channelState = eChannelPlaybackState_Playing;

return !IsReal() || _realChannel.Play(sound->CreateInstance(_collection));
SoundInstance* instance = sound->CreateInstance();

const bool success = !IsReal() || _realChannel.Play(instance);
if (!success)
SoundImpl::DestroyInstance(instance);

return success;
}

bool ChannelInternalState::PlaySound()
Expand All @@ -819,6 +830,13 @@ namespace SparkyStudios::Audio::Amplitude
_fader = Fader::Construct(_faderName);

_channelState = eChannelPlaybackState_Playing;
return !IsReal() || _realChannel.Play(_sound->CreateInstance());

SoundInstance* instance = _sound->CreateInstance();

const bool success = !IsReal() || _realChannel.Play(instance);
if (!success)
SoundImpl::DestroyInstance(instance);

return success;
}
} // namespace SparkyStudios::Audio::Amplitude
75 changes: 73 additions & 2 deletions src/Mixer/Amplimix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,54 @@ namespace SparkyStudios::Audio::Amplitude
bool m_locked = false;
};

struct AmplimixLayerMutexLocker
{
explicit AmplimixLayerMutexLocker(AmplimixLayerImpl* layer)
: m_layer(layer)
{
Lock();
}

~AmplimixLayerMutexLocker()
{
Unlock();
}

[[nodiscard]] bool IsLocked() const
{
return m_layer->mutexLocked[Thread::GetCurrentThreadId()];
}

void Lock() const
{
if (IsLocked())
return;

Thread::LockMutex(m_layer->mutex);
m_layer->mutexLocked[Thread::GetCurrentThreadId()] = true;
}

void Unlock() const
{
if (!IsLocked())
return;

Thread::UnlockMutex(m_layer->mutex);
m_layer->mutexLocked[Thread::GetCurrentThreadId()] = false;
}

private:
AmplimixLayerImpl* m_layer;
};

constexpr AmUInt32 kProcessedFramesCount = GetSimdBlockSize();

static void OnSoundDestroyed(AmplimixImpl* mixer, AmplimixLayerImpl* layer);

static bool ShouldLoopSound(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

const auto* sound = layer->snd->sound.get();
const AmUInt32 loopCount = sound->GetSettings().m_loopCount;

Expand All @@ -79,6 +121,8 @@ namespace SparkyStudios::Audio::Amplitude

static void OnSoundStarted(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

const auto* sound = layer->snd->sound.get();
amLogDebug("Started sound: '" AM_OS_CHAR_FMT "'.", sound->GetSound()->GetPath().c_str());

Expand All @@ -90,6 +134,8 @@ namespace SparkyStudios::Audio::Amplitude

static void OnSoundPaused(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

const auto* sound = layer->snd->sound.get();
amLogDebug("Paused sound: '" AM_OS_CHAR_FMT "'.", sound->GetSound()->GetPath().c_str());

Expand All @@ -101,6 +147,8 @@ namespace SparkyStudios::Audio::Amplitude

static void OnSoundResumed(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

const auto* sound = layer->snd->sound.get();
amLogDebug("Resumed sound: '" AM_OS_CHAR_FMT "'.", sound->GetSound()->GetPath().c_str());

Expand All @@ -112,6 +160,8 @@ namespace SparkyStudios::Audio::Amplitude

static void OnSoundStopped(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

const auto* sound = layer->snd->sound.get();
amLogDebug("Stopped sound: '" AM_OS_CHAR_FMT "'.", sound->GetSound()->GetPath().c_str());

Expand All @@ -123,6 +173,8 @@ namespace SparkyStudios::Audio::Amplitude

static bool OnSoundLooped(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

auto* sound = layer->snd->sound.get();
amLogDebug("Looped sound: '" AM_OS_CHAR_FMT "'.", sound->GetSound()->GetPath().c_str());

Expand All @@ -143,6 +195,8 @@ namespace SparkyStudios::Audio::Amplitude

static AmUInt64 OnSoundStream(AmplimixImpl* mixer, AmplimixLayerImpl* layer, AmUInt64 offset, AmUInt64 frames)
{
AmplimixLayerMutexLocker lock(layer);

if (!layer->snd->stream)
return 0;

Expand All @@ -152,6 +206,8 @@ namespace SparkyStudios::Audio::Amplitude

static void OnSoundEnded(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

auto* sound = layer->snd->sound.get();
amLogDebug("Ended sound: '" AM_OS_CHAR_FMT "'.", sound->GetSound()->GetPath().c_str());

Expand Down Expand Up @@ -227,6 +283,8 @@ namespace SparkyStudios::Audio::Amplitude

static void OnSoundDestroyed(AmplimixImpl* mixer, AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

if (layer->snd == nullptr)
return;

Expand All @@ -246,7 +304,7 @@ namespace SparkyStudios::Audio::Amplitude
const auto x = xsimd::load_aligned<simd_arch>(&in[index]);
const auto y = xsimd::load_aligned<simd_arch>(&out[index]);

xsimd::store_aligned(&out[index], xsimd::fma(x, gain, y));
xsimd::store_aligned<simd_arch>(&out[index], xsimd::fma(x, gain, y));
#else
out[index] += in[index] * gain;
#endif // AM_SIMD_INTRINSICS
Expand Down Expand Up @@ -502,6 +560,7 @@ namespace SparkyStudios::Audio::Amplitude
bool AmplimixImpl::SetObstruction(AmUInt32 id, AmUInt32 layer, AmReal32 obstruction)
{
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if (id != lay->id || AMPLIMIX_LOAD(&lay->flag) <= ePSF_STOP)
Expand All @@ -520,6 +579,7 @@ namespace SparkyStudios::Audio::Amplitude
bool AmplimixImpl::SetOcclusion(AmUInt32 id, AmUInt32 layer, AmReal32 occlusion)
{
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if (id != lay->id || AMPLIMIX_LOAD(&lay->flag) <= ePSF_STOP)
Expand All @@ -538,6 +598,7 @@ namespace SparkyStudios::Audio::Amplitude
bool AmplimixImpl::SetGainPan(AmUInt32 id, AmUInt32 layer, AmReal32 gain, AmReal32 pan)
{
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if (id != lay->id || AMPLIMIX_LOAD(&lay->flag) <= ePSF_STOP)
Expand All @@ -561,6 +622,7 @@ namespace SparkyStudios::Audio::Amplitude
bool AmplimixImpl::SetPitch(AmUInt32 id, AmUInt32 layer, AmReal32 pitch)
{
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if ((id == lay->id) && (AMPLIMIX_LOAD(&lay->flag) > ePSF_STOP))
Expand All @@ -578,6 +640,7 @@ namespace SparkyStudios::Audio::Amplitude
bool AmplimixImpl::SetCursor(AmUInt32 id, AmUInt32 layer, AmUInt64 cursor)
{
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if ((id == lay->id) && (AMPLIMIX_LOAD(&lay->flag) > ePSF_STOP))
Expand Down Expand Up @@ -646,7 +709,8 @@ namespace SparkyStudios::Audio::Amplitude
PlayStateFlag AmplimixImpl::GetPlayState(AmUInt32 id, AmUInt32 layer)
{
// get layer based on the lowest bits of id
const auto* lay = GetLayer(layer);
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if (PlayStateFlag flag; (id == lay->id) && ((flag = AMPLIMIX_LOAD(&lay->flag)) > ePSF_STOP))
Expand All @@ -662,6 +726,7 @@ namespace SparkyStudios::Audio::Amplitude
bool AmplimixImpl::SetPlaySpeed(AmUInt32 id, AmUInt32 layer, AmReal32 speed)
{
auto* lay = GetLayer(layer);
AmplimixLayerMutexLocker lock(lay);

// check id and state flag to make sure the id is valid
if ((id == lay->id) && (AMPLIMIX_LOAD(&lay->flag) > ePSF_STOP))
Expand Down Expand Up @@ -761,6 +826,8 @@ namespace SparkyStudios::Audio::Amplitude

void AmplimixImpl::MixLayer(AmplimixLayerImpl* layer, AudioBuffer* buffer, AmUInt64 frameCount)
{
AmplimixLayerMutexLocker lock(layer);

if (layer->snd == nullptr)
{
AMPLITUDE_ASSERT(false); // This should technically never appear
Expand Down Expand Up @@ -982,6 +1049,8 @@ namespace SparkyStudios::Audio::Amplitude

bool AmplimixImpl::ShouldMix(AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

if (layer->snd == nullptr)
return false;

Expand All @@ -994,6 +1063,8 @@ namespace SparkyStudios::Audio::Amplitude

void AmplimixImpl::UpdatePitch(AmplimixLayerImpl* layer)
{
AmplimixLayerMutexLocker lock(layer);

const AmReal32 pitch = AMPLIMIX_LOAD(&layer->pitch);
const AmReal32 speed = AMPLIMIX_LOAD(&layer->userPlaySpeed);

Expand Down
51 changes: 27 additions & 24 deletions src/Mixer/Amplimix.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,37 +82,40 @@ namespace SparkyStudios::Audio::Amplitude
AudioConverter* dataConverter; // miniaudio resampler & channel converter
PipelineInstance* pipeline; // pipeline for this layer

AmMutexHandle mutex; // mutex for thread-safe access
std::unordered_map<AmThreadID, bool> mutexLocked; // true if mutex is locked

/**
* @brief Resets the layer.
*/
void Reset();

void ResetPipeline();

AmUInt32 GetId() const override;
AmUInt64 GetStartPosition() const override;
AmUInt64 GetEndPosition() const override;
AmUInt64 GetCurrentPosition() const override;
AmReal32 GetGain() const override;
AmReal32 GetStereoPan() const override;
AmReal32 GetPitch() const override;
AmReal32 GetObstruction() const override;
AmReal32 GetOcclusion() const override;
AmReal32 GetPlaySpeed() const override;
AmVec3 GetLocation() const override;
Entity GetEntity() const override;
Listener GetListener() const override;
Room GetRoom() const override;
Channel GetChannel() const override;
Bus GetBus() const override;
SoundFormat GetSoundFormat() const override;
eSpatialization GetSpatialization() const override;
bool IsLoopEnabled() const override;
bool IsStreamEnabled() const override;
const Sound* GetSound() const override;
const EffectInstance* GetEffect() const override;
const Attenuation* GetAttenuation() const override;
AmUInt32 GetSampleRate() const override;
[[nodiscard]] AmUInt32 GetId() const override;
[[nodiscard]] AmUInt64 GetStartPosition() const override;
[[nodiscard]] AmUInt64 GetEndPosition() const override;
[[nodiscard]] AmUInt64 GetCurrentPosition() const override;
[[nodiscard]] AmReal32 GetGain() const override;
[[nodiscard]] AmReal32 GetStereoPan() const override;
[[nodiscard]] AmReal32 GetPitch() const override;
[[nodiscard]] AmReal32 GetObstruction() const override;
[[nodiscard]] AmReal32 GetOcclusion() const override;
[[nodiscard]] AmReal32 GetPlaySpeed() const override;
[[nodiscard]] AmVec3 GetLocation() const override;
[[nodiscard]] Entity GetEntity() const override;
[[nodiscard]] Listener GetListener() const override;
[[nodiscard]] Room GetRoom() const override;
[[nodiscard]] Channel GetChannel() const override;
[[nodiscard]] Bus GetBus() const override;
[[nodiscard]] SoundFormat GetSoundFormat() const override;
[[nodiscard]] eSpatialization GetSpatialization() const override;
[[nodiscard]] bool IsLoopEnabled() const override;
[[nodiscard]] bool IsStreamEnabled() const override;
[[nodiscard]] const Sound* GetSound() const override;
[[nodiscard]] const EffectInstance* GetEffect() const override;
[[nodiscard]] const Attenuation* GetAttenuation() const override;
[[nodiscard]] AmUInt32 GetSampleRate() const override;
};

struct MixerCommand
Expand Down
Loading

0 comments on commit ce6296d

Please sign in to comment.