Skip to content

Commit

Permalink
feat(pipeline): Add nodes related to direct sound path simulation.
Browse files Browse the repository at this point in the history
Signed-off-by: Axel Nana <[email protected]>
  • Loading branch information
na2axl committed Aug 29, 2024
1 parent 0bf3e0b commit c82832c
Show file tree
Hide file tree
Showing 36 changed files with 1,172 additions and 169 deletions.
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,13 @@ set(SA_SOURCE
src/DSP/Resamplers/LibsamplerateResampler.h
src/DSP/Resamplers/R8BrainResampler.h
src/DSP/AudioConverter.cpp
src/DSP/Delay.cpp
src/DSP/Delay.h
src/DSP/Filter.cpp
src/DSP/Gain.cpp
src/DSP/Gain.h
src/DSP/NearFieldProcessor.cpp
src/DSP/NearFieldProcessor.h
src/DSP/Resampler.cpp

src/HRTF/HRIRSphere.cpp
Expand Down Expand Up @@ -316,8 +320,14 @@ set(SA_SOURCE

src/Mixer/Nodes/AttenuationNode.cpp
src/Mixer/Nodes/AttenuationNode.h
src/Mixer/Nodes/NearFieldEffectNode.cpp
src/Mixer/Nodes/NearFieldEffectNode.h
src/Mixer/Nodes/OcclusionNode.cpp
src/Mixer/Nodes/OcclusionNode.h
src/Mixer/Nodes/StereoPanningNode.cpp
src/Mixer/Nodes/StereoPanningNode.h
src/Mixer/Nodes/StereoMixerNode.cpp
src/Mixer/Nodes/StereoMixerNode.h

src/Mixer/Pipeline/BinauralProcessor.h
src/Mixer/Pipeline/ClipProcessor.h
Expand Down
14 changes: 9 additions & 5 deletions include/SparkyStudios/Audio/Amplitude/Core/Listener.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace SparkyStudios::Audio::Amplitude
explicit Listener(ListenerInternalState* state);

/**
* @brief Uninitialize this Listener.
* @brief Deinitialize this Listener.
*
* Note that this does not destroy the internal state it references,
* it just removes this reference to it. To destroy the Listener,
Expand Down Expand Up @@ -139,6 +139,13 @@ namespace SparkyStudios::Audio::Amplitude
*/
[[nodiscard]] AmReal32 GetDirectivitySharpness() const;

/**
* @brief Gets the inverse matrix of the Listener.
*
* You can use this matrix to transform 3D global space into Listener space.
*/
[[nodiscard]] const AmMat4& GetInverseMatrix() const;

/**
* @brief Update the state of this Listener.
*
Expand All @@ -152,10 +159,7 @@ namespace SparkyStudios::Audio::Amplitude
*
* @return ListenerInternalState*
*/
[[nodiscard]] ListenerInternalState* GetState() const
{
return _state;
}
[[nodiscard]] ListenerInternalState* GetState() const;

private:
ListenerInternalState* _state;
Expand Down
2 changes: 1 addition & 1 deletion include/SparkyStudios/Audio/Amplitude/Mixer/Amplimix.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ namespace SparkyStudios::Audio::Amplitude

virtual AmReal32 GetGain() const = 0;

virtual AmReal32 GetPan() const = 0;
virtual AmReal32 GetStereoPan() const = 0;

virtual AmReal32 GetPitch() const = 0;

Expand Down
7 changes: 7 additions & 0 deletions include/SparkyStudios/Audio/Amplitude/Sound/Sound.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ namespace SparkyStudios::Audio::Amplitude
* @return true if looping is enabled, false otherwise.
*/
[[nodiscard]] virtual bool IsLoop() const = 0;

/**
* @brief Gets the near field effect gain of the sound object.
*
* @return The sound object near field effect gain.
*/
[[nodiscard]] virtual const RtpcValue& GetNearFieldGain() const = 0;
};
} // namespace SparkyStudios::Audio::Amplitude

Expand Down
4 changes: 2 additions & 2 deletions include/SparkyStudios/Audio/Amplitude/Sound/SoundObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ namespace SparkyStudios::Audio::Amplitude
virtual ~SoundObject() = default;

