rust-vst2 icon indicating copy to clipboard operation
rust-vst2 copied to clipboard

GUI Support

Open Earlz opened this issue 9 years ago • 69 comments

So, I've been trying for the past few days to get any form of GUI working in my VST host (Reaper) using rust-vst2, and I've faced nothing but difficulties. I'm also pretty new to Rust itself, so my inexperience doesn't help.

Anyway, have you had any success prototyping such a thing, and ideas on what to use? I know the readme says Conrod+SDL, and this is the primary thing I've focused on, but I can't help but wonder if there might be an easier and more stable way. The C++ VST kit that everyone, wdl-ol, includes an OpenGL framework with built in easy support for drawing knobs and such. I'd be curious if doing FFI to that might be easier. Do you have any ideas?

Earlz avatar Dec 30 '15 16:12 Earlz

So if you look at src/editor.rs, it is up to the plugin to create an editor that implements the Editor trait. One of the things that we have to work with is a window pointer, which can be used by the plugin to do something e.g. initialize an SDL2 context. It seems libraries like wdl-ol abstract over more than just the bare VST api, I'm thinking it might be out of scope for this library to do something like that.

There are a few approaches that we can take though, the main thing I'm interested in is seeing if we can get Piston's 2D graphics library to work with an editor. I don't necessarily want to use OpenGL for drawing as it might not work on all systems and we can use a simpler interface for drawing 2D UI elements. If it's easier though I'm good to get just something running with a UI even if it is OpenGL.

We would also need to have the appropriate button controls like knobs and sliders, I'm thinking these can be done in another crate to separate concerns (or we can use conrod!). It also opens up the possibility of code reuse for something such as an Audio Unit rust implementation which needs UI elements as well.

There's a lot of stuff that needs to be done and I'm OK with trying out a bunch of things, just not sure which direction to head in. If you want to take a stab at it, the very first thing would be to see if you can get a graphics context to work with the pointer given in Editor::open. I'll see if I can help out if you do end up making a public fork/repo.

overdrivenpotato avatar Dec 31 '15 00:12 overdrivenpotato

Yea, I definitely think it'd fall under a completely different crate, but I feel like it could belong to the same project, or at least be related..

I did some investigating in the wdl-ol code, and I think the way they handle it (very thick codebase, so difficult to be sure) is that they basically have a platform dependent native set of graphics code for each platform. This graphics code handles basically the bare minimum however. It exposes a framebuffer, and allows for the normal event setup (ie, keys, mouse, etc). Then, most programs use their LICE abstraction that works directly on the exposed framebuffer for shapes, glyphs, etc. And finally, the programs that care about OpenGL, basically setup OpenGL to render to an invisible hidden window, and manually blit/copy the framebuffer of the OpenGL window to the VST-host window.

I'm not sure that this is the best way to go, but I'm sure there's a reason why they chose this way rather than directly rendering everything using the window handle.

I've been trying to get Editor::open to work properly with SDL, but having very little success thus far.. I intend to keep chasing this though. It'd be awesome if VST work on Rust was actually preferred over C++, since already this is incredibly easy compared to making a VST plugin in C++.. just the GUI bit isn't there, and eventually the DSP other common audio operation helpers

Earlz avatar Dec 31 '15 04:12 Earlz

Sorry about the delay, holidays and all :tada: (Happy new year!)

Alright well it looks like you're right about the wdl-ol implementation, we can definitely do the same thing and implement platform specific graphics code as I am not aware of a rust library that does this. One of my goals would be to have rust-vst2 work with Linux, OS X and Windows though so I would like to support all three platforms properly. I guess the first step is to decide whether we're going down the route of using graphics as a base or whether we should just create a new library from scratch, your thoughts?

overdrivenpotato avatar Jan 04 '16 03:01 overdrivenpotato

You might want to look at how the JUCE C++ framework deals with different platforms. A rust equivalent to JUCE would be amazing, albeit a very ambitious project!

adamski avatar Feb 12 '16 22:02 adamski

graphics provides primitives to draw, but you still need a backend for it to initialize an sdl context.

I have a different problem though. I want to implement a microtonal (31 tone) sequencer (like Hex). It will have a complex UI, so I rather should use gtk-rs. But I also want it to be a plugin, to use it in my DAW of choice.

So, how do I use gtk in vst? I heard this is not an easy thing to do, but I also know Carla uses Qt and can be loaded as a plugin.

suhr avatar Sep 22 '16 04:09 suhr

Did anyone have luck in initializing a graphics backend from the hwnd handle on windows? I'm currently trying to get this working, but i'm kinda stuck.

sklopi avatar Nov 06 '16 18:11 sklopi

@suhr I'm not really sure how this should be handled, I do think the best way going forward would be to use some sort of a toolkit but as I don't have experience with either QT or GTK I don't really have any suggestions.

