Naudio 生成的音频波形与某些 .wav 文件和 .m4a 文件的声音不匹配

问题描述

我尝试开发一个音频字幕工具,并使用 Naudio 生成波形供用户识别声音,每个音频大约 1 小时,我发现某些音频的波形与中间的声音不匹配的音频。

这是代码

public static class WaveFormRendererTool
    {
        public static void draw(int width,string filename)
        {
            string imagepath = filename+".png";
            var maxPeakProvider = new MaxPeakProvider();
            var rmsPeakProvider = new RmsPeakProvider(200); // e.g. 200
            var samplingPeakProvider = new SamplingPeakProvider(200); // e.g. 200
            var averagePeakProvider = new AveragePeakProvider(4); // e.g. 4

            SolidBrush brush = new SolidBrush(Color.Green);

            var myRendererSettings = new StandardWaveFormRendererSettings();
            //var myRendererSettings = new SoundCloudBlockWaveFormSettings(Color.Red,Color.Green,Color.Yellow,Color.Blue);
            myRendererSettings.Width = width;
            myRendererSettings.TopHeight = 75;
            myRendererSettings.BottomHeight = 75;
            myRendererSettings.BackgroundColor = Color.White;
            myRendererSettings.PixelsPerPeak = 1;
            myRendererSettings.TopPeakPen = new Pen(brush);
            myRendererSettings.BottomPeakPen = new Pen(brush);
            myRendererSettings.TopSpacerPen = new Pen(brush);
            



            
            var renderer = new WaveFormRenderer();
            var audioFilePath = filename;
            var image = renderer.Render(audioFilePath,averagePeakProvider,myRendererSettings);
           /* if (File.Exists(imagepath)) {
                File.Delete(imagepath);
            }*/
            image.Save(imagepath,ImageFormat.Png);
            renderer=null;
        }

the wave form is flat but already speak

这里是宽度的代码


                MediaFoundationReader wf = new MediaFoundationReader(file.FullName);
                audioLength = wf.TotalTime.TotalSeconds;
                int width = Convert.ToInt32(wf.TotalTime.TotalSeconds*10);
                
                canvas.Width = width * canstf.ScaleX;
                canvaswidth = canvas.Width;
                canvas.Height = 150;
                img.Width = width;
                //img.Height = 100;
                //img.Height = 100

                img.source = null;
                WaveFormRendererTool.draw(width,file.FullName);
                img.source = ImageRotation.LoadImageFile(file.FullName + ".png");
                scroller1.ScrollToHorizontalOffset(0);
                initialCanvas(width);

解决方法

如果无法访问您的特定源文件,我们就无法重现您的问题,尤其是因为我不知道 width 有什么价值,所以这将是一些猜测。根据音频文件,并假设您使用的是 Mark 的 NAudio.WaveFormRenderer 代码,这可能是完全准确的。

老实说,从代码和拉伸的图像来看,我不确定您是否已将图像与时间戳正确同步。如果您在初始化 4 个不同的 PeakProvider 变体时得到相似的结果,那么几乎可以肯定您的缩放有问题。

很遗憾您没有提供实际的显示代码,所以我无法指出错误可能在哪里。您需要返回并交叉检查将时间映射到渲染波形图像宽度的代码。


我在周末玩了这个文件,我有理由确定主要问题是您的计算与 WaveFormRenderer 类完成的计算之间存在脱节,这导致了长时间的漂移。

Here's the codeWaveFormRenderer 用于确定每个柱将使用多少个样本:

int bytesPerSample = (reader.WaveFormat.BitsPerSample / 8);
var samples = reader.Length / (bytesPerSample);
var samplesPerPixel = (int)(samples / settings.Width);
var stepSize = settings.PixelsPerPeak + settings.SpacerPixels;
peakProvider.Init(reader,samplesPerPixel * stepSize);

使用您的代码调用它,samplesPerPixel 会截断为 1599 - 1/10 秒内样本数的一小部分。因此,渲染图像中的每个像素不是 0.1 秒,而是每个像素 0.0999375 秒。在文件中的 00:24:14(您的屏幕截图所在的位置),累积漂移约为 0.91 秒。

幸运的是,修复很简单,如果对舍入错误的所有讨论有点违反直觉,例如:在计算宽度时使用截断而不是舍入:

int width = (int)(wf.TotalTime.TotalSeconds*10);

这应该可以保证 samplesPerPixel 的计算结果始终为 1600 而不是 1599,这样您就可以在没有漂移的情况下同步播放。这肯定比尝试重新调整代码中的所有内容以适应非常轻微的每像素漂移更简单。