Anyone who's done serious work in Unity knows that it's notorious for audio issues. It's basically impossible to get a perfectly-looping audio clip...you know, like the background music of every game ever made. It'll be fine in your music editor of choice, but then Unity just screws it up somehow. All the usual online answer boards will suggest things like changing the file type or changing import settings, which might work if you're lucky... I endorsed Introloop after using it for Alien Cow Farm, and it does a good job of removing the telltale clicking and popping that Unity randomly adds after every loop iteration. But even it can only do so much. When I made my web build demo for Undersea Odyssey, the audio was all kinds of screwed up again. I chose to ignore it there because I read that WebGL builds always seem to have a unique set of audio problems of their own, and that wasn't the target platform for my final build anyway. But when I loaded a full-audio build onto my phone to test also, I still had problems. In this case, a constant crackling during all of the BGM.
So, back to the interwebs for answers. Apparently the problem is literally the Unity AudioSource component's interaction with the Android's music player, and the suggested solution to my problem and many others was to cut out the middle man and use the Android native audio's Media Player to play all the game's BGM. Okay. What, you're not a crack Java developer?
That's the catch. Androids run on Java, which I absolutely should have remembered from that one app development class I took that one time. But I also discovered that Unity has a few tricks left up its own sleeve, as it includes a way to use C# to call Java library functions. Do a search on "Unity AndroidJavaClass" or "Unity AndroidJavaObject" and you'll find a way to create Java classes and access their properties and methods...just as long as you know the full path of the exact library they reside in, and don't screw anything up regarding the string name or arguments, because there'll be no Intellisense for you. Anyway, I happened to be lucky enough to find a perfect example of creating a Media Player, and all it took was some object-oriented tomfoolery to make the audio play using Unity while I continued to do editor tests, but then switch to the ANA in a phone build. The crackling artefacts were gone...and the imprecise looping came right back.
So now I had a new problem to look up, namely that Android can't loop their damn audio correctly either. It's a known issue, no dev-supported solution, try changing the file type...same old, same old. And do you know what the most upvoted solution I found for this one was? Instead of a single Media Player with the [broken] looping setting set, create two and have them trade-off playing at the designated loop time. You know, pretty much exactly what Introloop does with Unity's AudioSources. The only problem is, this time I really did have to go become an expert in Java to be able to implement the switching callback.
Okay, it wasn't that bad. Unity's got one more Java helper class, the AndroidJavaProxy. You create a new class of your own, then, just like before, you feed it the Java library of the class you're mimicking. The real tricky bit was that I needed to have access to my class' methods and data inside of the callback, and the only Unity example on the subject only shows you how to access static class data, which absolutely wouldn't work for me. But in trying some other things, I discovered that the AndroidJavaProxy doesn't have to be a perfect copy of the original class...namely, you can add your own stuff on top. I tried to give my callback a reference to my main class object, and set said reference at the same time as I'm creating the callback. Now I could access everything I needed.
And voila, it worked! Not just the questionable custom code still calling everything correctly, but the solution in general as well. The BGM was crackle-free from using ANA, and it was back to loop-popping-free from handling that myself. Here's the complete code in my AndroidAudioPlayer class to do everything I've just described (the Play, Pause, and other simple functionality implementations aren't shown, you can find dozens of examples of them if you've gotten this far):
Comments