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.
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! 😀
Here’s an update to WowPlot that supports Patch 4.2 Combat Logs with extended actor flags.
A further update with Lion-specific features (fullscreen, auto-saving, …) is in the works as well.
I had some itching coding fingers again and thus added support for the new World of Warcraft Patch 4.0.1 changes to WowPlot. As usual, the direct download is here and the source is over at bitbucket.
If a log from 4.0.1 causes trouble, get in touch.
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 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.
WowPlot has been updated to version 0.1.1, featuring the following improvements:
- loading / saving of plots
- more speed (back-end is now multi-threaded using NSOperation)
- Sparkle auto-updating
- better parsing
- some error-messages
- more consistent splitting
WowPlot is a graphical analysis tool for World of Warcraft® combat logs (compatible only with the new combat log format introduced in version 2.4). Its main focus lies in evaluating time-dependant combat performance in a very free-form fashion, which is in contrast to the mainly statistical approach of other tools.
WowPlot requires Mac OS X Leopard (10.5) and is a Universal application.
It might pay off to search the internet for NSOperationQueue…
For Kompressor.app, I’ve written a group of Foundation classes for implementing a worker-thread paradigm in Objective-C. The general idea is you have some non-trivial amount of processing you want done and then (optionally) be notified when it’s done.
This implementation has a number of advantages compared to others:
- Threads are created once and reused, no expensive thread creation for each work unit.
- Non-polling (using NSConditionLock).
- Low communication overhead (e.g. no Distributed Objects).
- Small (although the code is spread out over 3 classes / files).
How do you use it?
- Create an instance of SIWorkManager (usually there should only be one); by default it creates as many worker threads as CPU cores are available.
- For each unit of work, create an SIWorkUnit object (which contains the target object and selector where the actual computation is done, as well as an optional didEndSelector which is called on the main-thread of your application with either the result of calling [target selector] or the originally given argument). The target and argument are retained by the work unit.
- Tell the work manager object about the work unit (via addWorkUnit:).
If you want to wait for a batch of N work units to be done, create an NSConditionLock L, and at the end of the work selector, you do [L lock]; [L unlockWithCondition:[L condition] + 1];. After dispatching all the work units in the main thread (or wherever), you wait with [L lockWhenCondition:N]; [L unlockWithCondition:0];.
An (untested as I am only on a Linux machine at the moment) example demonstrating both simple usage as well as waiting for a batch to be finished is in BatchExample.m.
Remember to only access shared data structures (or your user-interface) in the didEndSelector. SIWorkManager also has messages for removing work on or referring to a given object. Use nil to remove all pending work units.
Here is the source code with all required files. Feedback / fixes appreciated.