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

Pin connector refactor #2

Merged
merged 45 commits into from
Dec 14, 2024
Merged

Pin connector refactor #2

merged 45 commits into from
Dec 14, 2024

Conversation

messmerd
Copy link
Owner

@messmerd messmerd commented Dec 5, 2024

Major refactor of audio plugin implementations to add support for the pin connector to MIDI-based instrument plugins and all effect plugins. Also refactors parts of the pin connector itself.

The idea was to add a new class template called AudioPluginInterface which audio plugins can inherit from rather than inheriting directly from Instrument or Effect. This class template uses template parameters to allow compile-time customization for plugins (i.e. how many inputs, how many outputs, whether in-place processing is used, etc.) and is responsible for owning and using the pin connector and its routing capabilities. AudioPluginInterface is entirely optional - a plugin implementation could choose to continue inheriting from Instrument or Effect directly and all it would mean is no support for the pin connector.

One of the challenges along the way was how to deal with different working buffers used by plugins. The size, data type, and data layout differ depending on plugin characteristics such as how many in/out channels there are, whether in-place processing is used, etc. But also, VSTs and ZynAddSubFX cannot just use any buffer - they use a shared memory buffer.

The solution I came up with is to introduce AudioPluginBufferInterface which uses AudioPluginInterface's compile-time customization options to customize its itself based on the needs of the plugin implementation. AudioPluginBufferDefaultImpl is provided which implements the interface in a way that works for most plugins. For VST and ZynAddSubFX, they specifically request (via the customBuffer option) to implement the buffer themselves and provide the audio plugin interface access to it through AudioPluginBufferInterfaceProvider. LV2, CLAP, Carla, and LADSPA could also take advantage of the custom buffers feature to reuse the same buffers they provide to their plugin APIs which would avoid extra memory and extra copying, though this work has not been done yet.

While this system is as flexible, simple, and performant as I could make it, it is not thread-safe which leads to a consequence I didn't foresee - NotePlayHandle-based instruments (which are all instruments except for Carla, CLAP, LV2, OpulenZ, Vestige, and ZynAddSubFX) are unable to use AudioPluginInterface because all the note play handles will use the same working buffer concurrently which messes up the audio. However, this isn't much of an issue since all NotePlayHandle-based instruments have exactly two fixed audio output channels, and the main pin connector use case for instruments is to provide support for a variable number of output channels or sidechain inputs. Pin connector support for NotePlayHandle-based instruments could potentially be added in the future, though it probably wouldn't be worth it.

This PR also adds unit tests for the pin connector.

@JohannesLorenz
Copy link

Some developers had already proposed that using potentially different threads for different note play handles is overkill. This might even be changed when we make LMMS realtime safe. I assume in that case, thee instruments would be able to use AudioPluginInterface?

Pin connector support can be added in later. I think it's best to hold
off for now since Carla and Lv2 do not implement support for multiple
plugin channels yet, which could complicate upgrading project files
created between now and when that support is added.

Also cleaned up the plugin headers, removing some unecessary changes
@messmerd
Copy link
Owner Author

@JohannesLorenz Yes, I'm not aware of anything that would prevent AudioPluginInterface from working with NotePlayHandle-based instruments if all the note play handles were to use the same thread.

@messmerd messmerd merged commit 6e1d921 into pin-connector Dec 14, 2024
13 of 20 checks passed
Copy link

@JohannesLorenz JohannesLorenz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional code review finished 🎉

Further reviews (style/testing/2nd functional) shall only be done on the base PR IMO.

Technical scope: The scope is only this PR, not the base PR. The base PR will have a separate review.

@@ -195,7 +189,7 @@ class CARLABASE_EXPORT CarlaInstrument : public Instrument
QString nodeName() const override;
void saveSettings(QDomDocument& doc, QDomElement& parent) override;
void loadSettings(const QDomElement& elem) override;
void play(SampleFrame* workingBuffer) override;
void playImpl(CoreAudioDataMut out) override;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there even a play function that would cause conflicts? If not, play would be shorter and possibly easier to understand for many (The name change also requires changing comments that referenced play(), e.g. the one above the fMutex variable.).

Even if this does conflict with play of a parent class, it might make more sense to rename that upper function and keep play in the final Plugin implementations consistent to the past.

It also looks strange because of asymmetry: saveSettings, loadSettings etc are also implementations, but not named ...Impl.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants