Skip to content

Commit

Permalink
Replace AudioDataLayout with bool
Browse files Browse the repository at this point in the history
  • Loading branch information
messmerd committed Jan 27, 2025
1 parent 16767ad commit a2915e5
Show file tree
Hide file tree
Showing 11 changed files with 47 additions and 90 deletions.
50 changes: 6 additions & 44 deletions include/AudioData.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,56 +46,18 @@ enum class AudioDataKind : std::uint8_t

namespace detail {

//! Specialize this struct to enable
//! Specialize this struct to enable the use of an audio data kind
template<AudioDataKind kind> struct AudioDataType;

template<> struct AudioDataType<AudioDataKind::F32> { using type = float; };

} // namespace detail


//! Metafunction to convert `AudioDataKind` to its type
template<AudioDataKind kind>
using GetAudioDataType = typename detail::AudioDataType<kind>::type;

//! Conventions for passing audio data
enum class AudioDataLayout : bool
{
/**
* Given:
* - N == Frame count
* - C == Number of channels
* - i == Sample index, where 0 <= i < N
* - `samples` has the type sample_t*
* - `samples` size == N * C
*/

/**
* Layout where the samples for each channel are interleaved.
* i.e. "LRLRLRLR"
*
* Samples for individual channels can be accessed like this:
* - Channel #0 samples: samples[C*i]
* - Channel #1 samples: samples[C*i + 1]
* - Channel #2 samples: samples[C*i + 2]
* - Channel #3 samples: samples[C*i + 3]
* - ...
*/
Interleaved,

/**
* Layout where all samples for a particular channel are grouped together.
* i.e. "LLLLRRRR"
*
* Samples for individual channels can be accessed like this:
* - Channel #0 samples: samples[i]
* - Channel #1 samples: samples[1*N + i]
* - Channel #2 samples: samples[2*N + i]
* - Channel #3 samples: samples[3*N + i]
* - ...
*/
Split
};


/**
* A simple type alias for floating point audio data types which documents the data layout.
Expand All @@ -105,14 +67,14 @@ enum class AudioDataLayout : bool
*
* NOTE: Can add support for integer sample types later
*/
template<AudioDataLayout layout, typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
template<bool interleaved, typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using SampleType = T;

template<typename T>
using SplitSampleType = SampleType<AudioDataLayout::Split, T>;
using SplitSampleType = SampleType<false, T>;

template<typename T>
using InterleavedSampleType = SampleType<AudioDataLayout::Interleaved, T>;
using InterleavedSampleType = SampleType<true, T>;


