My Profile Photo

Deviant Syndrome


coding, multimedia, gamedev, engineering


MacOS Catalina Docker Wine For Windows VST Plugins

AKA What the Hell am I Doing with My Life

Do you like strange audio software like I do? Most of really odd and retro stuff, like a total emulation of PPG Wave Computer Suite, or this micro electro drum machine, runs only under Win32 and, therefore out of my direct reach as a typical mac user. This also applies to a lot of software written for academic research purposes. Usually those programs were made to perform specific calculations that prove the thesis stated in one’s master’s paper. Usually, written by people who do not specialize in software, during pre-Jupiter pre-cloud era, the choice of platform and tooling was . Yes, there were the times, when they were teaching engineers and mathetimaticans to program on Delphi and Prolog. Don’t ask me why. Fun fact, I know for sure, that there are still people who support and maintain in-house Delphi projects.

So, what are my options? Well, current versions of Wine does not support MacOS Catalina, and Winebottler has a very strange paradigm of usage which seems totally alien to me, Like packing exe into Mac App? Automated installations? On what planet am I? And, of course, there is no way, I’m trying to build Wine myself, as it now requires pulling all of the 32-bit versions of development libraries and such. Something is sure gonna break, and I am not smart enough to deal with that confidently. So, why not try docker instead, since I do care about latency for the specified use cases.

So, I’ve assembled a docker image, lousely based on the ones provided by Jess Frazelle for PulseAudio support and Scott Hardy for X session support.

Dockerfile is available in this repo which also includes pulseaudio config files.

To get the X forwarding from container, which would act as a thin client of sorts. We will be using XQuartz for MacOS. From the XQuartz preferences, in the security tab, make sure Allow connections from network clients is enabled. Restart XQuartz.

xhost +localhost

Did not won over MONO(?)/framework installation problems, though. Every time I run a fresh image it would ask me to download a few things. Not critcal for me right now. When running a Linux distro inside docker, ALSA will not work properly out of the box, as it would not be able to locate a hardware audio interface (no dummy hw bindings allowed at ALSA level, if you want that - welcome to kernel recomplication!). On most UNIX and Unix-like environments, you can just bind your /snd/audio from host to docker container, but not on your shiny polished MacOS. One of the possible solutions here, is to use pulseaudio client-server over its native transport.

Pulseaudio was once designed to be a “distribution layer” between ALSA and applications, for them to share audio channels and buffers properly . It worked quite well for general-purpose multimedia apps, but fell a bit short when it came to .. working with audio (i.e DAWs, audio-editors, samplers, etc). Therefore, people designed Jack which serves a similar purpose, but with much richer feature set and latency footprint. I believe, Jack is currently the “unofficial standard” for audio and music production on Linux.

As you see from the file, I also had to install KXStudio stuff. Setup repos, according to the instructions on their site. I decided to go with Carla as a VST host. For it, I also installed wine bridges, x32 and x64 one. There was some typical open-source fun stuff along the way, of course. Like, I had to go to Carla’s settings and enable the corresponding experimental feature. After that I tried to scan for new plugins, but that did not work (worked when I ran it under root privileges for some reason). Finally, I got it working by just dragging and dropping a DLL file onto Carla’s rack panel.

Curious fact, I’ve also tried to run Pedalboard2 VSTHost through Wine. It starts, it loads the Win32 plugin DLL, however the audio it produces consists of clicks and pops exclusively. In theory, WineASIO should mitigate those problems, however it is built exclusively around Jack. And this is where we will be facing the fundamental problem with this kind of setup: pulseaudio and Jack does not mesh well together at all. You cannot output from Jack to pulseaudio sink. Jack forwarding requires a Jack server on the host machine. And installing and running Jack on MacOS is the most unnatural thing ever (by my standards, of course). So, I opted for pulseaudio and it’s native sink. Thankfully, there is a brew formula for pulseaudio. So, just run the sink like this:

pulseaudio --load=module-native-protocol-tcp --exit-idle-time=-1 --daemon

For more or less correct VST plugin GUI display, I also had to install several Wine-specific fix-ups.

winetricks mfc42
winetricks gdiplus
winetricks corefonts

Finally, to run the image with X and pulseaudio forwarding:

docker run -it -e DISPLAY=host.docker.internal:0 -e PULSE_SERVER=docker.for.mac.localhost -v ~/.config/pulse:/home/pulseaudio/.config/pulse --entrypoint /bin/bash --ipc=host --rm dieheart/pulse2:latest

So, conceptually it works. As you can see, I’m sharing this “pulseaudio-cookie” file across guest and host by simply mounting the config directory to container’s filesystem. And now, the caveats.

  • It seems, like PA can only output to a physical audio device. I tried editing the config in multiple ways, and it explicitly stated that in the error message: [github link]. Not really total deal breaker of course, if you only need monitoring. You can monitor and then simply record it to WAV inside container.
  • No freaking MIDI support. MIDI sequencing and events are all handled within ALSA, therefore require a device to be present. Well, you know what. I can use OSC protocol for that, and pass through a configured docker network port.

There is a handful of directions we can head from here. And it feels that they all go to Hell. So, I took the random one, which would be try to compile pulseaudio myself, and make an override that will output to virtual device.

The intricacies of that should be somewhere in PulseAudio CoreAudio Module