Core Audio for .NET

The story of why and how Core Audio .NET came to be, in three chapters

Chapter 1: The Good Ol’ Audio Mixer API

Up until Vista, Windows used the same API from Windows 95 to let third-party programs consume a series of resources (exposed by the drivers) that allowed them to discover and manipulate all the available settings on a sound card.

This API was known as the Audio Mixer API and it relied on the ability to first, enumerate all the available mixers (references to the sound cards, as exposed by their drivers) and then query them for the lines they exposed. Each line then had its own set of objects, called controls, which were a representation of the physical properties on the sound card, such as the volume of the microphone, the mute state of the CD line, etc.
A control could also expose a series of items that provided access to some advanced feature on the sound card, such as a loudness setting, or peak meter, or some other resource that was exposed by the sound card’s driver.

Sounds simple, right? Well let me tell you that it was anything but.
This API is awful, unorganized, complicated and worst of all, back in the day, it lacked documentation. No, the documentation wasn’t scarce, poorly documented or lacked samples — it simply didn’t exist. There were mentions, here and there, about the members, structures and other topics but there was nothing concise about it.

mixapp

Microsoft’s mixapp

I’m sure Microsoft partners and affiliates of some sort had access to some privileged documentation, but we (the freelance developers) did not.

So, if you wanted to implement some sort of, for example, volume control on your application your best bet was to get a copy of the Windows SDK and check the source code for an application that it included, appropriately named mixapp.

This little program (just 61 KiB in size) was made available as a compiled binary accompanied by its C source code. Unfortunately, the code wasn’t documented!
Anyway, the little mixapp program did everything I mentioned before:

  • Enumerate all the mixers
  • Enumerate their lines
  • Enumerate each line’s controls
  • And, finally, it exposed (if any) the item controls inside each control

I had already spent a considerable amount of time fiddling with the API (without any valuable results) and seeing this (so damn small) program do what it did, appeared as a magic to me.

Fast forward three weeks (yep, that’s what it took me to fully understand how the API worked) and I had the first working prototype… in VB6.

Chapter 2: EQPro

The first incarnation of the wrapper I created (to allow VB6 programs to access the mixer API) was called EQPro.
EQPro, released on October 5th 1998, was an ActiveX control that let you manipulate, through a Slider, a specific volume control from any line in any of the installed sound cards. It was a hit. Actually, it was the very first program I sold on the Internet.

Then on June 26th 1999 the first wrapper for the Audio Mixer API was sold for US$75.00

For me, EQPro was more than enough as it provided all the functionality I needed for one application I was working on: the very first dual MP3 player intended for DJs. Yep, you read that right. I was, probably, the first to ever develop such an application.

xmPlayer

xmPlayer, as in Xavier and Marino Player

But the control sold like crazy and its users wanted a lot more control, more flexibility and, above every thing else, the ability to have full access to all the resources the sound card’s drivers could provide.

That’s how EQPro 2.0 came to be.
Version 2.0 was no longer a user control, instead, it was distributed as a class library that exposed all the functionality in the Audio Mixer API.
From that moment on, full control over every single resource on a sound card was accessible to anyone developing in VB6.

Then, on October 8th 2003, EQPro was discontinued in favor of MixerPro, which was a considerable enhancement over the way the wrapper interfaced with the API, providing a set of useful functions to easily perform advanced queries to detect and manipulate all the resources exposed by the driver of any sound card. MixerPro made the Audio Mixer API so accessible that even AudioScience, makers of some of the most recognized professional grade audio boards in the market, decided to use it for their own mixer-related applications. If you would happen to have owned an ASI5111 PCI Sound Card (for example), then the mixer app distributed with this sound card was developed using MixerPro.

And MixerPro remained, mostly unchanged, as the most powerful ActiveX library for manipulating a sound card’s mixer for almost a decade until Microsoft released Windows Crap… I mean Vista.

Chapter 3: …and support for Core Audio was implemented

On November 10th 2004 I released a .NET version of MixerPro, called MixerProNET.
The .NET version was nothing more that a port of the VB6 version to .NET with some minor enhancements.

Fast forward 3 years and Microsoft releases Windows Vista. Among many annoyances, Vista implements a completely new mixer API which, according to Microsoft, should enhance and enrich the end-user experience… when… using the volume from the taskbar?

Like it or not, Vista, among other things, was Microsoft’s attempt to prove to the media industry that it was a safe platform for media consumption and that its users wouldn’t be able (or at least, not as easily as with XP) to steal copy protected content. Sure there are those who claim this isn’t true but I guess they aren’t developers.

