Search code examples
c#unity-game-engineaudiotriggersmicrophone

Read microphone decibels and pitch/frequency


I am trying to make a game in which my character shoots when a loud enough sound is heard true the mic (in Unity). I have however no idea how to start.

Thank you for your help!


Solution

  • You can get the decibels from a Microphone by retrieving the block of the currently playing Microphone's output data with the AudioSource.GetOutputData function. To get the dB from this data, you need sum the data samples then calculate the RMS. That's RMS value you can use to calculate the dB with 20 * Mathf.Log10 (RMS / refVal).

    There is a complete example on about this on Unity's post. You can read that for more information and the code below is based on that:

    public float rmsVal;
    public float dbVal;
    public float pitchVal;
    
    private const int QSamples = 1024;
    private const float RefValue = 0.1f;
    private const float Threshold = 0.02f;
    
    float[] _samples;
    private float[] _spectrum;
    private float _fSample;
    
    void Start()
    {
        _samples = new float[QSamples];
        _spectrum = new float[QSamples];
        _fSample = AudioSettings.outputSampleRate;
    }
    
    void Update()
    {
        AnalyzeSound();
    
        Debug.Log("RMS: " + rmsVal.ToString("F2"));
        Debug.Log(dbVal.ToString("F1") + " dB");
        Debug.Log(pitchVal.ToString("F0") + " Hz");
    }
    
    void AnalyzeSound()
    {
        GetComponent<AudioSource>().GetOutputData(_samples, 0); // fill array with samples
        int i;
        float sum = 0;
        for (i = 0; i < QSamples; i++)
        {
            sum += _samples[i] * _samples[i]; // sum squared samples
        }
        rmsVal = Mathf.Sqrt(sum / QSamples); // rms = square root of average
        dbVal = 20 * Mathf.Log10(rmsVal / RefValue); // calculate dB
        if (dbVal < -160) dbVal = -160; // clamp it to -160dB min
                                        // get sound spectrum
        GetComponent<AudioSource>().GetSpectrumData(_spectrum, 0, FFTWindow.BlackmanHarris);
        float maxV = 0;
        var maxN = 0;
        for (i = 0; i < QSamples; i++)
        { // find max 
            if (!(_spectrum[i] > maxV) || !(_spectrum[i] > Threshold))
                continue;
    
            maxV = _spectrum[i];
            maxN = i; // maxN is the index of max
        }
        float freqN = maxN; // pass the index to a float variable
        if (maxN > 0 && maxN < QSamples - 1)
        { // interpolate index using neighbours
            var dL = _spectrum[maxN - 1] / _spectrum[maxN];
            var dR = _spectrum[maxN + 1] / _spectrum[maxN];
            freqN += 0.5f * (dR * dR - dL * dL);
        }
        pitchVal = freqN * (_fSample / 2) / QSamples; // convert index to frequency
    }