Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Plugin Pin Connector #7459

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
93884a2
Add PluginPortConfig class
messmerd May 8, 2024
f1f1010
Avoid misleading types and weird casts
messmerd May 8, 2024
c78c17f
Use PluginPortConfig for mono VSTs
messmerd May 8, 2024
4da60af
Refactor; Fix automation clip tooltip
messmerd Jun 16, 2024
798e088
Begin pin connector implementation (WIP)
messmerd Aug 11, 2024
af4d6b3
Loading/saving (WIP)
messmerd Aug 11, 2024
aafe41c
More efficient loading
messmerd Aug 11, 2024
360eca6
Use new routing methods (WIP)
messmerd Aug 11, 2024
7b7f674
Progress (WIP)
messmerd Aug 12, 2024
1568d3a
Merge branch 'master' into pin-connector
messmerd Aug 12, 2024
5f31c04
Fix up post-merge (WIP)
messmerd Aug 12, 2024
853d80c
Fix crash
messmerd Aug 14, 2024
7935718
Begin implementing PluginPinConnectorView
messmerd Aug 14, 2024
b3ad72a
Progress (WIP)
messmerd Aug 14, 2024
b55bdec
Fix pin icon while dragging
messmerd Aug 14, 2024
bda93bc
More progress
messmerd Aug 15, 2024
15f395e
Draw plugin channel text
messmerd Aug 15, 2024
8d2acad
Fix Linux build
messmerd Aug 15, 2024
a00cdba
Some refactoring
messmerd Aug 16, 2024
c98353f
Fix build
messmerd Aug 16, 2024
774e3b6
Introduce MatrixView class
messmerd Aug 17, 2024
e768a58
Use layouts
messmerd Aug 17, 2024
2ee2c08
Make PluginPinConnector::Matrix a struct; Add tooltips
messmerd Aug 17, 2024
8445f2c
Set model display names
messmerd Aug 17, 2024
ecc6318
Fix a display bug
messmerd Aug 17, 2024
a317e16
Remove some unused code
messmerd Aug 17, 2024
2571547
Fix crash when reloading project with pin connector open
messmerd Aug 17, 2024
ada15d3
Fix miscalculation in mousePressEvent
messmerd Aug 18, 2024
a308725
Better pin connector button text; Better naming and comments
messmerd Aug 25, 2024
09eb59f
Use pointing hand cursor when hovering over cells
messmerd Aug 25, 2024
f6910c6
Make routing methods const
messmerd Sep 14, 2024
ba3ffc6
Improve routing normalization
messmerd Sep 14, 2024
af5c16b
Use bitwise OR
messmerd Sep 14, 2024
32557d4
Move some pin connector methods into Matrix
messmerd Sep 14, 2024
1716186
Use `const float*` for process's in buffer
messmerd Sep 14, 2024
23fd48b
Rename `m_trackChannelsUsed` to `m_trackChannelsUpperBound`
messmerd Sep 21, 2024
089ffb4
Merge branch 'master' into pin-connector
messmerd Sep 21, 2024
62efd0e
Various changes
messmerd Sep 23, 2024
b7883f1
Use `MAXIMUM_BUFFER_SIZE`
messmerd Sep 23, 2024
bd404df
Revert strongly typed sample types
messmerd Oct 16, 2024
dcf1fc9
Minor optimization in `mixInputs` lambda
messmerd Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions include/AudioData.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* AudioData.h - Audio data types
*
* Copyright (c) 2024 Dalton Messmer <messmer.dalton/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#ifndef LMMS_AUDIO_DATA_H
#define LMMS_AUDIO_DATA_H

#include <type_traits>

#include "lmms_basics.h"

namespace lmms
{

//! Conventions for passing audio data
enum class AudioDataLayout
{
/**
* 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 and integer audio data types
* which documents the data layout.
*
* For example, `const InterleavedSampleType<sample_t>*` can be used as a replacement for `const sample_t*`
* parameters in order to document that the data layout of the audio is interleaved.
*/
template<AudioDataLayout layout, typename T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
using SampleType = T;

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

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


/**
* A span for storing audio data of a particular layout.
*
* All data is contiguous in memory.
* The size should be equal to the frame count * the channel count.
*/
template<AudioDataLayout layout, typename T>
using AudioBufferView = Span<SampleType<layout, T>>;

template<typename T>
using SplitAudioBufferView = AudioBufferView<AudioDataLayout::Split, T>;

template<typename T>
using InterleavedAudioBufferView = AudioBufferView<AudioDataLayout::Interleaved, T>;


} // namespace lmms

#endif // LMMS_AUDIO_DATA_H
4 changes: 2 additions & 2 deletions include/DspEffectLibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ namespace lmms::DspEffectLibrary
rightFX().setGain(gain);
}

void nextSample(SampleFrame & in)
void nextSample(SampleFrame& in)
{
nextSample(in.left(), in.right());
nextSample(in.leftRef(), in.rightRef());
}

void nextSample( sample_t& inLeft, sample_t& inRight )
Expand Down
163 changes: 163 additions & 0 deletions include/PluginPinConnector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* PluginPinConnector.h - Specifies how to route audio channels
* in and out of a plugin.
*
* Copyright (c) 2024 Dalton Messmer <messmer.dalton/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#ifndef LMMS_PLUGIN_PIN_CONNECTOR_H
#define LMMS_PLUGIN_PIN_CONNECTOR_H

#include <vector>

#include "AutomatableModel.h"
#include "lmms_export.h"
#include "SampleFrame.h"
#include "SerializingObject.h"

class QWidget;

