Since music plays such an important roll in Ephemerid, a custom system was written for handling audio in Unity. It has been through several iterations, and I thought it would be helpful to share the current state of its implementation here. If you're doing anything with either musical timing or looping in Unity, you may find some helpful information below.
Note: Ephemerid is being developed in Unity 3.5.X. Most of the techniques below are still relevant in 4.X, but I will try to specify anywhere there have been changes/improvements in 4.X.
Audio ManagerBefore doing anything else, if your project depends on music timing, then it's a good idea to set the "DSPBuffer Size". In Unity, go to Edit->Project Settings->Audio, and change DPSBuffer Size to "Best Latency"
With this set, Unity will choose the smallest appropriate DSP buffer size depending on the build target. This means that the actual chunks of audio sent to the DSP will be smaller, and you will get more fine-grained feedback from audioSource.time
Music Timing and TweeningThe tweening system in Ephemerid was written from the start with the intention of being driven by music. When a Tween is created, it looks for a default "Stepper" class, and adds itself to it. The Stepper class provides timings to its tweens. In almost all Ephemerid scenes the concrete Stepper class is a MusicStepper.
MusicStepperMusicStepper is a fairly simple class that acts as a director of all audio tracks (handled by AudioPlayer described below) in the scene. These are the key jobs of MusicStepper:
- If audio is playing, it uses that timing
- When a track completes, it progresses to the next track
- If there is no audio playing, it calculates the timing based on a specified BPM and Unity's deltaTime
AudioPlayerThis is where it gets interesting. AudioPlayer provides the delta times to a MusicStepper. There are several ways to do rhythm timing in Unity:
Option 1.
Calculate the beats passed by using Unity's deltaTime and a specified BPM (which is converted to beatsPerSecond at creation)
beatDelta = Time.deltaTime * beatsPerSecond
Option 2.
Monitor the current AudioSource's time, and calculate the delta beats based strictly on how it changes. The "time" property of AudioSource is used to get audioTime and previousAudioTime. †
beatDelta = (audioTime - previousAudioTime) * beatsPerSecond
Option 3.
A combination of 1 and 2. Use 1 as long as it doesn't deviate too far from the audio timing. When it deviates, account for the error.
Ephemerid uses method 3 in order to avoid things like timing differences from the particle system, but the method used should be chosen based on your specific project needs.
In the next update to this article, I'll go into further detail on how AudioPlayer handles looping and defining events at specific beats in a song.
† In Unity 4.X, you can get the actual DSP time from AudioSettings.dspTime. While I haven't had a chance to experiment, this is probably better than AudioSource.time for calculating beats passed.