Introduction
As I mentioned in my previous article (C Sharp MP3 Compressor), I offer now a Windows Media Audio (WMA) compressor. This time I used managed C++ instead of C# in order to avoid the translation of interfaces and structures of the Windows Media Format SDK (WMF SDK). Microsoft has provided very limited managed support for their Windows Media SDKs (e.g. for the Windows Media Services SDK), but not for the WMF SDK. I think that Microsoft will logically include managed support for their WMF SDK in future versions, that's why I decided to avoid doing any translation. What I did was to create managed C++ classes that use some unmanaged classes, and have those unmanaged classes interact directly with the various Windows Media Format objects and interfaces.
In this work, there is code from the article A low level audio player in C# by Ianier Munoz.
Background
If you have never looked into the WMF SDK before, it is advisable that you take a look at the Windows Media Format SDK documentation for better understanding this article.
The following diagram represents the steps to create an Advanced Systems Format stream (ASF: the container format for Windows Media Audio and Windows Media Video-based content) using the WMF SDK:
Figure 1: Writing ASF Streams.
This diagram shows how to create ASF streams using a custom "Writer Sink". Custom writer sinks are needed for writing the resulting ASF stream to any kind of stream (in this case, any class derived from System.IO.Stream
). As the diagram shows, the process is relatively simple. For more details about ASF creation, you can see Writing ASF Files and Using Custom Sinks in the Windows Media Format SDK documentation.
The compressor
The following figure is a simplified class diagram, which describes the managed version of the WMA compressor:
Figure 2: Windows Media Audio Compressor: Managed class diagram.
The main class is WmaWriter
, which interacts with the WMF SDK through unmanaged wrapper classes. It also acts as the sink object so the buffers received by its Write
method are sent to the WMF writer object, which in turn sends back the compressed buffers through the sink interface. Finally, those buffers are written to the destination stream. WmaWriter
receives the profile information through the class WmaWriterProfile
, which represents the WMF IWMProfile
.
WmaWriterProfileManger
is a static class that contains some methods to handle profile creation and information. It also contains an array of WmaWriterProfile
instances whose values represent the WMF System Profiles (audio only). This list of profiles should be enough for most purposes, but other profiles could also be created.
ManBuffer
is a utility class for interfacing with the WMF SDK. It is just a managed implementation of buffer objects that implement INSSBuffer
. Finally, ProfileManager
is a utility class that creates and holds the WMF Profile Manager (IWMProfileManager
).
If the WMF SDK was ported to managed code, this compressor could be implemented as shown in figure 2, with no more added complexity. Unfortunately, there is no such managed WMF SDK, therefore, some tricks and wrapper classes are necessary to implement the compressor. If you are interested in knowing the details, you can look at figure 3 in the implementation details section of this article.
Using the code
Using the WmaWriter
is easy. You can use it in the same way you use BinaryWriter
(WmaWriter
is a specialization of BinaryWriter
: see figure 2). The only difference with BinaryWriter
is that you must indicate at creation time the format of the input data (data that the writer will receive) using a WaveFormat
instance, and a profile defining the settings of the resulting compressed stream.
The following VB.NET code shows a simple way of compressing a WAV file using this compressor:
Imports Yeti.MMedia
Imports Yeti.MMedia.Wmf
Imports WaveLib
Imports System.IO
....
Dim InStr As WaveStream
Dim writer As AudioWriter
Dim buff() As Byte
Dim read As Integer
InStr = New WaveStream("SomeFile.wav")
Try
writer = New WmaWriter(New FileStream("SomeFile.wma", FileMode.Create), _
InStr.Format)
Try
buff = New Byte(writer.OptimalBufferSize - 1) {}
read = InStr.Read(buff, 0, buff.Length)
While (read > 0)
writer.Write(buff, 0, read)
read = InStr.Read(buff, 0, buff.Length)
End While
Finally
writer.Close()
End Try
Finally
InStr.Close()
End Try
I made the sample project in VB.NET just to please those who love VB. Anyway, you can use the compressor in C#, Managed C++, J# or any other CLS compliant language. The sample project of the MP3 compressor (also included in the source files) is written in C#. You can also refer to my C Sharp MP3 Compressor article.
Another example of using the writer could be an improved version of the ripper described in my article "C Sharp Ripper" to rip directly to WMA format. In the file Form1.cs, inside the handler of the "Save as.." button, there is the line:
m_Writer = new WaveWriter(WaveFile, Format);
Which may be changed to:
m_Writer = new WmaWriter(WaveFile, Format);
The rest of the code remains without change and, of course, if you need more control on the compressor parameters, then you should add extra code to supply a WmaWriterProfile
instance in the constructor.
The figure below is a more complete class diagram of the compressor, showing the managed classes and interfaces and their relations with the WMF SDK interfaces.
Figure 3: Windows Media Audio Compressor: implementation details.
I'm going to avoid the explanation of the implementation details. I just included the class diagram for those who want to see how this project was implemented and help them to better understand the code included with this article.
If you only want to use the compressor as is, then you can safely ignore these implementation details.
Conclusion
As showed here, the use of this compressor is relatively easy even if the implementation is somewhat complex due to interaction of managed and unmanaged code. There are some features of Windows Media Format that are not included in this project, such as DRM support, metadata attributes and two-pass encoding.
The sample code is given for demonstration purposes only, you can use it as reference if you need to develop some product with these technologies. When a managed version of WMF SDK is available, it will be easier to implement managed solutions using WMF. At the moment, this approach could be a good solution, considering that the unmanaged part can be suppressed without changing the design of managed part.