Encoding HD audio to WMA/ASF

Perhaps you've tried it in GraphEdit, or perhaps even your own code but couldn't get it work? The following will illustrate the requirements so that you can understand what is required and why it doesn't work in GraphEdit.

#1: Extensible or not extensible?

They are 2 variants of file storage for your source .WAV files. Most of the audio editors out there, especially the cross platform ones, do not use the WAVEFORMATEXTENSIBLE structure but rather the traditional WAVEFORMATEX. Conversely, most Microsoft components insist on the usage of WAVEFORMATEXTENSIBLE and will reject WAVEFORMATEX if one of the following is true:

   - Sample rate exeeds 48kHz
   - Channel count is greater than 2
   - Bit depth is greater than 16

Try the simple test of playing back a 24-bit or 96kHz WAV file created with your favorite audio editor, such as Audacity, in Windows Media Player and see what happens. Does it play? If it doesn't, install the Legacy HD Audio filter from the main page and try again. If it plays now that is because the filter is converting the format definition from WAVEFORMATEX to WAVEFORMATEXTENSIBLE, all without changing a single bit of data.

#2: WM ASF Writer

The v9 DirectShow WM ASF Writer (from the WMF 9.0 Runtime) does not support WAVEFORMATEXTENSIBLE. As you might know, the DirectShow WM ASF Writer is basically just a light DirectShow wrapper over the actual writer which is implemented in the Format SDK. The filter does some sanity checking before passing requests through. Is this case the sanity check goes horribly wrong as the filter checks to see if the format TagID is 1. For WAVEFORMATEXTENSIBLE the TagID is not 1, it is 0xFFFE, so the connection is rejected before the actual writer even gets to get a look at it. So how do you write to the actual writer in DirectShow without either re-writing the wrapper or using the Format SDK directly?
NOTE: The v11 WMF Runtime seems to have fixed this limitation.

#3: Workaround - Compress First

The WM ASF Writer will also allow compressed connections provided an appropriate profile is set. This means that you can compress the data using the WMA Audio DMO, connect the DMO to the writer and you're good to go. Well, almost. The ASF Writer property page doesn't have an option of loading a profile, only of selecting one of the old v8 profiles, so there is no HD audio choice for you to make in GraphEdit. To do this in code you have to do the following:
   - Create an instance of the profile manager IWMProfileManager with
   CComPtr<IWMProfileManager> pProfileManager;
   hr = WMCreateProfileManager(&pProfileManager);
   - Create the profile instance with the manager giving it the profile in text form, returning an IWMProfile object.
   CComPtr<IWMProfile> pProfile;
   hr = pProfileManager->LoadProfileByData(szProfile, &pProfile);
   - Get the IConfigASFWriter interface and set the writer to receive compressed samples
   CComQIPtr<IConfigAsfWriter2> pConfigWriter;
   pConfigWriter = asfWriter;
   - Use the IConfigASFWriter interface to set the profile
   hr = pConfigWriter->ConfigureFilterUsingProfile(pProfile);
The filters will now connect using a media type derived from the profile.

#4: Putting it all together

You will need an encoding graph that looks similar to this.
WAV to ASF conversion graph consisting of File Source, Wave Parser, AudioTypeConvert filter, WMA Encoder DMO, and WM ASF Writer
There's a code sample for an encoding graph available here, but without much in the way of error checking.
Sample 6-channel 24-bit WAV file made up of tone, clicks and a pluck, made with Audacity
Sample WMA/ASF Profile to use with this sample. (text version)

Note that the sample presented here does not work with the WMF v11 Runtime. There seems to be a currently unknown bug that causes a NULL pointer pin reference to accessed.