Skip to content

Commit

Permalink
feat(core): Next frame callbacks.
Browse files Browse the repository at this point in the history
Added the possibility to postpone an action for the next call of AdvanceFrame.

Signed-off-by: Axel Nana <[email protected]>
  • Loading branch information
na2axl committed Jun 23, 2024
1 parent 43179ff commit 3de6ad8
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 36 deletions.
12 changes: 12 additions & 0 deletions include/SparkyStudios/Audio/Amplitude/Core/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef SS_AMPLITUDE_AUDIO_ENGINE_H
#define SS_AMPLITUDE_AUDIO_ENGINE_H

#include <queue>
#include <string>

#include <SparkyStudios/Audio/Amplitude/Core/Common.h>
Expand Down Expand Up @@ -134,6 +135,13 @@ namespace SparkyStudios::Audio::Amplitude
*/
void AdvanceFrame(AmTime delta) const;

/**
* @brief Executes the given callback on the next frame.
*
* @param callback The callback to be called when the next frame is ready.
*/
void NextFrame(std::function<void(AmTime delta)> callback) const;

/**
* @brief Gets the total elapsed time in milliseconds since the start of the engine.
*
Expand Down Expand Up @@ -1230,6 +1238,10 @@ namespace SparkyStudios::Audio::Amplitude
// The lis of paths in which search for plugins.
static std::set<AmOsString> _pluginSearchPaths;

AmMutexHandle _frameThreadMutex;
// The list of pending next frame callbacks.
mutable std::queue<std::function<void(AmTime)>> _nextFrameCallbacks;

// Hold the engine config file contents.
AmString _configSrc;

Expand Down
111 changes: 75 additions & 36 deletions src/Core/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ namespace SparkyStudios::Audio::Amplitude
}

Engine::Engine()
: _configSrc()
: _frameThreadMutex(nullptr)
, _configSrc()
, _state(nullptr)
, _defaultListener(nullptr)
, _fs()
Expand Down Expand Up @@ -573,6 +574,8 @@ namespace SparkyStudios::Audio::Amplitude
Filter::LockRegistry();
Fader::LockRegistry();

_frameThreadMutex = Thread::CreateMutex(500);

Check failure on line 577 in src/Core/Engine.cpp

View workflow job for this annotation

GitHub Actions / Amplitude Audio SDK / [release] Windows Latest MinGW

'CreateMutexA' is not a member of 'SparkyStudios::Audio::Amplitude::Thread'; did you mean 'CreateMutex'?

Check failure on line 577 in src/Core/Engine.cpp

View workflow job for this annotation

GitHub Actions / Amplitude Audio SDK / [debug] Windows Latest MinGW

'CreateMutexA' is not a member of 'SparkyStudios::Audio::Amplitude::Thread'; did you mean 'CreateMutex'?

// Create the internal engine state
_state = ampoolnew(MemoryPoolKind::Engine, EngineInternalState);
_state->version = &Amplitude::Version();
Expand Down Expand Up @@ -729,6 +732,8 @@ namespace SparkyStudios::Audio::Amplitude
ampooldelete(MemoryPoolKind::Engine, EngineInternalState, _state);
_state = nullptr;

Thread::DestroyMutex(_frameThreadMutex);

// Unlock registries
Driver::UnlockRegistry();
Codec::UnlockRegistry();
Expand Down Expand Up @@ -2092,6 +2097,19 @@ namespace SparkyStudios::Audio::Amplitude
if (_state->paused)
return;

// Executre pending frame callbacks.
Thread::LockMutex(_frameThreadMutex);
{
while (!_nextFrameCallbacks.empty())
{
const auto& callback = _nextFrameCallbacks.front();
callback(delta);

_nextFrameCallbacks.pop();
}
}
Thread::UnlockMutex(_frameThreadMutex);

EraseFinishedSounds(_state);

for (const auto& rtpc : _state->rtpc_map | std::views::values)
Expand Down Expand Up @@ -2159,6 +2177,15 @@ namespace SparkyStudios::Audio::Amplitude
_state->total_time += delta;
}

void Engine::NextFrame(std::function<void(AmTime delta)> callback) const
{
Thread::LockMutex(_frameThreadMutex);
{
_nextFrameCallbacks.push(std::move(callback));
}
Thread::UnlockMutex(_frameThreadMutex);
}

AmTime Engine::GetTotalTime() const
{
return _state->total_time;
Expand Down Expand Up @@ -2266,26 +2293,30 @@ namespace SparkyStudios::Audio::Amplitude
insertionPoint, &_state->playing_channel_list, &_state->real_channel_free_list, &_state->virtual_channel_free_list,
_state->paused);

// The sound could not be added to the list; not high enough priority.
// The channel could not be added to the list; not high enough priority.
if (newChannel == nullptr)
{
amLogDebug("Cannot play switch container: Not high enough priority.");
return Channel(nullptr);
}

// Now that we have our new sound, set the data on it and update the next
// pointers.
// Now that we have our new channel, set the data on it and update the next pointers.
newChannel->SetEntity(entity);
newChannel->SetSwitchContainer(handle);
newChannel->SetUserGain(userGain);

// Attempt to play the sound if the engine is not paused.
if (!_state->paused)
{
if (!newChannel->Play())
// Attempt to play the channel, if the engine is paused, the channel will be played later.
NextFrame(
[this, newChannel, handle](AmTime delta)
{
// Error playing the sound, put it back in the free list.
InsertIntoFreeList(_state, newChannel);
return Channel(nullptr);
}
}
if (!newChannel->Play())
{
amLogError("Failed to play switch container: %s.", handle->GetName().c_str());

// Error playing the sound, put it back in the free list.
InsertIntoFreeList(_state, newChannel);
}
});

newChannel->SetGain(gain);
newChannel->SetPan(pan);
Expand Down Expand Up @@ -2334,26 +2365,30 @@ namespace SparkyStudios::Audio::Amplitude
insertionPoint, &_state->playing_channel_list, &_state->real_channel_free_list, &_state->virtual_channel_free_list,
_state->paused);

// The sound could not be added to the list; not high enough priority.
// The channel could not be added to the list; not high enough priority.
if (newChannel == nullptr)
{
amLogDebug("Cannot play collection: Not high enough priority.");
return Channel(nullptr);
}

// Now that we have our new sound, set the data on it and update the next
// pointers.
// Now that we have our new channel, set the data on it and update the next pointers.
newChannel->SetEntity(entity);
newChannel->SetCollection(handle);
newChannel->SetUserGain(userGain);

// Attempt to play the sound if the engine is not paused.
if (!_state->paused)
{
if (!newChannel->Play())
// Attempt to play the channel, if the engine is paused, the channel will be played later.
NextFrame(
[this, newChannel, handle](AmTime delta)
{
// Error playing the sound, put it back in the free list.
InsertIntoFreeList(_state, newChannel);
return Channel(nullptr);
}
}
if (!newChannel->Play())
{
amLogError("Failed to play collection: %s.", handle->GetName().c_str());

// Error playing the sound, put it back in the free list.
InsertIntoFreeList(_state, newChannel);
}
});

newChannel->SetGain(gain);
newChannel->SetPan(pan);
Expand Down Expand Up @@ -2403,24 +2438,28 @@ namespace SparkyStudios::Audio::Amplitude

// The sound could not be added to the list; not high enough priority.
if (newChannel == nullptr)
{
amLogDebug("Cannot play sound: Not high enough priority.");
return Channel(nullptr);
}

// Now that we have our new sound, set the data on it and update the next
// pointers.
// Now that we have our new channel, set the data on it and update the next pointers.
newChannel->SetEntity(entity);
newChannel->SetSound(handle);
newChannel->SetUserGain(userGain);

// Attempt to play the sound if the engine is not paused.
if (!_state->paused)
{
if (!newChannel->Play())
// Attempt to play the channel, if the engine is paused, the channel will be played later.
NextFrame(
[this, newChannel, handle](AmTime delta)
{
// Error playing the sound, put it back in the free list.
InsertIntoFreeList(_state, newChannel);
return Channel(nullptr);
}
}
if (!newChannel->Play())
{
amLogError("Failed to play sound: %s.", handle->GetName().c_str());

// Error playing the sound, put it back in the free list.
InsertIntoFreeList(_state, newChannel);
}
});

newChannel->SetGain(gain);
newChannel->SetPan(pan);
Expand Down
1 change: 1 addition & 0 deletions src/Mixer/Mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ namespace SparkyStudios::Audio::Amplitude

// store flag last, releasing the layer to the mixer thread
AMPLIMIX_STORE(&lay->flag, flag);
OnSoundStarted(this, lay);
}

return layer;
Expand Down

0 comments on commit 3de6ad8

Please sign in to comment.