LMMS
Loading...
Searching...
No Matches
lmms::AudioBuffer Class Reference

#include <AudioBuffer.h>

Classes

class  ChannelGroup
 Non-owning collection of audio channels + metadata. More...

Public Types

using ChannelFlags = std::bitset<MaxChannelsPerAudioBuffer>

Public Member Functions

 AudioBuffer ()=delete
 AudioBuffer (const AudioBuffer &)=delete
 AudioBuffer (AudioBuffer &&) noexcept=default
auto operator= (const AudioBuffer &) -> AudioBuffer &=delete
auto operator= (AudioBuffer &&) noexcept -> AudioBuffer &=default
 AudioBuffer (f_cnt_t frames, ch_cnt_t channels=DEFAULT_CHANNELS, std::pmr::memory_resource *resource=std::pmr::get_default_resource())
template<class F>
 AudioBuffer (f_cnt_t frames, ch_cnt_t channels, group_cnt_t groups, std::pmr::memory_resource *resource, F &&groupVisitor)
void allocateInterleavedBuffer ()
 The presence of the temporary interleaved buffer is opt-in. Call this to create it.
auto hasInterleavedBuffer () const -> bool
auto groupCount () const -> group_cnt_t
auto group (group_cnt_t index) const -> const ChannelGroup &
auto group (group_cnt_t index) -> ChannelGroup &
auto allBuffers () const -> PlanarBufferView< const float >
auto allBuffers () -> PlanarBufferView< float >
auto groupBuffers (group_cnt_t index) const -> PlanarBufferView< const float >
auto groupBuffers (group_cnt_t index) -> PlanarBufferView< float >
auto buffer (ch_cnt_t channel) const -> std::span< const float >
auto buffer (ch_cnt_t channel) -> std::span< float >
auto totalChannels () const -> ch_cnt_t
auto frames () const -> f_cnt_t
auto interleavedBuffer () const -> InterleavedBufferView< const float, 2 >
auto interleavedBuffer () -> InterleavedBufferView< float, 2 >
auto addGroup (ch_cnt_t channels) -> ChannelGroup *
 Adds a new channel group at the end of the list.
template<class F>
void setGroups (group_cnt_t groups, F &&groupVisitor)
 Changes the channel grouping without changing the channel count. Does not reallocate any buffers.
auto silenceFlags () const -> const ChannelFlags &
void assumeNonSilent (ch_cnt_t channel)
 Forcibly pessimizes silence tracking for a specific channel.
void enableSilenceTracking (bool enabled)
auto silenceTrackingEnabled () const -> bool
void mixSilenceFlags (const AudioBuffer &other)
 Mixes the silence flags of the other AudioBuffer with this AudioBuffer.
auto hasSignal (const ChannelFlags &channels) const -> bool
auto hasAnySignal () const -> bool
 Checks whether any channel is non-silent (has a signal).
void sanitize (const ChannelFlags &channels, ch_cnt_t upperBound=MaxChannelsPerAudioBuffer)
 Sanitizes specified channels of any Inf/NaN values if "nanhandler" setting is enabled.
void sanitizeAll ()
 Sanitizes all channels.
auto updateSilenceFlags (const ChannelFlags &channels, ch_cnt_t upperBound=MaxChannelsPerAudioBuffer) -> bool
 Updates the silence status of the given channels, up to the upperBound index.
auto updateAllSilenceFlags () -> bool
 Updates the silence status of all channels.
void silenceChannels (const ChannelFlags &channels, ch_cnt_t upperBound=MaxChannelsPerAudioBuffer)
 Silences (zeroes) the given channels.
void silenceAllChannels ()
 Silences (zeroes) all channels.
auto absPeakValue (ch_cnt_t channel) const -> float

Static Public Member Functions

static auto allocationSize (f_cnt_t frames, ch_cnt_t channels, bool withInterleavedBuffer=false) -> std::size_t

Private Attributes

std::pmr::vector< float > m_sourceBuffer
std::pmr::vector< float * > m_accessBuffer
std::pmr::vector< float > m_interleavedBuffer
ArrayVector< ChannelGroup, MaxGroupsPerAudioBufferm_groups
 Divides channels into arbitrary groups.
f_cnt_t m_frames = 0
 Frame count for every channel buffer.
ChannelFlags m_silenceFlags
bool m_silenceTrackingEnabled = false

Detailed Description

An owning collection of audio channels for an instrument track, mixer channel, or audio processor.