It looks like it boils down to either getting an existing toolkit to use the raw window handle for each platform, or creating a GUI system specific to this library. Personally I'd be interesting in seeing something such as electron being used for the GUI and delegating the implementation/DSP down to rust code, but again I'm not sure where I'd even begin.

overdrivenpotato avatar Nov 12 '16 23:11 overdrivenpotato

I have proof-of-concept working currently on Windows at https://github.com/ycros/vparty using a fork of glutin https://github.com/ycros/glutin/commit/dd0de4444dbcc6c4be86ebf6d3d61a9ffc804ddb - eventually I'd like to get conrod up and running, but this requires either implementing a whole new conrod backend or getting some changes into piston. I know how to get things running on OSX as well, though things are a little trickier there (your pointer may be a Carbon windowRef or an NSWindow depending on 32bit/64bit/your daw). I've been busy with a new job so I haven't had time to come back to working on this.

Basically, for whatever sort of gui lib you want to end up using, you will need to handle spawning your window as a child window under whatever window handle/pointer/thing you get from your host. There's other open source C++ VST wrappers such as wdl-ol that you can peek at to see how they handle gui initialisation.

ycros avatar Nov 13 '16 23:11 ycros

I forked glutin, conrod and the piston window stuff and modified it to support multiple child windows in the same thread (required for VST but by default glutin creates a new thread for each window and doesn't allow child windows) and passing the parent window handle all the way through to where the win32 window creation functions are called, you can find the forks on my page, but I ran into some issues like this: https://github.com/PistonDevelopers/piston_window/issues/167 (The problem is piston_window uses its own event loop. I either don't get draw events or no input events.) Right now I'm working on my own lightweight GUI lib on top of glutin, I already have layouting, buttons, knobs, font rendering etc (with batched rendering). but then I got sidetracked working on a game.. I'm getting back to this though. My plan is to create a VST framework on top of this lib that includes GUI support and common DSP routines that are useful and can be composed to create larger plugins, kind of like JUCE but for Rust. I currently have a basic starting point, ported some code from Will Pirkle's book to Rust. If you are interested, we can plan this together on IRC, #rust-music I think Rust has great potential for audio DSP.

