Микрофон, значение усиления и значения спектра не синхронизируются с использованием Unity - PullRequest
0 голосов
/ 22 апреля 2019

Я делаю простую программу визуализации голоса. Мои цели:

  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 Ответ

1 голос
/ 22 апреля 2019

К 1.

Вы можете . Unity позволяет определить Start как сопрограмму

private IEnumerator Start()
{
    ...
} 

На этом пути вы можете использовать неблокирующую

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