/**
* @brief Gets the actual gain of the sound object.
* @brief Gets the linear gain of the sound object.
*
* @return The sound object gain.
* @return The sound object linear gain.
*/
[[nodiscard]] virtual const RtpcValue& GetGain() const = 0;

Expand Down
6 changes: 3 additions & 3 deletions include/SparkyStudios/Audio/Amplitude/Sound/SwitchContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace SparkyStudios::Audio::Amplitude
* changes its state between one of the values where this item is registered.
*
* If this value is set to @c false, each the sound will be stopped and played again
* from the begginning.
* from the beginning.
*/
bool m_continueBetweenStates;

Expand All @@ -65,7 +65,7 @@ namespace SparkyStudios::Audio::Amplitude
AmString m_fadeOutAlgorithm;

/**
* @brief The custom gain applied on this item.
* @brief The custom linear gain applied on this item.
*
* The final gain will be computed with this value multiplied with the gain of the
* attenuation model, if any.
Expand Down Expand Up @@ -125,7 +125,7 @@ namespace SparkyStudios::Audio::Amplitude
*
* @return The list of sound object IDs registered to the given state.
*/
[[nodiscard]] virtual const std::vector<SwitchContainerItem>& GetSoundObjects(AmObjectID stateId) const = 0;
[[nodiscard]] virtual const std::vector<SwitchContainerItem>& GetSoundObjects(AmObjectID stateId) const = 0;
};
} // namespace SparkyStudios::Audio::Amplitude

