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

求NAudio WaveIn搭配BPMDetect类的示例,当前BPM检测始终为0

解决NAudio BPMDetect始终返回0的问题

我看了你的代码,确实有几个关键问题导致BPM检测一直返回0,咱们一个个来修正:

问题1:声道数不匹配

你初始化BPMDetect时用的是设备的原始通道数(channels = WaveIn.GetCapabilities(deviceNumber).Channels),但紧接着把WaveIn强制设置为单声道(new WaveFormat(ConstWaveInSampleRate, 16, 1))。这就导致BPMDetect期望接收多声道数据,但实际传入的是单声道,数据格式不匹配,自然无法正确检测。

问题2:样本转换错误

你的音频输入是16位PCM格式(每个样本占2字节),但你处理缓冲区的方式完全错了:

  • 你用e.Buffer.Where((value, index) => index % 2 == 0).ToArray()把偶数索引的字节单独取出来,这相当于把每个16位样本砍掉了一半,数据直接损坏。
  • 然后你把这个错误的byte数组当成FloatBuffer传入PutSamples,但16位PCM是整数格式,不是浮点格式,BPMDetect根本无法识别这些无效数据。

问题3:检测时机过早

BPMDetect需要积累足够多的音频数据(通常至少几秒的音频)才能计算出可靠的BPM值。你每次DataAvailable事件触发就立刻输出m_BpmDetect.Bpm,这时候数据量太少,还不足以完成检测,所以返回0。


修正后的完整示例代码

using NAudio.Wave;
using NAudio.Dsp;
using System;
using System.Linq;

public class NAudioBpmDetect
{
    const int ConstWaveInSampleRate = 44100;
    private BufferedWaveProvider bufferedWaveProvider;
    private WaveIn m_WaveIn;
    private WaveOut m_WaveOut;
    private BPMDetect m_BpmDetect;
    // 记录累计的样本数,用于判断是否足够进行BPM检测
    private int totalSamplesReceived = 0;
    // 大概需要3秒左右的单声道44.1kHz音频(44100*3=132300样本)
    private const int MinSamplesForBpm = 132300;

    public NAudioBpmDetect()
    {
        int deviceNumber = 0;
        m_WaveIn = new WaveIn();
        m_WaveIn.DeviceNumber = deviceNumber;
        
        // 统一使用单声道,确保和BPMDetect的通道数一致
        int targetChannels = 1;
        m_WaveIn.WaveFormat = new WaveFormat(ConstWaveInSampleRate, 16, targetChannels);
        
        m_WaveOut = new WaveOut();
        // 初始化BPMDetect时使用和WaveIn一致的通道数
        m_BpmDetect = new BPMDetect(targetChannels, ConstWaveInSampleRate);
        
        m_WaveIn.DataAvailable += WaveIn_DataAvailable;
        bufferedWaveProvider = new BufferedWaveProvider(m_WaveIn.WaveFormat);
        bufferedWaveProvider.DiscardOnBufferOverflow = true;
        
        m_WaveOut.Init(bufferedWaveProvider);
        m_WaveIn.StartRecording();
        m_WaveOut.Play();
        
        Console.WriteLine("开始录音,正在积累音频数据...");
    }

    void WaveIn_DataAvailable(object sender, WaveInEventArgs e)
    {
        bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
        
        // 处理16位PCM样本:每个样本占2字节,转换为short数组
        var waveBuffer = new WaveBuffer(e.Buffer);
        int sampleCount = e.BytesRecorded / 2; // 16位,每个样本2字节
        short[] shortSamples = new short[sampleCount];
        Array.Copy(waveBuffer.ShortBuffer, shortSamples, sampleCount);
        
        // 将short转换为float(BPMDetect需要float类型的样本,范围-1到1)
        float[] floatSamples = shortSamples.Select(s => s / 32768f).ToArray();
        
        // 传入样本到BPMDetect
        m_BpmDetect.PutSamples(floatSamples, (uint)floatSamples.Length);
        totalSamplesReceived += floatSamples.Length;
        
        // 当积累足够样本后,再输出BPM
        if (totalSamplesReceived >= MinSamplesForBpm)
        {
            float detectedBpm = m_BpmDetect.Bpm;
            // 避免输出0(可能还在检测中,多等几帧)
            if (detectedBpm > 0)
            {
                Console.WriteLine($"检测到BPM: {detectedBpm:F2}");
                // 可以选择停止检测或者继续更新
                // m_WaveIn.StopRecording();
                // m_WaveOut.Stop();
            }
        }
    }
}

额外说明

  • 我添加了totalSamplesReceived来跟踪积累的样本数,当达到大概3秒的音频量时再尝试获取BPM,这样结果会更可靠。
  • 16位PCM转float的正确方式是把short值除以32768(因为16位有符号整数的范围是-32768到32767,转换后得到-1到1之间的浮点值,符合BPMDetect的要求)。
  • 如果需要处理立体声,只需要把targetChannels改成2,同时确保WaveIn的WaveFormat也是2通道,BPMDetect会自动处理立体声数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:50:20