namespace lmms
{

namespace gui
{

class PluginPinConnectorView;

} // namespace gui

//! Configuration for audio channel routing in/out of plugin
class LMMS_EXPORT PluginPinConnector
: public Model
, public SerializingObject
{
Q_OBJECT

public:
//! [track channel][plugin channel]
using PinMap = std::vector<std::vector<BoolModel*>>;
JohannesLorenz marked this conversation as resolved.
Show resolved Hide resolved
JohannesLorenz marked this conversation as resolved.
Show resolved Hide resolved

//! A plugin's input or output connections and other info
class Matrix
{
public:
auto pins() const -> const PinMap& { return m_pins; }

auto channelCount() const -> int { return m_channelCount; }

auto channelName(int channel) const -> QString;

auto enabled(std::uint8_t trackChannel, unsigned pluginChannel) const -> bool
{
return m_pins[trackChannel][pluginChannel]->value();
}

friend class PluginPinConnector;

private:
void setTrackChannelCount(PluginPinConnector* parent, int count, const QString& nameFormat);
void setPluginChannelCount(PluginPinConnector* parent, int count, const QString& nameFormat);

void setDefaultConnections();

void saveSettings(QDomDocument& doc, QDomElement& elem) const;
void loadSettings(const QDomElement& elem);

PinMap m_pins;
int m_channelCount = 0;
std::vector<QString> m_channelNames; //!< optional
};

PluginPinConnector(Model* parent = nullptr);
PluginPinConnector(int pluginChannelCountIn, int pluginChannelCountOut, Model* parent = nullptr);

/**
* Getters
*/
auto in() const -> const Matrix& { return m_in; }
auto out() const -> const Matrix& { return m_out; }
auto trackChannelCount() const -> std::size_t { return s_totalTrackChannels; }

/**
* Setters
*/
void setPluginChannelCounts(int inCount, int outCount);
void setPluginChannelCountIn(int inCount);
void setPluginChannelCountOut(int outCount);

void setDefaultConnections();

/*
* Routes audio from LMMS track channels to plugin inputs according to the plugin pin connector configuration.
*
* Iterates through each output channel, mixing together all input audio routed to the output channel.
* If no audio is routed to an output channel, the output channel's buffer is zeroed.
*
* `frames` : number of frames in each `in`/`out` audio buffer
* `in` : track channels from LMMS core (currently just the main track channel pair)
* `out` : plugin input channels in Split form
*/
void routeToPlugin(f_cnt_t frames, CoreAudioBufferView in, SplitAudioBufferView<sample_t> out) const;

/*
* Routes audio from plugin outputs to LMMS track channels according to the plugin pin connector configuration.
*
* Iterates through each output channel, mixing together all input audio routed to the output channel.
* If no audio is routed to an output channel, `inOut` remains unchanged for audio bypass.
*
* `frames` : number of frames in each `in`/`out` audio buffer
* `in` : plugin output channels in Split form
* `inOut` : track channels from/to LMMS core (inplace processing)
*/
void routeFromPlugin(f_cnt_t frames, SplitAudioBufferView<const sample_t> in, CoreAudioBufferViewMut inOut) const;

/**
* SerializingObject implementation
*/
void saveSettings(QDomDocument& doc, QDomElement& elem) override;
void loadSettings(const QDomElement& elem) override;
auto nodeName() const -> QString override { return "pins"; }

auto instantiateView(QWidget* parent = nullptr) -> gui::PluginPinConnectorView*;
auto getChannelCountText() const -> QString;

static constexpr std::size_t MaxTrackChannels = 256; // TODO: Move somewhere else

public slots:
void setTrackChannelCount(int count);

private:
Matrix m_in; //!< LMMS --> Plugin
Matrix m_out; //!< Plugin --> LMMS

//! TODO: Move this somewhere else; Will be >= 2 once there is support for adding new track channels
static constexpr std::size_t s_totalTrackChannels = DEFAULT_CHANNELS;

//! This value is <= to the total number of track channels (currently always 2)
unsigned int m_trackChannelsUpperBound = DEFAULT_CHANNELS;

// TODO: When full routing is added, get LMMS channel counts from bus or router class
};

} // namespace lmms

#endif // LMMS_PLUGIN_PIN_CONNECTOR_H
82 changes: 82 additions & 0 deletions include/PluginPinConnectorView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* PluginPinConnectorView.h - Displays pin connectors
*
* Copyright (c) 2024 Dalton Messmer <messmer.dalton/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/

#ifndef LMMS_GUI_PLUGIN_PIN_CONNECTOR_VIEW_H
#define LMMS_GUI_PLUGIN_PIN_CONNECTOR_VIEW_H

#include <QWidget>

#include "embed.h"
#include "lmms_export.h"
#include "ModelView.h"

class QPixmap;
//class QScrollArea;

namespace lmms
{

class BoolModel;
class PluginPinConnector;

namespace gui
{

class SubWindow;

class LMMS_EXPORT PluginPinConnectorView
: public QWidget
, public ModelView
{
Q_OBJECT

public:
PluginPinConnectorView(PluginPinConnector* model, QWidget* parent);

auto sizeHint() const -> QSize override;
auto minimumSizeHint() const -> QSize override;

void toggleVisibility();
void closeWindow();

protected:
void paintEvent(QPaintEvent* pe) override;

private:
class MatrixView;

void updateGeometry();

SubWindow* m_subWindow = nullptr;
//QScrollArea* m_scrollArea = nullptr;

MatrixView* m_inView = nullptr;
MatrixView* m_outView = nullptr;
};

} // namespace gui

} // namespace lmms

#endif // LMMS_GUI_PLUGIN_PIN_CONNECTOR_VIEW_H
Loading