//! Use when the number of channels is not known at compile time
Expand All @@ -124,7 +86,7 @@ inline constexpr int DynamicChannelCount = -1;
*
* TODO C++23: Use std::mdspan
*/
template<typename SampleT, int channelCount = DynamicChannelCount, typename = SplitSampleType<SampleT>>
template<typename SampleT, int channelCount = DynamicChannelCount>
class SplitAudioData
{
public:
Expand Down
16 changes: 8 additions & 8 deletions include/AudioPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,8 @@ template<AudioPluginConfig config, class AudioPortT>
class AudioPlugin<Instrument, config, AudioPortT>
: public Instrument
, public AudioProcessingMethod<Instrument,
typename AudioDataViewSelector<config.kind, config.layout, config.inputs, false>::type,
typename AudioDataViewSelector<config.kind, config.layout, config.inputs, true>::type,
typename AudioDataViewSelector<config.kind, config.interleaved, config.inputs, false>::type,
typename AudioDataViewSelector<config.kind, config.interleaved, config.inputs, true>::type,
config.inplace, AudioPortT::provideProcessBuffers()>
{
public:
Expand Down Expand Up @@ -242,8 +242,8 @@ template<AudioPluginConfig config, class AudioPortT>
class AudioPlugin<Effect, config, AudioPortT>
: public Effect
, public AudioProcessingMethod<Effect,
typename AudioDataViewSelector<config.kind, config.layout, config.inputs, false>::type,
typename AudioDataViewSelector<config.kind, config.layout, config.inputs, true>::type,
typename AudioDataViewSelector<config.kind, config.interleaved, config.inputs, false>::type,
typename AudioDataViewSelector<config.kind, config.interleaved, config.inputs, true>::type,
config.inplace, AudioPortT::provideProcessBuffers()>
{
public:
Expand Down Expand Up @@ -360,8 +360,8 @@ class AudioPlugin<Effect, config, AudioPortT>
* interfaces with LMMS Core.
*
* This design allows for some compile-time customization over aspects of the plugin implementation
* such as the number of in/out channels and the audio data layout, so plugin developers can
* implement their plugin in whatever way works best for them. All the mapping from their plugin to/from
* such as the number of in/out channels and whether samples are interleaved, so plugin developers can
* implement their plugin in whatever way works best for them. All the mapping of their plugin to/from
* LMMS Core is handled here, at compile-time where possible for best performance.
*
* A `processImpl` interface method is provided which must be implemented by the plugin implementation.
Expand Down Expand Up @@ -394,14 +394,14 @@ class AudioPlugin
// NOTE: NotePlayHandle-based instruments are not supported yet
using DefaultMidiInstrument = AudioPlugin<Instrument, AudioPluginConfig {
.kind = AudioDataKind::SampleFrame,
.layout = AudioDataLayout::Interleaved,
.interleaved = true,
.inputs = 0,
.outputs = 2,
.inplace = true }>;

using DefaultEffect = AudioPlugin<Effect, AudioPluginConfig {
.kind = AudioDataKind::SampleFrame,
.layout = AudioDataLayout::Interleaved,
.interleaved = true,
.inputs = 2,
.outputs = 2,
.inplace = true }>;
Expand Down
25 changes: 12 additions & 13 deletions include/AudioPluginBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,22 @@ namespace lmms
{


namespace detail
{
namespace detail {

/**
* Metafunction to select the appropriate non-owning audio buffer view
* given the layout, sample type, and channel count
*/
template<AudioDataKind kind, AudioDataLayout layout, int channels, bool isConst>
template<AudioDataKind kind, bool interleaved, int channels, bool isConst>
struct AudioDataViewSelector
{
static_assert(always_false_v<AudioDataViewSelector<kind, layout, channels, isConst>>,
static_assert(always_false_v<AudioDataViewSelector<kind, interleaved, channels, isConst>>,
"Unsupported audio data type");
};

//! Non-interleaved specialization
template<AudioDataKind kind, int channels, bool isConst>
struct AudioDataViewSelector<kind, AudioDataLayout::Split, channels, isConst>
struct AudioDataViewSelector<kind, false, channels, isConst>
{
using type = SplitAudioData<
std::conditional_t<isConst, const GetAudioDataType<kind>, GetAudioDataType<kind>>,
Expand All @@ -64,7 +63,7 @@ struct AudioDataViewSelector<kind, AudioDataLayout::Split, channels, isConst>

//! SampleFrame specialization
template<int channels, bool isConst>
struct AudioDataViewSelector<AudioDataKind::SampleFrame, AudioDataLayout::Interleaved, channels, isConst>
struct AudioDataViewSelector<AudioDataKind::SampleFrame, true, channels, isConst>
{
static_assert(channels == 0 || channels == 2,
"Plugins using SampleFrame buffers must have exactly 0 or 2 inputs or outputs");
Expand All @@ -84,10 +83,10 @@ class AudioPluginBufferInterface<config, false>
virtual ~AudioPluginBufferInterface() = default;

virtual auto inputBuffer()
-> typename detail::AudioDataViewSelector<config.kind, config.layout, config.inputs, false>::type = 0;
-> typename detail::AudioDataViewSelector<config.kind, config.interleaved, config.inputs, false>::type = 0;

virtual auto outputBuffer()
-> typename detail::AudioDataViewSelector<config.kind, config.layout, config.outputs, false>::type = 0;
-> typename detail::AudioDataViewSelector<config.kind, config.interleaved, config.outputs, false>::type = 0;

virtual auto frames() const -> fpp_t = 0;

Expand All @@ -102,7 +101,7 @@ class AudioPluginBufferInterface<config, true>
virtual ~AudioPluginBufferInterface() = default;

virtual auto inputOutputBuffer()
-> typename detail::AudioDataViewSelector<config.kind, config.layout, config.inputs, false>::type = 0;
-> typename detail::AudioDataViewSelector<config.kind, config.interleaved, config.inputs, false>::type = 0;

virtual auto frames() const -> fpp_t = 0;

Expand All @@ -112,12 +111,12 @@ class AudioPluginBufferInterface<config, true>

//! Default implementation of `AudioPluginBufferInterface`
template<AudioPluginConfig config,
AudioDataKind kind = config.kind, AudioDataLayout layout = config.layout, bool inplace = config.inplace>
AudioDataKind kind = config.kind, bool interleaved = config.interleaved, bool inplace = config.inplace>
class AudioPluginBufferDefaultImpl;

//! Specialization for non-inplace, non-interleaved buffers
template<AudioPluginConfig config, AudioDataKind kind>
class AudioPluginBufferDefaultImpl<config, kind, AudioDataLayout::Split, false>
class AudioPluginBufferDefaultImpl<config, kind, false, false>
: public AudioPluginBufferInterface<config>
{
static constexpr bool s_hasStaticChannelCount
Expand Down Expand Up @@ -202,7 +201,7 @@ class AudioPluginBufferDefaultImpl<config, kind, AudioDataLayout::Split, false>

//! Specialization for inplace, non-interleaved buffers
template<AudioPluginConfig config, AudioDataKind kind>
class AudioPluginBufferDefaultImpl<config, kind, AudioDataLayout::Split, true>
class AudioPluginBufferDefaultImpl<config, kind, false, true>
: public AudioPluginBufferInterface<config>
{
static constexpr bool s_hasStaticChannelCount
Expand Down Expand Up @@ -281,7 +280,7 @@ class AudioPluginBufferDefaultImpl<config, kind, AudioDataLayout::Split, true>

//! Specialization for 2-channel SampleFrame buffers
template<AudioPluginConfig config>
class AudioPluginBufferDefaultImpl<config, AudioDataKind::SampleFrame, AudioDataLayout::Interleaved, true>
class AudioPluginBufferDefaultImpl<config, AudioDataKind::SampleFrame, true, true>
: public AudioPluginBufferInterface<config>
{
public:
Expand Down
4 changes: 2 additions & 2 deletions include/AudioPluginConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ struct AudioPluginConfig
//! The audio data type used by the plugin
AudioDataKind kind;

//! The audio data layout used by the plugin
AudioDataLayout layout;
//! The audio data layout used by the plugin: interleaved or non-interleaved
bool interleaved;

//! The number of plugin input channels, or `DynamicChannelCount` if unknown at compile time
int inputs = DynamicChannelCount;
Expand Down
20 changes: 10 additions & 10 deletions include/PluginPinConnector.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,16 +199,16 @@ class LMMS_EXPORT PluginPinConnector
* `inOut` : track channels from/to LMMS core
* `inOut.frames` provides the number of frames in each `in`/`inOut` audio buffer
*/
template<AudioPluginConfig config, AudioDataKind kind = config.kind, AudioDataLayout layout = config.layout>
template<AudioPluginConfig config, AudioDataKind kind = config.kind, bool interleaved = config.interleaved>
class Router
{
static_assert(always_false_v<Router<config, kind, layout>>,
static_assert(always_false_v<Router<config, kind, interleaved>>,
"A router for the requested configuration is not implemented yet");
};

//! Non-`SampleFrame` routing
template<AudioPluginConfig config, AudioDataKind kind>
class Router<config, kind, AudioDataLayout::Split>
class Router<config, kind, false>
{
using SampleT = GetAudioDataType<kind>;

Expand All @@ -224,7 +224,7 @@ class LMMS_EXPORT PluginPinConnector

//! `SampleFrame` routing
template<AudioPluginConfig config>
class Router<config, AudioDataKind::SampleFrame, AudioDataLayout::Interleaved>
class Router<config, AudioDataKind::SampleFrame, true>
{
public:
explicit Router(const PluginPinConnector& parent) : m_pc{&parent} {}
Expand Down Expand Up @@ -316,7 +316,7 @@ public slots:
// Non-`SampleFrame` Router out-of-class definitions

template<AudioPluginConfig config, AudioDataKind kind>
inline void PluginPinConnector::Router<config, kind, AudioDataLayout::Split>::routeToPlugin(
inline void PluginPinConnector::Router<config, kind, false>::routeToPlugin(
CoreAudioBus in, SplitAudioData<SampleT, config.inputs> out) const
{
if constexpr (config.inputs == 0) { return; }
Expand All @@ -337,7 +337,7 @@ inline void PluginPinConnector::Router<config, kind, AudioDataLayout::Split>::ro

for (std::uint32_t outChannel = 0; outChannel < out.channels(); ++outChannel)
{
SampleType<config.layout, SampleT>* outPtr = out.buffer(outChannel);
SampleType<config.interleaved, SampleT>* outPtr = out.buffer(outChannel);

for (std::uint8_t inChannelPairIdx = 0; inChannelPairIdx < inSizeConstrained; ++inChannelPairIdx)
{
Expand Down Expand Up @@ -384,7 +384,7 @@ inline void PluginPinConnector::Router<config, kind, AudioDataLayout::Split>::ro
}

template<AudioPluginConfig config, AudioDataKind kind>
inline void PluginPinConnector::Router<config, kind, AudioDataLayout::Split>::routeFromPlugin(
inline void PluginPinConnector::Router<config, kind, false>::routeFromPlugin(
SplitAudioData<const SampleT, config.outputs> in, CoreAudioBusMut inOut) const
{
if constexpr (config.outputs == 0) { return; }
Expand Down Expand Up @@ -436,7 +436,7 @@ inline void PluginPinConnector::Router<config, kind, AudioDataLayout::Split>::ro

for (pi_ch_t inChannel = 0; inChannel < in.channels(); ++inChannel)
{
const SampleType<config.layout, const SampleT>* inPtr = in.buffer(inChannel);
const SampleType<config.interleaved, const SampleT>* inPtr = in.buffer(inChannel);

if constexpr (rc == 0b11)
{
Expand Down Expand Up @@ -525,7 +525,7 @@ inline void PluginPinConnector::Router<config, kind, AudioDataLayout::Split>::ro

template<AudioPluginConfig config>
inline void PluginPinConnector::Router<config,
AudioDataKind::SampleFrame, AudioDataLayout::Interleaved>::routeToPlugin(
AudioDataKind::SampleFrame, true>::routeToPlugin(
CoreAudioBus in, CoreAudioDataMut out) const
{
if constexpr (config.inputs == 0) { return; }
Expand Down Expand Up @@ -617,7 +617,7 @@ inline void PluginPinConnector::Router<config,

template<AudioPluginConfig config>
inline void PluginPinConnector::Router<config,
AudioDataKind::SampleFrame, AudioDataLayout::Interleaved>::routeFromPlugin(
AudioDataKind::SampleFrame, true>::routeFromPlugin(
CoreAudioData in, CoreAudioBusMut inOut) const
{
if constexpr (config.outputs == 0) { return; }
Expand Down
2 changes: 1 addition & 1 deletion include/RemotePluginAudioPort.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class RemotePluginAudioPort
}

static_assert(config.kind == AudioDataKind::F32, "RemotePlugin only supports float");
static_assert(config.layout == AudioDataLayout::Split, "RemotePlugin only supports non-interleaved");
static_assert(config.interleaved == false, "RemotePlugin only supports non-interleaved");
static_assert(!config.inplace, "RemotePlugin does not support inplace processing");

auto controller() -> RemotePluginAudioPortController&
Expand Down
2 changes: 1 addition & 1 deletion plugins/Vestige/Vestige.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class VestigeInstrumentView;

constexpr auto VestigeConfig = AudioPluginConfig {
.kind = AudioDataKind::F32,
.layout = AudioDataLayout::Split
.interleaved = false
};

class VestigeInstrument
Expand Down
2 changes: 1 addition & 1 deletion plugins/VstEffect/VstEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class VstPlugin;

constexpr auto VstEffectConfig = AudioPluginConfig {
.kind = AudioDataKind::F32,
.layout = AudioDataLayout::Split
.interleaved = false
};

class VstEffect : public AudioPlugin<Effect, VstEffectConfig, RemotePluginAudioPort<VstEffectConfig>>
Expand Down
4 changes: 0 additions & 4 deletions plugins/ZynAddSubFx/ZynAddSubFx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,10 +459,6 @@ void ZynAddSubFxInstrument::initPlugin()

m_remotePlugin->updateSampleRate( Engine::audioEngine()->outputSampleRate() );

// temporary workaround until the VST synchronization feature gets stripped out of the RemotePluginClient class
// causing not to send buffer size information requests
//m_remotePlugin->sendMessage( RemotePlugin::message( IdBufferSizeInformation ).addInt( Engine::audioEngine()->framesPerPeriod() ) );

m_remotePlugin->showUI();
m_remotePlugin->unlock();
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/ZynAddSubFx/ZynAddSubFx.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class ZynAddSubFxRemotePlugin

constexpr auto ZynConfig = AudioPluginConfig {
.kind = AudioDataKind::F32,
.layout = AudioDataLayout::Split,
.interleaved = false,
.inputs = 0,
.outputs = 2
};
Expand Down
Loading

0 comments on commit a2915e5

Please sign in to comment.