Synchronizing Metronome with Audio Input on Mobile Devices

Hello everyone,
I’m working on a Unity project for mobile devices where I need to synchronize a metronome with incoming audio from a microphone. The goal is to detect drum hits in real-time and align them with the metronome ticks. Below is a simplified version of my current DrumInput script:

using UnityEngine;
using System.Collections;

public class DrumInput : MonoBehaviour
{
    private AudioClip microphoneClip; // Audio clip to capture microphone input
    private int sampleRate = 44100; // Sample rate for audio capture
    public float threshold = 0.1f; // Threshold to detect drum hits
    public int smoothingWindow = 10; // Window size for smoothing amplitude
    public float checkInterval = 0.01f; // Interval for checking audio input
    private float[] prevSamples; // Array to store previous samples for smoothing

    void Start()
    {
        StartMicrophone();
        prevSamples = new float[smoothingWindow];
        StartCoroutine(CheckAudio());
    }

    // Start capturing audio from the microphone
    void StartMicrophone()
    {
        string device = Microphone.devices[0];
        microphoneClip = Microphone.Start(device, true, 1, sampleRate);
    }

    // Coroutine to periodically check audio input
    IEnumerator CheckAudio()
    {
        while (true)
        {
            int micPosition = Microphone.GetPosition(null);
            float[] samples = new float[microphoneClip.samples * microphoneClip.channels];
            microphoneClip.GetData(samples, 0);
            ProcessAudio(samples, micPosition);
            yield return new WaitForSeconds(checkInterval);
        }
    }

    // Process audio samples to detect drum hits
    void ProcessAudio(float[] samples, int micPosition)
    {
        float smoothedAmplitude = 0.0f;
        for (int i = 0; i < micPosition; i++)
        {
            smoothedAmplitude = (smoothedAmplitude * (smoothingWindow - 1) + Mathf.Abs(samples[i])) / smoothingWindow;
            prevSamples[i % smoothingWindow] = Mathf.Abs(samples[i]);

            if (smoothedAmplitude > threshold)
            {
                double currentTime = AudioSettings.dspTime;
                double inputLatency = (micPosition / (double)sampleRate);
                double adjustedHitTime = currentTime - inputLatency;
               
                Debug.Log($"Drum hit detected at: {currentTime:F1}, Adjusted Hit Time: {adjustedHitTime:F1}, Amplitude: {smoothedAmplitude}, Threshold exceeded by: {smoothedAmplitude - threshold}, Input Latency: {inputLatency * 1000:F1}ms (Compensation Time: {inputLatency:F4}s)");
                // Handle drum hit detection
                break;
            }
        }
    }
}

The script captures audio input, processes it to detect drum hits, and attempts to compensate for input latency. However, I’m having difficulty achieving precise synchronization between the detected drum hits and the metronome ticks.Does anyone have experience or advice on how to better synchronize real-time audio input with a metronome in Unity, particularly for mobile devices? Any tips on improving the accuracy of latency compensation or handling timing discrepancies would be greatly appreciated.Thank you!

Your best friend for audio sync in Unity is PlayScheduled.

If you know when the tick should happen exactly in dspTime, setting it with this method in advance will have it play at the exact moment.

But as a fellow musician and drumming enthusiast, I have to say something here: if you’re trying to have a metronome sync to a musician’s tempo rather than the opposite… I have to warn you… evil music spirits might come to haunt you… beware of the forces at play here! :wink:

1 Like