Hi! I’m making a simple metronome in Unity and have some problems with it. At first I tried this:
function Start ()
{
bpm = 140;
bpmInSeconds = 60 / bpm;
metronome = true;
nextTime = Time.time;
StartMetronome();
}
function StartMetronome()
{
while(metronome)
{
Debug.Log("Tick");
audio.Play();
nextTime += bpmInSeconds;
yield WaitForSeconds(nextTime - Time.time);
}
}
However it’s not solid.
In Unity docs there is this AudioSettings.dspTime.
// The code example shows how to implement a metronome that procedurally generates the click sounds via the OnAudioFilterRead callback.
// While the game is paused or the suspended, this time will not be updated and sounds playing will be paused. Therefore developers of music scheduling routines do not have to do any rescheduling after the app is unpaused
@script RequireComponent(AudioSource)
public var bpm : double = 140.0;
public var gain : float = 0.5f;
public var signatureHi : int = 4;
public var signatureLo : int = 4;
private var nextTick : double = 0.0;
private var amp : float = 0.0f;
private var phase : float = 0.0f;
private var sampleRate : double = 0.0;
private var accent : int;
private var running : boolean = false;
function Start ()
{
accent = signatureHi;
var startTick = AudioSettings.dspTime;
sampleRate = AudioSettings.outputSampleRate;
nextTick = startTick * sampleRate;
running = true;
}
function OnAudioFilterRead(data:float[], channels:int)
{
if(!running)
return;
var samplesPerTick = sampleRate * (60.0f / bpm) * (4.0 / signatureLo);
var sample = AudioSettings.dspTime * sampleRate;
var dataLen = data.length / channels;
for(var n = 0; n < dataLen; n++)
{
var x : float = gain * amp * Mathf.Sin(phase);
for(var i = 0; i < channels; i++)
data[n * channels + i] += x;
while (sample + n >= nextTick)
{
nextTick += samplesPerTick;
amp = 1.0;
if(++accent > signatureHi)
{
accent = 1;
amp *= 2.0;
}
Debug.Log("Tick: " + accent + "/" + signatureHi);
}
phase += amp * 0.3;
amp *= 0.993;
}
}
This was working great! But…
Then I tried to add code that I need in my app after the actual “tick”.
Debug.Log("Tick: " + accent + “/” + signatureHi);
If I put in: audio.Play();
}
Debug.Log("Tick: " + accent + "/" + signatureHi);
audio.Play();
}
...
Or try to change a sprite with: GetComponent(SpriteRenderer).sprite = music[4];
I get an error:
get_audio can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don’t use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
So my question is. Is there a way to make sprite change, audio played etc with this AudioSettings.dspTime script? I quess the AudioSettings.dspTime and other functions are not “in the same time” or something and therefore can’t be called?
Or is there a better way to do a metronome in Unity?