Perfect Beat Tracking in Unity?

Just putting this down here as in our project we had quite severe issues with music tracks going out of sync. The best way for us to reproduce these issues was to spam pausing our game, which uses setPaused for the buses.

Out of sync here meaning that the values that we got from the callbacks looked correct, but the music sounded way off. Also, the different layers (or tracks) within the FMOD event were clearly not in sync with each other often after this.

What fixed these issues was to se the priority of the music event to “Highest”. I’ve noticed it on some other posts but decided to put it here as off-sync is critical on rhythm-based games.

Thanks for the useful info! If you don’t already know, setting “Persistent” on the FMOD Events to On helps as well, it keeps the track playing even if it’s muted or the volume falls below -80 dB.

@bloo_regard_q_kazoo Hey just a thing I stumbled upon.

In your project, did you ever try to speed up/slow down the music?
I can see that the script has checks for tempo changing, but I’m using pitch specifically.

The bad thing about setting pitch is that it doesn’t change the tempo of the track (at least timelineInfo.currentTempo doesn’t change).

In my usecase I’ve had to replace the master channel group from being used in the UpdateDSPClock method with the channel group of the music event, as pausing the game would keep the master channel group’s dspClock increasing. For me, this caused pretty much everything to get out of sync quite fast.

Since I’m using the music event’s channel group to update the dsp clock, if I decrease the pitch, let’s say to 0.75, it also means that the dspClock value change also decreases. For this reason I’m keeping the beatInterval value the same as before the pitch change since the dsp clock value change is already “slowed down”. Changing both would mean that the value of currentTime is increasing slower, but also the interval it’s checking against would be longer.

My issue is that for some reason, whenever I slow-down or speed-up the music event with pitch, it causes minor off-sync between the beat tracking and the music itself, and I’m yet to figure out why. Not sure if there is some behaviour/delay that happens when you use setPitch within FMOD that causes these things.

Another question might be related to the first point I brought up regarding pausing the game causing issues with master channel group. Did you pause the master channel group when pausing the game, or how did you handle pausing? Since if you just pause the music bus and not the master channel group, the master CG’s dspClock value keeps increasing, and when you unpause and the music continues, the dspClock value of the music and master channel group might not be in-sync anymore.

Sorry for the wall of text, I’m just unable to figure out why the tracking gets out of sync when using setPitch (or a parameter in FMOD that changes the pitch of the event instantly).

Hi,

What version of the FMOD integration are you using?

Would it be possible to get a screenshot of the event in Studio, could I please also get a code snippet where you are trying to control the pitch?

Could you also clarify exactly the issue you are experiencing?

Hey!

The integration version is 2.02.20.

The code I’m using is mostly same as what bloo has provided above, but some tweaks as I mentioned. The main difference is that the UpdateDSPClock method is using musicPlayEvent’s channel group instead of master channel group like this:

private void UpdateDSPClock()
    {
        MusicEventChannelGroup().getDSPClock(out dspClock, out parentDSP);

        currentSamples = dspClock;
        currentTime = currentSamples / masterSampleRate;
    }

The way I control the pitch is just:

musicPlayEvent.setPitch(.75f);

We have a rhythm mechanic where the player can see an indicator moving a long a line of inputs in sync with the music, and the player has to hits those inputs in beat. The issue with changing the pitch I’m noticing is that sometimes the indicator does not match to the music. For example, with the .75f pitch, the indicator would be slightly (but noticeably) late, compared to what it is with normal 1f pitch. The issue doesn’t happen consistently and this slight offset also seems to change. The game also works fine when turning back the normal pitch.

I think the issue is related to just how I’ve just modified the beat tracker script to use the music channel group instead of master channel group. Changing the pitch of the music event makes the dsp clock changes of the channel group also slow/speed up, which may cause this unexpected behaviour, which the script is not yet taking into account. My previous Unity Audio implementation was tracking something similar as to this tracker scripts currentTime, but with unity’s AudioSettings.dspTime, which would stop whenever the game paused, but would be affected by pitch changes. So I think I have to figure out something similar here, maybe a non-master channel group that pauses during game pause, but doesn’t get affected by pitch. Or possibly just a simple timer value based on unscaledDeltaTime.

I originally decided to use the Master Channel Group over the music event’s Channel Group because it never worked, it’s time would always return 0. But apparently that only happens if it’s assigned to a class level variable instead of getting it exactly when it’s needed, like you’re doing.

I just modified mine to work with pitch changing using the example you provided and it all seems to work well. I guess I could try giving you the modified version and see if that works. I’ll try to strip out all the unnecessary parts I added since the original version.

As for pausing the track, I haven’t really tried that yet. I mostly planned on having the music just continue to run in the background while the game is paused. I’ll let you know if I figure out another way, of course.

Alright. I have a new script here that hopefully continues to work. I recorded an example of it working here.. In the video I have a bass drum and a hi hat cymbal playing on the down and up beats respectively. I am controlling the pitch of the music in real time.

The new Beat Tracker can be found here., Hopefully I didn’t leave any unnecessary code in there.

I’ve added two new functions for the Pitch, “SetPitch” and “GetPitch”, they do exactly what they sound like.

A few things to make sure you’re doing in your project:

  • In FMOD, make sure your music event has “Persistent” set to ON, and “Priority” is highest.
  • In FMOD, make sure “Streaming” is OFF on your audio file.
  • In Unity, set “Run in Background” to ON, Edit > Project Settings > Resolution and Presentation > Run in Background.

I don’t think I forgot anything but let me know if it doesn’t work.

Hey, thanks a lot for this!

Unfortunately using these settings did not solve my issue.
The video you sent looks correct, I think there is just some issue within my other scripts that try to match based on last beat’s time. When the off-sync happens it stays the same until I change the pitch, so there’s just something I’m handling wrong probably. Have to investigate more!

You may already know this but just to be sure, you can subscribe functions in other scripts using BeatTracker.onFixedBeat += MyFunction, this will cause it to be called exactly on the beat. Not sure if this is the problem though.

Yeah, thanks!

I’ve used that for many things but some systems need to work on longer periods and need to sync up with the music.

I managed to fix the issue. It was only how I replaced my previous Unity Audio implementation’s AudioSettings.dspTime with the dspTime (dsp clock / sample rate) of the music event. Basically the dspTime of the music event would progress slower/faster based on pitch, which made other systems behave in weird ways…

The solution for me was to make a separate dspTime variable, where I add the change of dsp time of the master channel group every frame, but only if the game is not paused. This has gone pretty much off-topic for this post haha, thanks for the help!

1 Like

Thank you for all your help!

1 Like

Thank you for sharing your solution. If you have any more questions please do not hesitate to ask!

1 Like