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:
parent
9b7b653f85
commit
b9747ca206
1 changed files with 47 additions and 52 deletions
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue