You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NAudio中AudioFileReader.Length与TotalTime的关联及波形图适配问题

嘿,这个问题我刚好碰到过,让我一步步帮你解决!

理清TotalTime和Length的关联

首先得明确AudioFileReader里这两个属性的核心关系:

  • Length:是解码后PCM音频的总字节数(MP3是压缩格式,AudioFileReader会先把它解码成原始PCM数据,这个值就是解码后的数据大小)
  • TotalTime:是基于PCM参数计算出的实际播放时长,公式是:
    TotalTime = TimeSpan.FromSeconds(Length / (SampleRate * Channels * BitsPerSample/8))
    
    反过来,用时长也能反推PCM字节数:
    Length = SampleRate * Channels * (BitsPerSample/8) * TotalTime.TotalSeconds
    
    这俩是完全匹配的,因为TotalTime本质上就是用Length和WaveFormat的参数计算出来的。
解决波形图空白问题:按时间动态计算Bitmap宽度

你想要1秒对应15像素,直接用TotalTime算宽度就刚好匹配音频时长,不会有多余空白:

// 定义每秒对应的像素数
int pixelsPerSecond = 15;
// 计算准确的Bitmap宽度:向上取整避免丢帧
int bitmapWidth = (int)Math.Ceiling(reader.TotalTime.TotalSeconds * pixelsPerSecond);
// 高度保持你原来的200
Bitmap bim = new Bitmap(bitmapWidth, 200);
using (Graphics g = Graphics.FromImage(bim))
{
    // 后续绘制波形的逻辑
}
采样数与像素的映射(绘制波形的关键)

要让波形准确适配宽度,还得把采样数据对应到每个像素上:

  1. 先算总采样帧数(每个帧包含所有通道的采样点,比如立体声就是左右两个采样算一个帧):

    // 用时长+采样率计算,更直观
    int totalSampleFrames = (int)(reader.TotalTime.TotalSeconds * reader.WaveFormat.SampleRate);
    // 或者用Length算,结果完全一致
    int totalSampleFrames = (int)(reader.Length / reader.WaveFormat.BlockAlign);
    

    (BlockAlign是每个采样帧的字节数,等于Channels * BitsPerSample/8)

  2. 计算每个像素需要覆盖的采样帧数:

    int samplesPerPixel = totalSampleFrames / bitmapWidth;
    int remainingSamples = totalSampleFrames % bitmapWidth;
    

    剩下的remainingSamples可以分配到前几个像素上,保证所有采样都被覆盖到。

  3. 绘制时,每个像素对应samplesPerPixel个采样帧,计算这段采样的峰值(或平均值)来确定波形的高度,这样出来的波形既完整又不会有空白。

为什么之前会有空白?

你固定了12800像素的宽度,但你的音频时长对应的实际宽度(按1秒15像素算)小于12800,所以绘制完波形后剩下的像素自然就是空白啦。现在用动态计算的宽度,刚好匹配音频长度,问题就解决了。

内容的提问来源于stack exchange,提问作者Ben

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:33:28