Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Aumplib: C# Namespace And Classes For Audio Conversion

0.00/5 (No votes)
12 Oct 2004 4  
A namespace of various classes that provide audio conversion capabilities, and can convert between many audio formats, including MP3.

Sample Image - Aumplib.gif

Introduction

Aumplib (fully qualified name: Arbingersys.Audio.Aumplib) is a namespace of various classes that provide audio conversion capabilities, and can convert between many audio formats, including MP3. Aumplib provides an OO interface to several prominent Open Source projects, using P/Invoke. These projects are LAME (MP3 encoding), libsndfile (non-MP3 audio formats), and madlldlib/libmad (MP3 decoding). It supports conversion between a large number of audio formats, and in the future should support even more.

Background

Aumplib was designed to provide a clean, easy, and powerful audio conversion interface in C# to prominent Open Source audio conversion libraries (which were written in C/C++). These libraries have existed on the Internet for some time, but were not readily usable by C# programmers because the data structures were not clearly mapped or they were not in DLL form. Aumplib "wraps" these libraries using P/Invoke and offers various objects that simplify using them.

Using the code

Aumplib is made up of various "wrapper" classes that communicate with the third-party DLLs, and Aumpel, the main interface class, which "wraps the wrapper" classes to make conversion as simple a task as possible. An example, TestForm.cs, is included in the source that demonstrates using the Aumpel class. We will make a brief study of this since it is the primary interface. Each of the wrapper classes can be used individually, but for most purposes Aumpel should suffice.

TestForm.cs defines a class called TestForm which is derived from the "Systems.Windows.Forms" class; basically, it is a window rather than a console application. As you scan down the source, you will see that it first declares variables, most of which are window controls (i.e., ComboBox, ProgressBar). The constructor TestForm() instantiates the controls, defines their properties, sets up event handlers, and adds them to the form. To understand this portion, it would be best to reference some documentation on creating a Windows application in C#.

Three private variables are declared that are important further on in the code. These are audioConverter, an Aumpel object, and two Aumpel.soundFormat types that are used to determine the input and output sound file formats.

private Aumpel audioConverter = new Aumpel();
private Aumpel.soundFormat inputFileFormat;
private Aumpel.soundFormat outputFileFormat;

As we scan further, we reach the definitions of the delegate functions. These are crucial to the operation of Aumplib. The delegates do two things:

  1. They receive updates from Aumplib (i.e., how many bytes have been processed in the current conversion) that you will use to update status of your application, and
  2. reference either structures or Aumpel itself, giving you the ability to garner further information, change settings, or cancel a conversion.

If you want MP3 decoding capabilities in your class, you will have to define at least two delegates (the MP3 decoding delegate takes slightly different parameters).

// Conversion callback (lame,libsndfile)

private static void
ReportStatus(int totalBytes, 
    int processedBytes, Aumpel aumpelObj)
{
     progressBar1.Value = (int)(((float)processedBytes/(float)totalBytes)*100);
}

The first delegate, ReportStatus, handles status updates for both MP3 encoding and non-MP3 conversions (i.e., WAV, AIFF, AU, etc). As you can see, it is very simple. All it really does is take the parameters totalBytes (total bytes to process), and processedBytes (how many of the total have been converted), and updates the progress bar from a calculation based on them. The aumpelObj parameter is a reference to the Aumpel object. It can be used for cancellation of a conversion.

// Decoding callback (madlldlib)


private static bool 

ReportStatusMad(uint frameCount, 
        uint byteCount, ref MadlldlibWrapper.mad_header mh) 
{

    progressBar1.Value = (int)(((float)byteCount/(float)soundFileSize)*100);
    return true;
}

The second delegate we declare, ReportStatusMad, handles status updates for the MP3 decoding. Its parameters are frameCount (how many MP3 frames processed), byteCount (total bytes processed), and a reference to the MadlldlibWrapper.mad_header structure. This structure is a mapping to a structure in the DLL and provides information specific to the MP3 file (i.e., layer, frequency). Because only the bytes processed are returned each time this delegate is called, we have to use another "class" variable, soundFileSize to give us the total size of the file in bytes (it is calculated when the input file is selected). We then use it in the calculation to update the progress bar. If this method returns false, the conversion will abort.

Next, we come upon the event handlers of the form. First, we define the handlers for sourceFileButton and destFileButton, which use a dialog box to select an input file (file to convert) and an output file. After that comes the method that invokes Aumpel, the handler for convertButton, convertButton_Click(). We will study it in pieces below, since it contains the bulk of the conversion code.

