crossfading music (not -sound-)

Monkey Forums/Monkey Programming/crossfading music (not -sound-)

Derron(Posted 2015) [#1]
(Edited title - as it got truncated having quotationmarks in it...)
Hi,

my prior implementation to crossfading music was to use the "PlaySound"-functionality for multiple channels and to lower/raise volume on these channels accordingly (just like a crossfader works).

Trying to do this on some android devices is not possible - because these sounds do not play on my my android devices:

hundreds of lines:
E/AudioCache(  300): Heap size overflow! req size: 1052672, max size: 1048576
E/AudioCache(  300): Heap size overflow! req size: 1052672, max size: 1048576


So I assume the problem is that my music loops exceed the allowed space for "sounds" somehow.

Checking the sources "sounds" are different to "music", for "music" the MediaPlayer is used on Android) - I think this will stream the ogg-music and therefor need less RAM.


My questions:
How to crossfade music - as we only have 1 music channel available?

Is there a reason for just having one music channel? Couldn't the whole system just allow the usage of a "mediaplayer" for all kind of streamed audio (even for "sounds" ?).

Are there other options to play looped samples as background music (each sample is about 15 seconds and loopable, they are played multiple times in a row (random) and then crossfaded to the next looped-sample). Using a big single-music-file and seeking in there to have random "songs" without gaps is not what I want to use - as this does not crossfade and on some devices the delay might be recognizeable.


Edit: Just have read this:
http://stackoverflow.com/questions/7428448/android-soundpool-heapsize-overflow
and they mentioned, that the "SoundPool" (used by Monkey) only has 1 MB of buffer size for loaded sounds.
One suggestion there was to use multiple SoundPools if you want to play more than the limitation allows.
Is this true for us - or can we circumvent it?

I hope there is a solution to this problem (without myself having to limit my app or to climb very high mountains) - as this is what I expect of "Monkey X" to handle for me.

Thanks in advance,

bye
Ron


