65.9K
CodeProject 正在变化。 阅读更多。
Home

Aumplib: 用于音频转换的 C# 命名空间和类

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.04/5 (24投票s)

2004年10月12日

7分钟阅读

viewsIcon

276770

downloadIcon

7964

一个命名空间,包含各种提供音频转换功能的类,可以转换包括 MP3 在内的多种音频格式。

Sample Image - Aumplib.gif

引言

Aumplib (完全限定名:Arbingersys.Audio.Aumplib) 是一个命名空间,包含各种提供音频转换功能的类,可以转换包括 MP3 在内的多种音频格式。Aumplib 通过 P/Invoke 为多个著名的开源项目提供面向对象的接口。这些项目包括 LAME (MP3 编码)、libsndfile (非 MP3 音频格式) 和 madlldlib/libmad (MP3 解码)。它支持大量音频格式之间的转换,并且未来还将支持更多格式。

背景

Aumplib 的设计宗旨是为 C# 程序员提供一个简洁、易用且强大的音频转换接口,以方便使用著名的开源音频转换库 (这些库是用 C/C++ 编写的)。这些库在互联网上存在已久,但由于数据结构映射不清或不是 DLL 形式,C# 程序员不易使用。Aumplib 使用 P/Invoke “封装”了这些库,并提供了各种对象来简化它们的使用。

使用代码

Aumplib 由各种与第三方 DLL 通信的“包装器”类组成,以及主要的接口类 Aumpel,它“包装了包装器”类,以尽可能简化转换任务。源代码中包含了一个名为 TestForm.cs 的示例,演示了如何使用 Aumpel 类。我们将对其进行简要研究,因为它是主要的接口。每个包装器类都可以单独使用,但对于大多数情况,Aumpel 应该足够了。

TestForm.cs 定义了一个名为 TestForm 的类,该类派生自“Systems.Windows.Forms”类;基本上,它是一个窗口而不是控制台应用程序。当您向下浏览源代码时,会看到它首先声明变量,其中大多数是窗口控件(例如 ComboBoxProgressBar)。构造函数 TestForm() 实例化控件,定义它们的属性,设置事件处理程序,并将它们添加到窗体。要理解这部分内容,最好参考一些关于在 C# 中创建 Windows 应用程序的文档。

声明了三个私有变量,它们在后续代码中很重要。它们是 audioConverter(一个 Aumpel 对象)以及两个 Aumpel.soundFormat 类型,用于确定输入和输出声音文件的格式。

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

继续浏览,我们到达了委托函数的定义。这些对于 Aumplib 的运行至关重要。委托执行两项操作:

  1. 它们接收来自 Aumplib 的更新 (例如,当前转换已处理的字节数),您将使用这些更新来更新应用程序的状态,以及
  2. 引用结构体或 Aumpel 本身,使您能够获取更多信息、更改设置或取消转换。

如果您想在类中使用 MP3 解码功能,则必须定义至少两个委托 (MP3 解码委托接受的参数略有不同)。

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

第一个委托 ReportStatus 处理 MP3 *编码*和非 MP3 转换 (例如 WAV、AIFF、AU 等) 的状态更新。正如您所见,它非常简单。它所做的就是接收参数 totalBytes (要处理的总字节数) 和 processedBytes (已转换的总字节数),并根据它们之间的计算来更新进度条。aumpelObj 参数是对 Aumpel 对象的引用。它可以用于取消转换。

// 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;
}

我们声明的第二个委托 ReportStatusMad 处理 MP3 解码的状态更新。它的参数是 frameCount (已处理的 MP3 帧数)、byteCount (已处理的总字节数) 以及对 MadlldlibWrapper.mad_header 结构体的引用。此结构体是 DLL 中结构的映射,并提供 MP3 文件特有的信息 (例如,层、频率)。由于每次调用此委托时仅返回已处理的字节数,因此我们必须使用另一个“类”变量 soundFileSize 来获取文件总大小 (以字节为单位,在选择输入文件时计算)。然后,我们使用它来进行计算以更新进度条。如果此方法返回 false,则转换将中止。

