求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
相关产品推荐
相关产品推荐

