Godot引擎中获取麦克风输入音高的实现方案求助
获取麦克风输入音调的实现方案
如果你用的是Unity相关音频API,get_magnitude_for_frequency_range更适合获取某段频段的总能量,而非精准定位基频(也就是你要的音调)。直接通过频谱数据找峰值频率是更可靠的方式,以下是具体实现步骤和代码:
1. 初始化麦克风音频流
首先确保正确获取麦克风权限并启动音频录制:
using UnityEngine; public class PitchDetector : MonoBehaviour { private AudioSource _audioSource; private const int SampleRate = 44100; void Start() { _audioSource = GetComponent<AudioSource>(); if (Microphone.devices.Length == 0) { Debug.LogError("未检测到麦克风设备"); return; } string micDevice = Microphone.devices[0]; // 启动麦克风录制,循环模式,1秒缓冲区,采样率44100 _audioSource.clip = Microphone.Start(micDevice, true, 1, SampleRate); // 等待麦克风初始化完成 while (Microphone.GetPosition(micDevice) <= 0) { } _audioSource.Play(); } }
2. 分析频谱并提取基频
在Update循环中获取频谱数据,定位能量最高的峰值频率(即当前音调):
public int FftSampleSize = 2048; // 建议用2的幂数,比如1024、2048 private float[] _spectrumData; public float NoiseThreshold = 0.001f; // 过滤噪声的阈值 public int MinTargetFreq = 200; // 儿童乐器最低音调范围 public int MaxTargetFreq = 2000; // 儿童乐器最高音调范围 void Update() { if (_audioSource == null || !_audioSource.isPlaying) return; _spectrumData = new float[FftSampleSize]; // 获取频谱数据,使用BlackmanHarris窗口减少频谱泄漏 _audioSource.GetSpectrumData(_spectrumData, 0, FFTWindow.BlackmanHarris); float maxMagnitude = 0; int peakIndex = 0; // 计算目标频率对应的频谱索引范围,减少遍历范围 int minIndex = Mathf.RoundToInt(MinTargetFreq * 2 * FftSampleSize / (float)SampleRate); int maxIndex = Mathf.RoundToInt(MaxTargetFreq * 2 * FftSampleSize / (float)SampleRate); minIndex = Mathf.Clamp(minIndex, 1, FftSampleSize - 2); maxIndex = Mathf.Clamp(maxIndex, minIndex, FftSampleSize - 2); // 遍历频谱找峰值(排除局部小波动) for (int i = minIndex; i < maxIndex; i++) { bool isPeak = _spectrumData[i] > _spectrumData[i-1] && _spectrumData[i] > _spectrumData[i+1]; if (isPeak && _spectrumData[i] > maxMagnitude && _spectrumData[i] > NoiseThreshold) { maxMagnitude = _spectrumData[i]; peakIndex = i; } } // 计算峰值对应的实际频率 float detectedPitch = peakIndex * (float)SampleRate / (2 * FftSampleSize); if (maxMagnitude > NoiseThreshold) { Debug.Log($"当前检测到的音调:{detectedPitch:F1} Hz"); // 这里可以把detectedPitch用于你的游戏逻辑,比如匹配乐器音符 } }
关键说明
- 为什么不用
get_magnitude_for_frequency_range:这个方法返回的是某段频段的总能量,无法直接得到单个频率的峰值,用来找基频精度低且效率差。 - 频谱窗口选择:
FFTWindow.BlackmanHarris能有效减少频谱泄漏,让峰值更清晰。 - 噪声过滤:通过
NoiseThreshold忽略低能量的噪声信号,避免误判。 - 频率范围限制:针对儿童乐器的音调范围缩小遍历区间,提升性能和准确性。
内容的提问来源于stack exchange,提问作者AlvinCodin
相关产品推荐
相关产品推荐