Features:

  • Up to MaxChannelsPerAudioBuffer total channels
  • Audio data in planar format (plus a temporary interleaved buffer for conversions until we use planar only)
  • All planar buffers are sourced from the same large buffer for better cache locality
  • Custom allocator support
  • Silence tracking for each channel (NOTE: requires careful use so that non-silent data is not written to a channel marked silent without updating that channel's silence flag afterward)
  • Methods for sanitizing, silencing, and calculating the absolute peak value of channels, and doing so more efficiently using the data from silence tracking
  • Can organize channels into arbitrary groups. For example, you could have 6 total channels divided into 2 groups where the 1st group contains 2 channels (stereo) and the 2nd contains 4 channels (quadraphonic).
  • Extensive unit testing - AudioBufferTest.cpp

Audio data layout explanation:

  • All planar audio data for all channels in an AudioBuffer is sourced from the same large contiguous buffer called the source buffer (m_sourceBuffer).
  • The source buffer consists of the buffer for 1st channel followed by the buffer for the 2nd channel, and so on for all channels. In total, the number of elements is channels * frames.
  • A separate vector of non-owning pointers to channel buffers is also maintained. In this vector, each index corresponds to a channel, providing a mapping from the channel index to a pointer to the start of that channel's buffer within the source buffer. This is called the access buffer (m_accessBuffer).
  • The purpose of the access buffer is to provide channel-wise access to buffers within the source buffer, so it's m_accessBuffer[channelIdx][frameIdx] instead of m_sourceBuffer[channelIdx * frames + frameIdx]. This is very important since many APIs dealing with planar audio expect it in this float** 2D array form.
  • Groups have no effect on the audio data layout in the source/access buffers and are merely a layer built on top. Conveniently, if you take m_accessBuffer and offset it by channelIndex, you get another float** starting at that channel. This what the float** buffer stored in each ChannelGroup is.

Naming notes:

  • When this class is used in an instrument track or mixer channel, its channels could be referred to as "track channels" or "internal channels", since they are equivalent to the "track channels" used in other DAWs such as REAPER.
  • When this class is used in an audio processor or audio plugin, its channels could be referred to as "processor channels" or "plugin channels".

Member Typedef Documentation

◆ ChannelFlags

Constructor & Destructor Documentation

◆ AudioBuffer() [1/5]

lmms::AudioBuffer::AudioBuffer ( )
delete

◆ AudioBuffer() [2/5]

lmms::AudioBuffer::AudioBuffer ( const AudioBuffer & )
delete

◆ AudioBuffer() [3/5]

lmms::AudioBuffer::AudioBuffer ( AudioBuffer && )
defaultnoexcept

◆ AudioBuffer() [4/5]

lmms::AudioBuffer::AudioBuffer ( f_cnt_t frames,
ch_cnt_t channels = DEFAULT_CHANNELS,
std::pmr::memory_resource * resource = std::pmr::get_default_resource() )
explicit

Creates AudioBuffer with a 1st (main) channel group.

Silence tracking is enabled or disabled depending on the auto-quit setting.

Parameters
framesframe count for each channel
channelschannel count for the 1st group, or zero to skip adding the 1st group
resourcememory resource for all buffers

◆ AudioBuffer() [5/5]

template<class F>
lmms::AudioBuffer::AudioBuffer ( f_cnt_t frames,
ch_cnt_t channels,
group_cnt_t groups,
std::pmr::memory_resource * resource,
F && groupVisitor )
inline

Creates AudioBuffer with groups defined.

Silence tracking is enabled or disabled depending on the auto-quit setting.

Parameters
framesframe count for each channel
channelstotal channel count
groupsgroup count
resourcememory resource for all buffers
groupVisitorsee setGroups

Member Function Documentation

◆ absPeakValue()

auto lmms::AudioBuffer::absPeakValue ( ch_cnt_t channel) const -> float
Returns
absolute peak sample value for the given channel

◆ addGroup()

auto lmms::AudioBuffer::addGroup ( ch_cnt_t channels) -> ChannelGroup *

Adds a new channel group at the end of the list.

If the memory resource is SharedMemoryResource, all buffers (source, channels, and interleaved) will be reallocated. The number of bytes allocated will be allocationSize(frames(), totalChannels() + channels, hasInterleavedBuffer()).

Parameters
channelshow many channels the new group should have
Returns
the newly created group, or nullptr upon failure

◆ allBuffers() [1/2]

auto lmms::AudioBuffer::allBuffers ( ) -> PlanarBufferView< float >
inline
Returns
the buffers for all channel groups

◆ allBuffers() [2/2]

auto lmms::AudioBuffer::allBuffers ( ) const -> PlanarBufferView< const float >
inline
Returns
the buffers for all channel groups

◆ allocateInterleavedBuffer()

void lmms::AudioBuffer::allocateInterleavedBuffer ( )

The presence of the temporary interleaved buffer is opt-in. Call this to create it.

◆ allocationSize()

auto lmms::AudioBuffer::allocationSize ( f_cnt_t frames,
ch_cnt_t channels,
bool withInterleavedBuffer = false ) -> std::size_t
static
Returns
the number of bytes needed to allocate buffers with given frame and channel counts. Useful for preallocating a buffer for a shared memory resource.

◆ assumeNonSilent()

void lmms::AudioBuffer::assumeNonSilent ( ch_cnt_t channel)
inline

Forcibly pessimizes silence tracking for a specific channel.

◆ buffer() [1/2]

auto lmms::AudioBuffer::buffer ( ch_cnt_t channel) -> std::span< float >
inline
Returns
the buffer for the given channel

◆ buffer() [2/2]

auto lmms::AudioBuffer::buffer ( ch_cnt_t channel) const -> std::span< const float >
inline
Returns
the buffer for the given channel

◆ enableSilenceTracking()

void lmms::AudioBuffer::enableSilenceTracking ( bool enabled)

When silence tracking is enabled, channels will be checked for silence whenever their data may have changed, so it'll always be known whether they are silent or non-silent. There is a performance cost to this, but it is likely worth it since this information allows many effects to be put to sleep when their inputs are silent ("auto-quit"). When a channel is known to be silent, it also enables optimizations in buffer sanitization, buffer zeroing, and finding the absolute peak sample value.

When silence tracking is disabled, channels are not checked for silence, so a silence flag may be unset despite the channel being silent. Non-silence must be assumed whenever the silence status is not known, so the optimizations which silent buffers allow will not be possible as often.

◆ frames()

auto lmms::AudioBuffer::frames ( ) const -> f_cnt_t
inline
Returns
the frame count for each channel buffer

◆ group() [1/2]

auto lmms::AudioBuffer::group ( group_cnt_t index) -> ChannelGroup &
inline

◆ group() [2/2]

auto lmms::AudioBuffer::group ( group_cnt_t index) const -> const ChannelGroup &
inline

◆ groupBuffers() [1/2]

auto lmms::AudioBuffer::groupBuffers ( group_cnt_t index) -> PlanarBufferView< float >
inline
Returns
the buffers of the given channel group

◆ groupBuffers() [2/2]

auto lmms::AudioBuffer::groupBuffers ( group_cnt_t index) const -> PlanarBufferView< const float >
inline
Returns
the buffers of the given channel group

◆ groupCount()

auto lmms::AudioBuffer::groupCount ( ) const -> group_cnt_t
inline
Returns
current number of channel groups

◆ hasAnySignal()

auto lmms::AudioBuffer::hasAnySignal ( ) const -> bool

Checks whether any channel is non-silent (has a signal).

See also
hasSignal

◆ hasInterleavedBuffer()

auto lmms::AudioBuffer::hasInterleavedBuffer ( ) const -> bool
inline

◆ hasSignal()

auto lmms::AudioBuffer::hasSignal ( const ChannelFlags & channels) const -> bool

Checks whether any of the selected channels are non-silent (has a signal).

If silence tracking is disabled, all channels that aren't marked as silent are assumed to be non-silent.

A processor could check for a signal present at any of its inputs by calling this method selecting all of the track channels that are routed to at least one of its inputs.

Parameters
channelschannels to check for a signal; 1 = selected, 0 = ignore

◆ interleavedBuffer() [1/2]

auto lmms::AudioBuffer::interleavedBuffer ( ) -> InterleavedBufferView< float, 2 >
inline
Returns
scratch buffer for conversions between interleaved and planar TODO: Remove once using planar only

◆ interleavedBuffer() [2/2]

auto lmms::AudioBuffer::interleavedBuffer ( ) const -> InterleavedBufferView< const float, 2 >
inline
Returns
scratch buffer for conversions between interleaved and planar TODO: Remove once using planar only

◆ mixSilenceFlags()

void lmms::AudioBuffer::mixSilenceFlags ( const AudioBuffer & other)

Mixes the silence flags of the other AudioBuffer with this AudioBuffer.

◆ operator=() [1/2]

auto lmms::AudioBuffer::operator= ( AudioBuffer && ) -> AudioBuffer &=default
defaultnoexcept

◆ operator=() [2/2]

auto lmms::AudioBuffer::operator= ( const AudioBuffer & ) -> AudioBuffer &=delete
delete

◆ sanitize()

void lmms::AudioBuffer::sanitize ( const ChannelFlags & channels,
ch_cnt_t upperBound = MaxChannelsPerAudioBuffer )

Sanitizes specified channels of any Inf/NaN values if "nanhandler" setting is enabled.

Parameters
channelschannels to sanitize; 1 = selected, 0 = skip
upperBoundany channel indexes at or above this are skipped

◆ sanitizeAll()

void lmms::AudioBuffer::sanitizeAll ( )

Sanitizes all channels.

See also
sanitize

◆ setGroups()

template<class F>
void lmms::AudioBuffer::setGroups ( group_cnt_t groups,
F && groupVisitor )
inline

Changes the channel grouping without changing the channel count. Does not reallocate any buffers.

Parameters
groupsthe new group count
groupVisitorcalled for each new group, passed the index and group reference, and is expected to return the channel count for that group. The visitor may also set the group's metadata.

◆ silenceAllChannels()

void lmms::AudioBuffer::silenceAllChannels ( )

Silences (zeroes) all channels.

See also
silenceChannels

◆ silenceChannels()

void lmms::AudioBuffer::silenceChannels ( const ChannelFlags & channels,
ch_cnt_t upperBound = MaxChannelsPerAudioBuffer )

Silences (zeroes) the given channels.

Parameters
channelschannels to silence; 1 = selected, 0 = skip
upperBoundany channel indexes at or above this are skipped

◆ silenceFlags()

auto lmms::AudioBuffer::silenceFlags ( ) const -> const ChannelFlags &
inline

Channels which are known to be quiet, AKA the silence status. 1 = channel is known to be silent 0 = channel is assumed to be non-silent (or, when silence tracking is enabled, known to be non-silent)

NOTE: If any channel buffers are used and their data modified outside of this class, their silence flags will be invalidated until updateSilenceFlags() is called. Therefore, calling code must be careful to always keep the silence flags up-to-date.

◆ silenceTrackingEnabled()

auto lmms::AudioBuffer::silenceTrackingEnabled ( ) const -> bool
inline

◆ totalChannels()

auto lmms::AudioBuffer::totalChannels ( ) const -> ch_cnt_t
inline
Returns
the total channel count (never exceeds MaxChannelsPerAudioBuffer)

◆ updateAllSilenceFlags()

auto lmms::AudioBuffer::updateAllSilenceFlags ( ) -> bool

Updates the silence status of all channels.

See also
updateSilenceFlags

◆ updateSilenceFlags()

auto lmms::AudioBuffer::updateSilenceFlags ( const ChannelFlags & channels,
ch_cnt_t upperBound = MaxChannelsPerAudioBuffer ) -> bool

Updates the silence status of the given channels, up to the upperBound index.

Parameters
channelschannels to update; 1 = selected, 0 = skip
upperBoundany channel indexes at or above this are skipped
Returns
true if all selected channels were silent

Member Data Documentation

◆ m_accessBuffer

std::pmr::vector<float*> lmms::AudioBuffer::m_accessBuffer
private

Provides access to individual channel buffers within the source buffer.

[channel index][frame index]

◆ m_frames

f_cnt_t lmms::AudioBuffer::m_frames = 0
private

Frame count for every channel buffer.

◆ m_groups

ArrayVector<ChannelGroup, MaxGroupsPerAudioBuffer> lmms::AudioBuffer::m_groups
private

Divides channels into arbitrary groups.

◆ m_interleavedBuffer

std::pmr::vector<float> lmms::AudioBuffer::m_interleavedBuffer
private

Interleaved scratch buffer for conversions between interleaved and planar.

TODO: Remove once using planar only

◆ m_silenceFlags

ChannelFlags lmms::AudioBuffer::m_silenceFlags
private

Stores which channels are known to be quiet, AKA the silence status.

This must always be kept in sync with the buffer data when enabled - at minimum avoiding any false positives where a channel is marked as "silent" when it isn't. Any channel bits at or above totalChannels() must always be marked silent.

1 = channel is known to be silent 0 = channel is assumed to be non-silent (or, when silence tracking is enabled, known to be non-silent)

◆ m_silenceTrackingEnabled

bool lmms::AudioBuffer::m_silenceTrackingEnabled = false
private

◆ m_sourceBuffer

std::pmr::vector<float> lmms::AudioBuffer::m_sourceBuffer
private

Large buffer that all channel buffers are sourced from.

[channel index]


The documentation for this class was generated from the following files: