如何通过编程将2个或多个.WAV文件合并在一起?

20

我需要通过编程的方式,使用C#将2个或多个.wav文件合并为一个.wav文件。我知道有一个System.Media.SoundPlayer类,但我不是要播放.wav文件,而是要创建它。请注意,第三方产品不是可选项。

5个回答

32

这是一个使用NAudio构建的基本WAV串联函数。这将确保仅串联数据块(与此CodeProject文章中的代码示例不同)。它还可保护您免受串联格式不相同的WAV文件的影响。

public static void Concatenate(string outputFile, IEnumerable<string> sourceFiles)
{
    byte[] buffer = new byte[1024];
    WaveFileWriter waveFileWriter = null;

    try
    {
        foreach (string sourceFile in sourceFiles)
        {
            using (WaveFileReader reader = new WaveFileReader(sourceFile))
            {
                if (waveFileWriter == null)
                {
                    // first time in create new Writer
                    waveFileWriter = new WaveFileWriter(outputFile, reader.WaveFormat);
                }
                else
                {
                    if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat))
                    {
                        throw new InvalidOperationException("Can't concatenate WAV Files that don't share the same format");
                    }
                }

                int read;
                while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                {
                    waveFileWriter.WriteData(buffer, 0, read);
                }
            }
        }
    }
    finally
    {
        if (waveFileWriter != null)
        {
            waveFileWriter.Dispose();
        }
    }

}

良好的样例...我确认wavformat比较不像预期那样工作,正如davidair所指出的。 - Peter Walke
在将Nuget Naudio添加到项目并使用上述函数进行合并后,会出现一个构建错误。错误信息为“无法解析来自'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'的'System.Runtime.InteropServices.StandardOleMarshalObject'引用”。这是为什么呢? - Riyas
为什么不把这个加入到库中呢? - frenchie

1

如果您需要获取仅字节数组,以便插入数据库或其他人使用。您可以使用内存流:

        public static byte[] Concatenate(IEnumerable<byte[]> sourceData)
    {
        var buffer = new byte[1024 * 4];
        WaveFileWriter waveFileWriter = null;

        using (var output = new MemoryStream())
        {
            try
            {
                foreach (var binaryData in sourceData)
                {
                    using (var audioStream = new MemoryStream(binaryData))
                    {
                        using (WaveFileReader reader = new WaveFileReader(audioStream))
                        {
                            if (waveFileWriter == null)
                                waveFileWriter = new WaveFileWriter(output, reader.WaveFormat);
                            else
                                AssertWaveFormat(reader, waveFileWriter);

                            WaveStreamWrite(reader, waveFileWriter, buffer);
                        }
                    }
                }

                waveFileWriter.Flush();

                return output.ToArray();
            }
            finally
            {
                waveFileWriter?.Dispose();
            }
        }
    }

    private static void AssertWaveFormat(WaveFileReader reader, WaveFileWriter writer)
    {
        if (!reader.WaveFormat.Equals(writer.WaveFormat))
        {
            throw new InvalidOperationException("Can't concatenate WAV Files that don't share the same format");
        }
    }

    private static void WaveStreamWrite(WaveFileReader reader, WaveFileWriter writer, byte[] buffer)
    {
        int read;
        while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
        {
            writer.Write(buffer, 0, read);
        }
    }

1

请参考如何将.Wav文件合并在一起进行操作。

    private void JoinWav()
    {
        string[] files = new string[] { "1990764-ENG-CONSEC-RESPONSE7.WAV","1990764-ND_A.WAV", "1990764-SIGHT-SP.WAV",
            "1990764-SP-CONSEC-RESPONSE6.WAV","1990764-VOCABWORD-004-12-SP.WAV","bi-consec-1-successful.wav",
            "bi-transition-instruct.wav","nd_B.wav","sightreceived_B.wav","teststamp_A.wav" };
        AudioCompressionManager.Join("res.wav", files);
    }

什么是AudioCompressionManager? - Yuki
它的NuGet是什么? - Riyas

1

看看这个代码项目的例子,似乎正是你需要的,并且有很好的解释如何实现:

使用C# 2005连接波形文件

它似乎主要由从所有wav文件中提取和合并声音数据组成一个新的文件头的数据块。

编辑:我没有使用过这个,也不是专家。我只是偶然发现了这篇文章,认为它可能有用。请参见Mark Heath的答案以获得更好的解决方案。


我建议不要使用这篇文章中的代码。它假设 fmt 块的长度始终完全相同,并且数据块在所有 WAV 文件中的位置也完全相同。它还假设没有其他块存在。在 WAV 文件中,这些都不能被视为理所当然的,因此很容易生成一个垃圾 WAV 文件。 - Mark Heath
@MarkHeath 我也遇到了完全相同的问题,它创建了一个合并但不受支持的wav文件,但它适用于Android而不适用于iOS。请有任何替代想法的人提供建议。先感谢您。 - Riyas

1

对Mark的回答进行评论:

在比较波形格式时,==运算符似乎对我不起作用。更安全的做法是这样的:

if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat))

或者,您可以将读取器包装在WaveFormatConversionStream中,并完全摆脱格式检查(不确定是否适用于所有情况,但我已成功测试过)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接