麦克风,增益值和频谱值无法使用Unity同步

时间:2019-04-22 13:14:16

标签: unity3d audio-recording

我正在制作一个简单的语音可视化程序。我的目标是:

  1. 播放麦克风输入

  2. 可视化语音频谱并实时获取

这是我的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class VisualizeVoice : MonoBehaviour
{
    private const int NUM_SPECTRUM_SAMPLES = 256;
    private const int NUM_SPECTRUM_BARS = 32;
    private const int NUM_PCM_SAMPLES = 16000;
    private const float BAR_DROP_SPEED = 1e-3f;
    private const int NUM_SAMPLES_TO_AVERAGE = 8;

    private string _deviceName;
    private float[] _spectrumData = new float[NUM_SPECTRUM_SAMPLES];
    private float[] _fPCMData = new float[NUM_PCM_SAMPLES];
    private float _gain = 0;

    private AudioClip _audio;           // Audio from microphone
    private AudioSource _playback;      // To play the audio from microphone

    // For visualization
    private GameObject[] _spectrumBars = new GameObject[NUM_SPECTRUM_BARS];
    private GameObject _gainBar;

    // Start is called before the first frame update
    void Start()
    {
        if (Microphone.devices.Length == 0) {
            Debug.LogError("No Microphone");
            return;
        }
        _deviceName = Microphone.devices[0];
        Debug.Log("Current microphone is " + _deviceName);

        if ((_playback = this.GetComponent<AudioSource>()) == null) {
            _playback = this.gameObject.AddComponent<AudioSource>();
        }
        _playback.loop = true;
        _playback.bypassEffects = true;
        _playback.bypassListenerEffects = true;
        _playback.bypassReverbZones = true;
        _playback.priority = 0;
        _playback.pitch = 1;
        _playback.clip = _audio = Microphone.Start(_deviceName, true, 1, AudioSettings.outputSampleRate);
        // Sync microphone and playback, but it always fails
        float waitTime = 0;
        while (!(Microphone.GetPosition(_deviceName) > 0) && waitTime <= 2)
            waitTime += Time.deltaTime;
        if (waitTime > 2) {
            Debug.LogError("time out waiting for microphone");
        }
        _playback.Play();

        InitVisualization();
    }

    // Update is called once per frame
    void Update()
    {
        // Get PCM data and calculate gain
        var audioPosition = Microphone.GetPosition(_deviceName);
        _audio.GetData(_fPCMData, audioPosition);
        UpdateGain();
        // Get spectrum data
        _playback.GetSpectrumData(_spectrumData, 0, FFTWindow.BlackmanHarris);
        // Update visualization
        UpdateVisualization();
    }

    private void InitVisualization() 
    {
        // Initialize spectrum bars
        for (int ibar = 0; ibar < NUM_SPECTRUM_BARS; ibar++) {
            _spectrumBars[ibar] = GameObject.CreatePrimitive(PrimitiveType.Cube);
            _spectrumBars[ibar].transform.parent = this.transform;
            _spectrumBars[ibar].transform.localPosition = new Vector3(ibar, 0, 0);
            _spectrumBars[ibar].transform.localScale = new Vector3(1, 0, 1);
        }
        // Initialize gain bar
        _gainBar = GameObject.CreatePrimitive(PrimitiveType.Cube);
        _gainBar.transform.parent = this.transform;
        _gainBar.transform.localPosition = new Vector3(-5, 0, 0);
        _gainBar.transform.localScale = new Vector3(4, 0, 1);

        // Overall dimension
        this.transform.localScale = new Vector3(0.2f, 10.0f, 0.2f);
    }

    private void UpdateVisualization() 
    {
        // Update spectrum bars
        int nSamplesPerBar = NUM_SPECTRUM_SAMPLES / NUM_SPECTRUM_BARS;
        for (int ibar = 0; ibar < NUM_SPECTRUM_BARS; ibar++) {
            // Calculate value of each bar
            float value = 0;
            for (int isample = 0; isample < nSamplesPerBar; isample++) {
                value += _spectrumData[ibar * nSamplesPerBar + isample];
            }
            value /= nSamplesPerBar;
            // Use current value if increasing, or slowly drop previous value if decreasing
            float prevValue = _spectrumBars[ibar].transform.localScale.y;
            if (value < prevValue)
                value = prevValue - BAR_DROP_SPEED;
            // Y scale is set to value
            _spectrumBars[ibar].transform.localScale = new Vector3(1, value, 1);
        }
        // Update gain bar
        _gainBar.transform.localScale = new Vector3(4, _gain, 1);
    }

    private void UpdateGain() 
    {
        _gain = 0;
        for(int i = 0; i < NUM_SAMPLES_TO_AVERAGE; i++) {
            _gain += Mathf.Abs(_fPCMData[NUM_PCM_SAMPLES - i - 1]);
        }
        _gain /= NUM_SAMPLES_TO_AVERAGE;
    }
}

这是我的问题:

  1. 我无法使用while (!Microphone.GetPosition(_deviceName) > 0));来避免麦克风到扬声器的延迟。如果使用它,我的应用程序将冻结。如果我添加允许超时的代码,则每次都有超时。

  2. 增益栏似乎与我的声音无关。我不知道我的计算是否正确。

  3. 我不确定是否需要对多个计算增益的样本进行平均,以及我需要对多少个样本进行平均。我以后需要这个增益值来检测静默时刻并剪切音频数据。

1 个答案:

答案 0 :(得分:1)

至1。

可以。 Unity允许将Start定义为Coroutine

private IEnumerator Start()
{
    ...
} 

通过这种方式,您可以使用非阻塞

while (!Microphone.GetPosition(_deviceName) > 0))
{
    yield return null;
}