briankendall / proxy-audio-device

A virtual audio driver for macOS to sends all audio to another output
The Unlicense
514 stars 33 forks source link

request: help me with my patch 🙏 #29

Open vegerot opened 1 year ago

vegerot commented 1 year ago

For my personal use, I want to make a CLI version of this app.

I hacked a quick prototype together, but ran into a bug that's blocked me from proceeding. I am calling AudioDevice::setObjectName the same way as the GUI is, but the playback device is not changing. I detail the exact problem in https://github.com/briankendall/proxy-audio-device/commit/0ac82715945f3c2e2aa7d6ed88f2b554b9cbc385

I would appreciate if you could take a look at it and lmk what I'm doing wrong!

Thanks ❤️

briankendall commented 1 year ago

You are running into a crazy bit of hackery that allows the proxy device to be configured without going through the obnoxious rigmarole that Apple expects. I think it's a pretty fun and clever bit of hackery, but then again I never really thought about if anyone else would have to deal with it.

If you haven't already, take a look at the comment starting on line 2241 of ProxyAudioDevice.cpp:

// To handle reading and setting configurations for the driver, we're using a rather ridiculous hack.
// However Apple makes it very, very difficult for a client process to configure the driver in any way.
// From the research I did, it seems to require at minimum using a LaunchDaemon to relay messages from a
// client process, which seemed like such a tedious and annoying thing to set up.

// So here's the alternative method: we use a few of the properties of the box device in ways they
// weren't intended to be used:

// First, if a process sets the "identify" property of the box device to its pid, then that pid will
// become the designated 'configurator' pid. The driver will then respond differently to read and write
// actions taken by the process for certain key properties on the box device.

// If the configurator process writes a negative value to the box device's identify property, then that
// sets the current setting (with the absolute value of what was written to the identify property
// corresponding to the ConfigType enum). The current setting is what will be returned if that same
// process reads the box device's name property. In the case of integer settings (such as the output
// device's buffer frame length) then it is converted to a string before being returned.

// Lastly, the configurator process can write out new values to the driver's settings again using the
// box's name property. When it sets it to a value in the form "settingName=value" then it will parse
// that string and adjust the specified setting accordingly.

// The identify and name properties of the box are used for this because they are a few of the only
// settings that can be written to at all, and aren't of particular importance to the operation of the
// driver.

I haven't looked over your code super closely, but I do think the main thing you're missing is making your cli the current "configurator" by writing the process's pid to the proxy device's identify property. The setCurrentProcessAsConfigurator method in WindowDelegate.mm shows you how to do that. Once that's done, writing out new settings values using the device's name property should work. Note too that you can also read in settings from the device by setting a negative value to its identify property, and then reading its name property. The negative values correspond to enum values for picking which setting you want to read.

Hopefully that's enough to get you on the right foot. I think it's cool you're making a cli for the proxy device, by the way! Let me know if you have any more questions and I'll try to help out.

vegerot commented 1 year ago

Thanks for your detailed answer, it worked perfectly! You can check out what I did in https://github.com/vegerot/proxy-audio-device/commit/proxy-audio-device-cli

I built this because occasionally when reconnecting or putting my MacBook to sleep, the proxy stops working and needs to be reset. Additionally, in the GUI I can't even re-click on the same device in "Proxied Device". I have to

  1. change the Proxied Device
  2. change it back

which is too many clicks 😁. So I wanted to make a CLI program I can quickly run instead.

For that reason, the cli does not have feature parity with the GUI and only supports the two flows I need when the proxy stops working.

Thanks again for your help ❤️ . Feel free to close this Issue