接下来,我们来看窗体的事件处理程序。首先,我们定义 sourceFileButtondestFileButton 的处理程序,它们使用对话框来选择输入文件 (要转换的文件) 和输出文件。之后是调用 Aumpel 的方法,即 convertButton 的处理程序,convertButton_Click()。我们将在下面分块研究它,因为它包含大部分转换代码。

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;
    }

...

首先,convertButton_Click() 检查控件 comboBox1 以查看用户指定的输出声音格式。如果没有指定,则通知用户,并且该方法返回。如果指定了,则为 outputFileFormat 分配 Aumpel 可理解的值。然后我们检查 outputFileFormat 来确定需要哪种类型的转换。

...
 
  // 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;
      }

  }

...

使用 outputFileFormat,我们检查需要执行的转换类型:转换为 MP3、从 MP3 转换,还是非 MP3 (WAV、AIFF...)。如果指定的输出格式是 MP3,则执行上面的代码块。首先,它从委托定义 ReportStatus 定义 defaultCallback,然后调用处理 MP3 编码的 Aumplib.Convert() 方法的重载。此方法的参数之一是我们定义的委托,如您所见。整个过程都包含在 try/catch 子句中,任何异常都会被捕获并显示给用户。

...

   // 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;
      }

  }

...

如果您是从 MP3 解码为 WAV,则执行上面的代码。请注意,在 MP3 编码块将 defaultCallback 定义为 Aumpel.Reporter 委托的地方,此块使用 MadlldlibWrapper.Callback 使用 ReportStatusMad 委托定义 defaultCallback,我们将上面定义的委托传递给它。这就是为什么两个委托的定义不同的原因。除了这个差异之外,此代码块的行为与第一个代码块基本相同;它调用 Aumpel.Convert() 方法的重载并等待转换完成。请注意,在此 Aumpel.Convert() 的重载中,缺少 inputFileFormat 参数。这是因为源文件格式是隐含的 (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;
      }

  }

}

如果未指定转换为 MP3 或从 MP3 转换,则尝试进行非 MP3 转换。再次使用 Aumpel.Convert() 方法的重载。此转换也使用 ReportStatus 委托。此块与上面的“转换为 MP3”块之间唯一显著的差异是,在 outputFileFormat 参数中使用了一个子类型,该子类型通过 | 运算符组合。(Aumpel.soundFormat 类型本质上就是 int,并且可以像这样对待。) 在上面的代码中,我们指定输出为 16 位 PCM 以及我们选择的任何文件格式 (outputFileFormat):WAV、AIFF 或 AU。

基本就是这样。当按下“Convert”按钮时,上述代码开始执行,如果一切设置正确,转换就会发生,并通过进度条显示其进度。

注释

  • TestForm.cs 没有使用计时器对象,而计时器对象在许多 Windows 应用程序中是常见的做法。您会注意到,当执行转换时,应用程序会停止响应直到转换完成。通过使用计时器对象,可以很容易地解决这个问题,它允许在转换等长时间操作期间处理其他按键或单击。
  • 提供的源代码依赖于前面提到的开源项目。它们不包含在内。您需要下载并可能编译它们。有关详细信息,请参阅发行版中的“readme.txt”文件。
  • 在撰写本文时,源代码处于 Beta 版本。我会尽量在代码更改时更新本文,但您应始终查看 我的网站 以获取最新更新。它们将首先在那里发布。您可以在此处或在我的开发者论坛 aumplib-dev 中提问。我将乐意回答我能回答的问题并有时间回答的问题,也乐于接受关于代码优化或方法的任何建议。
  • Aumplib 接口的开源项目包括:LAME (MP3 编码)、libsndfile (非 MP3 转换) 和 madlldlib/libmad (MP3 解码)。

历史

  • 2004-10-12:发布 1.0b3 版本。
© . All rights reserved.