Hmm.
Hmmmmmm!
HMMMMMMMMMM!
Meh. Unity 5 just got released today.
Also...
I've reinvented the wheel, I swear.
There wasn't much information in the Internet about seamless loop in Unity. Even less with looping from a certain point of the bgm.
I mean, no information.
So, here's a script I did that solved all of my problems.
[Version 2:
Here (Note the MIT License)
To use it you need to do, somewhere in your code, something like this:
1 2 | var myInfiniteMusic = new InfiniteBackgroundMusic(); myInfiniteMusic.ChangeTrack(myAudioSource, myAudioClip, myLoopPoint); |
And it should work.
Should.
Btw, referent to a certain comment, if you want to keep a bgm playing between levels...
Type this in the Play function, after the AudioClip.Create:
Object.DontDestroyOnLoad(audioClip);
Don't worry, that temporal Audio Clip is destroyed on each ChangeTrack call.
Again, it should work.]
[I did write some detailed explanation here, but somehow it got deleted. Heck]
A low-count-word (?) explanation:
I retrieve the full contents of the AudioClip, then I play it and when the bgm loops I wrap to the loop position.
Because the AudioSource time sets the time on every loop to 0 and not MusicLoopPoint, I made the Time and SampleTime properties that will return (and that you can set) the correct current time.
Note that it doesn't have Stop, Pause or similar functions. I didn't need them and I though that could be an exercise for the reader.
[The culprit of not having a detailed explanation is a Show/Hide button that I tried to insert here!]
To use it, attach a AudioSource to any GameObject, uncheck Loop and Play On Awake, attach this to some GameObject (can be the same) and drag n' drop the AudioSource to the Audio Source property in the Inspector. You can set the Spatial Blend to whatever you like.
Set the Audio Clip property to some awesome music, and specify correctly the Music Loop Point in seconds.
The Audio Clip must be decompressed on Load and I recommend Preload Audio Data and PCM compression.
Here's the code if the download fails:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | using UnityEngine; using System.Collections; public class InfiniteBackgroundMusic : MonoBehaviour { [Header("To get/set audio position, use Time and SampleTime")] public float MusicLoopPoint; public AudioClip audioClip; public AudioSource audioSource; public float Time { get { long p = position; p /= entire.Length; return p * audioClip.length; } set { double m = value / audioClip.length; position = (long)(m * entire.Length); } } public float Duration { get { return audioClip.length; } } public long SampleTime { get { return position; } set { if(value >= entire.Length) { Debug.LogError("Value is larger than audioClip.samples"); } if(value < 0) { Debug.LogError("Value is lesser than 0"); } position = value; } } public int SampleDuration { get { return entire.Length; } } float[] entire; long position; int sampleLoopPoint; string audioName; int channels; int start; void Start () { double mul = MusicLoopPoint / audioClip.length; sampleLoopPoint = (int)(mul * audioClip.samples); channels = audioClip.channels; audioName = audioClip.name; entire = new float[audioClip.samples * channels]; audioClip.GetData(entire, 0); audioClip = AudioClip.Create(audioName + "_Loop", audioClip.samples, channels, audioClip.frequency, true, OnAudioRead, OnAudioSetPos); audioSource.loop = true; audioSource.clip = audioClip; position = 0; audioSource.Play(); position = 0; } void OnAudioRead(float[] data) { if(start < 64) { start++; position = 0; return; } int count = 0; while(count < data.Length) { data[count] = entire[position]; position++; count++; if(position >= entire.Length) { position = sampleLoopPoint*channels; } } } void OnAudioSetPos(int newPos) { } } |
Never, ever, trust Show/Hide buttons...
Hello and thank you very much for posting this script. We had also been looking for a solution for looping audio and were shocked to see that not only did it not seem like it was built into the engine, but there weren't any discussions or common methods of providing this essential functionality. Audio compression and looping are one of the very first forms of game middleware ever created (Sega/CRI's ADX, Rad game tools, etc) after all... Anyway I was hoping you could help with a slight problem in using the script. It seems that when we use the script, any music we play from it is delayed exactly 2 seconds after the game/scene is started. We have our clip set to Pre-load, Decompress On Load, and PCM as suggested, and the Loop and Play On Awake are unchecked on the audio source. Any insight into this would be greatly appreciated! (or is this expected behavior?). Also, great blog, and good luck on your game - we're also very much into shmups and manga/anime here as well. :)
ReplyDeleteHm, yeah. Expected Behaviour. Hm... If I remember correctly, you should set between if(start < 64) { } a for looping the data and setting that into 0. Don't know why but Unity calls OnAudioRead a lot of times when playing a clip... I think I'll post a new script to fix it. Sorry about that.
DeleteThanks for the prompt and kind response. I messed with the value and changed the 64 to different values, and it eliminates the delay, but causes the very first part of the track to repeat itself, the time of which varies depending on the number it is changed to. For the track we are testing with, it actually sounds cool, since its a drum roll. I changed the 64 to an integer value for tweaking it in case no other solution was found. If you find out anything more or figure out a fix, I'll stay tuned. Thanks again! :)
ReplyDeleteI don't remember exactly, but months ago I was touching the script and I reduced that delay I think. You can see the updated script if you like, as I don't remember exactly and I don't want to check it because it works fine for me...
DeleteThis comment has been removed by the author.
ReplyDeleteThank you so much for this script. But I have an issue. When you move to a new scene, the script doesn't play the sound of this new scene. I don't understand why, any idea?
ReplyDeleteHm... yes. I'll update the script with a newer version. Thanks for pointing out!
DeleteDone. Check it out.
DeleteThanks!
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHello, Great script! It works but the only downside that i have noticed is that there is a delay of 1 or 2 seconds before the audio play.
ReplyDeleteI'm trying to do a crossfade between 2 audioclips by lerping the volume of the audioSource to 0 and then back to 1,
Is there a way to reduce at minimum the delay? I don't want those 2 seconds of silence.
I've followed all instruction on how to setup the audio files, all settings are as you suggested.
Thank you!
I tried to understand what the code does and how to fix that, and i got it working somehow.
DeleteInside OnAudioRead, i commented out the first if condition , and now there is no delay and the music loop fine.
The part i deleted is this:
if (start <= 16) {
...
}
But what exactly does that part?
Hm, in my tests, if I don't do that "if (start <= 16) { ... }" for some reason I get a bug that doesn't play the first seconds of music, or it loops awkardly. If for you works fine without it, then everything is ok.
DeleteThis comment has been removed by the author.
ReplyDeleteWow I am surprised that someone is having the same problem as me. I have just finished a Unity plugin called Introloop just for solving this intro to looping problem!
ReplyDeleteIt supports looping on only 1 audio file without cutting it up. (but Unity 5 is the requirement though.) Check it out if interested! http://forum.unity3d.com/threads/378370/
Interesting, but too late for me, my version works perfectly for my use. But at least I wasn't the only having the problem in the world xD.
DeleteHi! I want to say thanks for this awesome script. I will be using it in my fan-game project, Super Smash Brothers Feud. I've been looking for a good, efficient Unity music looping solution! I can't believe it's not built in from the start!
ReplyDeleteI just tested InfiniteBackgroundMusic out with a test subject, an OGG of mine, and given the correct loop point it worked for me like a charm! Seamless loops with not even a single "pop". The best looping solution a guy could ask for!
I have something to ask, however. If I wanted to use InfiniteBackgroundMusic in my commercial game, would I a) need your permission (I would credit you, of course!) and b) would I need to pay you? Or would you prefer I not sell it at all? :) Thanks,
Nick W., indie game developer.
Thanks, yeah, sometimes Unity just forgets the basics D=
DeleteAbout the commercial thingy, you can note in the right side of the webpage that everything (unless is explicitly stated that not) is CC BY-SA (http://creativecommons.org/licenses/by-sa/4.0/) so as long that you credit me, no worries. If you have a modified version of mine, you can also distribute it (and under the same license, but only the script, nothing more, also crediting me).
Again, thanks for using my script (and say it's awesome =D )
Thank you, GM. I greatly appreciate you taking time our of your day for replying! I will put you in the credits roll!
DeleteFor my serious game, it'll be much better than my solution, which sometimes lags. D:
And no problemo! ;) Consider yourself "hired" haha.