1
0
Fork 0
forked from len0rd/rockbox

Hopefully FS#12064 - Android: Possible fix for audio stopping on high CPU load.

It's not enterly clear what fixed it but it seems to be a combination of
increasing the buffer size and reducing the amount of code run in the callback.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29961 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Thomas Martitz 2011-06-04 20:39:56 +00:00
parent 9b7b653f85
commit b9747ca206

View file

@ -22,7 +22,6 @@
package org.rockbox; package org.rockbox;
import java.util.Arrays; import java.util.Arrays;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -37,10 +36,6 @@ import android.util.Log;
public class RockboxPCM extends AudioTrack public class RockboxPCM extends AudioTrack
{ {
private byte[] raw_data;
private PCMListener l;
private HandlerThread ht;
private Handler h = null;
private static final int streamtype = AudioManager.STREAM_MUSIC; private static final int streamtype = AudioManager.STREAM_MUSIC;
private static final int samplerate = 44100; private static final int samplerate = 44100;
/* should be CHANNEL_OUT_STEREO in 2.0 and above */ /* should be CHANNEL_OUT_STEREO in 2.0 and above */
@ -48,16 +43,20 @@ public class RockboxPCM extends AudioTrack
AudioFormat.CHANNEL_CONFIGURATION_STEREO; AudioFormat.CHANNEL_CONFIGURATION_STEREO;
private static final int encoding = private static final int encoding =
AudioFormat.ENCODING_PCM_16BIT; AudioFormat.ENCODING_PCM_16BIT;
/* 24k is plenty, but some devices may have a higher minimum */ /* 32k is plenty, but some devices may have a higher minimum */
private static final int buf_len = private static final int buf_len =
Math.max(24<<10, getMinBufferSize(samplerate, channels, encoding)); Math.max(32<<10, 4*getMinBufferSize(samplerate, channels, encoding));
private AudioManager audiomanager; private AudioManager audiomanager;
private RockboxService rbservice;
private byte[] raw_data;
private int refillmark;
private int maxstreamvolume; private int maxstreamvolume;
private int setstreamvolume = -1; private int setstreamvolume = -1;
private float minpcmvolume; private float minpcmvolume;
private float curpcmvolume = 0;
private float pcmrange; private float pcmrange;
private RockboxService rbservice;
private void LOG(CharSequence text) private void LOG(CharSequence text)
{ {
@ -68,12 +67,11 @@ public class RockboxPCM extends AudioTrack
{ {
super(streamtype, samplerate, channels, encoding, super(streamtype, samplerate, channels, encoding,
buf_len, AudioTrack.MODE_STREAM); buf_len, AudioTrack.MODE_STREAM);
ht = new HandlerThread("audio thread", HandlerThread ht = new HandlerThread("audio thread",
Process.THREAD_PRIORITY_URGENT_AUDIO); Process.THREAD_PRIORITY_URGENT_AUDIO);
ht.start(); ht.start();
raw_data = new byte[buf_len]; /* in shorts */ raw_data = new byte[buf_len]; /* in shorts */
Arrays.fill(raw_data, (byte) 0); Arrays.fill(raw_data, (byte) 0);
l = new PCMListener(buf_len);
/* find cleaner way to get context? */ /* find cleaner way to get context? */
rbservice = RockboxService.get_instance(); rbservice = RockboxService.get_instance();
@ -86,6 +84,13 @@ public class RockboxPCM extends AudioTrack
setupVolumeHandler(); setupVolumeHandler();
postVolume(audiomanager.getStreamVolume(streamtype)); postVolume(audiomanager.getStreamVolume(streamtype));
refillmark = buf_len / 4; /* higher values don't work on many devices */
/* getLooper() returns null if thread isn't running */
while(!ht.isAlive()) Thread.yield();
setPlaybackPositionUpdateListener(
new PCMListener(buf_len / 2), new Handler(ht.getLooper()));
refillmark = bytes2frames(refillmark);
} }
private native void postVolumeChangedEvent(int volume); private native void postVolumeChangedEvent(int volume);
@ -133,18 +138,21 @@ public class RockboxPCM extends AudioTrack
new IntentFilter("android.media.VOLUME_CHANGED_ACTION")); new IntentFilter("android.media.VOLUME_CHANGED_ACTION"));
} }
@SuppressWarnings("unused")
private int bytes2frames(int bytes) private int bytes2frames(int bytes)
{ {
/* 1 sample is 2 bytes, 2 samples are 1 frame */ /* 1 sample is 2 bytes, 2 samples are 1 frame */
return (bytes/4); return (bytes/4);
} }
@SuppressWarnings("unused")
private int frames2bytes(int frames) private int frames2bytes(int frames)
{ {
/* 1 frame is 2 samples, 1 sample is 2 bytes */ /* 1 frame is 2 samples, 1 sample is 2 bytes */
return (frames*4); return (frames*4);
} }
@SuppressWarnings("unused")
private void play_pause(boolean pause) private void play_pause(boolean pause)
{ {
RockboxService service = RockboxService.get_instance(); RockboxService service = RockboxService.get_instance();
@ -164,38 +172,45 @@ public class RockboxPCM extends AudioTrack
service.startForeground(); service.startForeground();
if (getPlayState() == AudioTrack.PLAYSTATE_STOPPED) if (getPlayState() == AudioTrack.PLAYSTATE_STOPPED)
{ {
if (getState() == AudioTrack.STATE_INITIALIZED) setNotificationMarkerPosition(refillmark);
{ /* need to fill with silence before starting playback */
if (h == null) write(raw_data, 0, raw_data.length);
h = new Handler(ht.getLooper());
if (setNotificationMarkerPosition(bytes2frames(buf_len)/4)
!= AudioTrack.SUCCESS)
LOG("setNotificationMarkerPosition Error");
else
setPlaybackPositionUpdateListener(l, h);
}
/* need to fill with silence before starting playback */
write(raw_data, frames2bytes(getPlaybackHeadPosition()),
raw_data.length);
} }
play(); play();
} }
} }
@Override @Override
public void stop() throws IllegalStateException public synchronized void stop() throws IllegalStateException
{ {
/* flush pending data, but turn the volume off so it cannot be heard.
* This is so that we don't hear old data if music is resumed very
* quickly after (e.g. when seeking).
*/
float old_vol = curpcmvolume;
try { try {
setStereoVolume(0, 0);
flush();
super.stop(); super.stop();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} finally {
setStereoVolume(old_vol, old_vol);
} }
Intent widgetUpdate = new Intent("org.rockbox.UpdateState"); Intent widgetUpdate = new Intent("org.rockbox.UpdateState");
widgetUpdate.putExtra("state", "stop"); widgetUpdate.putExtra("state", "stop");
RockboxService.get_instance().sendBroadcast(widgetUpdate); RockboxService.get_instance().sendBroadcast(widgetUpdate);
RockboxService.get_instance().stopForeground(); RockboxService.get_instance().stopForeground();
} }
public int setStereoVolume(float leftVolume, float rightVolume)
{
curpcmvolume = leftVolume;
return super.setStereoVolume(leftVolume, rightVolume);
}
@SuppressWarnings("unused")
private void set_volume(int volume) private void set_volume(int volume)
{ {
LOG("java:set_volume("+volume+")"); LOG("java:set_volume("+volume+")");
@ -228,15 +243,10 @@ public class RockboxPCM extends AudioTrack
private class PCMListener implements OnPlaybackPositionUpdateListener private class PCMListener implements OnPlaybackPositionUpdateListener
{ {
private int max_len;
private int refill_mark;
private byte[] buf; private byte[] buf;
public PCMListener(int len) public PCMListener(int refill_bufsize)
{ {
max_len = len; buf = new byte[refill_bufsize];
/* refill to 100% when reached the 25% */
buf = new byte[max_len*3/4];
refill_mark = max_len - buf.length;
} }
public void onMarkerReached(AudioTrack track) public void onMarkerReached(AudioTrack track)
@ -248,32 +258,17 @@ public class RockboxPCM extends AudioTrack
result = track.write(buf, 0, buf.length); result = track.write(buf, 0, buf.length);
if (result >= 0) if (result >= 0)
{ {
switch(track.getPlayState()) switch(getPlayState())
{ {
case AudioTrack.PLAYSTATE_PLAYING: case PLAYSTATE_PLAYING:
case AudioTrack.PLAYSTATE_PAUSED: case PLAYSTATE_PAUSED:
/* refill at 25% no matter of how many setNotificationMarkerPosition(pcm.refillmark);
* bytes we've written */
if (setNotificationMarkerPosition(
bytes2frames(refill_mark))
!= AudioTrack.SUCCESS)
{
LOG("Error in onMarkerReached: " +
"Could not set notification marker");
}
else /* recharge */
setPlaybackPositionUpdateListener(this, h);
break; break;
case AudioTrack.PLAYSTATE_STOPPED: case PLAYSTATE_STOPPED:
LOG("State STOPPED"); LOG("Stopped");
break; break;
} }
} }
else
{
LOG("Error in onMarkerReached (result="+result+")");
stop();
}
} }
public void onPeriodicNotification(AudioTrack track) public void onPeriodicNotification(AudioTrack track)