Looping sounds keep playing when Home is pressed

Monkey Targets Forums/Android/Looping sounds keep playing when Home is pressed

Grey Alien(Posted 2014) [#1]
I've encountered a very strange problem. With the game I just finished, on some devices e.g. Nexus 4 and Nexus 7, but not on others e.g. Nexus S and some crappy tablet thing I have, this can occur:

I have some saucers that play a looping sound. If you press Home, the looping sound stops as expected. Also in my code I stop the looping sound when the saucer is deleted, and that seems to work fine.

However, on rare occasions the looping sound plays even when there are no saucers (there's only one place in my code where that sound is played) and it won't stop when Home is pressed! Nor will it stop if I run through ALL game channels and call StopSound. There's a lot going on but I think it starts playing when a normal enemy shoots, so it is playing the wrong sound, but this should not be possible.

This suggests to me that the handle to the sound has been lost and so Monkey (or Android) is not stopping it when Home is pressed. Also I've no idea why it starts playing the wrong sound.

Note that this doesn't occur on the PC build ever.


Grey Alien(Posted 2014) [#2]
Btw, is it safe to just call PlaySound with a new sound on a channel that may already be playing? As there's no way on Android to check the ChannelState (it always returns -1) all I can do is call PlaySound(). Perhaps I should always call StopChannel() on that channel first for safety? Any thoughts? [EDIT: OK I can see in mojo.android.java that in theory I don't need to call StopChannel() first as PlaySound will stop the channel if it is already running]


Grey Alien(Posted 2014) [#3]
I found the suspend code in mojo.android.java:
	int Suspend(){
		if( musicState==1 ) music.pause();
		for( int i=0;i<32;++i ){
			if( channels[i].state==1 ) pool.pause( channels[i].stream );
		}
		return 0;
	}

So perhaps the rogue saucer sound is playing but its channel.state is not 1 and so it is not being paused...

How could that occur?

Well the state is set in PlaySample:

	int PlaySample( gxtkSample sample,int channel,int flags ){
		gxtkChannel chan=channels[channel];
		if( chan.stream!=0 ) pool.stop( chan.stream );
		float rv=(chan.pan * .5f + .5f) * chan.volume;
		float lv=chan.volume-rv;
		int loops=(flags&1)!=0 ? -1 : 0;

		//chan.stream=pool.play( sample.sound,lv,rv,0,loops,chan.rate );
		//chan.state=1;
		//return 0;
		
		//Ugly as hell, but seems to work for now...pauses 10 secs max...
		for( int i=0;i<100;++i ){
			chan.stream=pool.play( sample.sound,lv,rv,0,loops,chan.rate );
			if( chan.stream!=0 ){
				chan.state=1;
				return 0;
			}
//			throw new Error( "PlaySample failed to play sound" );
			try{
				Thread.sleep( 100 );
			}catch( java.lang.InterruptedException ex ){
			}
		}
		throw new Error( "PlaySample failed to play sound" );
	}

Which looks like it would always set the state otherwise it would throw an Error.


Grey Alien(Posted 2014) [#4]
I also noticed that StopChannel() doesn't clear chan.stream, it only clears chan.state. Maybe it should do because otherwise when PlaySample() is called on that channel again chan.stream will still be set and so pool.stop(chan.stream) will be called. This basically means pool.stop is being called on a channel that has already been stopped.

According to the Android docs for stop(): "If the stream is not playing, it will have no effect." - so it should be safe, but maybe it's not? I'm suspicious anyway.

Perhaps it should look like this? (Note how I've also changed the if to look for chan.stream instead of chan.state).
	int StopChannel( int channel ){
		gxtkChannel chan=channels[channel];
		if( chan.stream!=0 ){ 'EDITED THIS LINE
			pool.stop( chan.stream );
			chan.state=0;
			chan.stream=0; 'NEW LINE
		}
		return 0;
	}


I'll test it out, but can anyone see any obvious problems with doing it like this instead?

ORIGINAL CODE FOR REFERENCE:
int StopChannel( int channel ){
		gxtkChannel chan=channels[channel];
		if( chan.state!=0 ){
			pool.stop( chan.stream );
			chan.state=0;
		}
		return 0;
	}



Grey Alien(Posted 2014) [#5]
OK that appears to have fixed the problem. Or more like, the problem hasn't occurred since I made that change

@Mark May I suggest this as a permanent fix/safe alternative please?


marksibly(Posted 2014) [#6]
Yes, it loos like pool.stop() could potentially be called twice which is probably not ideal.

For a solution though, I think I'd prefer to change the PlaySample code to:

if( chan.state!=0 ) pool.stop( chan.stream );

This should have the same effect (correct?) but is IMO a bit tidier, as this way chan.state is only ever being tested. Otherwise, the code is checking chan.state in some places, chan.stream in others.


Grey Alien(Posted 2014) [#7]
Thanks for the quick response, appreciated. Yep sounds good to me. I approve of this tweak. Like I said, I *think* this fixed my problem but I'm not 100% sure and the code change you suggested is safer just in case.

It still makes me mildly uncomfortable that chan.Stream is not set to 0 when StopChannel is called. Could still be worth doing in case you (or anyone else) extends the code in the future and checks/uses chain.stream in some way instead of chan.state. But that's just my "possible future bug" paranoia talking...