Expand Down
10 changes: 5 additions & 5 deletions schemas/collection_definition.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ table DefaultCollectionEntry {
/// Special gain to apply only to this entry.
gain:RtpcCompatibleValue;

/// Custom pitch (0.0..2.0) to apply to the sound. The final value will be computed with the
/// Custom pitch to apply to the sound. The final value will be computed with the
/// real pitch from the doppler effect.
pitch:RtpcCompatibleValue;
}
Expand All @@ -99,7 +99,7 @@ table RandomSchedulerCollectionEntry {
/// Special gain to apply only to this entry.
gain:RtpcCompatibleValue;

/// Custom pitch (0.0..2.0) to apply to the sound. The final value will be computed with the
/// Custom pitch to apply to the sound. The final value will be computed with the
/// real pitch from the doppler effect.
pitch:RtpcCompatibleValue;

Expand All @@ -116,7 +116,7 @@ table SequenceSchedulerCollectionEntry {
/// Special gain to apply only to this entry.
gain:RtpcCompatibleValue;

/// Custom pitch (0.0..2.0) to apply to the sound. The final value will be computed with the
/// Custom pitch to apply to the sound. The final value will be computed with the
/// real pitch from the doppler effect.
pitch:RtpcCompatibleValue;
}
Expand Down Expand Up @@ -148,14 +148,14 @@ table CollectionDefinition {
/// Overall linear gain (0.0 .. 1.0) of the collection when played back by the audio system.
gain:RtpcCompatibleValue;

/// Custom pitch (0.0..2.0) to apply to the sound. The final value will be computed with the
/// Custom pitch to apply to the sound. The final value will be computed with the
/// real pitch from the doppler effect.
pitch:RtpcCompatibleValue;

/// ID of the bus this collection should be played on.
bus:uint64;

/// Set of samples from which a weighted random selection is performed to
/// Set of samples from which a scheduler is executed to
/// determine which sample to play when Sound playback is triggered.
sounds:[CollectionEntry];

Expand Down
7 changes: 6 additions & 1 deletion schemas/sound_definition.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ table SoundDefinition {
/// Linear gain (0.0 .. 1.0) of the sound when played back by the audio system.
gain:RtpcCompatibleValue;

/// Custom pitch (0.0..2.0) to apply to the sound. The final value will be computed with the
/// Near Field Effect gain (0.0 .. 1.0) of the sound. This value is used by the near
/// field effect node in the Pipeline, to apply effects relative to sound source near the
/// listener (<1m).
near_field_gain:RtpcCompatibleValue;

/// Custom pitch to apply to the sound. The final value will be computed with the
/// real pitch from the doppler effect.
pitch:RtpcCompatibleValue;

Expand Down
3 changes: 2 additions & 1 deletion schemas/switch_container_definition.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum SwitchContainerUpdateBehavior: byte {
/// Update sounds only on play requests. Previous play requests will still
/// have the previous switch state.
UpdateOnPlay,

/// Update sounds on each switch state change.
UpdateOnChange,
}
Expand Down Expand Up @@ -48,7 +49,7 @@ table SwitchContainerEntry {
/// A custom gain to apply only on this object.
gain:RtpcCompatibleValue;

/// Custom pitch (0.0..2.0) to apply to the sound. The final value will be computed with the
/// Custom pitch to apply to the sound. The final value will be computed with the
/// real pitch from the doppler effect.
pitch:RtpcCompatibleValue;

Expand Down
11 changes: 11 additions & 0 deletions src/Core/Listener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ namespace SparkyStudios::Audio::Amplitude
return _state->GetDirectivitySharpness();
}

const AmMat4& Listener::GetInverseMatrix() const
{
AMPLITUDE_ASSERT(Valid());
return _state->GetInverseMatrix();
}

const AmVec3& Listener::GetLocation() const
{
AMPLITUDE_ASSERT(Valid());
Expand Down Expand Up @@ -108,4 +114,9 @@ namespace SparkyStudios::Audio::Amplitude
AMPLITUDE_ASSERT(Valid());
_state->Update();
}

ListenerInternalState* Listener::GetState() const
{
return _state;
}
} // namespace SparkyStudios::Audio::Amplitude
4 changes: 3 additions & 1 deletion src/Core/Playback/ChannelInternalState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,8 @@ namespace SparkyStudios::Audio::Amplitude
if (_activeListener.Valid())
{
_hrtfContext.m_PreviousDirection = _hrtfContext.m_CurrentDirection;
_hrtfContext.m_CurrentDirection = AM_Mul(_activeListener.GetState()->GetInverseMatrix(), AM_V4V(GetLocation() - _activeListener.GetLocation(), 1.0f)).XYZ;
_hrtfContext.m_CurrentDirection =
AM_Mul(_activeListener.GetState()->GetInverseMatrix(), AM_V4V(GetLocation() - _activeListener.GetLocation(), 1.0f)).XYZ;
_hrtfContext.m_CurrentDirection = AM_Norm(_hrtfContext.m_CurrentDirection);

_hrtfContext.m_PreviousGain = _hrtfContext.m_CurrentGain;
Expand Down Expand Up @@ -716,6 +717,7 @@ namespace SparkyStudios::Audio::Amplitude
settings.m_spatialization = definition->spatialization();
settings.m_priority = _switchContainer->GetPriority();
settings.m_gain = item.m_gain;
settings.m_nearFieldGain = sound->GetNearFieldGain();
settings.m_pitch = item.m_pitch;
settings.m_loop = sound->IsLoop();
settings.m_loopCount = sound->GetDefinition()->loop()->loop_count();
Expand Down
3 changes: 3 additions & 0 deletions src/DSP/AudioConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ namespace SparkyStudios::Audio::Amplitude
if (_srcInitialized)
_resampler->SetSampleRate(sourceSampleRate, targetSampleRate);
else
{
_resampler->Initialize(_settings.m_targetChannelCount, sourceSampleRate, targetSampleRate);
_srcInitialized = true;
}
}

AmUInt64 AudioConverter::GetRequiredInputFrameCount(AmUInt64 outputFrameCount) const
Expand Down
141 changes: 141 additions & 0 deletions src/DSP/Delay.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (c) 2024-present Sparky Studios. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <DSP/Delay.h>

namespace SparkyStudios::Audio::Amplitude
{
Delay::Delay(AmSize maxDelay, AmSize framesCount)
: _maxDelay(maxDelay)
, _framesCount(framesCount)
, _writePos(0)
{
AMPLITUDE_ASSERT(_framesCount > 0);
SetMaxDelay(maxDelay);
}

Delay::~Delay()
{
_buffer.reset(nullptr);
}

void Delay::SetMaxDelay(AmSize maxDelay)
{
_maxDelay = maxDelay;
const AmSize newFramesCount = _framesCount + _maxDelay;

if (_buffer == nullptr)
{
_buffer.reset(ampoolnew(MemoryPoolKind::Filtering, AudioBuffer, newFramesCount, 1));
_buffer->Clear();
return;
}

AudioBufferChannel* channel = &_buffer->GetChannel(0);
const AmSize oldFramesCount = _buffer->GetFrameCount();

if (newFramesCount > oldFramesCount)
{
AmUniquePtr<MemoryPoolKind::Filtering, AudioBuffer> newBuffer(
ampoolnew(MemoryPoolKind::Filtering, AudioBuffer, newFramesCount, 1));
newBuffer->Clear();

std::copy(channel->begin() + _writePos, channel->end(), newBuffer->GetChannel(0).begin());

if (_writePos > 0)
{
std::copy(channel->begin(), channel->begin() + _writePos, newBuffer->GetChannel(0).begin() + oldFramesCount - _writePos);
_writePos = oldFramesCount;
}

_buffer = std::move(newBuffer);
}
}

AmSize Delay::GetMaxDelay() const
{
return _maxDelay;
}

AmSize Delay::GetDelayInSamples() const
{
return _buffer->GetFrameCount();
}

void Delay::Clear()
{
_buffer->Clear();
}

void Delay::Insert(const AudioBufferChannel& channel)
{
AMPLITUDE_ASSERT(_buffer != nullptr);
AMPLITUDE_ASSERT(channel.size() == _framesCount);

const AmSize delayBufferSize = _buffer->GetFrameCount();

// Record the remaining space in the _buffer after the write cursor.
const AmSize remainingSizeWrite = delayBufferSize - _writePos;
AudioBufferChannel* delayChannel = &(*_buffer)[0];

// Copy the channel into the delay line.
if (remainingSizeWrite >= _framesCount)
{
AMPLITUDE_ASSERT(delayChannel->begin() + _writePos + _framesCount <= delayChannel->end());
std::copy(channel.begin(), channel.end(), delayChannel->begin() + _writePos);
}
else
{
AMPLITUDE_ASSERT(delayChannel->begin() + _writePos + remainingSizeWrite <= delayChannel->end());
AMPLITUDE_ASSERT(channel.begin() + remainingSizeWrite <= channel.end());
std::copy(channel.begin(), channel.begin() + remainingSizeWrite, delayChannel->begin() + _writePos);
AMPLITUDE_ASSERT(delayChannel->begin() + remainingSizeWrite <= delayChannel->end());
std::copy(channel.begin() + remainingSizeWrite, channel.end(), delayChannel->begin());
}

_writePos = (_writePos + _framesCount) % delayBufferSize;
}

void Delay::Process(AudioBufferChannel& channel, AmSize delaySamples)
{
AMPLITUDE_ASSERT(_buffer != nullptr);
AMPLITUDE_ASSERT(delaySamples >= 0U);
AMPLITUDE_ASSERT(delaySamples <= _maxDelay);

const AmSize delayBufferSize = _buffer->GetFrameCount();
// Position in the delay line to begin reading from.
AMPLITUDE_ASSERT(_writePos + delayBufferSize >= delaySamples + _framesCount);
const AmSize readCursor = (_writePos + delayBufferSize - delaySamples - _framesCount) % delayBufferSize;
// Record the remaining space in the _buffer after the read cursor.
const AmSize remainingSizeRead = delayBufferSize - readCursor;
AudioBufferChannel* delayChannel = &(*_buffer)[0];

// Extract a portion of the delay line into the channel.
if (remainingSizeRead >= _framesCount)
{
AMPLITUDE_ASSERT(channel.begin() + _framesCount <= channel.end());
AMPLITUDE_ASSERT(delayChannel->begin() + readCursor + _framesCount <= delayChannel->end());
std::copy(delayChannel->begin() + readCursor, delayChannel->begin() + readCursor + _framesCount, channel.begin());
}
else
{
AMPLITUDE_ASSERT(channel.begin() + delayChannel->size() - readCursor <= channel.end());
std::copy(delayChannel->begin() + readCursor, delayChannel->end(), channel.begin());

AMPLITUDE_ASSERT(channel.begin() + _framesCount <= channel.end());
AMPLITUDE_ASSERT(delayChannel->begin() + _framesCount - remainingSizeRead <= delayChannel->end());
std::copy(delayChannel->begin(), delayChannel->begin() + _framesCount - remainingSizeRead, channel.begin() + remainingSizeRead);
}
}
} // namespace SparkyStudios::Audio::Amplitude
Loading

0 comments on commit c82832c

Please sign in to comment.