ElectricBoogaloo(Posted 2015) [#2]
I imagine the "reason" is general compatibility. As much as a quadcore snapdragon might easily cope with playing multiple oggs at once, a cheapo 500mhz single core (HTC Wildfire sucks!!) wouldn't. Heck, it can barely cope with one!!
A lot of Monkey's "issues" boil down to Mark struggling to ensure that everything works all of the time, and if that means limiting audio, then that's what happens.

I always find it fun trying to find ways around such limits. In this case, if you REALLY want to crossfade, then I suggest finding a nice short mid-way sound.. Fade out track A, play sound over the top, and then fade in track B. Use the mid-sound to help join the two.
.. It probably won't work very well, unless you find just the right cross-sound, but realistically it's probably about as good as you're going to get.


Derron(Posted 2015) [#3]
Like said: my sound loops are already just 15 seconds.

Mark struggling to ensure that everything works all of the time

In that case he should take care that it does _not_ work an _all_ targets.

Regarding processor hunger: THIS is something the developer should take care of. It's like saying: you cannot DrawImage() on an old cyrix 6x86 running XP. Why shouldn't you? Of course you might run into the problem of low FPS but at least it works.
So for Sounds you should get stutters (buffers not filled fast enough for streaming audio) and _not_ just playing nothing at all.

In all cases you should decrease quality first (less renders per second, limit "maxChannels" for audio, limit update rate ...).


Nonetheless: using the one and only music channel + a bridge sound might be a idea with potential in some cases, I really doubt in that bridge sound playing in all cases if the "soundPool"-heap is already exceeded by some other sounds.

I do not see a way to automatically lower the amount of channels according the devices "horse power", nor does the native implementation lower the maximum channel count on its own (you then could handle such things nicely with your own soundManager and pool implementation).

So what I do not clearly read out of your post: is "MediaPlayer" that ressource hungry (when streaming an compressed [ogg] file)? If not, why not have the same for "music" what we have for "sounds" already? A list of MediaPlayers we could "pool" from. Seems you need such things in all cases of lengthy sounds (voice over, radio talk, atmospheric sounds of 10+ seconds, ...).

Implementing such things now on my own (for all targets I want to compile for) seems a bit pointless, regarding "monkey" being the product which wants to take especially that hurdles away: code once, compile everywhere.

Above is the reason for me to ask, how to circumvent this problem - or if someone wrote some code fixing this situation.


bye
Ron


ElectricBoogaloo(Posted 2015) [#4]
If you want to, you can hack away at Monkey to make it do what you need it to. That's the beauty of Monkey.


Derron(Posted 2015) [#5]
I think people buy Monkey (or in my case wanted to get it through the Jam) because they do not want to _hack away_ ... they want to "code once, run everywhere".

If I wanted to play with Java, I would stay with LibGDX as it already provides plenty of functionality. I tried to come along with Monkey because I like BlitzMax (and because of the smaller binary sizes).

As stated above: Monkey wants to be the middleware between you and the targets it officially supports. Seems it cannot handle that properly in all cases. Which isn't a problem for beta releases, but Monkey is sold for some years now - and it should mature, or explicitely state that it is _not_ cross platform in what its modules offer (maybe it does --- somewhere hidden in the depths of the website).
Maybe this sounds like a rant, but you have to understand that I felt bit of cheated now. I understand that you have to take care of device limitations: I cannot hope for mouse scrollwheels on android devices, nor can I expect touch-sling-events for desktop computers - but I expect "DrawImage()" to draw the image on all devices. I also expect "PlaySound" to play a sound on all devices - and if that device does not have sound output, then it should handle that _BUT_ also allow the developer to recognize this so appropriate handling of this could be included in the app.

Let me cite the monkey-website to make clear what I expect from monkey:
From Monkey to magic - write your Monkey code and let Monkey do the rest...

(I could cite the other phrases on the website too ... as they all imply that you do not have to care for such limitations - if it does work on X, it works on Y too)


What saddens me more is the fact, that most of the users here do _not_ seem to have problems with this limitation, which means they silently swallow the bug or did not even bother having more advanced "sound setups" (just playing their music file on a per-screen base, no composition of multiple basic sound loops into something in the likes of dynamic music - eg faster and more powerful music parts mixed into the "song" when boss comes in range).
But hey, if _you_ (the reader) are a one having circumvented this problem, feel free to write down your approach here. Best option of course is an extension to the source for all targets ("hack away") from users enjoying to do such things.


bye
Ron


Gerry Quinn(Posted 2015) [#6]
It is unfortunate that you are hitting a problem that frustrates you, but Monkey for the most part really does live up to the 'Monkey to magic' line. As you note, this works better when your needs are not too elaborate.

The other side is that interfacing to native code is pretty easy when you need something extra.


Derron(Posted 2015) [#7]
For me the problematic part is: I do not "speak" all target languages fluently. Which means: If I have to extend basic functionality because it is limited on target XY, I might have to do this for "unknown languages" too - sooner or later.

For this problem I am now reading articles for over 2 hours now - but this then only would fix the part for android, what if "ios" has a similar restriction? If I have to learn all languages at the end - then "monkey" is nothing more than an abstraction library. But I want to use it as a language with included library.


@MediaPlayer (or "multiple music channels"):
Is there a reason - next to additional cpu usage - to only use one music channel?

The current implementation allows my apps to have the following on android:
- 1 streamed music channel
- up to 31 sound effects you can adjust volume/pitch/stereo-wise, all of them should not exceed 1MB of Memory (uncompressed PCM)

For me this means: there is no way to play 2 sounds (exceeding 1MB) simultaneously (complex songs - or crossfading - or ...).
This is - pov of the "artist" in me - very limiting.

The purpose of a "programming pack" like Monkey X should be to _hide_ as much of these hassles from its users while _allowing_ to tinker with it (custom extensions).

Hmpf... did someone come up with a good and efficient solution (efficient resulting application, not efficient regarding workload / work time). I fully understood that I could create 3-4 tracks consisting of pregenerated "mixtures" of my loops and then play them with a short gap between ... but I am aiming for only 4-5 loops to include in the whole app but creating a bit of randomized-"songs".
Using the bridge ElectricBoogaloo suggested, is surely an option somehow, but just does not allow "crossfading", which is what I want to have.

With my limited knowledge I think I would be able to add a second music channel to GLFW/HTML5 and android (channels 32 and 31 instead just 32) but this is not a real "fix" to the problem, as the next one might need to have 3 simultaneous music streams or 4 - and then it would be better to eg. use "AudioTrack" on Android (caring for filled buffers on your own, which allows streamed audio without the heavy weight mediaplayer).

bye
Ron


Derron(Posted 2015) [#8]
Started to rewrite the audio function for "music" for html5/android/glfw.

To keep things simple I just made "music" and compagnons arrays in html5, adjusted the functions accordingly (and added conditionals to audio.monkey and audiodriver.monkey (so all other targets do not know about the availability of multiple music channels for now). Then I reduced the amount of channels accordingly (for the cases in which all but the music channel were affected).
Then I adjusted my SoundManager to use channel 31 and 32 for music.
Seems to work flawlessly on HTML5 for now.

For android the heap-errors are still happening, so I assume Monkey does not like one of my atmospheric sfx (~15-20 sec could uncompressed land at > 1MB).
This would mean I would need at least 3 music channels during crossfading and playing another sound file :-/.
Also it seems to ignore the volume adjustments (am not sure, but I think I somewhere read about that bug ... only one volume for all MediaPlayers)
Ignoring this, it works as expected and without visible performance hits on my older dual core (1.2ghz) - and is still not 100% perfect smooth on an even older 800mhz cpu.

You cannot tell me, that nobody of you needed more than 1 music stream (music + longer sfx file). Hmppf.


bye
Ron


Derron(Posted 2015) [#9]
Ok, just had another look at it, heap-errors were raised because I still loaded the long ogg-files to sound samples (loading screens fault :p).
So it now seems to work as expected.

Now I am not sure if splitting "music" and "sound" is the best bet we can have. Isnt it better to split them according to flags like a "STREAMED_AUDIO"-flag. I mean, the end-user (the coder) should not need to take care of what he plays, the engine (mojo) should decide on its own whether it is good to use "MediaPlayer" or "SoundPool". Of course the devs could declare something to be "music" by eg. giving a URI to a function instead of an object (Sound-class) - or for sounds using above mentioned flag.
So for Android this means it could come handy to have a "wrapping" object (native...) so changes are done transparent and stay backwards compatible as much as possible.


bye
Ron