summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-06-04 20:39:56 +0000
committerThomas Martitz <kugel@rockbox.org>2011-06-04 20:39:56 +0000
commitb9747ca2067ca1672b49fd9a7ac49ed6cea03653 (patch)
treeb1611ad1f05219b501a33c9eaa55305e288672a8
parent9b7b653f857bf2cc85fed75515eba98677507b47 (diff)
downloadrockbox-b9747ca2067ca1672b49fd9a7ac49ed6cea03653.tar.gz
rockbox-b9747ca2067ca1672b49fd9a7ac49ed6cea03653.zip
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
-rw-r--r--android/src/org/rockbox/RockboxPCM.java99
1 files changed, 47 insertions, 52 deletions
diff --git a/android/src/org/rockbox/RockboxPCM.java b/android/src/org/rockbox/RockboxPCM.java
index 47bc42f976..48178fcc92 100644
--- a/android/src/org/rockbox/RockboxPCM.java
+++ b/android/src/org/rockbox/RockboxPCM.java
@@ -22,7 +22,6 @@
22package org.rockbox; 22package org.rockbox;
23 23
24import java.util.Arrays; 24import java.util.Arrays;
25
26import android.content.BroadcastReceiver; 25import android.content.BroadcastReceiver;
27import android.content.Context; 26import android.content.Context;
28import android.content.Intent; 27import android.content.Intent;
@@ -37,10 +36,6 @@ import android.util.Log;
37 36
38public class RockboxPCM extends AudioTrack 37public class RockboxPCM extends AudioTrack
39{ 38{
40 private byte[] raw_data;
41 private PCMListener l;
42 private HandlerThread ht;
43 private Handler h = null;
44 private static final int streamtype = AudioManager.STREAM_MUSIC; 39 private static final int streamtype = AudioManager.STREAM_MUSIC;
45 private static final int samplerate = 44100; 40 private static final int samplerate = 44100;
46 /* should be CHANNEL_OUT_STEREO in 2.0 and above */ 41 /* should be CHANNEL_OUT_STEREO in 2.0 and above */
@@ -48,16 +43,20 @@ public class RockboxPCM extends AudioTrack
48 AudioFormat.CHANNEL_CONFIGURATION_STEREO; 43 AudioFormat.CHANNEL_CONFIGURATION_STEREO;
49 private static final int encoding = 44 private static final int encoding =
50 AudioFormat.ENCODING_PCM_16BIT; 45 AudioFormat.ENCODING_PCM_16BIT;
51 /* 24k is plenty, but some devices may have a higher minimum */ 46 /* 32k is plenty, but some devices may have a higher minimum */
52 private static final int buf_len = 47 private static final int buf_len =
53 Math.max(24<<10, getMinBufferSize(samplerate, channels, encoding)); 48 Math.max(32<<10, 4*getMinBufferSize(samplerate, channels, encoding));
54 49
55 private AudioManager audiomanager; 50 private AudioManager audiomanager;
51 private RockboxService rbservice;
52 private byte[] raw_data;
53
54 private int refillmark;
56 private int maxstreamvolume; 55 private int maxstreamvolume;
57 private int setstreamvolume = -1; 56 private int setstreamvolume = -1;
58 private float minpcmvolume; 57 private float minpcmvolume;
58 private float curpcmvolume = 0;
59 private float pcmrange; 59 private float pcmrange;
60 private RockboxService rbservice;
61 60
62 private void LOG(CharSequence text) 61 private void LOG(CharSequence text)
63 { 62 {
@@ -68,12 +67,11 @@ public class RockboxPCM extends AudioTrack
68 { 67 {
69 super(streamtype, samplerate, channels, encoding, 68 super(streamtype, samplerate, channels, encoding,
70 buf_len, AudioTrack.MODE_STREAM); 69 buf_len, AudioTrack.MODE_STREAM);
71 ht = new HandlerThread("audio thread", 70 HandlerThread ht = new HandlerThread("audio thread",
72 Process.THREAD_PRIORITY_URGENT_AUDIO); 71 Process.THREAD_PRIORITY_URGENT_AUDIO);
73 ht.start(); 72 ht.start();
74 raw_data = new byte[buf_len]; /* in shorts */ 73 raw_data = new byte[buf_len]; /* in shorts */
75 Arrays.fill(raw_data, (byte) 0); 74 Arrays.fill(raw_data, (byte) 0);
76 l = new PCMListener(buf_len);
77 75
78 /* find cleaner way to get context? */ 76 /* find cleaner way to get context? */
79 rbservice = RockboxService.get_instance(); 77 rbservice = RockboxService.get_instance();
@@ -86,6 +84,13 @@ public class RockboxPCM extends AudioTrack
86 84
87 setupVolumeHandler(); 85 setupVolumeHandler();
88 postVolume(audiomanager.getStreamVolume(streamtype)); 86 postVolume(audiomanager.getStreamVolume(streamtype));
87 refillmark = buf_len / 4; /* higher values don't work on many devices */
88
89 /* getLooper() returns null if thread isn't running */
90 while(!ht.isAlive()) Thread.yield();
91 setPlaybackPositionUpdateListener(
92 new PCMListener(buf_len / 2), new Handler(ht.getLooper()));
93 refillmark = bytes2frames(refillmark);
89 } 94 }
90 95
91 private native void postVolumeChangedEvent(int volume); 96 private native void postVolumeChangedEvent(int volume);
@@ -133,18 +138,21 @@ public class RockboxPCM extends AudioTrack
133 new IntentFilter("android.media.VOLUME_CHANGED_ACTION")); 138 new IntentFilter("android.media.VOLUME_CHANGED_ACTION"));
134 } 139 }
135 140
141 @SuppressWarnings("unused")
136 private int bytes2frames(int bytes) 142 private int bytes2frames(int bytes)
137 { 143 {
138 /* 1 sample is 2 bytes, 2 samples are 1 frame */ 144 /* 1 sample is 2 bytes, 2 samples are 1 frame */
139 return (bytes/4); 145 return (bytes/4);
140 } 146 }
141 147
148 @SuppressWarnings("unused")
142 private int frames2bytes(int frames) 149 private int frames2bytes(int frames)
143 { 150 {
144 /* 1 frame is 2 samples, 1 sample is 2 bytes */ 151 /* 1 frame is 2 samples, 1 sample is 2 bytes */
145 return (frames*4); 152 return (frames*4);
146 } 153 }
147 154
155 @SuppressWarnings("unused")
148 private void play_pause(boolean pause) 156 private void play_pause(boolean pause)
149 { 157 {
150 RockboxService service = RockboxService.get_instance(); 158 RockboxService service = RockboxService.get_instance();
@@ -164,38 +172,45 @@ public class RockboxPCM extends AudioTrack
164 service.startForeground(); 172 service.startForeground();
165 if (getPlayState() == AudioTrack.PLAYSTATE_STOPPED) 173 if (getPlayState() == AudioTrack.PLAYSTATE_STOPPED)
166 { 174 {
167 if (getState() == AudioTrack.STATE_INITIALIZED) 175 setNotificationMarkerPosition(refillmark);
168 { 176 /* need to fill with silence before starting playback */
169 if (h == null) 177 write(raw_data, 0, raw_data.length);
170 h = new Handler(ht.getLooper());
171 if (setNotificationMarkerPosition(bytes2frames(buf_len)/4)
172 != AudioTrack.SUCCESS)
173 LOG("setNotificationMarkerPosition Error");
174 else
175 setPlaybackPositionUpdateListener(l, h);
176 }
177 /* need to fill with silence before starting playback */
178 write(raw_data, frames2bytes(getPlaybackHeadPosition()),
179 raw_data.length);
180 } 178 }
181 play(); 179 play();
182 } 180 }
183 } 181 }
184 182
185 @Override 183 @Override
186 public void stop() throws IllegalStateException 184 public synchronized void stop() throws IllegalStateException
187 { 185 {
186 /* flush pending data, but turn the volume off so it cannot be heard.
187 * This is so that we don't hear old data if music is resumed very
188 * quickly after (e.g. when seeking).
189 */
190 float old_vol = curpcmvolume;
188 try { 191 try {
192 setStereoVolume(0, 0);
193 flush();
189 super.stop(); 194 super.stop();
190 } catch (IllegalStateException e) { 195 } catch (IllegalStateException e) {
191 throw new IllegalStateException(e); 196 throw new IllegalStateException(e);
197 } finally {
198 setStereoVolume(old_vol, old_vol);
192 } 199 }
200
193 Intent widgetUpdate = new Intent("org.rockbox.UpdateState"); 201 Intent widgetUpdate = new Intent("org.rockbox.UpdateState");
194 widgetUpdate.putExtra("state", "stop"); 202 widgetUpdate.putExtra("state", "stop");
195 RockboxService.get_instance().sendBroadcast(widgetUpdate); 203 RockboxService.get_instance().sendBroadcast(widgetUpdate);
196 RockboxService.get_instance().stopForeground(); 204 RockboxService.get_instance().stopForeground();
197 } 205 }
206
207 public int setStereoVolume(float leftVolume, float rightVolume)
208 {
209 curpcmvolume = leftVolume;
210 return super.setStereoVolume(leftVolume, rightVolume);
211 }
198 212
213 @SuppressWarnings("unused")
199 private void set_volume(int volume) 214 private void set_volume(int volume)
200 { 215 {
201 LOG("java:set_volume("+volume+")"); 216 LOG("java:set_volume("+volume+")");
@@ -228,15 +243,10 @@ public class RockboxPCM extends AudioTrack
228 243
229 private class PCMListener implements OnPlaybackPositionUpdateListener 244 private class PCMListener implements OnPlaybackPositionUpdateListener
230 { 245 {
231 private int max_len;
232 private int refill_mark;
233 private byte[] buf; 246 private byte[] buf;
234 public PCMListener(int len) 247 public PCMListener(int refill_bufsize)
235 { 248 {
236 max_len = len; 249 buf = new byte[refill_bufsize];
237 /* refill to 100% when reached the 25% */
238 buf = new byte[max_len*3/4];
239 refill_mark = max_len - buf.length;
240 } 250 }
241 251
242 public void onMarkerReached(AudioTrack track) 252 public void onMarkerReached(AudioTrack track)
@@ -248,32 +258,17 @@ public class RockboxPCM extends AudioTrack
248 result = track.write(buf, 0, buf.length); 258 result = track.write(buf, 0, buf.length);
249 if (result >= 0) 259 if (result >= 0)
250 { 260 {
251 switch(track.getPlayState()) 261 switch(getPlayState())
252 { 262 {
253 case AudioTrack.PLAYSTATE_PLAYING: 263 case PLAYSTATE_PLAYING:
254 case AudioTrack.PLAYSTATE_PAUSED: 264 case PLAYSTATE_PAUSED:
255 /* refill at 25% no matter of how many 265 setNotificationMarkerPosition(pcm.refillmark);
256 * bytes we've written */
257 if (setNotificationMarkerPosition(
258 bytes2frames(refill_mark))
259 != AudioTrack.SUCCESS)
260 {
261 LOG("Error in onMarkerReached: " +
262 "Could not set notification marker");
263 }
264 else /* recharge */
265 setPlaybackPositionUpdateListener(this, h);
266 break; 266 break;
267 case AudioTrack.PLAYSTATE_STOPPED: 267 case PLAYSTATE_STOPPED:
268 LOG("State STOPPED"); 268 LOG("Stopped");
269 break; 269 break;
270 } 270 }
271 } 271 }
272 else
273 {
274 LOG("Error in onMarkerReached (result="+result+")");
275 stop();
276 }
277 } 272 }
278 273
279 public void onPeriodicNotification(AudioTrack track) 274 public void onPeriodicNotification(AudioTrack track)