protected void
convertButton_Click (object sender, System.EventArgs e)
{

    // Set conversion type


    switch((string)comboBox1.SelectedItem)
    {
        case "WAV":
            outputFileFormat = Aumpel.soundFormat.WAV;
            break;
        case "MP3":
            outputFileFormat = Aumpel.soundFormat.MP3;
            break;
        case "AU":
            outputFileFormat = Aumpel.soundFormat.AU;
            break;
        case "AIFF":
            outputFileFormat = Aumpel.soundFormat.AIFF;
            break;
        default:
            MessageBox.Show("You must select a type to convert to.",
                "Error", MessageBoxButtons.OK);
            return;
    }

...

First, convertButton_Click() checks the control comboBox1 to see what output sound format the user specified. If none is specified, the user is notified, and the method returns. If one is, it assigns outputFileFormat a value Aumpel understands. Then we check outputFileFormat to determine what type of conversion is needed.

...
 
  // Convert to MP3


  if ( (int)outputFileFormat == (int)Aumpel.soundFormat.MP3 )
  {

      try
      {

          Aumpel.Reporter defaultCallback = new Aumpel.Reporter(ReportStatus);

          audioConverter.Convert(inputFile, 
                  (int)inputFileFormat, outputFile, 
                  (int)outputFileFormat, defaultCallback);

          progressBar1.Value = 0;

          destFileLabel.Text = outputFile = "";
          sourceFileLabel.Text = inputFile = "";
          
          MessageBox.Show("Conversion finished.", "Done.", MessageBoxButtons.OK);

      }
      catch (Exception ex)
      {
          ShowExceptionMsg(ex);
          return;
      }

  }

...

Using outputFileFormat, we check to see what kind of conversion needs to be done: to MP3, from MP3, or non-MP3 (WAV, AIFF...). If the output format specified was MP3, then the block of code above executes. First, it defines defaultCallback from the delegate definition ReportStatus, and then calls the overload of the Aumplib.Convert() method that handles MP3 encoding. One of the parameters to this method is the delegate we have defined, as you can see. The entire thing is wrapped in a try/catch clause, and any exceptions are caught and displayed to the user.

...

   // From MP3:


  else if ( (int)inputFileFormat == (int)Aumpel.soundFormat.MP3 )
  {

      try
      {

          MadlldlibWrapper.Callback defaultCallback = 
              new MadlldlibWrapper.Callback(ReportStatusMad);

          // Determine file size

          FileInfo fi = new FileInfo(inputFile);
          soundFileSize = (int)fi.Length;

          audioConverter.Convert(inputFile, 
              outputFile, outputFileFormat, defaultCallback);
          progressBar1.Value = 0;

          destFileLabel.Text = outputFile = "";
          sourceFileLabel.Text = inputFile = "";

          MessageBox.Show("Conversion finished.", "Done.", MessageBoxButtons.OK);

      }
      catch (Exception ex)
      {
          ShowExceptionMsg(ex);
          return;
      }

  }

...

If you are decoding from MP3 to WAV, the above code executes. Notice that where the MP3 encoding block defines defaultCallback as an Aumpel.Reporter delegate, this block defines defaultCallback using MadlldlibWrapper.Callback, passing it the delegate definition ReportStatusMad which we defined above. This is the reason the definitions of the two delegates are different. Other than that difference, this block of code behaves essentially the same as the first one; it calls an overload of the Aumpel.Convert() method and waits until the conversion is finished. Note that in this overload of Aumpel.Convert(), the inputFileFormat parameter is missing. This is because the source file format is implied (MP3).

...

  // Non-MP3 soundfile conversion:


  else
  {

      try
      {

          Aumpel.Reporter defaultCallback = new Aumpel.Reporter(ReportStatus);

          audioConverter.Convert(inputFile, 
                  (int)inputFileFormat, 
                  outputFile, 
                  (int)(outputFileFormat | Aumpel.soundFormat.PCM_16), 
                  defaultCallback);

          progressBar1.Value = 0;

          destFileLabel.Text = outputFile = "";
          sourceFileLabel.Text = inputFile = "";
          
          MessageBox.Show("Conversion finished.", "Done.", MessageBoxButtons.OK);

      }
      catch (Exception ex)
      {
          ShowExceptionMsg(ex);
          return;
      }

  }

}

If neither conversion to MP3 nor from MP3 is specified, then a non-MP3 conversion is attempted. Again, an overload of the Aumpel.Convert() method is used. The ReportStatus delegate is used for this conversion as well. The only significant difference between this block and the "conversion-to-MP3" block above is that a sub-type is used with the outputFileFormat parameter that is combined via the | operator. (The Aumpel.soundFormat types are basically just int, and can be treated as such.) In the code above, we are specifying an output of 16-bit PCM and whatever file format we have selected (outputFileFormat): WAV, AIFF, or AU.

That's basically it. When the "Convert" button is pressed, the above code begins to fire, and if everything is set appropriately, the conversion will take place, showing you its progress via the progress bar.

Notes

  • TestForm.cs does not use a timer object, which is common practice in many Windows applications. You will notice when you execute a conversion that the application stops responding until the conversion is finished. This can pretty easily be fixed by using the timer object, which allows other key presses or clicks to be processed during a long operation like a conversion.
  • The source provided depends on the Open Source projects mentioned. They are not included. You will need to download and possibly compile them. See the "readme.txt" file in the distro for details about this.
  • The source at the time of this writing is in Beta. I will try to keep this article updated as code changes, but you should always check my website for the latest updates. They will be there first. You can ask questions here or in my developer's forum, aumplib-dev. I would be glad to field what questions I can and have time for, and also glad for any suggestions about code optimizations or approaches.
  • The Open Source projects interfaced by Aumplib are: LAME (MP3 encoding), libsndfile (non-MP3 conversion), and madlldlib/libmad (MP3 decoding).

History

  • 2004-10-12: Initial post of version 1.0b3.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here