Category Archives: cocoa

Mac OS X and Objective-C

CoreAudio Taps for Dummies

I’ve finally spent some time on implementing CoreAudio taps in SoundPusher 1.5. These let you listen in on (“tap”) the output that is generated by certain processes or sent to certain devices.

So far, SoundPusher has done this with a loopback device that reflects any output it receives back into an input stream that looks to the system like a (6-channel) microphone. This has required increasingly more permissions from the system to access microphone input (since the OS doesn’t know what the SoundPusher Audio device records), and results in a big orange blob in the menu bar to warn the user that they’re being recorded.

Apple has started to provide APIs for recording system audio through audio “taps”, and if they’re good enough for Rogue Amoeba than maybe they’re good enough for me as well.

Unfortunately, Apple’s developer documentation is terrible as usual even though the actual C headers contain some more information. So this is mostly what I’ve figured out.

Taps work by attaching them to an aggregate device using the kAudioAggregateDeviceTapListKey in the dictionary provided to AudioHardwareCreateAggregateDevice(). See https://github.com/q-p/SoundPusher/blob/v1.5.1/SoundPusher/AudioTap.mm#L52 for an example.

The aggregate device with the tap (and nothing else — initially I put the device I was tapping into the aggregate device as well but that only confused matters) will then provide an input stream that contains the tapped data. This is similar to reading from a (virtual) microphone, so I didn’t have to change the rest of the code much. Starting to read from such a tap requires a TCC / privacy authorization and results in a less obnoxious purple dot compared to the orange microphone blob. There doesn’t seem to be a way to query this authorization status as is possible for the microphone access using [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio], so if the user denies this request all you will get is silence (and you cannot inform them about the authorization status since you don’t know).

Do note that if the device you’re tapping also contains input streams (like my SoundPusher Audio loopback device did), then you will in addition still need microphone access to tap that device. I have tried all sorts of ways to disable the input streams to avoid the microphone permissions when tapping a device with inputs, but no such luck. That’s why I have removed all input (and therefore loopback) support from SoundPusher Audio.

Why is this dummy device then needed at all? Because I need 5.1 channels of input data, and unless the user has a real 6 channel audio device, there won’t be anything good to tap. The system can provide a mono or stereo mix-down when tapping, but if you want something more specific than that, then you need a real (fake) device to prescribe the shape and format you want tapped…

SoundPusher

I’ve been on a bit of a coding spree during my time off work, and managed to make some progress on my old attempt to provide a virtual 5.1 sound-card that does real-time AC3 encoding to a digital output connection. This time around, I didn’t actually stop a third of the way in and it actually works (and quite well at that — at least on my system — if I may say so).

The code is on GitHub. If you’re interested helping with testing, feel free to get in touch.

For some internals, I reduced the number of components to two, as I eschewed AudioUnits (and thus the separate encoder) altogether. There’s now just the user-space LoopbackAudio driver (AudioServerPlugIn) and the SoundPusher application that reads from the loopback-driver, compresses that and then sends it to the real digital output stream. Latency is reasonable from my experiments, as is CPU usage.

Code-Project: Virtual 5.1 Soundcard (Mac)

2015-12 Addendum: See SoundPusher for an update to (and new name for) this project.

As far as I’m aware there very few real-time 5.1 (Dolby Digital / AC-3) encoders for the Mac that integrate into the default CoreAudio HAL (and can thus be used by any application); I’ve only stumbled upon ac3jack, which builds on the multi-platform JACK Audio Connection Kit.
I thought it’d be interesting to learn something about CoreAudio and low-latency coding and thus decided to write such a system. Currently, this is split into 3 components:

  • CoreAudio AudioCodec for transforming LPCM into encoded AC-3. I’m using libavcodec as the encoder for this.
  • Audio driver presenting a virtual sound output to the system, and forwards that data as a provided sound input. The idea is to select the virtual output as default / system output, and then encode from the virtual input into the compressed format and forward that to a physical optical output.
    In 10.8 “Mountain Lion” you can write these sorts of plug-ins as sandboxed user-space components, which is quite neat (but not well documented at the moment).
  • A normal application / menu extra that takes raw sound from the virtual sound input, passes that to the AC-3 audio encoder, and then forwards the resulting stream to a physical digital output port.

The encoder seems to work; I’m currently working on the driver and then comes the user-space connector / configuration application.

Fun times! 😀

bitbucket

I finally got around to a) updating my old WordPress-installation (after finding out spam-bots had already created custom folders on my installation) and b) uploading all my public code to bitbucket (as Mercurial repositories).
That includes my wavelet image compression library, its Mac OS X previewing GUI as well as WowPlot (including some fairly decent Objective-C WoWCombatLog.txt parsing). I converted most of those repositories from darcs (which in the case of my wavelet lib took about 3h to convert from darcs1 to darcs2 format), but on first glance they look alright.

WowPlot 0.1.2

WowPlot has been updated to version 0.1.2, featuring the following improvements:

  • Added an option to automatically create new plots when splitting (by holding the Alt(⌥)-key).
  • Added new 2.4.3 combat log events (at least the ones I could find).
  • Removed PetFixers entries for Shaman’s summoned elementals (as 2.4.3 fixes this).
  • Added Fire Bomb spell to belong to Jan’Alai.
  • Updated Sparkle to 1.5b4.