Btw, this is an old screenshot of my GUI lib (this example isn't running in a VST but it works in a VST too):

Boscop avatar Nov 14 '16 13:11 Boscop

My plan is to create a VST framework on top of this lib that includes GUI support and common DSP routines that are useful and can be composed to create larger plugins, kind of like JUCE but for Rust.

Then it should also support LV2 and AU.

suhr avatar Nov 15 '16 01:11 suhr

@suhr Yeah, but I don't know anything about those formats... So it would be helpful if we can work together. Probably we should design an intermediate API for plugin architectures so that each plugin only has to interface that API and can be compiled to all supported backends. I think that's how WDL-OL does it. Currently, my glutin fork only allows creating child windows on windows. But I abstracted the WindowID into a type, which can be defined in a platform-independent way, so feel free to submit a PR for other platforms. Also I added set_timer() and kill_timer() so that the VST GUI can be drawn at a consistent framerate triggered by the timer, that's how it should be done on other platforms, too. This requires storing a pointer to the plugin in the userdata of the window, this is how I do it on windows. My glutin fork also supports sending a Timer event instead of using a callback for the timer, but you can't poll events out of nothing, something has to trigger it, and if the mouse is not over your VST window, the host will call idle on the plugin only at like 4 fps. That's why I switched to the timer callback, because my spectrum view needed a higher framerate.

Boscop avatar Nov 15 '16 05:11 Boscop

@Boscop This looks great, I definitely would be interested in discussing this further. Which IRC network is #rust-music on?

overdrivenpotato avatar Nov 15 '16 06:11 overdrivenpotato

@overdrivenpotato irc.mozilla.org

Boscop avatar Nov 15 '16 13:11 Boscop

cast @poidl

suhr avatar Nov 15 '16 15:11 suhr

I think the idea of using CEF would be a good one. Step one would be to get a CEF window running with its event loop running on a separate thread of course. As far as communication between the window and the VST, there would need to be a queue of events being passed from the window handler into the VST. The queue would need to be available to the processing callback and ideally it would be a lock-free SPSC queue. A big question that needs to be asked is wether this library should be responsible for the actual rendering and layout or if it were to only provide the window and the IPC mechanisms needed to communicate with the plugin. I feel that a good starting point would be the latter option and then work on a standardized set of UI elements and such.

zyvitski avatar Jan 27 '17 02:01 zyvitski

@overdrivenpotato That screenshot looks cool! Can bindgen be used for generating bindings to CEF?

Boscop avatar Jan 27 '17 12:01 Boscop

I found another alternative, these guys are using awesonium for their game/editor GUI: https://www.reddit.com/r/rust_gamedev/comments/5vqlln/shar_one_year_with_rust/ https://youtu.be/OVYQs3KY2EE?t=55 http://www.awesomium.com/ https://github.com/not-fl3/awesomium-rs I think this is a very promising possibility for VST GUIs.

Boscop avatar Mar 02 '17 17:03 Boscop

Personally, I don't think HTML is a good fit for these kinds of apps. Although something like React's JSX for declarative UI could work. Audio apps often require lots of custom UI elements, some of which need to be updated at high rates to represent what's going on in the audio. I might have got the wrong end of the stick here though...

adamski avatar Mar 13 '17 21:03 adamski

I agree. I think at a very base level, declarative UI is really nice and accessible (like XAML) but I don't think running a web UI for a VST would be the best idea.

piedoom avatar Mar 14 '17 02:03 piedoom

Has anyone made any progress on getting a GUI setup? I'd be interested to hear how you dealt with the issue of working with the native window handle that's vst hands you

zyvitski avatar Apr 26 '17 03:04 zyvitski

@zyvitski Here is a description of what I'm doing in my glutin fork: https://github.com/tomaka/winit/issues/159 I haven't updated my forks in months though (I've been busy with work), the conrod fork should be updated to support the glium backend so that one can render stuff with custom shaders... I'm working on a kickdrum plugin right now where I plan to use raymarching to render a GUI with correct shadows on the knobs / glass LEDs and using different noise functions to generate textures like wood for ultimate skeuomorphism...

Boscop avatar Apr 26 '17 04:04 Boscop

@Boscop Are your changes to glutin only setup to work on windows or have you setup a cross platform solution as well? I have both a Mac and Linux setup and would love to get things working across the board.

zyvitski avatar Apr 26 '17 12:04 zyvitski

@Boscop I forked a copy of your glutin fork, I will see what I can do to get it going on OS X. Once I have it setup I will issue a pull request. I think It would be a good idea to try and set up a portable timer to replace the windows timer you used ( can just be a wrapper over the windows one with conditional compilation). Once that is up and going I will give it a go on my linux box.

zyvitski avatar Apr 26 '17 13:04 zyvitski

Is anyone familiar with how the cocoa api backend for glutin works? I have never dealt nuch with cocoa. I know that the window handle coming in from the host should be a NSView* (I chekced the vst2 sdk source) but I am not sure what the proper method for working with the NSView is? I tried setting it up where the the view created by glutin would be attached to the incoming view but that just resulted in having two windows.

Is the host handing me a new window to work on or a handle to it's window to attach a new window to?

zyvitski avatar Apr 26 '17 16:04 zyvitski

I think it's a great idea to have cross platform support, but I've never dealt with OS X so unfortunately I can't help much there. But I know it also has a way to store data in a custom pointer associated with a window (which I use to store the plug-in pointer so that the timer callback can access the plug-in.

The host creates a window with or without decorations (depends on the host) and passes the handle to the plug-in. It has to create a child window without decoration in that parent window.

On windows, all child windows have to live in the same thread because all parent windows live in one thread and child windows have to run in the same thread as their parent. Hence the solution with the event hash map.

To bring winit up to speed with the fork it would require to apply the same changes outlined in that post, but in a cross platform manner. So we need these things as cross platform:

  • callback timer on window with callback having access to plugin (e.g. store pointer in window user data)
  • a way to create child windows such that they run in the same thread (abstracted window handle as hashmap key for event queues)

On all platforms the window handle is a pointer, so we can just have a WindowId or WindowHandle type that is basically a pointer.. (in my forks I called it WindowId)

Once we have the cross platform windowing going we can also create au plugins... :)

Boscop avatar Apr 27 '17 21:04 Boscop

After spending an entire 8 hour day trying to model your changes to glutin in the osx backend I finally gave up on it and decided that I am just going to opt for the nuclear option on solving this problem once and for all. I have started work on a native port of vstgui-4 which so far is turning out to not be that bad of a task (time consuming yes, but pretty straight forward). Shouldn't take me too long to get the basic building blocks in place to at least get things moving (without widgets and fancy features). As mentioned above I don't wave a windows box, If anyone would like to jump on board once the skeleton is down and cover the windows backend that would be great ( @Boscop, or anyone else interested).

zyvitski avatar Apr 28 '17 00:04 zyvitski

As a JUCE developer who is watching Rust and hoping to dive in soon, I very much value being able to use the same GUI classes to build plugins as well as standalone apps. It would be great if we could use something like Conrod or Glutin to build plugin GUI's rather than be tied to a VST-specific library.

adamski avatar Apr 28 '17 09:04 adamski

I've never worked with vstgui but if it's possible to create professional looking GUIs with it, I'm interested.. But if vstgui works on Mac, we can see how it does the window / event handling there and do the same in glutin / winit.. Then we can use conrod on top, and custom shaders for fancier knobs and custom widgets like spectrum views..
@adamski Great to have a juce developer here too. I hope together we can make Rust more popular for VST development :) Are you working on the juce library or making plugins with it?

Boscop avatar Apr 29 '17 10:04 Boscop

I hope the conrod itself won't be mandatory. It's quite complicated.

suhr avatar Apr 29 '17 17:04 suhr