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

C# WAV 文件类,音频混合和一些轻量级音频处理

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (27投票s)

2009 年 4 月 20 日

CPOL

7分钟阅读

viewsIcon

312016

downloadIcon

13073

一个用于读取和写入 WAV 文件、轻量级音频处理以及 WAV 文件混合的 C# 类。

引言

我对音乐的兴趣和爱好之一。最近,我创建了一个 C# 类,可以用来打开、读取和写入 WAV 音频文件。我的 WAVFile 类支持 8 位和 16 位音频,单声道或立体声。它的一个特殊功能是提供一个可以混合 WAV 音频文件的方法,以便可以同时听到每个源 WAV 文件的音频。可以混合任意数量的 WAV 文件。唯一的限制是它们都需要具有相同的采样率。否则,WAV 文件可以是单声道或立体声,包含 8 位或 16 位音频。

所有音频处理和混合都在 WAVFile 类内部完成,不需要任何外部库。

除了 WAVFile 类之外,我还包含了一个用于混合 WAV 音频文件的应用程序。编译后的应用程序和完整源代码包含在文章顶部的链接中。

包含的应用程序

下面是应用程序的屏幕截图

要将 WAV 文件添加到列表中,您可以将文件拖放到 GUI 上,或选择“文件”>“添加音频文件”。

必备组件

编译后的应用程序需要 .NET 运行时版本 3.5。提供的代码是用 Visual C# 2008 创建的;早期版本的 Visual C# 可能无法打开或编译代码。

背景

WAV 音频文件由文件开头的头组成,其中包含标识文件类型的字符串(“RIFF”和“WAVE”),以及有关文件中音频的信息(声道数、采样率、每声道位数、数据大小等)。头后面是所有音频数据。数字音频数据是数值:每个样本是一个整数,表示该时间点的音频信号的电平。

在混合音频(以及一般处理音频)时需要处理的一个问题是 音频削波。数字音频削波是由样本大小(8 位或 16 位)的数值范围限制引起的:当音频样本被处理时(例如,如果音量增加),结果值可能会超出数值范围。发生这种情况时,会产生(通常很响的)爆裂声和咔嗒声,这是不希望的。因此,为了混合 WAV 文件,我的 WAVFile 类中的 MergeAudioFiles() 方法将首先分析每个音频文件以确定最高的音频样本,然后降低所有音频文件的音量,并将它们复制到一个临时目录。音频音量降低的程度足以确保在混合过程中添加音频样本时,不会超出数据的数值范围。混合后,最终混合文件的音量会提高到可接受的水平。在某些情况下,混合过程可能需要一两分钟,这是正常的。

Using the Code

WAVFile 类将是一个用于读取和写入 WAV 音频文件的有用类。该类可以打开、读取和写入 WAV 音频。以下是一些最重要的方法(请注意,它们都将为严重或潜在严重的错误抛出异常)

  • string Open(String pFilename, WAVFileMode pMode): 打开现有的 WAV 文件。返回一个 string,成功时为空,失败时包含失败原因。此方法将为更严重的错误抛出异常。
  • void Create(String pFilename, bool pStereo, int pSampleRate, short pBitsPerSample, bool pOverwrite): 创建一个新的 WAV 文件。
  • byte[] GetNextSample_ByteArray(): 在读取或读/写模式下,此方法将下一个样本作为字节数组返回,对于 8 位音频将包含一个字节,对于 16 位音频将包含两个字节。
  • byte GetNextSample_8bit(): 这是一个方便的方法,以字节(8 位)的形式返回下一个音频样本。这仅在处理 8 位音频时有效。
  • short GetNextSample_16bit(): 这是一个方便的方法,以短整型(16 位)的形式返回下一个音频样本。这仅在处理 16 位音频时有效。
  • short GetNextSampleAs16Bit(): 与上述方法类似,除了它将始终返回一个 16 位值,无论音频是 8 位还是 16 位。此外,如果音频是 8 位,该值将被缩放到 16 位。例如,如果音频是 8 位,那么最大值的 50% 在缩放到 16 位值后仍然是最大值的 50%。
  • byte GetNextSampleAs8Bit(): 与上述方法类似,无论音频是 8 位还是 16 位,它始终以 8 位值返回下一个样本,并且 16 位值将被缩减到 8 位。
  • AddSample_ByteArray(byte[] pSample): 在写入或读/写模式下,此方法将音频样本添加到 WAV 文件。它接受一个字节数组作为参数;对于 8 位音频,数组需要包含一个字节,对于 16 位音频,需要包含两个字节。
  • void AddSample_8bit(byte pSample): 一个方便的方法,用于将 8 位值添加到 WAV 文件。WAV 文件必须包含 8 位音频。
  • void AddSample_16bit(short pSample): 一个方便的方法,用于将 16 位值添加到 WAV 文件。WAV 文件必须包含 16 位音频。
  • void Close(): 关闭音频文件。

