1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2010 Thomas Martitz
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
24 import android
.media
.AudioFormat
;
25 import android
.media
.AudioManager
;
26 import android
.media
.AudioTrack
;
27 import android
.os
.Handler
;
28 import android
.os
.HandlerThread
;
29 import android
.os
.Process
;
30 import android
.util
.Log
;
32 public class RockboxPCM
extends AudioTrack
36 private PCMListener l
;
37 private HandlerThread ht
;
38 private Handler h
= null;
40 private void LOG(CharSequence text
)
42 Log
.d("Rockbox", (String
) text
);
47 super(AudioManager
.STREAM_MUSIC
,
49 /* should be CHANNEL_OUT_STEREO in 2.0 and above */
50 AudioFormat
.CHANNEL_CONFIGURATION_STEREO
,
51 AudioFormat
.ENCODING_PCM_16BIT
,
53 AudioTrack
.MODE_STREAM
);
54 ht
= new HandlerThread("audio thread", Process
.THREAD_PRIORITY_URGENT_AUDIO
);
56 buf_len
= 24<<10; /* in bytes */
58 raw_data
= new byte[buf_len
]; /* in shorts */
59 for(int i
= 0; i
< raw_data
.length
; i
++) raw_data
[i
] = (byte)0;
60 l
= new PCMListener(buf_len
);
63 int bytes2frames(int bytes
) {
64 /* 1 sample is 2 bytes, 2 samples are 1 frame */
68 int frames2bytes(int frames
) {
69 /* 1 frame is 2 samples, 1 sample is 2 bytes */
73 @SuppressWarnings("unused")
74 private void play_pause(boolean pause
) {
81 if (getPlayState() == AudioTrack
.PLAYSTATE_STOPPED
)
83 RockboxService
.startForeground();
84 if (getState() == AudioTrack
.STATE_INITIALIZED
)
87 h
= new Handler(ht
.getLooper());
88 if (setNotificationMarkerPosition(bytes2frames(buf_len
)/4) != AudioTrack
.SUCCESS
)
89 LOG("setNotificationMarkerPosition Error");
91 setPlaybackPositionUpdateListener(l
, h
);
93 /* need to fill with silence before starting playback */
94 write(raw_data
, frames2bytes(getPlaybackHeadPosition()), raw_data
.length
);
101 public void stop() throws IllegalStateException
105 } catch (IllegalStateException e
) {
106 throw new IllegalStateException(e
);
108 RockboxService
.stopForeground();
111 @SuppressWarnings("unused")
112 private void set_volume(int volume
)
114 /* volume comes from 0..-990 from Rockbox */
115 /* TODO volume is in dB, but this code acts as if it were in %, convert? */
117 /* special case min and max volume to not suffer from floating point accuracy */
120 else if (volume
== -990)
123 fvolume
= (volume
+ 990)/990.0f
;
124 setStereoVolume(fvolume
, fvolume
);
127 public native void pcmSamplesToByteArray(byte[] dest
);
129 private class PCMListener
implements OnPlaybackPositionUpdateListener
{
133 public PCMListener(int len
) {
135 /* refill to 100% when reached the 25% */
136 buf
= new byte[max_len
*3/4];
137 refill_mark
= max_len
- buf
.length
;
140 public void onMarkerReached(AudioTrack track
) {
141 // push new data to the hardware
142 RockboxPCM pcm
= (RockboxPCM
)track
;
144 pcm
.pcmSamplesToByteArray(buf
);
145 result
= track
.write(buf
, 0, buf
.length
);
148 switch(track
.getPlayState())
150 case AudioTrack
.PLAYSTATE_PLAYING
:
151 case AudioTrack
.PLAYSTATE_PAUSED
:
152 /* refill at 25% no matter of how many bytes we've written */
153 if (setNotificationMarkerPosition(bytes2frames(refill_mark
)) != AudioTrack
.SUCCESS
)
154 LOG("Error in onMarkerReached: Could not set notification marker");
156 setPlaybackPositionUpdateListener(this, h
);
158 case AudioTrack
.PLAYSTATE_STOPPED
:
159 LOG("State STOPPED");
165 LOG("Error in onMarkerReached (result="+result
+")");
171 public void onPeriodicNotification(AudioTrack track
) {
172 // TODO Auto-generated method stub