The old API was convoluted but it worked and I’ve never heard any sound card manufacturer complain about it. So, why was it modified so drastically? What was the point? Who had the necessity?
And if I’m wrong, why is the infamous “Stereo Mix” device disabled by default under Vista (and Windows 7)?

Anyway, this wasn’t supposed to be a rant but an explanation of how and why MixerProNET got to have Core Audio support.

The why is easy to explain: simply because the legacy support that Microsoft left in Vista and 7 simply sucks. Any application making use of the Audio Mixer API  under post-Vista is as good as nothing.
Just take a look at how the nice little mixapp behaves under Windows 7:

mixapp running under Windows 7

mixapp running under Windows 7

Where do I start…?

  • First of all, mixerapp cannot properly enumerate all the mixers
  • The Speakers line only exposes a volume and a mute switch. But this is incorrect, since the same sound card under different operating systems exposes additional controls
  • The volume control is exposed as a monophonic control when I perfectly know that my sound card is very well capable of independently controlling the output volume for all the channels it supports. Why would the proprietary mixer application distributed with the sound card expose a balance control then?
    Proprietary Mixer Controls Under Windows 7

So what’s happening here? The thing is that Microsoft introduced what’s known as sessions, which are nothing more than instances of programs that in one way or another, are either streaming or capturing audio through a sound card and what this does is that the legacy mixer APIs are only applied to the current session.

Useful? Well, maybe… it’s kind of nice, as an end-user, to be able to control the volume of the System Sounds independently from other programs but that doesn’t condone the way the legacy support was implement.
Why didn’t Microsoft simply extend the legacy API to support sessions?

When Vista was released, I learned all about these changes and I decided that I wouldn’t lift a finger to add support for the new APIs.

But then came Windows 7.
It was a pleasure to use my computer again — streamlined, smooth, fast, organized, consistent… what else can we say about this marvelous piece of technology?
Finally! Microsoft got it right.

So I guess I felt encouraged again and I began to study the new API.

A week or so later I decided that it just wasn’t worth it — sure it had many new exciting features but, again, it was unnecessarily convoluted as hell.

Fast forward a couple years… until one day, sitting in-front of my computer with nothing else better to do I decided to give it another chance.
This time instead, I did a search to see what others had done about it and found almost nothing useful. I found almost nothing except for an attempt to create a full Core Audio wrapper in C# by Ray Molenkamp from CodeProject.

After downloading his library and analyzing it I realized that he had already done all the hard work. The library wasn’t MixerProNET ready yet, but it was close. So, in just one sitting I implemented all the interfaces that were required for MixerProNET to be fully Vista and Windows 7 compliant. Of course, thanks to the marvelous job already done by Ray.

In less than 48 hours I had implemented all the required interfaces to query and manipulate any sound card, under a post-Vista version of Windows.

The next step was to enhance MixerProNET so that it could support the new Core Audio API and this was done by adding a series of new classes that interfaced the functions in the Core Audio library.

And that’s how MixerProNET 2.0 came to be.
MixerProNET 2.0 exposes a two-tier abstraction layer over the actual API. The first one by the Core Audio .NET library and the second one through the CCoreAudio class implemented in MixerProNET. This provides the end-user (that who has already had some experience with previous versions of the MixerProNET library) with a familiar interface of classes, collections and methods.
Actually, those who have never used MixerProNET will find that the way it exposes the Core Audio APIs is extremely simple and convenient, since the code behind the control does all the hard, complicated and convoluted work for you.

Let me give you an example:

A mixer device, in Core Audio, may expose what’s known as an IAudioEndPointVolume for controlling its main volume, with its own specific callbacks (for notification changes).
The same mixer may expose several additional controls (such as a control for the Line-In volume) of type IAudioVolumeLevel (among others, depending on the type of control).
A session exposes an ISimpleAudioVolume. Yet another interface to control both volume and mute states.

Why all these different interfaces to perform the exact same action? Who knows. But the fact is that MixerProNET’s implementation exposes a single object regardless of its source allowing your to query it and manipulate it without having to worry about the object’s source.

Basically, this means that if you want to mute a control (a control that supports muting, that is) you just set a “Mute” property to “True” and you are done. It’s that easy.

The next version of MixerProNET will include a series of WinForm user controls that mimic the look & feel and behavior of the controls Microsoft uses in their mixer-related applications.
Here’s an example:

MixerProNET 2.5

MixerProNET 2.5

I hope you haven’t just scrolled all the way down here without having read the article, as it explains a lot about what you are about to download.