Open follower opened 6 years ago
Today's additional progress was what led me to decide to actually post what I've got so far...
I've managed to get multiple VST plugins to display their functional GUI within a standalone Qt application. What's particularly nice is that the approach I'm using doesn't require any Objective-C code to be written, we just need to pass around an opaque value retrieved via QWidget::winId()
.
I've got a more detailed write-up here but in summary:
// In a .ui file:
//
// <widget class="QWidget" name="vstTarget" native="true"/>
// In .cpp files:
auto wVstTarget = this->findChild<QWidget *>("vstTarget");
// ...
// Load the plugin (via QLibrary::resolve()) then...
auto winId = wVstTarget->winId();
_thePlugin->editOpen((void *) winId);
wVstTarget->setFixedWidth(_thePlugin->getWidth());
wVstTarget->setFixedHeight(_thePlugin->getHeight());
// Where `editOpen()` is roughly implemented as...
void editOpen(void * m_window) {
auto result = this->m_plugin->dispatcher(this->m_plugin, effEditOpen, 0, 0, m_window, 0);
// From LMMS:
struct ERect
{
short top;
short left;
short bottom;
short right;
} ;
ERect * er;
result = this->m_plugin->dispatcher(this->m_plugin, effEditGetRect, 0, 0, &er, 0);
if ((er->left == 0) && (er->top == 0)) {
this->_width = er->right - er->left;
this->_height = er->bottom - er->top;
}
result = this->m_plugin->dispatcher(this->m_plugin, effEditTop, 0, 0, 0, 0);
}
Apparently on Mac OS X winId
is (a pointer to) the type NSView
.
The VSTs I tested with will quite happily allow you to interact with them, load presets, etc etc. Of course, I haven't tested anything sound-related yet but this seems like good progress :)
Here's a pretty picture:
It would be good to get to a stage where there's some audio happening, so I'll probably keep hacking away at the VST plugin code base to try to get something happening with that. There's some Windows-specific thread/message-passing code in there so I'm not sure how straight-forward this stage will be.
Once I've managed to get some sound my plan is to look at re-implementing things in a less hacky manner in preparation for a PR (not for 1.2 maybe for 1.3 or 1.4). I think ideally the platform-specific code would be implemented as a (sub-)class or something but I suspect initially it'll be easiest to stick with the #ifdef
approach of the existing code.
Cross-references to related issues/PRs: https://github.com/LMMS/lmms/issues/1468#issuecomment-72732311, #698, #2393
Oh, also, I've been collecting a bunch of Mac related VST information here:
we just need to pass around an opaque value retrieved via
QWidget::winId()
.
Looks similar to the way LMMS embeds VST editors on Windows. However, LMMS uses a bridge process to launch (Windows)VSTs. The process creates a window and LMMS either embeds it or lets the VST runs in a separate window. In other words, we don't use QWidget
instance directly, for VST editor GUI.
And you may want to look into #3786.
Is anyone still working on this issue?
@Anonymouqs if you want to help you may consider reaching out to @follower directly, especially if you have the ability to help, http://rancidbacon.com/.
If you get further, here or Discord are great places to share your findings.
@PhysSong Yeah, my intention was to use the exact same method the LMMS Windows VST support uses with the bridge to a separate process.
@Anonymouqs Well, I haven't abandoned it entirely. :)
FWIW I got a bit discouraged with my LMMS development efforts due to how "unstable" the stable branch was. :) :/ (I apparently hit my "yak shaving limit", primarily due to the combination of the introduction of submodules and the change to an entirely new memory allocation system implementation that lead (potentially unnecessarily) to a new compiler version being required without warning...)
I'm still interested in enabling native Mac VST support but don't currently see myself getting back to it in the immediate future.
Feel free to work on it yourself if it's of interest--my suggestion for the next step would be to look at replacing the Windows-specific code in the bridge from LMMS to the external VST wrapper process.
@follower sorry for the stable
troubles you've been having. Both of those changes have caused a large amount of grief but were done with the importance of stability and performance in mind. They made the build process less stable
from a dependency perspective, but the effect on the binaries did not yield instability. :)
Anyway, we're always hanging out in #devtalk, #programming and #support waiting for the next question. https://lmms.io/chat
I was able to open up the windows version of LMMS on a mac using wine, however I did not actually run it, because I didn't want anything to break. This would probably let me run VeSTige. Not sure if this would help though...
This would probably let me run VeSTige.
Yes, it would!
Not sure if this would help though...
No, it wouldn't. š
Dammit. :p
Okay, fine, don't get too excited but...
Well, maybe get a little bit excited... :D
.wav
file directly...)It's all hacked together on an old version and not 100% functional and has issues but...at some level it's also Mac native VST support. :)
In the interests of "'imperfect & visible' is better than 'want to be perfect but it is not done'" what follows is a dump of the current changes (against 9fb74d9bffdc719cc8f060ad5e655635d0adfcf3). Yes, it'd be better as a branch but this was quicker. :)
I know there's been a bunch of recent VST-related code changes but I've not yet looked closely to see how it'll affect this.
While not yet completely functional there's not actually a huge amount of code changes to get this working. A bit of CMake configuration though.
If some of the Windows/WINE-specific code was changed to use Qt functionality like I've done here there could be fewer #ifdef
s around. I wondered about having classes rather than #ifdef
but since the existing code uses the latter I've stuck with that for now.
The GUI isn't yet displayed and I've not split things into threads yet either. (This is the result of "Hack-Till-It-Works (then take a screenshot)" development methodology. :D ) There's also a heap of debugging code.
You also have to rename the Foo.vst/Contents/MacOS/Foo
file to be Foo.dll
. :p
[Edit: Also, it seems like there's a crash (freeing an unallocated pointer) when deleting a pattern section for a Vestige track--not sure if that's related to my hacking around or not...]
Oh, also, I dunno if it's the same elsewhere but the shared memory use can cause problems during development if there's many crashes. These are useful commands for viewing & removing the stale shared memory:
ipcs -m -a
ipcrm
(Don't ask me how long it took me to discover that...)
Looking forward to (at some point) see a native VST implementation for Mac OS running in LMMS.
@tresf I was wondering if you/lmms have a preference in terms of integrating the changes required to make VST cross-platform? The #ifdef
approach "works" but feels pretty inelegant.
I wonder if having specific sub-classes implementing the platform-specific parts might work better in terms of keeping related code together while still sharing as much as possible. But anything other than the #ifdef
approach seems like it would require modifying existing working code on other platforms.
Seems like the potential target support matrix looks something like:
A potentially related factor is that on Mac VST plugins are "Bundles" which are directories containing a bunch of files rather than, e.g. a single .dll
as on Windows. This would mean that the project files wouldn't be entirely cross-platform compatible. I'm not sure if this would really be an issue though.
@tresf I was wondering if you/lmms have a preference in terms of integrating the changes required to make VST cross-platform? The #ifdef approach "works" but feels pretty inelegant.
Agreed. I've felt pretty strongly that the existing #ifdef
approach is difficult to read but having touched very little of the code, I don't feel qualified to make a call on this. Instead I'd defer this to @DomClark, @lukas-w and @PhysSong. Dom's been doing the most of our VST fixes lately whereas Lukas and Phys have been making the X11 stuff work properly with Qt5. Sorry to defer this one, but I think they're better judges with this codebase.
Also, somewhat related... https://lists.linuxaudio.org/archives/linux-audio-dev/2018-June/037131.html. The aeffectx.h
is under some (possibly unwarranted) scrutiny right now. Not directly on-topic, but I wanted to mention it.
A potentially related factor is that on Mac VST plugins are "Bundles" which are directories containing a bunch of files rather than, e.g. a single
.dll
as on Windows. This would mean that the project files wouldn't be entirely cross-platform compatible. I'm not sure if this would really be an issue though.
This is an excellent point. I'm not sure what you mean by "cross-platform"... but to infer by experience... I think what I'm reading is that for /Library/Audio/Plug-Ins/VST/Synth1.component
(a MacOS "package" containing a bunch of other files), Windows and Linux would (hypothetically) use C:\Program Files\Steinberg\VstPlugins\Synth1.dll
. I've never seen the remote process logic in C++ open a bundle (instead of a binary) so I can't speak to the technicalities, but we already obfuscate the plugin extensions in the Plugin and LADSPA areas of LMMS (e.g. .dll
versus .so
) so that's a good place to start. I know on the Mac desktop, it generally doesn't care if you call open LMMS.app
vs. open LMMS.app/Contents/MacOS/lmms
but how C++ handles this is not something I'm familiar with.
The way we handle VSTs cross-platform today (despite possible hard-coding of file extensions) is that we use the same antiquated LMMS WORKING DIRECTORY
concept, and it works quite well. If you can honor that, we'll be in great shape.
I don't feel I've been with the project long enough yet to prescribe a solution here, but here's my opinion, along with some details on where I'd like to take VST support in future versions:
RemoteVstPlugin.cpp
currently implements support for Windows VSTs, on both Windows and Linux. Using it for Mac VSTs on Mac doesn't fit in well with this. I think it would be a worthwhile goal to support Windows VSTs on Mac too, and we're already most of the way there - we have a Windows VST host process that runs under Wine on Linux; someone just needs to figure out how to get this to work on Mac. I recall reading that people have had success with Windows VSTs and LMMS on Mac simply by running the whole thing under Wine, so I don't doubt it is possible.One potential issue with this approach is handling 32-bit native VSTs on 64-bit Mac and Linux. I'm not sure how much of an issue this will be however - even if we don't tackle this we will already have expanded VST support a lot, and VST3, should we get that far (which I would like to), is 64-bit only if I recall correctly.
Regarding loading VST bundles on Mac, I'm not sure what kind of support Qt offers in this area (QLibrary
, etc.), but should we choose to do it directly ourselves, a quick Google search brought up this documentation from Apple. At the bottom, under "Loading Plug-in Code With Bundles", there are indications towards techniques for loading functions from bundles at run-time.
Once the stable-1.2
VST support is reasonably bug free, I'd like to move on to implementing in-process VST support, and have it work for native VSTs across Windows, Linux and Mac. I don't, however, own a Mac, nor have any experience with them, so someone familiar in that area would be invaluable in fully achieving this.
I realise this basically summarises to "I think it should be done differently to how you've done it", and I don't at all want your work to go to waste, so I will wait and let others chime in with ideas. However, should we go down the path I suggested, it would be great if you could try and get Windows VSTs going under Wine, and later on help with native in-process VSTs.
Sorry for the late response.
I agree with most points made here. The current #ifdef
approach is really hard to read and to maintain, efforts such as #2390 are needed to improve this.
Native VSTs will likely be more stable and certainly easier to implement than the remote approach. However, I think we should at least delay its implementation. I'm not sure if that's what you're suggesting @DomClark so I wanted to point this out. The remote implementation will always be there because we need it for Windows VSTs through Wine on Linux or macOS. I believe we should spend our efforts on simplifying it and reducing its complexity. Wine VSTs will likely be a very common use case for a long time to come, so it will continue to receive bug fixes and stability improvements. Adding a in-process implementation is not going to make those problems go away. If we're lucky, we manage to make the remote-process implementation so good that an in-process one offers too little benefit to be worth the added complexity. If well done, a remote-process solution can be more stable then the traditional implementation because it isolates the host from the guest (LMMS doesn't crash if the plugin crashes).
So I think we should implement native VSTs using the remote-process solution we already have ā at least for now ā because it makes for less code complexity, and thus less code paths to test and maintain.
As an FYI, I have pushed a couple of branches to my fork against a semi-recent LMMS master:
https://github.com/follower/lmms/tree/feature-vst-mac-native-3975 (see) -- This is essentially the patch from https://github.com/LMMS/lmms/issues/3975#issuecomment-373162398 updated to work with recent Qt/VST/build changes. It turned out that some of the changes actually made this patch easier/smaller! ;) Also, I figured out I could "extend"/override a CMake plugin without code duplication which reduced the patch size. So, this version results in Vestige plugin that can be used to generate sound with Mac native VST(i)s--if you dig into the .vst
bundle to select the actual executable. Presets for many (but not all) plugins are also usable.
https://github.com/follower/lmms/tree/feature-vst-mac-native-3975-hacky-wip (see) -- This is a hacked up WIP dump that builds on the above patch and also displays semi-interactive GUIs for VSTs (with non-embedded windows spawned from the remote process). It's hacky because in most cases I'm providing replacement defines for Windows constants etc that don't exist on Mac rather than doing it properly. puke emoji It's also hacky because I'm not updating the GUI properly because I've not got the UI/processing threads properly set up yet. (This is "push early & often" code not intended for actual use, just a backup in case I get distracted by something else again and someone else wants to build on it. :) )
Obligatory screenshot:
if you dig into the .vst bundle to select the actual executable
Can we do some .app
detection logic to fix that? Although this may be hard to do with the file section dialog, right? Because it's a folder?
e.g. ...
QString p = m_plugin;
if(p.endsWith(".app")) {
// traverse, set p to the executable
}
if( QFileInfo( p ).dir().isRelative() )
{
p = ConfigManager::inst()->vstDir() + p;
}
if you dig into the .vst bundle to select the actual executable
Can we do some .app detection logic to fix that?
Thanks for the code suggestion.
I've not responded on this aspect specifically before now (in part because it's somewhat a "solved problem" technically but has some "platform integration" aspects that are susceptible to bike-shedding :D so I've left it unaddressed) but I do have some notes related to how to approach this here: http://www.labradoc.com/i/follower/p/notes-lmms#20180502.
Essentially, my impression is that the "platform native" approach is to auto-scan the standard platform VST directory locations (e.g. /Library/Audio/Plug-Ins/VST/
& ~/Library/Audio/Plug-Ins/VST/
) and then provide a list of the available VSTs by name with no explicit reference to the bundle file path. So, strictly speaking, I suspect providing a file dialog is non-native behavior.
But I suspect for initial ease of implementation I'll probably just adapt the existing file dialog code and use one of the approaches referenced in the link above.
Quick update: I've just hacked up the threads-side of things so that the VST GUIs are fully interactive & update/respond to clicks properly and it seems mostly straight-forward--still need to handle program exit cleanly though.
Can we do some .app detection logic to fix that?
I do have some notes related to how to approach this here: http://www.labradoc.com/i/follower/p/notes-lmms#20180502.
QFileDialog::Directory
combined with QFileDialog::ShowDirsOnly
however it appears setNameFilter(...)
strips out the directories. Fortunately we do roll out our own FileDialog.cpp
so there's some customization options.
"platform native" approach is to auto-scan the standard platform VST directory locations (e.g.
/Library/Audio/Plug-Ins/VST/
&~/Library/Audio/Plug-Ins/VST/
) and then provide a list of the available VSTs by name with no explicit reference to the bundle file path. So, strictly speaking, I suspect providing a file dialog is non-native behavior.
Agreed. This is how @falkTX does it in Carla, which we'll be offering support for in stable-1.2
.
@tresf Thanks for the pointer about the approach Carla uses.
What's the likely minimum level of VST-selection functionality that would be accepted into master? (Even if Mac-native VST support is not enabled by default I'd like to avoid bit-rot by getting a MVP into master sooner rather than later.)
I've just pushed the changes that make the plugin GUIs properly interactive: e7a29357a677602f397b64f65066acf159a27ca7
It also includes some code that is meant to avoid occurrences of this error on file close/application exit:
QProcess: Destroyed while process ("/<path>/lmms/build/plugins/RemoteVstPlugin") is still running.
Process error: QProcess::ProcessError(Crashed)
Remote plugin crashed
And while it helps reduce the number of occurrences it doesn't completely eliminate them so either I'm not handling plugin shutdown/exit requests properly or it's slower to do so than expected on other platforms so the calling code needs to be modified.
It's still hacky rather than production code but should at least be buildable & work for someone else.
It seems like some duplication/extra code could be prevented if Windows and/or Linux+WINE code used the cross-platform Qt code where appropriate (e.g. library loading & threading?) but I don't know whether the current non-Qt approach is required in order for the WINE side of things to work correctly (can anyone comment on that?).
I don't know whether the current non-Qt approach is required in order for the WINE side of things to work correctly (can anyone comment on that?).
@follower The best explanation I've seen is here: https://github.com/LMMS/lmms/pull/2304#issuecomment-136302025
For (my) future reference, it may be worth investigating if any of these Launch Services Keys are useful in this situation:
LSBackgroundOnly
LSUIElement
I don't know whether the current non-Qt approach is required in order for the WINE side of things to work correctly
If we use Qt with wine
, some unusual dependencies are required, like 32bit Qt on 64bit Linux.
BTW, it'd be nice to add an abstraction layer and separate platform-specific part from RemoteVstPlugin
. It will make the code cleaner and easier to read.
For possible future reference here's another FLOSS project's cross-platform VST-related implementation:
BTW I've been using my VST support off & on since September-ish with success.
Main issues I've encountered have been reliability related:
.vst
bundle. (So, not native behaviour.)It's been really nice being able to use VSTs but I think dealing with some of the above reliability issues will take a non-trivial amount of work--in addition to any work required to refactor the cross-platform implementation to be "nicer".
FWIW if we were able to add support piece-by-piece it'd probably be easier to prioritize making some PRs.
@follower we're fine with WIP code because it increases the likelihood of others getting in and fixing stuff.
Need to refer to executable within the .vst bundle. (So, not native behaviour.)
IIRC, A .vst
is mirrored after an .app
bundle, so we can infer the executable path, right? My guess, we'd just need to allow draggable folders when they end in .vst
. This should be doable. Ideally we'd just execute the .vst
folder directly (Mac allows this -- for example -- with the open
command), but I'm not sure how we'd handle IPC. Perhaps MacOS has a proprietary technique for this we can leverage? Launch Services may also have it registered too.
Sometimes processes and/or shared memory segments get left behind after an "abnormal exit" which can in theory prevent VSTs from loading on application start due to exhausting available resources (i.e. shared memory).
This isn't my wheelhouse, but if @DomClark is interested, the project can obtain a Mac Mini for him for testing. This offer extends to anyone capable BTW.
@follower Are you still on this? Also, could you create a PR from the WIP branch so we can review and/or make suggestions?
Short answer is sort-of (i.e. I haven't abandoned it) but also I've found that the Carla plugin in 1.2 makes VSTs usable on Mac (although not perfect) in the interim.
@follower I don't think Carla is supported on Mac https://lmms.io/wiki/index.php?title=Carla
Any updates on this? Would love even rudimentary support in OSX.
@spiderworm The wiki article is outdated. Carla is supported on Mac since 1.2.0-RC7.
@PhysSong I have the official release v 1.2.1 installed, but I don't see Carla anywhere in the UI. I don't see Carla Rack and Carla Patchbay in the Instruments panel, like the wiki says (which I get you already mentioned is out of date).
Are there any instructions anywhere for using LMMS Carla?
You need to install the Carla app separately. You can get it from from https://github.com/falkTX/Carla/releases.
Are there any instructions anywhere for using LMMS Carla?
Not currently, but if LMMS and Carla are both installed in /Applications/
LMMS will find it. It's the best we have right now.
I'm checking on the state of VST support for Mac. Is this something implemented at this point? Thanks.
I'm checking on the state of VST support for Mac. Is this something implemented at this point? Thanks.
No.
Ticket is missing macos
label
Background
I had originally assumed that the reason for LMMS using WINE for VST (non-)support on Mac was because there weren't sufficient native Mac VSTs to justify adding native support. After playing with Helio Workstation I discovered that there's plenty of native Mac VSTs so I decided to investigate adding support for them to LMMS.
Progress so far
I've reached a point[1] where I think I've made sufficient progress that I wanted to keep track of development here--meaning that if I don't get to finishing it hopefully someone else can build on it without having to re-learn all of my lessons. :) (I've discovered a lesson I just learned was already in an unmerged WIP PR from someone else. :) )
The two parts of progress so far are:
Proof-of-concept for opening Mac native VST within Qt 5.5 application: https://github.com/follower/qt-vst-mac-native-poc (which might also be suitable for a Qt-based mostly cross-platform implementation).
Compilation of sufficiently hacked up versions of
vst_base
,vestige
andRemoteVstPlugin
that LMMS will startup, show the Vestige plugin, allow it to be dragged onto a track, display a file open dialog box, allow selection of an actual binary of a VST renamed with a.dll
extension and then display the following error messages (without actually crashing):The final lesson I needed I eventually discovered had already been mentioned here: https://github.com/LMMS/lmms/pull/2393/commits/d7a63c5adda5d3878f033569453fed44e699e7e9#diff-0cfe54b7b7513b8854b14b3fb8411b78R115 (but, hey, at least I learned something along the way :) )
[[1] Context: I started writing up this issue maybe a couple of weeks ago and wanted to make it more "complete" before posting it but since that approach doesn't seem to be working I thought I'd just dump it in here now as is and then add to it as I get the opportunity.]