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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:05:13