此外,以下是一些额外的实用程序函数(它们都是静态的)

  • void MergeAudioFiles(String[] pFileList, String pOutputFilename, String pTempDir): 合并音频文件。此方法是静态的。
  • void AdjustVolume_Copy(String pSrcFilename, String pDestFilename, double pMultiplier): 调整 WAV 文件的音量,将其复制到新文件。
  • void AdjustVolumeInPlace(String pFilename, double pMultiplier): 调整 WAV 文件的音量(会修改原始文件)。
  • void AdjustVolume_Copy_8BitTo16Bit(String pSrcFilename, String pDestFilename, double pMultiplier): 调整 WAV 文件的音量,并将 8 位样本转换为 16 位样本,然后将其复制到新文件。
  • void Convert_8BitTo16Bit_Copy(String pSrcFilename, String pDestFilename): 将 8 位 WAV 文件转换为 16 位 WAV 文件,然后将其复制到新文件。
  • void CopyAndConvert(String pSrcFilename, String pDestFilename, short pBitsPerSample, bool pStereo): 将 WAV 文件复制到新文件,并更改每样本位数和/或声道数。

以下是一些(只读)属性

  • SampleRateHz: 音频采样率(以 Hz 为单位)。
  • BitsPerSample: 每样本位数(即 8 或 16)。
  • NumChannels: 音频声道数 - 1(单声道)或 2(立体声)。
  • IsStereo: 布尔值,表示音频是否为立体声(有两个声道)。
  • NumSamples: 音频样本数。
  • NumSamplesRemaining: 剩余音频样本数(当读取现有 WAV 文件时)。

一个打开 WAV 文件并循环读取每个音频样本的示例(假设音频文件包含 16 位音频)

WAVFile audioFile = new WAVFile();
String warning = audioFile.Open("someAudioFile.wav", WAVFile.WAVFileMode.READ);
if (warning == "")
{
   short audioSample = 0;
   for (int sampleNum = 0; sampleNum < audioFile.NumSamples; ++sampleNum)
   {
      audioSample = audioFile.GetNextSampleAs16Bit();
   }
}

MergeAudioFiles()方法是静态的,因此您无需 WAVFile 实例即可调用它。其参数包括一个字符串数组(源 WAV 文件名)、目标文件名以及可作为临时目录的目录。例如:

// Create an array of filenames for the WAVFile class
String[] audioFilenames = new String[3];
audioFilenames[0] = "SourceFile1.wav";
audioFilenames[1] = "SourceFile2.wav";
audioFilenames[2] = "SourceFile3.wav";
// Mix the audio files together into Output.wav
WAVFile.MergeAudioFiles(audioFilenames, "Output.wav", "C:\\Temp");

异常

以下(在 WAVFileExceptions.cs 中声明的)异常由 WAVFile 类抛出:

  • WAVFileReadException: 在读取 WAV 文件时发生问题时抛出。
  • WAVFileWriteException: 在写入 WAV 文件时发生问题时抛出。
  • WAVFileBitsPerSampleException: 遇到不支持的每样本位数时抛出。BitsPerSample 属性将包含无效的每样本位数。
  • WAVFileSampleRateException: 遇到不支持的采样率时抛出。SampleRate 属性将包含无效的采样率。
  • WAVFileAudioMergeException: 由 WAVFile.MergeAudioFiles() 抛出,在混合 WAV 文件时发生问题。
  • WAVFileIOException: 发生一般性 I/O 问题时抛出。
  • WAVFileException: 发生一般/未分类问题时抛出。

除了异常类提供的 Message 属性之外,这些异常类还提供另一个属性 ThrowingMethodName,它是一个 string,包含抛出异常的方法名称。

关注点

值得注意的是,根据规范,WAV 音频文件中的数据始终是小端序。在大端序系统上,在处理音频数据之前必须反转字节顺序,并且在将样本保存到 WAV 文件之前必须反转样本的字节顺序。我的 WAVFile 类会自动处理此问题;例如,如果系统是大端序,那么在使用 GetNextSample_16bit() 检索 WAV 文件中的音频样本时,或在使用 AddSample_16bit() 将 16 位样本添加到 WAV 文件时,字节顺序将自动反转,以便数据顺序正确。

在创建 WAVFile 类时,有必要查找 WAV 文件格式规范。我找到了许多描述 WAV 文件格式的网页。每个页面基本上都有相同的信息,但带有不同的注释。我发现以下四个页面很有用:

历史

  • 2009 年 4 月 20 日:创建。
© . All rights reserved.