为啥将拆分为 wav 文件的旋律转换为拆分的 mp3 会在片段边界处产生不好的声音?

Posted

技术标签:

【中文标题】为啥将拆分为 wav 文件的旋律转换为拆分的 mp3 会在片段边界处产生不好的声音?【英文标题】:Why converting splitted onto wav files melody into splitted mp3 gives bad sound at fragments borders?为什么将拆分为 wav 文件的旋律转换为拆分的 mp3 会在片段边界处产生不好的声音? 【发布时间】:2015-12-29 11:54:30 【问题描述】:

我有一个录音程序,可以从麦克风录制声音,然后将其拆分为 WAV 单秒片段,然后将每个 WAV 转换为 MP3。

将所有 WAV 文件连接在一起时,我得到了正常的旋律。 将所有 MP3 文件连接在一起时,我的旋律很糟糕。

怎么了?我虽然 wav -> mp3 转换不应该在文件中添加或删除任何块。 这是在wav和mp3版本中创建单秒片段的代码:

    public void CreateWavAndMp3(string wav_path, string mp3_path, WaveFormat recordingFormat)
    
        WaveFileWriter wav_writer = new WaveFileWriter(wav_path, recordingFormat);

        List<byte> complete_chunk = new List<byte>(); //to store chunks one after another

        for (int i = 0; i < this.Chunks.Count; i++) //here I have raw bytes stored in List<byte[]>. I just do it that way and since WAV files are fine it's no matter
            complete_chunk.AddRange(this.Chunks[i]);           

        long maxFileLength = recordingFormat.AverageBytesPerSecond * 60;
        var toWrite = (int)Math.Min(maxFileLength - wav_writer.Length, complete_chunk.Count);

        if (toWrite > 0)
        
            wav_writer.Write(complete_chunk.ToArray(), 0, complete_chunk.Count); //write wav based on stored chunks
            wav_writer.Dispose(); //wav file written
        

        //mp3 junk
        WaveLib.WaveStream InStr = new WaveLib.WaveStream(wav_path);
        Yeti.MMedia.Mp3.Mp3Writer mp3Writer;
        Yeti.MMedia.Mp3.Mp3WriterConfig m_Config = new Yeti.MMedia.Mp3.Mp3WriterConfig(InStr.Format);

        FileStream Mp3FS = new FileStream(mp3_path, FileMode.Create, FileAccess.Write);
        mp3Writer = new Yeti.MMedia.Mp3.Mp3Writer(Mp3FS, m_Config);

        byte[] mp3buff = new byte[mp3Writer.OptimalBufferSize];
        int read = 0;
        long total = InStr.Length;

        while ((read = InStr.Read(mp3buff, 0, mp3buff.Length)) > 0)
            mp3Writer.Write(mp3buff, 0, read);

        InStr.Dispose();
        mp3Writer.Dispose();
    

测试声音文件:https://www.dropbox.com/s/e43hh4y3oli13f4/livestream.7z?dl=0 这样你也能听到。尝试加入电影制作器等中的所有文件。

【问题讨论】:

我认为 mp3 编写器在每个 Write 方法调用之后都会在末尾添加一个填充。它甚至可以将前导空格放在第一个块上。我认为在这种情况下,最好将每个波块合并为一个,然后将它们全部转换在一起。 @TaW - 我不知道我在哪里拆分...我有 OnDataAvailable 事件,它给了我一些原始字节。我将此字节添加到Single-second 类中,并检查第二个是否已经过去。如果通过,那么我将下一个字节写入新的Single-second 类,您看到了该类创建 wav 和 mp3 的方法。 @WutipongWongsakuldej - 可悲的是,分割碎片是其工作原理的主要思想。我有直播应用程序,允许管理员向所有连接的浏览器广播他的声音。它接收声音片段并一个接一个地播放。 所以您接收多个波形并将其录制到一个 mp3 文件中,而不知道它何时会结束,对吗?我认为,如果您能找到不发生填充的帧大小(在实际存在的情况下),那么您就可以开始了。您可以将它们记录到单独的文件中,然后在会话结束后将它们组合在一起。 @WutipongWongsakuldej - 你是对的......无论如何确定什么填充是没有蛮力的? 【参考方案1】:

您遇到了与 MP3 编码方式有关的问题。编解码器本身的一部分在每个文件的开头和结尾添加了填充。这是无法避免的。如果您想将它们首尾相连,则需要使用不同的格式。

一些音乐播放器通过计算添加了多少静音来解决这个问题。但即使这也可能因编解码器而异。如果您想深入了解技术细节,请查看本文档的第 2 部分:http://lame.sourceforge.net/tech-FAQ.txt

(tl;dr:该文档说“576 个样本”,16 位立体声是每个样本 4 个字节。)

另一个不存在此问题的有损编解码器是 OGG。 “Vorbis”是一个 NuGet 包,据说支持使用这种格式。

【讨论】:

呃,mp3 是 IE html5 audio 标签支持的唯一编解码器,但感谢您提供信息。我会考虑如何处理填充。 你知道如何计算增加了多少静音吗? 它可能因编解码器而异。我猜您将无法对其进行解码并以编程方式自己查看数据(如果您使用的是 HTML5。)这意味着您所能做的就是做出有根据的猜测。如果你有一个不错的音频编辑器(Audacity、Audition 等)并且你知道 MP3 都来自同一个编解码器,你可以在音频编辑器中打开结果 MP3 并放大到足以测量静音之前的数量声音开始。 另外,如果您需要 IE 支持,并且不介意将 IE9 设为最低,您可以同时提供 AAC 和 OGG 格式。它们是有损格式,但应该足够了。在这两种格式之间,您应该拥有完整的浏览器支持。 谢谢。 Audacity 是一个很好的解决方案。我将在 JS 中使用计时器并开始主动播放下一个片段。

以上是关于为啥将拆分为 wav 文件的旋律转换为拆分的 mp3 会在片段边界处产生不好的声音?的主要内容,如果未能解决你的问题,请参考以下文章

将 G.723.1 转换为普通 wav 和拆分通道?

如何在java中将Wav文件拆分为通道?

iOS 将立体声 mp3 拆分为单声道 aac

将大型 mp3 文件拆分为多个文件的正确语法

将 MP3 文件转换为 WAV

在正则表达式上批量拆分文件名