diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-04-27 03:08:23 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-04-27 03:08:23 +0000 |
commit | c537d5958e8b421ac4f9bef6c8b9e7425a6cf167 (patch) | |
tree | 7ed36518fb6524da7bbd913ba7619b85b5d15d23 /apps/playback.c | |
parent | dcf0f8de4a37ff1d2ea510aef75fa67977a8bdcc (diff) | |
download | rockbox-c537d5958e8b421ac4f9bef6c8b9e7425a6cf167.tar.gz rockbox-c537d5958e8b421ac4f9bef6c8b9e7425a6cf167.zip |
Commit FS#12069 - Playback rework - first stages. Gives as thorough as possible a treatment of codec management, track change and metadata logic as possible while maintaining fairly narrow focus and not rewriting everything all at once. Please see the rockbox-dev mail archive on 2011-04-25 (Playback engine rework) for a more thorough manifest of what was addressed. Plugins and codecs become incompatible.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29785 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/playback.c')
-rw-r--r-- | apps/playback.c | 4590 |
1 files changed, 2981 insertions, 1609 deletions
diff --git a/apps/playback.c b/apps/playback.c index 632fd05d3d..a369d15715 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * | 9 | * |
10 | * Copyright (C) 2005-2007 Miika Pekkarinen | 10 | * Copyright (C) 2005-2007 Miika Pekkarinen |
11 | * Copyright (C) 2007-2008 Nicolas Pennequin | 11 | * Copyright (C) 2007-2008 Nicolas Pennequin |
12 | * Copyright (C) 2011 Michael Sevakis | ||
12 | * | 13 | * |
13 | * This program is free software; you can redistribute it and/or | 14 | * This program is free software; you can redistribute it and/or |
14 | * modify it under the terms of the GNU General Public License | 15 | * modify it under the terms of the GNU General Public License |
@@ -19,53 +20,61 @@ | |||
19 | * KIND, either express or implied. | 20 | * KIND, either express or implied. |
20 | * | 21 | * |
21 | ****************************************************************************/ | 22 | ****************************************************************************/ |
22 | |||
23 | /* TODO: Pause should be handled in here, rather than PCMBUF so that voice can | ||
24 | * play whilst audio is paused */ | ||
25 | #include "config.h" | 23 | #include "config.h" |
26 | #include "system.h" | 24 | #include "system.h" |
27 | #include <string.h> | ||
28 | #include "playback.h" | ||
29 | #include "codec_thread.h" | ||
30 | #include "kernel.h" | 25 | #include "kernel.h" |
26 | #include "panic.h" | ||
27 | #include "buffer.h" | ||
28 | #include "sound.h" | ||
29 | #include "ata.h" | ||
30 | #include "usb.h" | ||
31 | #include "codecs.h" | 31 | #include "codecs.h" |
32 | #include "buffering.h" | 32 | #include "codec_thread.h" |
33 | #include "voice_thread.h" | 33 | #include "voice_thread.h" |
34 | #include "usb.h" | 34 | #include "metadata.h" |
35 | #include "ata.h" | 35 | #include "cuesheet.h" |
36 | #include "buffering.h" | ||
37 | #include "talk.h" | ||
36 | #include "playlist.h" | 38 | #include "playlist.h" |
39 | #include "abrepeat.h" | ||
37 | #include "pcmbuf.h" | 40 | #include "pcmbuf.h" |
38 | #include "buffer.h" | 41 | #include "playback.h" |
39 | #include "cuesheet.h" | 42 | |
40 | #ifdef HAVE_TAGCACHE | 43 | #ifdef HAVE_TAGCACHE |
41 | #include "tagcache.h" | 44 | #include "tagcache.h" |
42 | #endif | 45 | #endif |
46 | |||
47 | #ifdef AUDIO_HAVE_RECORDING | ||
48 | #include "pcm_record.h" | ||
49 | #endif | ||
50 | |||
43 | #ifdef HAVE_LCD_BITMAP | 51 | #ifdef HAVE_LCD_BITMAP |
44 | #ifdef HAVE_ALBUMART | 52 | #ifdef HAVE_ALBUMART |
45 | #include "albumart.h" | 53 | #include "albumart.h" |
46 | #endif | 54 | #endif |
47 | #endif | 55 | #endif |
48 | #include "sound.h" | ||
49 | #include "metadata.h" | ||
50 | #include "splash.h" | ||
51 | #include "talk.h" | ||
52 | #include "panic.h" | ||
53 | 56 | ||
54 | #ifdef HAVE_RECORDING | 57 | /* TODO: The audio thread really is doing multitasking of acting like a |
55 | #include "pcm_record.h" | 58 | consumer and producer of tracks. It may be advantageous to better |
56 | #endif | 59 | logically separate the two functions. I won't go that far just yet. */ |
57 | 60 | ||
61 | /* Internal support for voice playback */ | ||
58 | #define PLAYBACK_VOICE | 62 | #define PLAYBACK_VOICE |
59 | 63 | ||
60 | /* amount of guess-space to allow for codecs that must hunt and peck | 64 | #if CONFIG_PLATFORM & PLATFORM_NATIVE |
61 | * for their correct seeek target, 32k seems a good size */ | 65 | /* Application builds don't support direct code loading */ |
66 | #define HAVE_CODEC_BUFFERING | ||
67 | #endif | ||
68 | |||
69 | /* Amount of guess-space to allow for codecs that must hunt and peck | ||
70 | * for their correct seek target, 32k seems a good size */ | ||
62 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) | 71 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) |
63 | 72 | ||
64 | /* Define LOGF_ENABLE to enable logf output in this file */ | 73 | /* Define LOGF_ENABLE to enable logf output in this file */ |
65 | /*#define LOGF_ENABLE*/ | 74 | /* #define LOGF_ENABLE */ |
66 | #include "logf.h" | 75 | #include "logf.h" |
67 | 76 | ||
68 | /* macros to enable logf for queues | 77 | /* Macros to enable logf for queues |
69 | logging on SYS_TIMEOUT can be disabled */ | 78 | logging on SYS_TIMEOUT can be disabled */ |
70 | #ifdef SIMULATOR | 79 | #ifdef SIMULATOR |
71 | /* Define this for logf output of all queuing except SYS_TIMEOUT */ | 80 | /* Define this for logf output of all queuing except SYS_TIMEOUT */ |
@@ -86,202 +95,292 @@ | |||
86 | #define LOGFQUEUE_SYS_TIMEOUT(...) | 95 | #define LOGFQUEUE_SYS_TIMEOUT(...) |
87 | #endif | 96 | #endif |
88 | 97 | ||
98 | /* Variables are commented with the threads that use them: | ||
99 | * A=audio, C=codec, O=other. A suffix of "-" indicates that the variable is | ||
100 | * read but not updated on that thread. Audio is the only user unless otherwise | ||
101 | * specified. | ||
102 | */ | ||
89 | 103 | ||
90 | static enum filling_state { | 104 | /** Miscellaneous **/ |
91 | STATE_IDLE, /* audio is stopped: nothing to do */ | 105 | bool audio_is_initialized = false; /* (A,O-) */ |
92 | STATE_FILLING, /* adding tracks to the buffer */ | 106 | extern struct codec_api ci; /* (A,C) */ |
93 | STATE_FULL, /* can't add any more tracks */ | 107 | |
94 | STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */ | 108 | /** Possible arrangements of the main buffer **/ |
95 | STATE_FINISHED, /* all remaining tracks are fully buffered */ | 109 | static enum audio_buffer_state |
96 | STATE_ENDING, /* audio playback is ending */ | 110 | { |
97 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 111 | AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */ |
98 | STATE_USB, /* USB mode, ignore most messages */ | 112 | AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */ |
113 | AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */ | ||
114 | } buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */ | ||
115 | |||
116 | /** Main state control **/ | ||
117 | static bool ff_rw_mode SHAREDBSS_ATTR = false; /* Pre-ff-rewind mode (A,O-) */ | ||
118 | |||
119 | enum play_status | ||
120 | { | ||
121 | PLAY_STOPPED = 0, | ||
122 | PLAY_PLAYING = AUDIO_STATUS_PLAY, | ||
123 | PLAY_PAUSED = AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE, | ||
124 | } play_status = PLAY_STOPPED; | ||
125 | |||
126 | /* Sizeable things that only need exist during playback and not when stopped */ | ||
127 | static struct audio_scratch_memory | ||
128 | { | ||
129 | struct mp3entry codec_id3; /* (A,C) */ | ||
130 | struct mp3entry unbuffered_id3; | ||
131 | struct cuesheet *curr_cue; /* Will follow this structure */ | ||
132 | } * audio_scratch_memory = NULL; | ||
133 | |||
134 | /* These are used to store the current, next and optionally the peek-ahead | ||
135 | * mp3entry's - this guarentees that the pointer returned by audio_current/ | ||
136 | * next_track will be valid for the full duration of the currently playing | ||
137 | * track */ | ||
138 | enum audio_id3_types | ||
139 | { | ||
140 | /* These are allocated statically */ | ||
141 | PLAYING_ID3 = 0, | ||
142 | NEXTTRACK_ID3, | ||
143 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
144 | /* The real playing metadata must has to be protected since it contains | ||
145 | critical info for other features */ | ||
146 | PLAYING_PEEK_ID3, | ||
99 | #endif | 147 | #endif |
100 | } filling; | 148 | ID3_TYPE_NUM_STATIC, |
101 | 149 | /* These go in the scratch memory */ | |
102 | /* As defined in plugins/lib/xxx2wav.h */ | 150 | UNBUFFERED_ID3 = ID3_TYPE_NUM_STATIC, |
103 | #define GUARD_BUFSIZE (32*1024) | 151 | CODEC_ID3, |
104 | 152 | }; | |
105 | bool audio_is_initialized = false; | 153 | static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */ |
106 | static bool audio_thread_ready SHAREDBSS_ATTR = false; | ||
107 | 154 | ||
108 | /* Variables are commented with the threads that use them: * | 155 | /* Peeking functions can yield and mess us up */ |
109 | * A=audio, C=codec, V=voice. A suffix of - indicates that * | 156 | static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,0)*/ |
110 | * the variable is read but not updated on that thread. */ | ||
111 | /* TBD: Split out "audio" and "playback" (ie. calling) threads */ | ||
112 | 157 | ||
113 | /* Main state control */ | ||
114 | static volatile bool playing SHAREDBSS_ATTR = false;/* Is audio playing? (A) */ | ||
115 | static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */ | ||
116 | 158 | ||
117 | /* Ring buffer where compressed audio and codecs are loaded */ | 159 | /** For Scrobbler support **/ |
118 | static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ | ||
119 | static size_t filebuflen = 0; /* Size of buffer (A/C-) */ | ||
120 | /* FIXME: make buf_ridx (C/A-) */ | ||
121 | 160 | ||
122 | /* Possible arrangements of the buffer */ | 161 | /* Previous track elapsed time */ |
123 | enum audio_buffer_state | 162 | static unsigned long prev_track_elapsed = 0; /* (A,O-) */ |
124 | { | ||
125 | AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */ | ||
126 | AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */ | ||
127 | AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */ | ||
128 | }; | ||
129 | static int buffer_state = AUDIOBUF_STATE_TRASHED; /* Buffer state */ | ||
130 | |||
131 | /* These are used to store the current and next (or prev if the current is the last) | ||
132 | * mp3entry's in a round-robin system. This guarentees that the pointer returned | ||
133 | * by audio_current/next_track will be valid for the full duration of the | ||
134 | * currently playing track */ | ||
135 | static struct mp3entry mp3entry_buf[2]; | ||
136 | struct mp3entry *thistrack_id3, /* the currently playing track */ | ||
137 | *othertrack_id3; /* prev track during track-change-transition, or end of playlist, | ||
138 | * next track otherwise */ | ||
139 | static struct mp3entry unbuffered_id3; /* the id3 for the first unbuffered track */ | ||
140 | |||
141 | /* for cuesheet support */ | ||
142 | static struct cuesheet *curr_cue = NULL; | ||
143 | 163 | ||
144 | 164 | ||
165 | /** For album art support **/ | ||
145 | #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT | 166 | #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT |
146 | |||
147 | #ifdef HAVE_ALBUMART | 167 | #ifdef HAVE_ALBUMART |
148 | 168 | ||
149 | static struct albumart_slot { | 169 | static struct albumart_slot |
150 | struct dim dim; /* holds width, height of the albumart */ | 170 | { |
151 | int used; /* counter, increments if something uses it */ | 171 | struct dim dim; /* Holds width, height of the albumart */ |
152 | } albumart_slots[MAX_MULTIPLE_AA]; | 172 | int used; /* Counter; increments if something uses it */ |
173 | } albumart_slots[MAX_MULTIPLE_AA]; /* (A,O) */ | ||
153 | 174 | ||
154 | #define FOREACH_ALBUMART(i) for(i = 0;i < MAX_MULTIPLE_AA; i++) | 175 | #define FOREACH_ALBUMART(i) for(i = 0;i < MAX_MULTIPLE_AA; i++) |
155 | #endif | 176 | #endif /* HAVE_ALBUMART */ |
156 | 177 | ||
157 | 178 | ||
158 | #define MAX_TRACK 128 | 179 | /** Information used for tracking buffer fills **/ |
159 | #define MAX_TRACK_MASK (MAX_TRACK-1) | ||
160 | 180 | ||
161 | /* Track info structure about songs in the file buffer (A/C-) */ | 181 | /* Buffer and thread state tracking */ |
162 | static struct track_info { | 182 | static enum filling_state |
163 | int audio_hid; /* The ID for the track's buffer handle */ | 183 | { |
164 | int id3_hid; /* The ID for the track's metadata handle */ | 184 | STATE_BOOT = 0, /* audio thread is not ready yet */ |
165 | int codec_hid; /* The ID for the track's codec handle */ | 185 | STATE_IDLE, /* audio is stopped: nothing to do */ |
166 | #ifdef HAVE_ALBUMART | 186 | STATE_FILLING, /* adding tracks to the buffer */ |
167 | int aa_hid[MAX_MULTIPLE_AA];/* The ID for the track's album art handle */ | 187 | STATE_FULL, /* can't add any more tracks */ |
188 | STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */ | ||
189 | STATE_FINISHED, /* all remaining tracks are fully buffered */ | ||
190 | STATE_ENDING, /* audio playback is ending */ | ||
191 | STATE_ENDED, /* audio playback is done */ | ||
192 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
193 | STATE_USB, /* USB mode, ignore most messages */ | ||
168 | #endif | 194 | #endif |
169 | int cuesheet_hid; /* The ID for the track's parsed cueesheet handle */ | 195 | } filling = STATE_BOOT; |
170 | 196 | ||
171 | size_t filesize; /* File total length */ | 197 | /* Track info - holds information about each track in the buffer */ |
198 | struct track_info | ||
199 | { | ||
200 | /* In per-track allocated order: */ | ||
201 | int id3_hid; /* Metadata handle ID */ | ||
202 | int cuesheet_hid; /* Parsed cueesheet handle ID */ | ||
203 | #ifdef HAVE_ALBUMART | ||
204 | int aa_hid[MAX_MULTIPLE_AA];/* Album art handle IDs */ | ||
205 | #endif | ||
206 | #ifdef HAVE_CODEC_BUFFERING | ||
207 | int codec_hid; /* Buffered codec handle ID */ | ||
208 | #endif | ||
209 | int audio_hid; /* Main audio data handle ID */ | ||
210 | size_t filesize; /* File total length on disk | ||
211 | TODO: This should be stored | ||
212 | in the handle or the | ||
213 | id3 and would use less | ||
214 | ram */ | ||
215 | }; | ||
172 | 216 | ||
173 | bool taginfo_ready; /* Is metadata read */ | 217 | /* Track list - holds info about all buffered tracks */ |
174 | 218 | #if MEMORYSIZE >= 32 | |
175 | } tracks[MAX_TRACK]; | 219 | #define TRACK_LIST_LEN 128 /* Must be 2^int(+n) */ |
220 | #elif MEMORYSIZE >= 16 | ||
221 | #define TRACK_LIST_LEN 64 | ||
222 | #elif MEMORYSIZE >= 8 | ||
223 | #define TRACK_LIST_LEN 32 | ||
224 | #else | ||
225 | #define TRACK_LIST_LEN 16 | ||
226 | #endif | ||
176 | 227 | ||
177 | static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ | 228 | #define TRACK_LIST_MASK (TRACK_LIST_LEN-1) |
178 | static int track_widx = 0; /* Track being buffered (A) */ | ||
179 | #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ | ||
180 | 229 | ||
181 | static struct track_info *prev_ti = NULL; /* Pointer to the previously played | 230 | static struct |
182 | track */ | 231 | { |
232 | /* read, write and current are maintained unwrapped, limited only by the | ||
233 | unsigned int range and wrap-safe comparisons are used */ | ||
183 | 234 | ||
184 | /* Information used only for filling the buffer */ | 235 | /* NOTE: there appears to be a bug in arm-elf-eabi-gcc 4.4.4 for ARMv4 where |
185 | /* Playlist steps from playing track to next track to be buffered (A) */ | 236 | if 'end' follows 'start' in this structure, track_list_count performs |
186 | static int last_peek_offset = 0; | 237 | 'start - end' rather than 'end - start', giving negative count values... |
238 | so leave it this way for now! */ | ||
239 | unsigned int end; /* Next open position */ | ||
240 | unsigned int start; /* First track in list */ | ||
241 | unsigned int current; /* Currently decoding track */ | ||
242 | struct track_info tracks[TRACK_LIST_LEN]; /* Buffered track information */ | ||
243 | } track_list; /* (A, O-) */ | ||
187 | 244 | ||
188 | /* Scrobbler support */ | ||
189 | static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ | ||
190 | 245 | ||
191 | /* Track change controls */ | 246 | /* Playlist steps from playlist position to next track to be buffered */ |
192 | static bool automatic_skip = false; /* Who initiated in-progress skip? (A) */ | 247 | static int playlist_peek_offset = 0; |
193 | extern bool track_transition; /* Are we in a track transition? */ | ||
194 | static bool dir_skip = false; /* Is a directory skip pending? (A) */ | ||
195 | static bool new_playlist = false; /* Are we starting a new playlist? (A) */ | ||
196 | static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */ | ||
197 | static bool skipped_during_pause = false; /* Do we need to clear the PCM buffer when playback resumes (A) */ | ||
198 | 248 | ||
199 | static bool start_play_g = false; /* Used by audio_load_track to notify | 249 | /* Metadata handle of track load in progress (meaning all handles have not |
200 | audio_finish_load_track about start_play */ | 250 | yet been opened for the track, id3 always exists or the track does not) |
201 | 251 | ||
202 | /* True when a track load is in progress, i.e. audio_load_track() has returned | 252 | Tracks are keyed by their metadata handles if track list pointers are |
203 | * but audio_finish_load_track() hasn't been called yet. Used to avoid allowing | 253 | insufficient to make comparisons */ |
204 | * audio_load_track() to get called twice in a row, which would cause problems. | 254 | static int in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; |
205 | */ | ||
206 | static bool track_load_started = false; | ||
207 | 255 | ||
208 | #ifdef HAVE_DISK_STORAGE | 256 | #ifdef HAVE_DISK_STORAGE |
209 | static size_t buffer_margin = 5; /* Buffer margin aka anti-skip buffer (A/C-) */ | 257 | /* Buffer margin A.K.A. anti-skip buffer (in seconds) */ |
258 | static size_t buffer_margin = 5; | ||
210 | #endif | 259 | #endif |
211 | 260 | ||
212 | /* Event queues */ | 261 | /* Values returned for track loading */ |
213 | struct event_queue audio_queue SHAREDBSS_ATTR; | 262 | enum track_load_status |
214 | static struct event_queue pcmbuf_queue SHAREDBSS_ATTR; | 263 | { |
264 | LOAD_TRACK_ERR_START_CODEC = -6, | ||
265 | LOAD_TRACK_ERR_FINISH_FAILED = -5, | ||
266 | LOAD_TRACK_ERR_FINISH_FULL = -4, | ||
267 | LOAD_TRACK_ERR_BUSY = -3, | ||
268 | LOAD_TRACK_ERR_NO_MORE = -2, | ||
269 | LOAD_TRACK_ERR_FAILED = -1, | ||
270 | LOAD_TRACK_OK = 0, | ||
271 | LOAD_TRACK_READY = 1, | ||
272 | }; | ||
215 | 273 | ||
216 | extern struct codec_api ci; | 274 | /** Track change controls **/ |
217 | extern unsigned int codec_thread_id; | 275 | |
276 | /* What sort of skip is pending globally? */ | ||
277 | enum track_skip_type | ||
278 | { | ||
279 | /* Relative to what user is intended to see: */ | ||
280 | /* Codec: +0, Track List: +0, Playlist: +0 */ | ||
281 | TRACK_SKIP_NONE = 0, /* no track skip */ | ||
282 | /* Codec: +1, Track List: +1, Playlist: +0 */ | ||
283 | TRACK_SKIP_AUTO, /* codec-initiated skip */ | ||
284 | /* Codec: +1, Track List: +1, Playlist: +1 */ | ||
285 | TRACK_SKIP_AUTO_NEW_PLAYLIST, /* codec-initiated skip is new playlist */ | ||
286 | /* Codec: xx, Track List: +0, Playlist: +0 */ | ||
287 | TRACK_SKIP_AUTO_END_PLAYLIST, /* codec-initiated end of playlist */ | ||
288 | /* Manual skip: Never pends */ | ||
289 | TRACK_SKIP_MANUAL, /* manual track skip */ | ||
290 | /* Manual skip: Never pends */ | ||
291 | TRACK_SKIP_DIR_CHANGE, /* manual directory skip */ | ||
292 | } skip_pending = TRACK_SKIP_NONE; | ||
293 | |||
294 | /* Note about TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
295 | Fixing playlist code to be able to peek into the first song of | ||
296 | the next playlist would fix any issues and this wouldn't need | ||
297 | to be a special case since pre-advancing the playlist would be | ||
298 | unneeded - it could be much more like TRACK_SKIP_AUTO and all | ||
299 | actions that require reversal during an in-progress transition | ||
300 | would work as expected */ | ||
301 | |||
302 | /* Used to indicate status for the events. Must be separate to satisfy all | ||
303 | clients so the correct metadata is read when sending the change events | ||
304 | and also so that it is read correctly outside the events. */ | ||
305 | static bool automatic_skip = false; /* (A, O-) */ | ||
306 | |||
307 | /* Pending manual track skip offset */ | ||
308 | static int skip_offset = 0; /* (A, O) */ | ||
309 | |||
310 | /* Track change notification */ | ||
311 | static struct | ||
312 | { | ||
313 | unsigned int in; /* Number of pcmbuf posts (audio isr) */ | ||
314 | unsigned int out; /* Number of times audio has read the difference */ | ||
315 | } track_change = { 0, 0 }; | ||
316 | |||
317 | /** Codec status **/ | ||
318 | /* Did the codec notify us it finished while we were paused or while still | ||
319 | in an automatic transition? | ||
320 | |||
321 | If paused, it is necessary to defer a codec-initiated skip until resuming | ||
322 | or else the track will move forward while not playing audio! | ||
323 | |||
324 | If in-progress, skips should not build-up ahead of where the WPS is when | ||
325 | really short tracks finish decoding. | ||
326 | |||
327 | If it is forgotten, it will be missed altogether and playback will just sit | ||
328 | there looking stupid and comatose until the user does something */ | ||
329 | static bool codec_skip_pending = false; | ||
330 | static int codec_skip_status; | ||
331 | static bool codec_seeking = false; /* Codec seeking ack expected? */ | ||
218 | 332 | ||
219 | /* Multiple threads */ | 333 | |
220 | /* Set the watermark to trigger buffer fill (A/C) */ | 334 | /* Event queues */ |
221 | static void set_filebuf_watermark(void); | 335 | static struct event_queue audio_queue SHAREDBSS_ATTR; |
222 | 336 | ||
223 | /* Audio thread */ | 337 | /* Audio thread */ |
224 | static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR; | 338 | static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR; |
225 | static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; | 339 | static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; |
226 | static const char audio_thread_name[] = "audio"; | 340 | static const char audio_thread_name[] = "audio"; |
341 | static unsigned int audio_thread_id = 0; | ||
342 | |||
343 | /* Forward declarations */ | ||
344 | enum audio_start_playback_flags | ||
345 | { | ||
346 | AUDIO_START_RESTART = 0x1, /* "Restart" playback (flush _all_ tracks) */ | ||
347 | AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */ | ||
348 | }; | ||
227 | 349 | ||
228 | static void audio_thread(void); | 350 | static void audio_start_playback(size_t offset, unsigned int flags); |
229 | static void audio_initiate_track_change(long direction); | ||
230 | static bool audio_have_tracks(void); | ||
231 | static void audio_reset_buffer(void); | ||
232 | static void audio_stop_playback(void); | 351 | static void audio_stop_playback(void); |
352 | static void buffer_event_buffer_low_callback(void *data); | ||
353 | static void buffer_event_rebuffer_callback(void *data); | ||
354 | static void buffer_event_finished_callback(void *data); | ||
355 | |||
233 | 356 | ||
234 | /**************************************/ | 357 | /**************************************/ |
235 | 358 | ||
236 | /** Pcmbuf callbacks */ | 359 | /** --- audio_queue helpers --- **/ |
237 | 360 | ||
238 | /* Between the codec and PCM track change, we need to keep updating the | 361 | /* codec thread needs access */ |
239 | * "elapsed" value of the previous (to the codec, but current to the | 362 | void audio_queue_post(long id, intptr_t data) |
240 | * user/PCM/WPS) track, so that the progressbar reaches the end. | ||
241 | * During that transition, the WPS will display othertrack_id3. */ | ||
242 | void audio_pcmbuf_position_callback(unsigned int time) | ||
243 | { | 363 | { |
244 | time += othertrack_id3->elapsed; | 364 | queue_post(&audio_queue, id, data); |
245 | othertrack_id3->elapsed = (time >= othertrack_id3->length) | ||
246 | ? othertrack_id3->length : time; | ||
247 | } | 365 | } |
248 | 366 | ||
249 | /* Post message from pcmbuf that the end of the previous track | 367 | static intptr_t audio_queue_send(long id, intptr_t data) |
250 | * has just been played. */ | ||
251 | void audio_post_track_change(bool pcmbuf) | ||
252 | { | 368 | { |
253 | if (pcmbuf) | 369 | return queue_send(&audio_queue, id, data); |
254 | { | ||
255 | LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED"); | ||
256 | queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | LOGFQUEUE("pcmbuf > audio Q_AUDIO_TRACK_CHANGED"); | ||
261 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | ||
262 | } | ||
263 | } | 370 | } |
264 | 371 | ||
265 | /* Scan the pcmbuf queue and return true if a message pulled */ | ||
266 | static bool pcmbuf_queue_scan(struct queue_event *ev) | ||
267 | { | ||
268 | if (!queue_empty(&pcmbuf_queue)) | ||
269 | { | ||
270 | /* Transfer message to audio queue */ | ||
271 | pcm_play_lock(); | ||
272 | /* Pull message - never, ever any blocking call! */ | ||
273 | queue_wait_w_tmo(&pcmbuf_queue, ev, 0); | ||
274 | pcm_play_unlock(); | ||
275 | return true; | ||
276 | } | ||
277 | |||
278 | return false; | ||
279 | } | ||
280 | 372 | ||
373 | /** --- MP3Entry --- **/ | ||
281 | 374 | ||
282 | /** Helper functions */ | 375 | /* Does the mp3entry have enough info for us to use it? */ |
376 | static struct mp3entry * valid_mp3entry(const struct mp3entry *id3) | ||
377 | { | ||
378 | return id3 && (id3->length != 0 || id3->filesize != 0) && | ||
379 | id3->codectype != AFMT_UNKNOWN ? (struct mp3entry *)id3 : NULL; | ||
380 | } | ||
283 | 381 | ||
284 | static struct mp3entry *bufgetid3(int handle_id) | 382 | /* Return a pointer to an mp3entry on the buffer, as it is */ |
383 | static struct mp3entry * bufgetid3(int handle_id) | ||
285 | { | 384 | { |
286 | if (handle_id < 0) | 385 | if (handle_id < 0) |
287 | return NULL; | 386 | return NULL; |
@@ -295,6 +394,7 @@ static struct mp3entry *bufgetid3(int handle_id) | |||
295 | return id3; | 394 | return id3; |
296 | } | 395 | } |
297 | 396 | ||
397 | /* Read an mp3entry from the buffer, adjusted */ | ||
298 | static bool bufreadid3(int handle_id, struct mp3entry *id3out) | 398 | static bool bufreadid3(int handle_id, struct mp3entry *id3out) |
299 | { | 399 | { |
300 | struct mp3entry *id3 = bufgetid3(handle_id); | 400 | struct mp3entry *id3 = bufgetid3(handle_id); |
@@ -308,1200 +408,1441 @@ static bool bufreadid3(int handle_id, struct mp3entry *id3out) | |||
308 | return false; | 408 | return false; |
309 | } | 409 | } |
310 | 410 | ||
311 | static bool clear_track_info(struct track_info *track) | 411 | /* Lock the id3 mutex */ |
412 | static void id3_mutex_lock(void) | ||
312 | { | 413 | { |
313 | /* bufclose returns true if the handle is not found, or if it is closed | 414 | mutex_lock(&id3_mutex); |
314 | * successfully, so these checks are safe on non-existant handles */ | ||
315 | if (!track) | ||
316 | return false; | ||
317 | |||
318 | if (track->codec_hid >= 0) { | ||
319 | if (bufclose(track->codec_hid)) | ||
320 | track->codec_hid = -1; | ||
321 | else | ||
322 | return false; | ||
323 | } | ||
324 | |||
325 | if (track->id3_hid >= 0) { | ||
326 | if (bufclose(track->id3_hid)) | ||
327 | track->id3_hid = -1; | ||
328 | else | ||
329 | return false; | ||
330 | } | ||
331 | |||
332 | if (track->audio_hid >= 0) { | ||
333 | if (bufclose(track->audio_hid)) | ||
334 | track->audio_hid = -1; | ||
335 | else | ||
336 | return false; | ||
337 | } | ||
338 | |||
339 | #ifdef HAVE_ALBUMART | ||
340 | { | ||
341 | int i; | ||
342 | FOREACH_ALBUMART(i) | ||
343 | { | ||
344 | if (track->aa_hid[i] >= 0) { | ||
345 | if (bufclose(track->aa_hid[i])) | ||
346 | track->aa_hid[i] = -1; | ||
347 | else | ||
348 | return false; | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | #endif | ||
353 | |||
354 | if (track->cuesheet_hid >= 0) { | ||
355 | if (bufclose(track->cuesheet_hid)) | ||
356 | track->cuesheet_hid = -1; | ||
357 | else | ||
358 | return false; | ||
359 | } | ||
360 | |||
361 | track->filesize = 0; | ||
362 | track->taginfo_ready = false; | ||
363 | |||
364 | return true; | ||
365 | } | 415 | } |
366 | 416 | ||
367 | /* --- External interfaces --- */ | 417 | /* Unlock the id3 mutex */ |
368 | 418 | static void id3_mutex_unlock(void) | |
369 | /* This sends a stop message and the audio thread will dump all it's | ||
370 | subsequenct messages */ | ||
371 | void audio_hard_stop(void) | ||
372 | { | 419 | { |
373 | /* Stop playback */ | 420 | mutex_unlock(&id3_mutex); |
374 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1"); | ||
375 | queue_send(&audio_queue, Q_AUDIO_STOP, 1); | ||
376 | #ifdef PLAYBACK_VOICE | ||
377 | voice_stop(); | ||
378 | #endif | ||
379 | } | 421 | } |
380 | 422 | ||
381 | bool audio_restore_playback(int type) | 423 | /* Return one of the collection of mp3entry pointers - collect them all here */ |
424 | static inline struct mp3entry * id3_get(enum audio_id3_types id3_num) | ||
382 | { | 425 | { |
383 | switch (type) | 426 | switch (id3_num) |
384 | { | 427 | { |
385 | case AUDIO_WANT_PLAYBACK: | 428 | case UNBUFFERED_ID3: |
386 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) | 429 | return &audio_scratch_memory->unbuffered_id3; |
387 | audio_reset_buffer(); | 430 | case CODEC_ID3: |
388 | return true; | 431 | return &audio_scratch_memory->codec_id3; |
389 | case AUDIO_WANT_VOICE: | ||
390 | if (buffer_state == AUDIOBUF_STATE_TRASHED) | ||
391 | audio_reset_buffer(); | ||
392 | return true; | ||
393 | default: | 432 | default: |
394 | return false; | 433 | return &static_id3_entries[id3_num]; |
395 | } | 434 | } |
396 | } | 435 | } |
397 | 436 | ||
398 | unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | 437 | /* Copy an mp3entry into one of the mp3 entries */ |
438 | static void id3_write(enum audio_id3_types id3_num, | ||
439 | const struct mp3entry *id3_src) | ||
399 | { | 440 | { |
400 | unsigned char *buf, *end; | 441 | struct mp3entry *dest_id3 = id3_get(id3_num); |
401 | |||
402 | if (audio_is_initialized) | ||
403 | { | ||
404 | audio_hard_stop(); | ||
405 | } | ||
406 | /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */ | ||
407 | |||
408 | /* Reset the buffering thread so that it doesn't try to use the data */ | ||
409 | buffering_reset(filebuf, filebuflen); | ||
410 | |||
411 | if (buffer_size == NULL) | ||
412 | { | ||
413 | /* Special case for talk_init to use since it already knows it's | ||
414 | trashed */ | ||
415 | buffer_state = AUDIOBUF_STATE_TRASHED; | ||
416 | return NULL; | ||
417 | } | ||
418 | 442 | ||
419 | if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED | 443 | if (id3_src) |
420 | || !talk_voice_required()) | 444 | copy_mp3entry(dest_id3, id3_src); |
421 | { | ||
422 | logf("get buffer: talk, audio"); | ||
423 | /* Ok to use everything from audiobuf to audiobufend - voice is loaded, | ||
424 | the talk buffer is not needed because voice isn't being used, or | ||
425 | could be AUDIOBUF_STATE_TRASHED already. If state is | ||
426 | AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't written | ||
427 | without the caller knowing what's going on. Changing certain settings | ||
428 | may move it to a worse condition but the memory in use by something | ||
429 | else will remain undisturbed. | ||
430 | */ | ||
431 | if (buffer_state != AUDIOBUF_STATE_TRASHED) | ||
432 | { | ||
433 | talk_buffer_steal(); | ||
434 | buffer_state = AUDIOBUF_STATE_TRASHED; | ||
435 | } | ||
436 | |||
437 | buf = audiobuf; | ||
438 | end = audiobufend; | ||
439 | } | ||
440 | else | 445 | else |
441 | { | 446 | wipe_mp3entry(dest_id3); |
442 | /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or | ||
443 | still AUDIOBUF_STATE_INITIALIZED */ | ||
444 | /* Skip talk buffer and move pcm buffer to end to maximize available | ||
445 | contiguous memory - no audio running means voice will not need the | ||
446 | swap space */ | ||
447 | logf("get buffer: audio"); | ||
448 | buf = audiobuf + talk_get_bufsize(); | ||
449 | end = audiobufend - pcmbuf_init(audiobufend); | ||
450 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | ||
451 | } | ||
452 | |||
453 | *buffer_size = end - buf; | ||
454 | |||
455 | return buf; | ||
456 | } | 447 | } |
457 | 448 | ||
458 | bool audio_buffer_state_trashed(void) | 449 | /* Call id3_write "safely" because peek aheads can yield, even if the fast |
450 | preview isn't enabled */ | ||
451 | static void id3_write_locked(enum audio_id3_types id3_num, | ||
452 | const struct mp3entry *id3_src) | ||
459 | { | 453 | { |
460 | return buffer_state == AUDIOBUF_STATE_TRASHED; | 454 | id3_mutex_lock(); |
455 | id3_write(id3_num, id3_src); | ||
456 | id3_mutex_unlock(); | ||
461 | } | 457 | } |
462 | 458 | ||
463 | #ifdef HAVE_RECORDING | 459 | |
464 | unsigned char *audio_get_recording_buffer(size_t *buffer_size) | 460 | /** --- Track info --- **/ |
461 | |||
462 | /* Close a handle and mark it invalid */ | ||
463 | static void track_info_close_handle(int *hid_p) | ||
465 | { | 464 | { |
466 | /* Stop audio, voice and obtain all available buffer space */ | 465 | int hid = *hid_p; |
467 | audio_hard_stop(); | ||
468 | talk_buffer_steal(); | ||
469 | 466 | ||
470 | unsigned char *end = audiobufend; | 467 | /* bufclose returns true if the handle is not found, or if it is closed |
471 | buffer_state = AUDIOBUF_STATE_TRASHED; | 468 | * successfully, so these checks are safe on non-existant handles */ |
472 | *buffer_size = end - audiobuf; | 469 | if (hid >= 0) |
470 | bufclose(hid); | ||
473 | 471 | ||
474 | return (unsigned char *)audiobuf; | 472 | /* Always reset to "no handle" in case it was something else */ |
473 | *hid_p = ERR_HANDLE_NOT_FOUND; | ||
475 | } | 474 | } |
476 | 475 | ||
477 | bool audio_load_encoder(int afmt) | 476 | /* Close all handles in a struct track_info and clear it */ |
477 | static void track_info_close(struct track_info *info) | ||
478 | { | 478 | { |
479 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 479 | /* Close them in the order they are allocated on the buffer to speed up |
480 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: %d", afmt); | 480 | the handle searching */ |
481 | return queue_send(&audio_queue, Q_AUDIO_LOAD_ENCODER, afmt) > 0; | 481 | track_info_close_handle(&info->id3_hid); |
482 | #else | 482 | track_info_close_handle(&info->cuesheet_hid); |
483 | (void)afmt; | 483 | #ifdef HAVE_ALBUMART |
484 | return true; | 484 | int i; |
485 | FOREACH_ALBUMART(i) | ||
486 | track_info_close_handle(&info->aa_hid[i]); | ||
485 | #endif | 487 | #endif |
486 | } /* audio_load_encoder */ | 488 | #ifdef HAVE_CODEC_BUFFERING |
489 | track_info_close_handle(&info->codec_hid); | ||
490 | #endif | ||
491 | track_info_close_handle(&info->audio_hid); | ||
492 | info->filesize = 0; | ||
493 | } | ||
487 | 494 | ||
488 | void audio_remove_encoder(void) | 495 | /* Invalidate all members to initial values - does not close handles */ |
496 | static void track_info_wipe(struct track_info * info) | ||
489 | { | 497 | { |
490 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 498 | info->id3_hid = ERR_HANDLE_NOT_FOUND; |
491 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: NULL"); | 499 | info->cuesheet_hid = ERR_HANDLE_NOT_FOUND; |
492 | queue_send(&audio_queue, Q_AUDIO_LOAD_ENCODER, AFMT_UNKNOWN); | 500 | #ifdef HAVE_ALBUMART |
501 | int i; | ||
502 | FOREACH_ALBUMART(i) | ||
503 | info->aa_hid[i] = ERR_HANDLE_NOT_FOUND; | ||
504 | #endif | ||
505 | #ifdef HAVE_CODEC_BUFFERING | ||
506 | info->codec_hid = ERR_HANDLE_NOT_FOUND; | ||
493 | #endif | 507 | #endif |
494 | } /* audio_remove_encoder */ | 508 | info->audio_hid = ERR_HANDLE_NOT_FOUND; |
509 | info->filesize = 0; | ||
510 | } | ||
495 | 511 | ||
496 | #endif /* HAVE_RECORDING */ | ||
497 | 512 | ||
513 | /** --- Track list --- **/ | ||
498 | 514 | ||
499 | struct mp3entry* audio_current_track(void) | 515 | /* Initialize the track list */ |
516 | static void track_list_init(void) | ||
500 | { | 517 | { |
501 | const char *filename; | 518 | int i; |
502 | struct playlist_track_info trackinfo; | 519 | for (i = 0; i < TRACK_LIST_LEN; i++) |
503 | int cur_idx; | 520 | track_info_wipe(&track_list.tracks[i]); |
504 | int offset = ci.new_track + wps_offset; | ||
505 | struct mp3entry *write_id3; | ||
506 | |||
507 | cur_idx = (track_ridx + offset) & MAX_TRACK_MASK; | ||
508 | 521 | ||
509 | if (cur_idx == track_ridx && *thistrack_id3->path) | 522 | track_list.start = track_list.end = track_list.current; |
510 | { | 523 | } |
511 | /* The usual case */ | ||
512 | if (tracks[cur_idx].cuesheet_hid >= 0 && !thistrack_id3->cuesheet) | ||
513 | { | ||
514 | bufread(tracks[cur_idx].cuesheet_hid, sizeof(struct cuesheet), curr_cue); | ||
515 | thistrack_id3->cuesheet = curr_cue; | ||
516 | } | ||
517 | return thistrack_id3; | ||
518 | } | ||
519 | else if (automatic_skip && offset == -1 && *othertrack_id3->path) | ||
520 | { | ||
521 | /* We're in a track transition. The codec has moved on to the next track, | ||
522 | but the audio being played is still the same (now previous) track. | ||
523 | othertrack_id3.elapsed is being updated in an ISR by | ||
524 | codec_pcmbuf_position_callback */ | ||
525 | if (tracks[cur_idx].cuesheet_hid >= 0 && !thistrack_id3->cuesheet) | ||
526 | { | ||
527 | bufread(tracks[cur_idx].cuesheet_hid, sizeof(struct cuesheet), curr_cue); | ||
528 | othertrack_id3->cuesheet = curr_cue; | ||
529 | } | ||
530 | return othertrack_id3; | ||
531 | } | ||
532 | 524 | ||
533 | if (offset != 0) | 525 | /* Return number of items allocated in the list */ |
534 | { | 526 | static unsigned int track_list_count(void) |
535 | /* Codec may be using thistrack_id3, so it must not be overwritten. | 527 | { |
536 | If this is a manual skip, othertrack_id3 will become | 528 | return track_list.end - track_list.start; |
537 | thistrack_id3 in audio_check_new_track(). | 529 | } |
538 | FIXME: If this is an automatic skip, it probably means multiple | ||
539 | short tracks fit in the PCM buffer. Overwriting othertrack_id3 | ||
540 | can lead to an incorrect value later. | ||
541 | Note that othertrack_id3 may also be used for next track. | ||
542 | */ | ||
543 | write_id3 = othertrack_id3; | ||
544 | } | ||
545 | else | ||
546 | { | ||
547 | write_id3 = thistrack_id3; | ||
548 | } | ||
549 | 530 | ||
550 | if (tracks[cur_idx].id3_hid >= 0) | 531 | /* Return true if the list is empty */ |
551 | { | 532 | static inline bool track_list_empty(void) |
552 | /* The current track's info has been buffered but not read yet, so get it */ | 533 | { |
553 | if (bufreadid3(tracks[cur_idx].id3_hid, write_id3)) | 534 | return track_list.end == track_list.start; |
554 | return write_id3; | 535 | } |
555 | } | ||
556 | 536 | ||
557 | /* We didn't find the ID3 metadata, so we fill temp_id3 with the little info | 537 | /* Returns true if the list is holding the maximum number of items */ |
558 | we have and return that. */ | 538 | static bool track_list_full(void) |
539 | { | ||
540 | return track_list.end - track_list.start >= TRACK_LIST_LEN; | ||
541 | } | ||
559 | 542 | ||
560 | memset(write_id3, 0, sizeof(struct mp3entry)); | 543 | /* Test if the index is within the allocated range */ |
544 | static bool track_list_in_range(int pos) | ||
545 | { | ||
546 | return (int)(pos - track_list.start) >= 0 && | ||
547 | (int)(pos - track_list.end) < 0; | ||
548 | } | ||
561 | 549 | ||
562 | playlist_get_track_info(NULL, playlist_next(0)+wps_offset, &trackinfo); | 550 | static struct track_info * track_list_entry(int pos) |
563 | filename = trackinfo.filename; | 551 | { |
564 | if (!filename) | 552 | return &track_list.tracks[pos & TRACK_LIST_MASK]; |
565 | filename = "No file!"; | 553 | } |
566 | 554 | ||
567 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) | 555 | /* Return the info of the last allocation plus an offset, NULL if result is |
568 | if (tagcache_fill_tags(write_id3, filename)) | 556 | out of bounds */ |
569 | return write_id3; | 557 | static struct track_info * track_list_last(int offset) |
570 | #endif | 558 | { |
559 | /* Last is before the end since the end isn't inclusive */ | ||
560 | unsigned int pos = track_list.end + offset - 1; | ||
571 | 561 | ||
572 | strlcpy(write_id3->path, filename, sizeof(write_id3->path)); | 562 | if (!track_list_in_range(pos)) |
573 | write_id3->title = strrchr(write_id3->path, '/'); | 563 | return NULL; |
574 | if (!write_id3->title) | ||
575 | write_id3->title = &write_id3->path[0]; | ||
576 | else | ||
577 | write_id3->title++; | ||
578 | 564 | ||
579 | return write_id3; | 565 | return track_list_entry(pos); |
580 | } | 566 | } |
581 | 567 | ||
582 | struct mp3entry* audio_next_track(void) | 568 | /* Allocate space at the end for another track if not full */ |
569 | static struct track_info * track_list_alloc_track(void) | ||
583 | { | 570 | { |
584 | int next_idx; | 571 | if (track_list_full()) |
585 | int offset = ci.new_track + wps_offset; | ||
586 | |||
587 | if (!audio_have_tracks()) | ||
588 | return NULL; | 572 | return NULL; |
589 | 573 | ||
590 | if (wps_offset == -1 && *thistrack_id3->path) | 574 | return track_list_entry(track_list.end++); |
591 | { | 575 | } |
592 | /* We're in a track transition. The next track for the WPS is the one | ||
593 | currently being decoded. */ | ||
594 | return thistrack_id3; | ||
595 | } | ||
596 | 576 | ||
597 | next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK; | 577 | /* Remove the last track entry allocated in order to support backing out |
578 | of a track load */ | ||
579 | static void track_list_unalloc_track(void) | ||
580 | { | ||
581 | if (track_list_empty()) | ||
582 | return; | ||
598 | 583 | ||
599 | if (tracks[next_idx].id3_hid >= 0) | 584 | track_list.end--; |
600 | { | ||
601 | if (bufreadid3(tracks[next_idx].id3_hid, othertrack_id3)) | ||
602 | return othertrack_id3; | ||
603 | else | ||
604 | return NULL; | ||
605 | } | ||
606 | 585 | ||
607 | if (next_idx == track_widx) | 586 | if (track_list.current == track_list.end && |
587 | track_list.current != track_list.start) | ||
608 | { | 588 | { |
609 | /* The next track hasn't been buffered yet, so we return the static | 589 | /* Current _must_ remain within bounds */ |
610 | version of its metadata. */ | 590 | track_list.current--; |
611 | return &unbuffered_id3; | ||
612 | } | 591 | } |
613 | |||
614 | return NULL; | ||
615 | } | 592 | } |
616 | 593 | ||
617 | /* gets a copy of the id3 data */ | 594 | /* Return current track plus an offset, NULL if result is out of bounds */ |
618 | bool audio_peek_track(struct mp3entry* id3, int offset) | 595 | static struct track_info * track_list_current(int offset) |
619 | { | 596 | { |
620 | int next_idx; | 597 | unsigned int pos = track_list.current + offset; |
621 | int new_offset = ci.new_track + wps_offset + offset; | ||
622 | |||
623 | if (!audio_have_tracks()) | ||
624 | return false; | ||
625 | next_idx = (track_ridx + new_offset) & MAX_TRACK_MASK; | ||
626 | 598 | ||
627 | if (tracks[next_idx].id3_hid >= 0) | 599 | if (!track_list_in_range(pos)) |
628 | return bufreadid3(tracks[next_idx].id3_hid, id3); | 600 | return NULL; |
629 | |||
630 | return false; | ||
631 | } | ||
632 | |||
633 | #ifdef HAVE_ALBUMART | ||
634 | |||
635 | int playback_current_aa_hid(int slot) | ||
636 | { | ||
637 | if (slot < 0) | ||
638 | return -1; | ||
639 | int cur_idx; | ||
640 | int offset = ci.new_track + wps_offset; | ||
641 | 601 | ||
642 | cur_idx = track_ridx + offset; | 602 | return track_list_entry(pos); |
643 | cur_idx &= MAX_TRACK_MASK; | ||
644 | return tracks[cur_idx].aa_hid[slot]; | ||
645 | } | 603 | } |
646 | 604 | ||
647 | int playback_claim_aa_slot(struct dim *dim) | 605 | /* Return current based upon what's intended that the user sees - not |
606 | necessarily where decoding is taking place */ | ||
607 | static struct track_info * track_list_user_current(int offset) | ||
648 | { | 608 | { |
649 | int i; | 609 | if (skip_pending == TRACK_SKIP_AUTO || |
650 | 610 | skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST) | |
651 | /* first try to find a slot already having the size to reuse it | ||
652 | * since we don't want albumart of the same size buffered multiple times */ | ||
653 | FOREACH_ALBUMART(i) | ||
654 | { | ||
655 | struct albumart_slot *slot = &albumart_slots[i]; | ||
656 | if (slot->dim.width == dim->width | ||
657 | && slot->dim.height == dim->height) | ||
658 | { | ||
659 | slot->used++; | ||
660 | return i; | ||
661 | } | ||
662 | } | ||
663 | /* size is new, find a free slot */ | ||
664 | FOREACH_ALBUMART(i) | ||
665 | { | 611 | { |
666 | if (!albumart_slots[i].used) | 612 | offset--; |
667 | { | ||
668 | albumart_slots[i].used++; | ||
669 | albumart_slots[i].dim = *dim; | ||
670 | return i; | ||
671 | } | ||
672 | } | 613 | } |
673 | /* sorry, no free slot */ | ||
674 | return -1; | ||
675 | } | ||
676 | 614 | ||
677 | void playback_release_aa_slot(int slot) | 615 | return track_list_current(offset); |
678 | { | ||
679 | /* invalidate the albumart_slot */ | ||
680 | struct albumart_slot *aa_slot = &albumart_slots[slot]; | ||
681 | |||
682 | if (aa_slot->used > 0) | ||
683 | aa_slot->used--; | ||
684 | } | 616 | } |
685 | 617 | ||
686 | #endif | 618 | /* Advance current track by an offset, return false if result is out of |
687 | void audio_play(long offset) | 619 | bounds */ |
620 | static struct track_info * track_list_advance_current(int offset) | ||
688 | { | 621 | { |
689 | logf("audio_play"); | 622 | unsigned int pos = track_list.current + offset; |
690 | 623 | ||
691 | #ifdef PLAYBACK_VOICE | 624 | if (!track_list_in_range(pos)) |
692 | /* Truncate any existing voice output so we don't have spelling | 625 | return NULL; |
693 | * etc. over the first part of the played track */ | ||
694 | talk_force_shutup(); | ||
695 | #endif | ||
696 | 626 | ||
697 | /* Start playback */ | 627 | track_list.current = pos; |
698 | LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset); | 628 | return track_list_entry(pos); |
699 | /* Don't return until playback has actually started */ | ||
700 | queue_send(&audio_queue, Q_AUDIO_PLAY, offset); | ||
701 | } | 629 | } |
702 | 630 | ||
703 | void audio_stop(void) | 631 | /* Clear tracks in the list, optionally preserving the current track - |
632 | returns 'false' if the operation was changed */ | ||
633 | enum track_clear_action | ||
704 | { | 634 | { |
705 | /* Stop playback */ | 635 | TRACK_LIST_CLEAR_ALL = 0, /* Clear all tracks */ |
706 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP"); | 636 | TRACK_LIST_KEEP_CURRENT, /* Keep current only; clear before + after */ |
707 | /* Don't return until playback has actually stopped */ | 637 | TRACK_LIST_KEEP_NEW /* Keep current and those that follow */ |
708 | queue_send(&audio_queue, Q_AUDIO_STOP, 0); | 638 | }; |
709 | } | ||
710 | 639 | ||
711 | void audio_pause(void) | 640 | static void track_list_clear(enum track_clear_action action) |
712 | { | 641 | { |
713 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE"); | 642 | logf("%s(%d)", __func__, (int)action); |
714 | /* Don't return until playback has actually paused */ | ||
715 | queue_send(&audio_queue, Q_AUDIO_PAUSE, true); | ||
716 | } | ||
717 | 643 | ||
718 | void audio_resume(void) | 644 | /* Don't care now since rebuffering is imminent */ |
719 | { | 645 | buf_set_watermark(0); |
720 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE resume"); | ||
721 | /* Don't return until playback has actually resumed */ | ||
722 | queue_send(&audio_queue, Q_AUDIO_PAUSE, false); | ||
723 | } | ||
724 | 646 | ||
725 | void audio_skip(int direction) | 647 | if (action != TRACK_LIST_CLEAR_ALL) |
726 | { | ||
727 | if (playlist_check(ci.new_track + wps_offset + direction)) | ||
728 | { | 648 | { |
729 | if (global_settings.beep) | 649 | struct track_info *cur = track_list_current(0); |
730 | pcmbuf_beep(2000, 100, 2500*global_settings.beep); | ||
731 | 650 | ||
732 | LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction); | 651 | if (!cur || cur->id3_hid < 0) |
733 | queue_post(&audio_queue, Q_AUDIO_SKIP, direction); | 652 | action = TRACK_LIST_CLEAR_ALL; /* Nothing worthwhile keeping */ |
734 | /* Update wps while our message travels inside deep playback queues. */ | ||
735 | wps_offset += direction; | ||
736 | } | 653 | } |
737 | else | 654 | |
655 | /* Noone should see this progressing */ | ||
656 | int start = track_list.start; | ||
657 | int current = track_list.current; | ||
658 | int end = track_list.end; | ||
659 | |||
660 | track_list.start = current; | ||
661 | |||
662 | switch (action) | ||
738 | { | 663 | { |
739 | /* No more tracks. */ | 664 | case TRACK_LIST_CLEAR_ALL: |
740 | if (global_settings.beep) | 665 | /* Result: .start = .current, .end = .current */ |
741 | pcmbuf_beep(1000, 100, 1500*global_settings.beep); | 666 | track_list.end = current; |
667 | break; | ||
668 | |||
669 | case TRACK_LIST_KEEP_CURRENT: | ||
670 | /* Result: .start = .current, .end = .current + 1 */ | ||
671 | track_list.end = current + 1; | ||
672 | break; | ||
673 | |||
674 | case TRACK_LIST_KEEP_NEW: | ||
675 | /* Result: .start = .current, .end = .end */ | ||
676 | end = current; | ||
677 | break; | ||
742 | } | 678 | } |
743 | } | ||
744 | 679 | ||
745 | void audio_next(void) | 680 | /* Close all open handles in the range except the for the current track |
746 | { | 681 | if preserving that */ |
747 | audio_skip(1); | 682 | while (start != end) |
748 | } | 683 | { |
684 | if (action != TRACK_LIST_KEEP_CURRENT || start != current) | ||
685 | { | ||
686 | struct track_info *info = | ||
687 | &track_list.tracks[start & TRACK_LIST_MASK]; | ||
749 | 688 | ||
750 | void audio_prev(void) | 689 | /* If this is the in-progress load, abort it */ |
751 | { | 690 | if (in_progress_id3_hid >= 0 && |
752 | audio_skip(-1); | 691 | info->id3_hid == in_progress_id3_hid) |
753 | } | 692 | { |
693 | in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | ||
694 | } | ||
754 | 695 | ||
755 | void audio_next_dir(void) | 696 | track_info_close(info); |
756 | { | 697 | } |
757 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP 1"); | ||
758 | queue_post(&audio_queue, Q_AUDIO_DIR_SKIP, 1); | ||
759 | } | ||
760 | 698 | ||
761 | void audio_prev_dir(void) | 699 | start++; |
762 | { | 700 | } |
763 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP -1"); | ||
764 | queue_post(&audio_queue, Q_AUDIO_DIR_SKIP, -1); | ||
765 | } | 701 | } |
766 | 702 | ||
767 | void audio_pre_ff_rewind(void) | ||
768 | { | ||
769 | LOGFQUEUE("audio > audio Q_AUDIO_PRE_FF_REWIND"); | ||
770 | queue_post(&audio_queue, Q_AUDIO_PRE_FF_REWIND, 0); | ||
771 | } | ||
772 | 703 | ||
773 | void audio_ff_rewind(long newpos) | 704 | /** --- Audio buffer -- **/ |
774 | { | ||
775 | LOGFQUEUE("audio > audio Q_AUDIO_FF_REWIND"); | ||
776 | queue_post(&audio_queue, Q_AUDIO_FF_REWIND, newpos); | ||
777 | } | ||
778 | 705 | ||
779 | void audio_flush_and_reload_tracks(void) | 706 | /* What size is needed for the scratch buffer? */ |
707 | static size_t scratch_mem_size(void) | ||
780 | { | 708 | { |
781 | LOGFQUEUE("audio > audio Q_AUDIO_FLUSH"); | 709 | size_t size = sizeof (struct audio_scratch_memory); |
782 | queue_post(&audio_queue, Q_AUDIO_FLUSH, 0); | 710 | |
711 | if (global_settings.cuesheet) | ||
712 | size += sizeof (struct cuesheet); | ||
713 | |||
714 | return size; | ||
783 | } | 715 | } |
784 | 716 | ||
785 | void audio_error_clear(void) | 717 | /* Initialize the memory area where data is stored that is only used when |
718 | playing audio and anything depending upon it */ | ||
719 | static void scratch_mem_init(void *mem) | ||
786 | { | 720 | { |
787 | #ifdef AUDIO_HAVE_RECORDING | 721 | audio_scratch_memory = (struct audio_scratch_memory *)mem; |
788 | pcm_rec_error_clear(); | 722 | id3_write_locked(UNBUFFERED_ID3, NULL); |
789 | #endif | 723 | id3_write(CODEC_ID3, NULL); |
724 | ci.id3 = id3_get(CODEC_ID3); | ||
725 | audio_scratch_memory->curr_cue = NULL; | ||
726 | |||
727 | if (global_settings.cuesheet) | ||
728 | { | ||
729 | audio_scratch_memory->curr_cue = | ||
730 | SKIPBYTES((struct cuesheet *)audio_scratch_memory, | ||
731 | sizeof (struct audio_scratch_memory)); | ||
732 | } | ||
790 | } | 733 | } |
791 | 734 | ||
792 | int audio_status(void) | 735 | /* Set up the audio buffer for playback */ |
736 | static void audio_reset_buffer(void) | ||
793 | { | 737 | { |
794 | int ret = 0; | 738 | /* |
739 | * Layout audio buffer as follows: | ||
740 | * [[|TALK]|SCRATCH|BUFFERING|PCM|] | ||
741 | */ | ||
795 | 742 | ||
796 | if (playing) | 743 | /* see audio_get_recording_buffer if this is modified */ |
797 | ret |= AUDIO_STATUS_PLAY; | 744 | logf("%s()", __func__); |
798 | 745 | ||
799 | if (paused) | 746 | /* If the setup of anything allocated before the file buffer is |
800 | ret |= AUDIO_STATUS_PAUSE; | 747 | changed, do check the adjustments after the buffer_alloc call |
748 | as it will likely be affected and need sliding over */ | ||
801 | 749 | ||
802 | #ifdef HAVE_RECORDING | 750 | /* Initially set up file buffer as all space available */ |
803 | /* Do this here for constitency with mpeg.c version */ | 751 | unsigned char *filebuf = audiobuf + talk_get_bufsize(); |
804 | /* FIXME: pcm_rec_status() is deprecated */ | 752 | size_t filebuflen = audiobufend - filebuf; |
805 | ret |= pcm_rec_status(); | 753 | size_t allocsize; |
806 | #endif | ||
807 | 754 | ||
808 | return ret; | 755 | ALIGN_BUFFER(filebuf, filebuflen, sizeof (intptr_t)); |
809 | } | ||
810 | 756 | ||
811 | bool audio_automatic_skip(void) | 757 | /* Subtract whatever the pcm buffer says it used plus the guard buffer */ |
812 | { | 758 | allocsize = pcmbuf_init(filebuf + filebuflen); |
813 | return automatic_skip; | ||
814 | } | ||
815 | 759 | ||
816 | int audio_get_file_pos(void) | 760 | /* Make sure filebuflen is a pointer sized multiple after adjustment */ |
817 | { | 761 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); |
818 | return 0; | 762 | if (allocsize > filebuflen) |
819 | } | 763 | goto bufpanic; |
820 | 764 | ||
821 | #ifdef HAVE_DISK_STORAGE | 765 | filebuflen -= allocsize; |
822 | void audio_set_buffer_margin(int setting) | ||
823 | { | ||
824 | static const unsigned short lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; | ||
825 | buffer_margin = lookup[setting]; | ||
826 | logf("buffer margin: %ld", (long)buffer_margin); | ||
827 | set_filebuf_watermark(); | ||
828 | } | ||
829 | #endif | ||
830 | 766 | ||
831 | #ifdef HAVE_CROSSFADE | 767 | /* Scratch memory */ |
832 | /* Take necessary steps to enable or disable the crossfade setting */ | 768 | allocsize = scratch_mem_size(); |
833 | void audio_set_crossfade(int enable) | 769 | if (allocsize > filebuflen) |
834 | { | 770 | goto bufpanic; |
835 | size_t offset; | ||
836 | bool was_playing; | ||
837 | size_t size; | ||
838 | 771 | ||
839 | /* Tell it the next setting to use */ | 772 | scratch_mem_init(filebuf); |
840 | pcmbuf_request_crossfade_enable(enable); | 773 | filebuf += allocsize; |
774 | filebuflen -= allocsize; | ||
841 | 775 | ||
842 | /* Return if size hasn't changed or this is too early to determine | 776 | buffering_reset(filebuf, filebuflen); |
843 | which in the second case there's no way we could be playing | ||
844 | anything at all */ | ||
845 | if (pcmbuf_is_same_size()) return; | ||
846 | 777 | ||
847 | offset = 0; | 778 | /* Clear any references to the file buffer */ |
848 | was_playing = playing; | 779 | buffer_state = AUDIOBUF_STATE_INITIALIZED; |
849 | 780 | ||
850 | /* Playback has to be stopped before changing the buffer size */ | 781 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) |
851 | if (was_playing) | 782 | /* Make sure everything adds up - yes, some info is a bit redundant but |
783 | aids viewing and the sumation of certain variables should add up to | ||
784 | the location of others. */ | ||
852 | { | 785 | { |
853 | /* Store the track resume position */ | 786 | size_t pcmbufsize; |
854 | offset = thistrack_id3->offset; | 787 | const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize); |
788 | logf("fbuf: %08X", (unsigned)filebuf); | ||
789 | logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); | ||
790 | logf("sbuf: %08X", (unsigned)audio_scratch_memory); | ||
791 | logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize)); | ||
792 | logf("pcmb: %08X", (unsigned)pcmbuf); | ||
793 | logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize)); | ||
855 | } | 794 | } |
795 | #endif | ||
856 | 796 | ||
857 | /* Blast it - audio buffer will have to be setup again next time | 797 | return; |
858 | something plays */ | ||
859 | audio_get_buffer(true, &size); | ||
860 | 798 | ||
861 | /* Restart playback if audio was running previously */ | 799 | bufpanic: |
862 | if (was_playing) | 800 | panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); |
863 | audio_play(offset); | ||
864 | } | 801 | } |
865 | #endif | ||
866 | |||
867 | /* --- Routines called from multiple threads --- */ | ||
868 | 802 | ||
869 | static void set_filebuf_watermark(void) | 803 | /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ |
804 | static void audio_update_filebuf_watermark(int seconds) | ||
870 | { | 805 | { |
871 | if (!filebuf) | 806 | size_t bytes = 0; |
872 | return; /* Audio buffers not yet set up */ | ||
873 | 807 | ||
874 | #ifdef HAVE_DISK_STORAGE | 808 | #ifdef HAVE_DISK_STORAGE |
875 | int seconds; | ||
876 | int spinup = ata_spinup_time(); | 809 | int spinup = ata_spinup_time(); |
810 | |||
811 | if (seconds == 0) | ||
812 | { | ||
813 | /* By current setting */ | ||
814 | seconds = buffer_margin; | ||
815 | } | ||
816 | else | ||
817 | { | ||
818 | /* New setting */ | ||
819 | buffer_margin = seconds; | ||
820 | |||
821 | if (buf_get_watermark() == 0) | ||
822 | { | ||
823 | /* Write a watermark only if the audio thread already did so for | ||
824 | itself or it will fail to set the event and the watermark - if | ||
825 | it hasn't yet, it will use the new setting when it does */ | ||
826 | return; | ||
827 | } | ||
828 | } | ||
829 | |||
877 | if (spinup) | 830 | if (spinup) |
878 | seconds = (spinup / HZ) + 1; | 831 | seconds += (spinup / HZ) + 1; |
879 | else | 832 | else |
880 | seconds = 5; | 833 | seconds += 5; |
881 | 834 | ||
882 | seconds += buffer_margin; | 835 | seconds += buffer_margin; |
883 | #else | 836 | #else |
884 | /* flash storage */ | 837 | /* flash storage */ |
885 | int seconds = 1; | 838 | seconds = 1; |
886 | #endif | 839 | #endif |
887 | 840 | ||
888 | /* bitrate of last track in buffer dictates watermark */ | 841 | /* Watermark is a function of the bitrate of the last track in the buffer */ |
889 | struct mp3entry* id3 = NULL; | 842 | struct mp3entry *id3 = NULL; |
890 | if (tracks[track_widx].taginfo_ready) | 843 | struct track_info *info = track_list_last(0); |
891 | id3 = bufgetid3(tracks[track_widx].id3_hid); | 844 | |
845 | if (info) | ||
846 | id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | ||
847 | |||
848 | if (id3) | ||
849 | { | ||
850 | if (get_audio_base_data_type(id3->codectype) == TYPE_PACKET_AUDIO) | ||
851 | { | ||
852 | bytes = id3->bitrate * (1000/8) * seconds; | ||
853 | } | ||
854 | else | ||
855 | { | ||
856 | /* Bitrate has no meaning to buffering margin for atomic audio - | ||
857 | rebuffer when it's the only track left unless it's the only | ||
858 | track that fits, in which case we should avoid constant buffer | ||
859 | low events */ | ||
860 | if (track_list_count() > 1) | ||
861 | bytes = info->filesize + 1; | ||
862 | } | ||
863 | } | ||
892 | else | 864 | else |
893 | id3 = bufgetid3(tracks[track_widx-1].id3_hid); | 865 | { |
894 | if (!id3) { | 866 | /* Then set the minimum - this should not occur anyway */ |
895 | logf("fwmark: No id3 for last track (r%d/w%d), aborting!", track_ridx, track_widx); | 867 | logf("fwmark: No id3 for last track (s%u/c%u/e%u)", |
896 | return; | 868 | track_list.start, track_list.current, track_list.end); |
897 | } | 869 | } |
898 | size_t bytes = id3->bitrate * (1000/8) * seconds; | 870 | |
899 | buf_set_watermark(bytes); | 871 | /* Actually setting zero disables the notification and we use that |
900 | logf("fwmark: %d", bytes); | 872 | to detect that it has been reset */ |
873 | buf_set_watermark(MAX(bytes, 1)); | ||
874 | logf("fwmark: %lu", (unsigned long)bytes); | ||
901 | } | 875 | } |
902 | 876 | ||
903 | /* --- Buffering callbacks --- */ | ||
904 | 877 | ||
905 | static void buffering_low_buffer_callback(void *data) | 878 | /** -- Track change notification -- **/ |
879 | |||
880 | /* Check the pcmbuf track changes and return write the message into the event | ||
881 | if there are any */ | ||
882 | static inline bool audio_pcmbuf_track_change_scan(void) | ||
906 | { | 883 | { |
907 | (void)data; | 884 | if (track_change.out != track_change.in) |
908 | logf("low buffer callback"); | 885 | { |
886 | track_change.out++; | ||
887 | return true; | ||
888 | } | ||
889 | |||
890 | return false; | ||
891 | } | ||
892 | |||
893 | /* Clear outstanding track change posts */ | ||
894 | static inline void audio_pcmbuf_track_change_clear(void) | ||
895 | { | ||
896 | track_change.out = track_change.in; | ||
897 | } | ||
898 | |||
899 | /* Post a track change notification - called by audio ISR */ | ||
900 | static inline void audio_pcmbuf_track_change_post(void) | ||
901 | { | ||
902 | track_change.in++; | ||
903 | } | ||
909 | 904 | ||
910 | if (filling == STATE_FULL || filling == STATE_END_OF_PLAYLIST) { | 905 | |
911 | /* force a refill */ | 906 | /** --- Helper functions --- **/ |
912 | LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER"); | 907 | |
913 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | 908 | /* Removes messages that might end up in the queue before or while processing |
909 | a manual track change. Responding to them would be harmful since they | ||
910 | belong to a previous track's playback period. Anything that would generate | ||
911 | the stale messages must first be put into a state where it will not do so. | ||
912 | */ | ||
913 | static void audio_clear_track_notifications(void) | ||
914 | { | ||
915 | static const long filter_list[][2] = | ||
916 | { | ||
917 | /* codec messages */ | ||
918 | { Q_AUDIO_CODEC_SEEK_COMPLETE, Q_AUDIO_CODEC_COMPLETE }, | ||
919 | /* track change messages */ | ||
920 | { Q_AUDIO_TRACK_CHANGED, Q_AUDIO_TRACK_CHANGED }, | ||
921 | }; | ||
922 | |||
923 | const int filter_count = ARRAYLEN(filter_list) - 1; | ||
924 | |||
925 | /* Remove any pcmbuf notifications */ | ||
926 | pcmbuf_monitor_track_change(false); | ||
927 | audio_pcmbuf_track_change_clear(); | ||
928 | |||
929 | /* Scrub the audio queue of the old mold */ | ||
930 | while (queue_peek_ex(&audio_queue, NULL, | ||
931 | filter_count | QPEEK_REMOVE_EVENTS, | ||
932 | filter_list)) | ||
933 | { | ||
934 | yield(); /* Not strictly needed, per se, ad infinitum, ra, ra */ | ||
914 | } | 935 | } |
915 | } | 936 | } |
916 | 937 | ||
917 | static void buffering_handle_rebuffer_callback(void *data) | 938 | /* Takes actions based upon track load status codes */ |
939 | static void audio_handle_track_load_status(int trackstat) | ||
918 | { | 940 | { |
919 | (void)data; | 941 | switch (trackstat) |
920 | LOGFQUEUE("audio >| audio Q_AUDIO_FLUSH"); | 942 | { |
921 | queue_post(&audio_queue, Q_AUDIO_FLUSH, 0); | 943 | case LOAD_TRACK_ERR_NO_MORE: |
944 | if (track_list_count() > 0) | ||
945 | break; | ||
946 | |||
947 | case LOAD_TRACK_ERR_START_CODEC: | ||
948 | audio_queue_post(Q_AUDIO_CODEC_COMPLETE, CODEC_ERROR); | ||
949 | break; | ||
950 | |||
951 | default: | ||
952 | break; | ||
953 | } | ||
922 | } | 954 | } |
923 | 955 | ||
924 | static void buffering_handle_finished_callback(void *data) | 956 | /* Announce the end of playing the current track */ |
957 | static void audio_playlist_track_finish(void) | ||
925 | { | 958 | { |
926 | logf("handle %d finished buffering", *(int*)data); | 959 | struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); |
927 | int hid = (*(int*)data); | ||
928 | 960 | ||
929 | if (hid == tracks[track_widx].id3_hid) | 961 | playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3); |
962 | |||
963 | if (id3) | ||
930 | { | 964 | { |
931 | int offset = ci.new_track + wps_offset; | 965 | send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); |
932 | int next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK; | 966 | prev_track_elapsed = id3->elapsed; |
933 | /* The metadata handle for the last loaded track has been buffered. | ||
934 | We can ask the audio thread to load the rest of the track's data. */ | ||
935 | LOGFQUEUE("audio > audio Q_AUDIO_FINISH_LOAD"); | ||
936 | queue_post(&audio_queue, Q_AUDIO_FINISH_LOAD, 0); | ||
937 | if (tracks[next_idx].id3_hid == hid) | ||
938 | send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, NULL); | ||
939 | } | 967 | } |
940 | else | 968 | else |
941 | { | 969 | { |
942 | /* This is most likely an audio handle, so we strip the useless | 970 | prev_track_elapsed = 0; |
943 | trailing tags that are left. */ | ||
944 | strip_tags(hid); | ||
945 | |||
946 | if (hid == tracks[track_widx-1].audio_hid | ||
947 | && filling == STATE_END_OF_PLAYLIST) | ||
948 | { | ||
949 | /* This was the last track in the playlist. | ||
950 | We now have all the data we need. */ | ||
951 | logf("last track finished buffering"); | ||
952 | filling = STATE_FINISHED; | ||
953 | } | ||
954 | } | 971 | } |
955 | } | 972 | } |
956 | 973 | ||
974 | /* Announce the beginning of the new track */ | ||
975 | static void audio_playlist_track_change(void) | ||
976 | { | ||
977 | struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); | ||
978 | |||
979 | if (id3) | ||
980 | send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3); | ||
957 | 981 | ||
958 | /* --- Audio thread --- */ | 982 | playlist_update_resume_info(id3); |
983 | } | ||
959 | 984 | ||
960 | static bool audio_have_tracks(void) | 985 | /* Change the data for the next track and send the event */ |
986 | static void audio_update_and_announce_next_track(const struct mp3entry *id3_next) | ||
961 | { | 987 | { |
962 | return (audio_track_count() != 0); | 988 | id3_write_locked(NEXTTRACK_ID3, id3_next); |
989 | send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, | ||
990 | id3_get(NEXTTRACK_ID3)); | ||
963 | } | 991 | } |
964 | 992 | ||
965 | static int audio_free_track_count(void) | 993 | /* Bring the user current mp3entry up to date and set a new offset for the |
994 | buffered metadata */ | ||
995 | static void playing_id3_sync(struct track_info *user_info, size_t offset) | ||
966 | { | 996 | { |
967 | /* Used tracks + free tracks adds up to MAX_TRACK - 1 */ | 997 | id3_mutex_lock(); |
968 | return MAX_TRACK - 1 - audio_track_count(); | 998 | |
999 | struct mp3entry *id3 = bufgetid3(user_info->id3_hid); | ||
1000 | |||
1001 | if (offset == (size_t)-1) | ||
1002 | { | ||
1003 | struct mp3entry *ply_id3 = id3_get(PLAYING_ID3); | ||
1004 | size_t play_offset = ply_id3->offset; | ||
1005 | long play_elapsed = ply_id3->elapsed; | ||
1006 | id3_write(PLAYING_ID3, id3); | ||
1007 | ply_id3->offset = play_offset; | ||
1008 | ply_id3->elapsed = play_elapsed; | ||
1009 | offset = 0; | ||
1010 | } | ||
1011 | else | ||
1012 | { | ||
1013 | id3_write(PLAYING_ID3, id3); | ||
1014 | } | ||
1015 | |||
1016 | if (id3) | ||
1017 | id3->offset = offset; | ||
1018 | |||
1019 | id3_mutex_unlock(); | ||
969 | } | 1020 | } |
970 | 1021 | ||
971 | int audio_track_count(void) | 1022 | /* Wipe-out track metadata - current is optional */ |
1023 | static void wipe_track_metadata(bool current) | ||
972 | { | 1024 | { |
973 | /* Calculate difference from track_ridx to track_widx | 1025 | id3_mutex_lock(); |
974 | * taking into account a possible wrap-around. */ | 1026 | |
975 | return (MAX_TRACK + track_widx - track_ridx) & MAX_TRACK_MASK; | 1027 | if (current) |
1028 | id3_write(PLAYING_ID3, NULL); | ||
1029 | |||
1030 | id3_write(NEXTTRACK_ID3, NULL); | ||
1031 | id3_write(UNBUFFERED_ID3, NULL); | ||
1032 | |||
1033 | id3_mutex_unlock(); | ||
976 | } | 1034 | } |
977 | 1035 | ||
978 | long audio_filebufused(void) | 1036 | /* Called when buffering is completed on the last track handle */ |
1037 | static void filling_is_finished(void) | ||
979 | { | 1038 | { |
980 | return (long) buf_used(); | 1039 | logf("last track finished buffering"); |
1040 | |||
1041 | /* There's no more to load or watch for */ | ||
1042 | buf_set_watermark(0); | ||
1043 | filling = STATE_FINISHED; | ||
981 | } | 1044 | } |
982 | 1045 | ||
983 | /* Update track info after successful a codec track change */ | 1046 | /* Stop the codec decoding or waiting for its data to be ready - returns |
984 | static void audio_update_trackinfo(void) | 1047 | 'false' if the codec ended up stopped */ |
1048 | static bool halt_decoding_track(bool stop) | ||
985 | { | 1049 | { |
986 | bool resume = false; | 1050 | /* If it was waiting for us to clear the buffer to make a rebuffer |
1051 | happen, it should cease otherwise codec_stop could deadlock waiting | ||
1052 | for the codec to go to its main loop - codec's request will now | ||
1053 | force-fail */ | ||
1054 | bool retval = false; | ||
987 | 1055 | ||
988 | /* Load the curent track's metadata into curtrack_id3 */ | 1056 | buf_signal_handle(ci.audio_hid, true); |
989 | if (CUR_TI->id3_hid >= 0) | ||
990 | bufreadid3(CUR_TI->id3_hid, thistrack_id3); | ||
991 | 1057 | ||
992 | /* Reset current position */ | 1058 | if (stop) |
993 | thistrack_id3->elapsed = 0; | 1059 | codec_stop(); |
1060 | else | ||
1061 | retval = codec_pause(); | ||
994 | 1062 | ||
995 | #ifdef HAVE_TAGCACHE | 1063 | audio_clear_track_notifications(); |
996 | /* Ignoring resume position for automatic track change if so configured */ | ||
997 | resume = global_settings.autoresume_enable && | ||
998 | (!automatic_skip /* Resume all manually selected tracks */ | ||
999 | || global_settings.autoresume_automatic == AUTORESUME_NEXTTRACK_ALWAYS | ||
1000 | || (global_settings.autoresume_automatic != AUTORESUME_NEXTTRACK_NEVER | ||
1001 | /* Not never resume? */ | ||
1002 | && autoresumable(thistrack_id3))); /* Pass Resume filter? */ | ||
1003 | #endif | ||
1004 | 1064 | ||
1005 | if (!resume) | 1065 | /* We now know it's idle and not waiting for buffered data */ |
1006 | { | 1066 | buf_signal_handle(ci.audio_hid, false); |
1007 | thistrack_id3->offset = 0; | ||
1008 | } | ||
1009 | 1067 | ||
1010 | logf("audio_update_trackinfo: Set offset for %s to %lX\n", | 1068 | codec_skip_pending = false; |
1011 | thistrack_id3->title, thistrack_id3->offset); | 1069 | codec_seeking = false; |
1012 | 1070 | ||
1013 | /* Update the codec API */ | 1071 | return retval; |
1014 | ci.filesize = CUR_TI->filesize; | ||
1015 | ci.id3 = thistrack_id3; | ||
1016 | ci.curpos = 0; | ||
1017 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | ||
1018 | } | 1072 | } |
1019 | 1073 | ||
1020 | /* Clear tracks between write and read, non inclusive */ | 1074 | /* Clear the PCM on a manual skip */ |
1021 | static void audio_clear_track_entries(void) | 1075 | static void audio_clear_paused_pcm(void) |
1022 | { | 1076 | { |
1023 | int cur_idx = track_widx; | 1077 | if (play_status == PLAY_PAUSED && !pcmbuf_is_crossfade_active()) |
1024 | 1078 | pcmbuf_play_stop(); | |
1025 | logf("Clearing tracks: r%d/w%d", track_ridx, track_widx); | 1079 | } |
1026 | 1080 | ||
1027 | /* Loop over all tracks from write-to-read */ | 1081 | /* End the ff/rw mode */ |
1028 | while (1) | 1082 | static void audio_ff_rewind_end(void) |
1083 | { | ||
1084 | /* A seamless seek (not calling audio_pre_ff_rewind) skips this | ||
1085 | section */ | ||
1086 | if (ff_rw_mode) | ||
1029 | { | 1087 | { |
1030 | cur_idx = (cur_idx + 1) & MAX_TRACK_MASK; | 1088 | ff_rw_mode = false; |
1031 | 1089 | ||
1032 | if (cur_idx == track_ridx) | 1090 | if (codec_seeking) |
1033 | break; | 1091 | { |
1092 | /* Clear the buffer */ | ||
1093 | pcmbuf_play_stop(); | ||
1094 | } | ||
1034 | 1095 | ||
1035 | clear_track_info(&tracks[cur_idx]); | 1096 | if (play_status != PLAY_PAUSED) |
1097 | { | ||
1098 | /* Seeking-while-playing, resume PCM playback */ | ||
1099 | pcmbuf_pause(false); | ||
1100 | } | ||
1036 | } | 1101 | } |
1037 | } | 1102 | } |
1038 | 1103 | ||
1039 | /* Clear all tracks */ | 1104 | /* Complete the codec seek */ |
1040 | static bool audio_release_tracks(void) | 1105 | static void audio_complete_codec_seek(void) |
1041 | { | 1106 | { |
1042 | int i, cur_idx; | 1107 | /* If a seek completed while paused, 'paused' is true. |
1043 | 1108 | * If seeking from seek mode, 'ff_rw_mode' is true. */ | |
1044 | logf("releasing all tracks"); | 1109 | if (codec_seeking) |
1045 | |||
1046 | for(i = 0; i < MAX_TRACK; i++) | ||
1047 | { | 1110 | { |
1048 | cur_idx = (track_ridx + i) & MAX_TRACK_MASK; | 1111 | audio_ff_rewind_end(); |
1049 | if (!clear_track_info(&tracks[cur_idx])) | 1112 | codec_seeking = false; /* set _after_ the call! */ |
1050 | return false; | ||
1051 | } | 1113 | } |
1114 | /* else it's waiting and we must repond */ | ||
1115 | } | ||
1052 | 1116 | ||
1053 | return true; | 1117 | /* Get the current cuesheet pointer */ |
1118 | static inline struct cuesheet * get_current_cuesheet(void) | ||
1119 | { | ||
1120 | return audio_scratch_memory->curr_cue; | ||
1054 | } | 1121 | } |
1055 | 1122 | ||
1056 | static bool audio_loadcodec(bool start_play) | 1123 | /* Read the cuesheet from the buffer */ |
1124 | static void buf_read_cuesheet(int handle_id) | ||
1057 | { | 1125 | { |
1058 | int prev_track, hid; | 1126 | struct cuesheet *cue = get_current_cuesheet(); |
1059 | char codec_path[MAX_PATH]; /* Full path to codec */ | ||
1060 | const struct mp3entry *id3, *prev_id3; | ||
1061 | 1127 | ||
1062 | if (tracks[track_widx].id3_hid < 0) { | 1128 | if (!cue || handle_id < 0) |
1063 | return false; | 1129 | return; |
1064 | } | ||
1065 | 1130 | ||
1066 | id3 = bufgetid3(tracks[track_widx].id3_hid); | 1131 | bufread(handle_id, sizeof (struct cuesheet), cue); |
1067 | if (!id3) | 1132 | } |
1068 | return false; | ||
1069 | 1133 | ||
1070 | const char *codec_fn = get_codec_filename(id3->codectype); | 1134 | /* Backend to peek/current/next track metadata interface functions - |
1071 | if (codec_fn == NULL) | 1135 | fill in the mp3entry with as much information as we may obtain about |
1136 | the track at the specified offset from the user current track - | ||
1137 | returns false if no information exists with us */ | ||
1138 | static bool audio_get_track_metadata(int offset, struct mp3entry *id3) | ||
1139 | { | ||
1140 | if (play_status == PLAY_STOPPED) | ||
1072 | return false; | 1141 | return false; |
1073 | 1142 | ||
1074 | tracks[track_widx].codec_hid = -1; | 1143 | if (id3->path[0] != '\0') |
1144 | return true; /* Already filled */ | ||
1145 | |||
1146 | struct track_info *info = track_list_user_current(offset); | ||
1075 | 1147 | ||
1076 | if (start_play) | 1148 | if (!info) |
1077 | { | 1149 | { |
1078 | /* Load the codec directly from disk and save some memory. */ | 1150 | struct mp3entry *ub_id3 = id3_get(UNBUFFERED_ID3); |
1079 | track_ridx = track_widx; | 1151 | |
1080 | ci.filesize = CUR_TI->filesize; | 1152 | if (offset > 0 && track_list_user_current(offset - 1)) |
1081 | ci.id3 = thistrack_id3; | 1153 | { |
1082 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | 1154 | /* Try the unbuffered id3 since we're moving forward */ |
1083 | ci.curpos = 0; | 1155 | if (ub_id3->path[0] != '\0') |
1084 | return codec_load(-1, id3->codectype); | 1156 | { |
1157 | copy_mp3entry(id3, ub_id3); | ||
1158 | return true; | ||
1159 | } | ||
1160 | } | ||
1085 | } | 1161 | } |
1086 | else | 1162 | else if (bufreadid3(info->id3_hid, id3)) |
1087 | { | 1163 | { |
1088 | /* If we already have another track than this one buffered */ | 1164 | return true; |
1089 | if (track_widx != track_ridx) | 1165 | } |
1090 | { | ||
1091 | prev_track = (track_widx - 1) & MAX_TRACK_MASK; | ||
1092 | |||
1093 | id3 = bufgetid3(tracks[track_widx].id3_hid); | ||
1094 | prev_id3 = bufgetid3(tracks[prev_track].id3_hid); | ||
1095 | 1166 | ||
1096 | /* If the previous codec is the same as this one and the current | 1167 | /* We didn't find the ID3 metadata, so we fill it with the little info we |
1097 | * one is the correct one, there is no need to put another copy of | 1168 | have and return that */ |
1098 | * it on the file buffer */ | ||
1099 | if (id3 && prev_id3) | ||
1100 | { | ||
1101 | int codt = get_codec_base_type(id3->codectype); | ||
1102 | int prev_codt = get_codec_base_type(prev_id3->codectype); | ||
1103 | int cod_loaded = get_codec_base_type(codec_loaded()); | ||
1104 | 1169 | ||
1105 | if (codt == prev_codt && codt == cod_loaded) | 1170 | char path[MAX_PATH+1]; |
1106 | { | 1171 | if (playlist_peek(offset, path, sizeof (path))) |
1107 | logf("Reusing prev. codec"); | 1172 | { |
1108 | return true; | 1173 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) |
1109 | } | 1174 | /* Try to get it from the database */ |
1110 | } | 1175 | if (!tagcache_fill_tags(id3, path)) |
1176 | #endif | ||
1177 | { | ||
1178 | /* By now, filename is the only source of info */ | ||
1179 | fill_metadata_from_path(id3, path); | ||
1111 | } | 1180 | } |
1181 | |||
1182 | return true; | ||
1112 | } | 1183 | } |
1113 | 1184 | ||
1114 | codec_get_full_path(codec_path, codec_fn); | 1185 | wipe_mp3entry(id3); |
1115 | 1186 | ||
1116 | hid = tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL); | 1187 | return false; |
1188 | } | ||
1117 | 1189 | ||
1118 | /* not an error if codec load it supported, will load it from disk | 1190 | /* Get a resume rewind adjusted offset from the ID3 */ |
1119 | * application builds don't support it | 1191 | unsigned long resume_rewind_adjusted_offset(const struct mp3entry *id3) |
1120 | */ | 1192 | { |
1121 | if (hid < 0 && hid != ERR_UNSUPPORTED_TYPE) | 1193 | unsigned long offset = id3->offset; |
1122 | return false; | 1194 | size_t resume_rewind = global_settings.resume_rewind * |
1195 | id3->bitrate * (1000/8); | ||
1123 | 1196 | ||
1124 | if (hid >= 0) | 1197 | if (offset < resume_rewind) |
1125 | logf("Loaded codec"); | 1198 | offset = 0; |
1126 | else | 1199 | else |
1127 | logf("Buffering codec unsupported, load later from disk"); | 1200 | offset -= resume_rewind; |
1128 | 1201 | ||
1129 | return true; | 1202 | return offset; |
1130 | } | 1203 | } |
1131 | 1204 | ||
1132 | /* Load metadata for the next track (with bufopen). The rest of the track | 1205 | /* Get the codec into ram and initialize it - keep it if it's ready */ |
1133 | loading will be handled by audio_finish_load_track once the metadata has been | 1206 | static bool audio_init_codec(struct track_info *track_info, |
1134 | actually loaded by the buffering thread. */ | 1207 | struct mp3entry *track_id3) |
1135 | static bool audio_load_track(size_t offset, bool start_play) | ||
1136 | { | 1208 | { |
1137 | char name_buf[MAX_PATH + 1]; | 1209 | int codt_loaded = get_audio_base_codec_type(codec_loaded()); |
1138 | const char *trackname; | 1210 | int hid = ERR_HANDLE_NOT_FOUND; |
1139 | int fd = -1; | ||
1140 | 1211 | ||
1141 | if (track_load_started) { | 1212 | if (codt_loaded != AFMT_UNKNOWN) |
1142 | /* There is already a track load in progress, so track_widx hasn't been | 1213 | { |
1143 | incremented yet. Loading another track would overwrite the one that | 1214 | int codt = get_audio_base_codec_type(track_id3->codectype); |
1144 | hasn't finished loading. */ | 1215 | |
1145 | logf("audio_load_track(): a track load is already in progress"); | 1216 | if (codt == codt_loaded) |
1146 | return false; | 1217 | { |
1218 | /* Codec is the same base type */ | ||
1219 | logf("Reusing prev. codec: %d", track_id3->codectype); | ||
1220 | #ifdef HAVE_CODEC_BUFFERING | ||
1221 | /* Close any buffered codec (we could have skipped directly to a | ||
1222 | format transistion that is the same format as the current track | ||
1223 | and the buffered one is no longer needed) */ | ||
1224 | track_info_close_handle(&track_info->codec_hid); | ||
1225 | #endif | ||
1226 | return true; | ||
1227 | } | ||
1228 | else | ||
1229 | { | ||
1230 | /* New codec - first make sure the old one's gone */ | ||
1231 | logf("Removing prev. codec: %d", codt_loaded); | ||
1232 | codec_unload(); | ||
1233 | } | ||
1147 | } | 1234 | } |
1148 | 1235 | ||
1149 | start_play_g = start_play; /* will be read by audio_finish_load_track */ | 1236 | logf("New codec: %d/%d", track_id3->codectype, codec_loaded()); |
1150 | 1237 | ||
1151 | /* Stop buffer filling if there is no free track entries. | 1238 | #ifdef HAVE_CODEC_BUFFERING |
1152 | Don't fill up the last track entry (we wan't to store next track | 1239 | /* Codec thread will close the handle even if it fails and will load from |
1153 | metadata there). */ | 1240 | storage if hid is not valid or the buffer load fails */ |
1154 | if (!audio_free_track_count()) | 1241 | hid = track_info->codec_hid; |
1155 | { | 1242 | track_info->codec_hid = ERR_HANDLE_NOT_FOUND; |
1156 | logf("No free tracks"); | 1243 | #endif |
1244 | |||
1245 | return codec_load(hid, track_id3->codectype); | ||
1246 | (void)track_info; /* When codec buffering isn't supported */ | ||
1247 | } | ||
1248 | |||
1249 | /* Start the codec for the current track scheduled to be decoded */ | ||
1250 | static bool audio_start_codec(bool auto_skip) | ||
1251 | { | ||
1252 | struct track_info *info = track_list_current(0); | ||
1253 | struct mp3entry *cur_id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | ||
1254 | |||
1255 | if (!cur_id3) | ||
1157 | return false; | 1256 | return false; |
1158 | } | ||
1159 | 1257 | ||
1160 | last_peek_offset++; | 1258 | buf_pin_handle(info->id3_hid, true); |
1161 | tracks[track_widx].taginfo_ready = false; | ||
1162 | 1259 | ||
1163 | logf("Buffering track: r%d/w%d", track_ridx, track_widx); | 1260 | if (!audio_init_codec(info, cur_id3)) |
1164 | /* Get track name from current playlist read position. */ | ||
1165 | while ((trackname = playlist_peek(last_peek_offset, name_buf, | ||
1166 | sizeof(name_buf))) != NULL) | ||
1167 | { | 1261 | { |
1168 | /* Handle broken playlists. */ | 1262 | buf_pin_handle(info->id3_hid, false); |
1169 | fd = open(trackname, O_RDONLY); | 1263 | return false; |
1170 | if (fd < 0) | ||
1171 | { | ||
1172 | logf("Open failed"); | ||
1173 | /* Skip invalid entry from playlist. */ | ||
1174 | playlist_skip_entry(NULL, last_peek_offset); | ||
1175 | } | ||
1176 | else | ||
1177 | break; | ||
1178 | } | 1264 | } |
1179 | 1265 | ||
1180 | if (!trackname) | 1266 | #ifdef HAVE_TAGCACHE |
1267 | bool autoresume_enable = global_settings.autoresume_enable; | ||
1268 | |||
1269 | if (autoresume_enable && !cur_id3->offset) | ||
1181 | { | 1270 | { |
1182 | logf("End-of-playlist"); | 1271 | /* Resume all manually selected tracks */ |
1183 | memset(&unbuffered_id3, 0, sizeof(struct mp3entry)); | 1272 | bool resume = !auto_skip; |
1184 | filling = STATE_END_OF_PLAYLIST; | 1273 | |
1274 | /* Send the "buffer" event to obtain the resume position for the codec */ | ||
1275 | send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); | ||
1185 | 1276 | ||
1186 | if (thistrack_id3->length == 0 && thistrack_id3->filesize == 0) | 1277 | if (!resume) |
1187 | { | 1278 | { |
1188 | /* Stop playback if no valid track was found. */ | 1279 | /* Automatic skip - do further tests to see if we should just |
1189 | audio_stop_playback(); | 1280 | ignore any autoresume position */ |
1281 | int autoresume_automatic = global_settings.autoresume_automatic; | ||
1282 | |||
1283 | switch (autoresume_automatic) | ||
1284 | { | ||
1285 | case AUTORESUME_NEXTTRACK_ALWAYS: | ||
1286 | /* Just resume unconditionally */ | ||
1287 | resume = true; | ||
1288 | break; | ||
1289 | case AUTORESUME_NEXTTRACK_NEVER: | ||
1290 | /* Force-rewind it */ | ||
1291 | break; | ||
1292 | default: | ||
1293 | /* Not "never resume" - pass resume filter? */ | ||
1294 | resume = autoresumable(cur_id3); | ||
1295 | } | ||
1190 | } | 1296 | } |
1191 | 1297 | ||
1192 | return false; | 1298 | if (!resume) |
1299 | cur_id3->offset = 0; | ||
1300 | |||
1301 | logf("%s: Set offset for %s to %lX\n", __func__, | ||
1302 | cur_id3->title, cur_id3->offset); | ||
1193 | } | 1303 | } |
1304 | #endif /* HAVE_TAGCACHE */ | ||
1194 | 1305 | ||
1195 | tracks[track_widx].filesize = filesize(fd); | 1306 | /* Rewind the required amount - if an autoresume was done, this also rewinds |
1307 | that by the setting's amount | ||
1196 | 1308 | ||
1197 | if (offset > tracks[track_widx].filesize) | 1309 | It would be best to have bookkeeping about whether or not the track |
1198 | offset = 0; | 1310 | sounded or not since skipping to it or else skipping to it while paused |
1311 | and back again will cause accumulation of silent rewinds - that's not | ||
1312 | our job to track directly nor could it be in any reasonable way | ||
1313 | */ | ||
1314 | cur_id3->offset = resume_rewind_adjusted_offset(cur_id3); | ||
1315 | |||
1316 | /* Update the codec API with the metadata and track info */ | ||
1317 | id3_write(CODEC_ID3, cur_id3); | ||
1199 | 1318 | ||
1200 | /* Set default values */ | 1319 | ci.audio_hid = info->audio_hid; |
1201 | if (start_play) | 1320 | ci.filesize = info->filesize; |
1321 | buf_set_base_handle(info->audio_hid); | ||
1322 | |||
1323 | /* All required data is now available for the codec */ | ||
1324 | codec_go(); | ||
1325 | |||
1326 | #ifdef HAVE_TAGCACHE | ||
1327 | if (!autoresume_enable || cur_id3->offset) | ||
1328 | #endif | ||
1202 | { | 1329 | { |
1203 | buf_set_watermark(filebuflen/2); | 1330 | /* Send the "buffer" event now */ |
1204 | dsp_configure(ci.dsp, DSP_RESET, 0); | 1331 | send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); |
1205 | playlist_update_resume_info(audio_current_track()); | ||
1206 | } | 1332 | } |
1207 | 1333 | ||
1208 | /* Get track metadata if we don't already have it. */ | 1334 | buf_pin_handle(info->id3_hid, false); |
1209 | if (tracks[track_widx].id3_hid < 0) | 1335 | return true; |
1336 | |||
1337 | (void)auto_skip; /* ifndef HAVE_TAGCACHE */ | ||
1338 | } | ||
1339 | |||
1340 | |||
1341 | /** --- Audio thread --- **/ | ||
1342 | |||
1343 | /* Load and parse a cuesheet for the file - returns false if the buffer | ||
1344 | is full */ | ||
1345 | static bool audio_load_cuesheet(struct track_info *info, | ||
1346 | struct mp3entry *track_id3) | ||
1347 | { | ||
1348 | struct cuesheet *cue = get_current_cuesheet(); | ||
1349 | track_id3->cuesheet = NULL; | ||
1350 | |||
1351 | if (cue && info->cuesheet_hid == ERR_HANDLE_NOT_FOUND) | ||
1210 | { | 1352 | { |
1211 | tracks[track_widx].id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL); | 1353 | /* If error other than a full buffer, then mark it "unsupported" to |
1354 | avoid reloading attempt */ | ||
1355 | int hid = ERR_UNSUPPORTED_TYPE; | ||
1356 | char cuepath[MAX_PATH]; | ||
1212 | 1357 | ||
1213 | if (tracks[track_widx].id3_hid < 0) | 1358 | #ifdef HAVE_IO_PRIORITY |
1359 | buf_back_off_storage(true); | ||
1360 | #endif | ||
1361 | if (look_for_cuesheet_file(track_id3->path, cuepath)) | ||
1214 | { | 1362 | { |
1215 | /* Buffer is full. */ | 1363 | hid = bufalloc(NULL, sizeof (struct cuesheet), TYPE_CUESHEET); |
1216 | get_metadata(&unbuffered_id3, fd, trackname); | ||
1217 | last_peek_offset--; | ||
1218 | close(fd); | ||
1219 | logf("buffer is full for now (get metadata)"); | ||
1220 | filling = STATE_FULL; | ||
1221 | return false; | ||
1222 | } | ||
1223 | 1364 | ||
1224 | if (track_widx == track_ridx) | 1365 | if (hid >= 0) |
1225 | { | 1366 | { |
1226 | /* TODO: Superfluos buffering call? */ | 1367 | void *cuesheet = NULL; |
1227 | buf_request_buffer_handle(tracks[track_widx].id3_hid); | 1368 | bufgetdata(hid, sizeof (struct cuesheet), &cuesheet); |
1228 | if (bufreadid3(tracks[track_widx].id3_hid, thistrack_id3)) | 1369 | |
1229 | { | 1370 | if (parse_cuesheet(cuepath, (struct cuesheet *)cuesheet)) |
1230 | thistrack_id3->offset = offset; | 1371 | { |
1231 | logf("audio_load_track: set offset for %s to %lX\n", | 1372 | /* Indicate cuesheet is present (while track remains |
1232 | thistrack_id3->title, | 1373 | buffered) */ |
1233 | offset); | 1374 | track_id3->cuesheet = cue; |
1375 | } | ||
1376 | else | ||
1377 | { | ||
1378 | bufclose(hid); | ||
1379 | hid = ERR_UNSUPPORTED_TYPE; | ||
1380 | } | ||
1234 | } | 1381 | } |
1235 | else | ||
1236 | memset(thistrack_id3, 0, sizeof(struct mp3entry)); | ||
1237 | } | 1382 | } |
1238 | 1383 | ||
1239 | if (start_play) | 1384 | #ifdef HAVE_IO_PRIORITY |
1385 | buf_back_off_storage(false); | ||
1386 | #endif | ||
1387 | if (hid == ERR_BUFFER_FULL) | ||
1240 | { | 1388 | { |
1241 | playlist_update_resume_info(audio_current_track()); | 1389 | logf("buffer is full for now (%s)", __func__); |
1390 | return false; | ||
1391 | } | ||
1392 | else | ||
1393 | { | ||
1394 | if (hid < 0) | ||
1395 | logf("Cuesheet loading failed"); | ||
1396 | |||
1397 | info->cuesheet_hid = hid; | ||
1242 | } | 1398 | } |
1243 | } | 1399 | } |
1244 | 1400 | ||
1245 | close(fd); | ||
1246 | track_load_started = true; /* Remember that we've started loading a track */ | ||
1247 | return true; | 1401 | return true; |
1248 | } | 1402 | } |
1249 | 1403 | ||
1250 | #ifdef HAVE_ALBUMART | 1404 | #ifdef HAVE_ALBUMART |
1251 | /* Load any album art for the file */ | 1405 | /* Load any album art for the file - returns false if the buffer is full */ |
1252 | static void audio_load_albumart(struct mp3entry *track_id3) | 1406 | static bool audio_load_albumart(struct track_info *info, |
1407 | struct mp3entry *track_id3) | ||
1253 | { | 1408 | { |
1254 | int i; | 1409 | int i; |
1255 | |||
1256 | FOREACH_ALBUMART(i) | 1410 | FOREACH_ALBUMART(i) |
1257 | { | 1411 | { |
1258 | struct bufopen_bitmap_data user_data; | 1412 | struct bufopen_bitmap_data user_data; |
1259 | int hid = ERR_HANDLE_NOT_FOUND; | 1413 | int *aa_hid = &info->aa_hid[i]; |
1414 | int hid = ERR_UNSUPPORTED_TYPE; | ||
1260 | 1415 | ||
1261 | /* albumart_slots may change during a yield of bufopen, | 1416 | /* albumart_slots may change during a yield of bufopen, |
1262 | * but that's no problem */ | 1417 | * but that's no problem */ |
1263 | if (tracks[track_widx].aa_hid[i] >= 0 || !albumart_slots[i].used) | 1418 | if (*aa_hid >= 0 || *aa_hid == ERR_UNSUPPORTED_TYPE || |
1419 | !albumart_slots[i].used) | ||
1264 | continue; | 1420 | continue; |
1265 | 1421 | ||
1266 | memset(&user_data, 0, sizeof(user_data)); | 1422 | memset(&user_data, 0, sizeof(user_data)); |
1267 | user_data.dim = &(albumart_slots[i].dim); | 1423 | user_data.dim = &albumart_slots[i].dim; |
1268 | 1424 | ||
1269 | /* we can only decode jpeg for embedded AA */ | 1425 | #ifdef HAVE_IO_PRIORITY |
1426 | buf_back_off_storage(true); | ||
1427 | #endif | ||
1428 | |||
1429 | /* We can only decode jpeg for embedded AA */ | ||
1270 | if (track_id3->embed_albumart && track_id3->albumart.type == AA_TYPE_JPG) | 1430 | if (track_id3->embed_albumart && track_id3->albumart.type == AA_TYPE_JPG) |
1271 | { | 1431 | { |
1272 | user_data.embedded_albumart = &(track_id3->albumart); | 1432 | user_data.embedded_albumart = &track_id3->albumart; |
1273 | hid = bufopen(track_id3->path, 0, TYPE_BITMAP, &user_data); | 1433 | hid = bufopen(track_id3->path, 0, TYPE_BITMAP, &user_data); |
1274 | } | 1434 | } |
1275 | 1435 | ||
1276 | if (hid < 0 && hid != ERR_BUFFER_FULL) | 1436 | if (hid < 0 && hid != ERR_BUFFER_FULL) |
1277 | { | 1437 | { |
1278 | /* no embedded AA or it couldn't be loaded, try other sources */ | 1438 | /* No embedded AA or it couldn't be loaded - try other sources */ |
1279 | char path[MAX_PATH]; | 1439 | char path[MAX_PATH]; |
1280 | 1440 | ||
1281 | if (find_albumart(track_id3, path, sizeof(path), | 1441 | if (find_albumart(track_id3, path, sizeof(path), |
1282 | &(albumart_slots[i].dim))) | 1442 | &albumart_slots[i].dim)) |
1283 | { | 1443 | { |
1284 | user_data.embedded_albumart = NULL; | 1444 | user_data.embedded_albumart = NULL; |
1285 | hid = bufopen(path, 0, TYPE_BITMAP, &user_data); | 1445 | hid = bufopen(path, 0, TYPE_BITMAP, &user_data); |
1286 | } | 1446 | } |
1287 | } | 1447 | } |
1288 | 1448 | ||
1449 | #ifdef HAVE_IO_PRIORITY | ||
1450 | buf_back_off_storage(false); | ||
1451 | #endif | ||
1289 | if (hid == ERR_BUFFER_FULL) | 1452 | if (hid == ERR_BUFFER_FULL) |
1290 | { | 1453 | { |
1291 | filling = STATE_FULL; | 1454 | logf("buffer is full for now (%s)", __func__); |
1292 | logf("buffer is full for now (get album art)"); | 1455 | return false; |
1293 | } | 1456 | } |
1294 | else if (hid < 0) | 1457 | else |
1295 | { | 1458 | { |
1296 | logf("Album art loading failed"); | 1459 | /* If error other than a full buffer, then mark it "unsupported" |
1297 | } | 1460 | to avoid reloading attempt */ |
1461 | if (hid < 0) | ||
1462 | { | ||
1463 | logf("Album art loading failed"); | ||
1464 | hid = ERR_UNSUPPORTED_TYPE; | ||
1465 | } | ||
1298 | 1466 | ||
1299 | tracks[track_widx].aa_hid[i] = hid; | 1467 | *aa_hid = hid; |
1468 | } | ||
1300 | } | 1469 | } |
1470 | |||
1471 | return true; | ||
1301 | } | 1472 | } |
1302 | #endif | 1473 | #endif /* HAVE_ALBUMART */ |
1303 | 1474 | ||
1304 | /* Second part of the track loading: We now have the metadata available, so we | 1475 | #ifdef HAVE_CODEC_BUFFERING |
1305 | can load the codec, the album art and finally the audio data. | 1476 | /* Load a codec for the file onto the buffer - assumes we're working from the |
1306 | This is called on the audio thread after the buffering thread calls the | 1477 | currently loading track - not called for the current track */ |
1307 | buffering_handle_finished_callback callback. */ | 1478 | static bool audio_buffer_codec(struct track_info *track_info, |
1308 | static void audio_finish_load_track(void) | 1479 | struct mp3entry *track_id3) |
1309 | { | 1480 | { |
1310 | size_t file_offset = 0; | 1481 | /* This will not be the current track -> it cannot be the first and the |
1311 | size_t offset = 0; | 1482 | current track cannot be ahead of buffering -> there is a previous |
1312 | bool start_play = start_play_g; | 1483 | track entry which is either current or ahead of the current */ |
1484 | struct track_info *prev_info = track_list_last(-1); | ||
1485 | struct mp3entry *prev_id3 = bufgetid3(prev_info->id3_hid); | ||
1313 | 1486 | ||
1314 | track_load_started = false; | 1487 | /* If the previous codec is the same as this one, there is no need to |
1488 | put another copy of it on the file buffer (in other words, only | ||
1489 | buffer codecs at format transitions) */ | ||
1490 | if (prev_id3) | ||
1491 | { | ||
1492 | int codt = get_audio_base_codec_type(track_id3->codectype); | ||
1493 | int prev_codt = get_audio_base_codec_type(prev_id3->codectype); | ||
1315 | 1494 | ||
1316 | if (tracks[track_widx].id3_hid < 0) { | 1495 | if (codt == prev_codt) |
1317 | logf("No metadata"); | 1496 | { |
1318 | return; | 1497 | logf("Reusing prev. codec: %d", prev_id3->codectype); |
1498 | return true; | ||
1499 | } | ||
1319 | } | 1500 | } |
1501 | /* else just load it (harmless) */ | ||
1320 | 1502 | ||
1321 | struct mp3entry *track_id3; | 1503 | /* Load the codec onto the buffer if possible */ |
1504 | const char *codec_fn = get_codec_filename(track_id3->codectype); | ||
1505 | if (!codec_fn) | ||
1506 | return false; | ||
1322 | 1507 | ||
1323 | if (track_widx == track_ridx) | 1508 | char codec_path[MAX_PATH+1]; /* Full path to codec */ |
1324 | track_id3 = thistrack_id3; | 1509 | codec_get_full_path(codec_path, codec_fn); |
1325 | else | 1510 | |
1326 | track_id3 = bufgetid3(tracks[track_widx].id3_hid); | 1511 | track_info->codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL); |
1327 | 1512 | ||
1328 | if (track_id3->length == 0 && track_id3->filesize == 0) | 1513 | if (track_info->codec_hid >= 0) |
1329 | { | 1514 | { |
1330 | logf("audio_finish_load_track: invalid metadata"); | 1515 | logf("Buffered codec: %d", afmt); |
1516 | return true; | ||
1517 | } | ||
1331 | 1518 | ||
1332 | /* Invalid metadata */ | 1519 | return false; |
1333 | bufclose(tracks[track_widx].id3_hid); | 1520 | } |
1334 | tracks[track_widx].id3_hid = -1; | 1521 | #endif /* HAVE_CODEC_BUFFERING */ |
1335 | 1522 | ||
1336 | /* Skip invalid entry from playlist. */ | 1523 | /* Load metadata for the next track (with bufopen). The rest of the track |
1337 | playlist_skip_entry(NULL, last_peek_offset--); | 1524 | loading will be handled by audio_finish_load_track once the metadata has |
1525 | been actually loaded by the buffering thread. | ||
1338 | 1526 | ||
1339 | /* load next track */ | 1527 | Each track is arranged in the buffer as follows: |
1340 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER %d", (int)start_play); | 1528 | <id3|[cuesheet|][album art|][codec|]audio> |
1341 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, start_play); | ||
1342 | 1529 | ||
1343 | return; | 1530 | The next will not be loaded until the previous succeeds if the buffer was |
1344 | } | 1531 | full at the time. To put any metadata after audio would make those handles |
1345 | /* Try to load a cuesheet for the track */ | 1532 | unmovable. |
1346 | if (curr_cue) | 1533 | */ |
1534 | static int audio_load_track(void) | ||
1535 | { | ||
1536 | if (in_progress_id3_hid >= 0) | ||
1347 | { | 1537 | { |
1348 | char cuepath[MAX_PATH]; | 1538 | /* There must be an info pointer if the in-progress id3 is even there */ |
1349 | if (look_for_cuesheet_file(track_id3->path, cuepath)) | 1539 | struct track_info *info = track_list_last(0); |
1540 | |||
1541 | if (info->id3_hid == in_progress_id3_hid) | ||
1350 | { | 1542 | { |
1351 | void *temp; | 1543 | if (filling == STATE_FILLING) |
1352 | tracks[track_widx].cuesheet_hid = | ||
1353 | bufalloc(NULL, sizeof(struct cuesheet), TYPE_CUESHEET); | ||
1354 | if (tracks[track_widx].cuesheet_hid >= 0) | ||
1355 | { | 1544 | { |
1356 | bufgetdata(tracks[track_widx].cuesheet_hid, | 1545 | /* Haven't finished the metadata but the notification is |
1357 | sizeof(struct cuesheet), &temp); | 1546 | anticipated to come soon */ |
1358 | struct cuesheet *cuesheet = (struct cuesheet*)temp; | 1547 | logf("%s(): in progress ok: %d". __func__, info->id3_hid); |
1359 | if (!parse_cuesheet(cuepath, cuesheet)) | 1548 | return LOAD_TRACK_OK; |
1360 | { | 1549 | } |
1361 | bufclose(tracks[track_widx].cuesheet_hid); | 1550 | else if (filling == STATE_FULL) |
1362 | track_id3->cuesheet = NULL; | 1551 | { |
1363 | } | 1552 | /* Buffer was full trying to complete the load after the |
1553 | metadata finished, so attempt to continue - older handles | ||
1554 | should have been cleared already */ | ||
1555 | logf("%s(): finishing load: %d". __func__, info->id3_hid); | ||
1556 | filling = STATE_FILLING; | ||
1557 | buffer_event_finished_callback(&info->id3_hid); | ||
1558 | return LOAD_TRACK_OK; | ||
1364 | } | 1559 | } |
1365 | } | 1560 | } |
1561 | |||
1562 | /* Some old, stray buffering message */ | ||
1563 | logf("%s(): already in progress: %d". __func__, info->id3_hid); | ||
1564 | return LOAD_TRACK_ERR_BUSY; | ||
1366 | } | 1565 | } |
1367 | 1566 | ||
1368 | #ifdef HAVE_ALBUMART | 1567 | filling = STATE_FILLING; |
1369 | audio_load_albumart(track_id3); | 1568 | |
1370 | #endif | 1569 | struct track_info *info = track_list_alloc_track(); |
1570 | if (info == NULL) | ||
1571 | { | ||
1572 | /* List is full so stop buffering tracks - however, attempt to obtain | ||
1573 | metadata as the unbuffered id3 */ | ||
1574 | logf("No free tracks"); | ||
1575 | filling = STATE_FULL; | ||
1576 | } | ||
1577 | |||
1578 | playlist_peek_offset++; | ||
1579 | |||
1580 | logf("Buffering track: s%u/c%u/e%u/p%d", | ||
1581 | track_list.start, track_list.current, track_list.end, | ||
1582 | playlist_peek_offset); | ||
1583 | |||
1584 | /* Get track name from current playlist read position */ | ||
1585 | int fd = -1; | ||
1586 | char name_buf[MAX_PATH + 1]; | ||
1587 | const char *trackname; | ||
1371 | 1588 | ||
1372 | /* Load the codec. */ | 1589 | while (1) |
1373 | if (!audio_loadcodec(start_play)) | ||
1374 | { | 1590 | { |
1375 | if (tracks[track_widx].codec_hid == ERR_BUFFER_FULL) | 1591 | |
1592 | trackname = playlist_peek(playlist_peek_offset, name_buf, | ||
1593 | sizeof (name_buf)); | ||
1594 | |||
1595 | if (!trackname) | ||
1596 | break; | ||
1597 | |||
1598 | /* Test for broken playlists by probing for the files */ | ||
1599 | fd = open(trackname, O_RDONLY); | ||
1600 | if (fd >= 0) | ||
1601 | break; | ||
1602 | |||
1603 | logf("Open failed"); | ||
1604 | /* Skip invalid entry from playlist */ | ||
1605 | playlist_skip_entry(NULL, playlist_peek_offset); | ||
1606 | |||
1607 | /* Sync the playlist if it isn't finished */ | ||
1608 | if (playlist_peek(playlist_peek_offset, NULL, 0)) | ||
1609 | playlist_next(0); | ||
1610 | } | ||
1611 | |||
1612 | if (!trackname) | ||
1613 | { | ||
1614 | /* No track - exhausted the playlist entries */ | ||
1615 | logf("End-of-playlist"); | ||
1616 | id3_write_locked(UNBUFFERED_ID3, NULL); | ||
1617 | |||
1618 | if (filling != STATE_FULL) | ||
1619 | track_list_unalloc_track(); /* Free this entry */ | ||
1620 | |||
1621 | playlist_peek_offset--; /* Maintain at last index */ | ||
1622 | |||
1623 | /* We can end up here after the real last track signals its completion | ||
1624 | and miss the transition to STATE_FINISHED esp. if dropping the last | ||
1625 | songs of a playlist late in their load (2nd stage) */ | ||
1626 | info = track_list_last(0); | ||
1627 | |||
1628 | if (info && buf_handle_remaining(info->audio_hid) == 0) | ||
1629 | filling_is_finished(); | ||
1630 | else | ||
1631 | filling = STATE_END_OF_PLAYLIST; | ||
1632 | |||
1633 | return LOAD_TRACK_ERR_NO_MORE; | ||
1634 | } | ||
1635 | |||
1636 | /* Successfully opened the file - get track metadata */ | ||
1637 | if (filling == STATE_FULL || | ||
1638 | (info->id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL)) < 0) | ||
1639 | { | ||
1640 | /* Buffer or track list is full */ | ||
1641 | struct mp3entry *ub_id3; | ||
1642 | |||
1643 | playlist_peek_offset--; | ||
1644 | |||
1645 | /* Load the metadata for the first unbuffered track */ | ||
1646 | ub_id3 = id3_get(UNBUFFERED_ID3); | ||
1647 | id3_mutex_lock(); | ||
1648 | get_metadata(ub_id3, fd, trackname); | ||
1649 | id3_mutex_unlock(); | ||
1650 | |||
1651 | if (filling != STATE_FULL) | ||
1376 | { | 1652 | { |
1377 | /* No space for codec on buffer, not an error */ | 1653 | track_list_unalloc_track(); |
1378 | filling = STATE_FULL; | 1654 | filling = STATE_FULL; |
1379 | return; | ||
1380 | } | 1655 | } |
1381 | 1656 | ||
1382 | /* This is an error condition, either no codec was found, or reading | 1657 | logf("%s: buffer is full for now (%u tracks)", __func__, |
1383 | * the codec file failed part way through, either way, skip the track */ | 1658 | track_list_count()); |
1384 | /* FIXME: We should not use splashf from audio thread! */ | 1659 | } |
1385 | splashf(HZ*2, "No codec for: %s", track_id3->path); | 1660 | else |
1386 | /* Skip invalid entry from playlist. */ | 1661 | { |
1387 | playlist_skip_entry(NULL, last_peek_offset); | 1662 | /* Successful load initiation */ |
1388 | return; | 1663 | info->filesize = filesize(fd); |
1664 | in_progress_id3_hid = info->id3_hid; /* Remember what's in-progress */ | ||
1389 | } | 1665 | } |
1390 | 1666 | ||
1391 | track_id3->elapsed = 0; | 1667 | close(fd); |
1392 | offset = track_id3->offset; | 1668 | return LOAD_TRACK_OK; |
1393 | size_t resume_rewind = (global_settings.resume_rewind * | 1669 | } |
1394 | track_id3->bitrate * 1000) / 8; | ||
1395 | 1670 | ||
1396 | if (offset < resume_rewind) | 1671 | /* Second part of the track loading: We now have the metadata available, so we |
1672 | can load the codec, the album art and finally the audio data. | ||
1673 | This is called on the audio thread after the buffering thread calls the | ||
1674 | buffering_handle_finished_callback callback. */ | ||
1675 | static int audio_finish_load_track(struct track_info *info) | ||
1676 | { | ||
1677 | int trackstat = LOAD_TRACK_OK; | ||
1678 | |||
1679 | if (info->id3_hid != in_progress_id3_hid) | ||
1397 | { | 1680 | { |
1398 | offset = 0; | 1681 | /* We must not be here if not! */ |
1682 | logf("%s: wrong track %d/%d", __func__, info->id3_hid, | ||
1683 | in_progress_id3_hid); | ||
1684 | return LOAD_TRACK_ERR_BUSY; | ||
1399 | } | 1685 | } |
1400 | else | 1686 | |
1687 | /* The current track for decoding (there is always one if the list is | ||
1688 | populated) */ | ||
1689 | struct track_info *cur_info = track_list_current(0); | ||
1690 | struct mp3entry *track_id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | ||
1691 | |||
1692 | if (!track_id3) | ||
1401 | { | 1693 | { |
1402 | offset -= resume_rewind; | 1694 | /* This is an error condition. Track cannot be played without valid |
1695 | metadata; skip the track. */ | ||
1696 | logf("No metadata for: %s", track_id3->path); | ||
1697 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
1698 | goto audio_finish_load_track_exit; | ||
1699 | } | ||
1700 | |||
1701 | /* Try to load a cuesheet for the track */ | ||
1702 | if (!audio_load_cuesheet(info, track_id3)) | ||
1703 | { | ||
1704 | /* No space for cuesheet on buffer, not an error */ | ||
1705 | filling = STATE_FULL; | ||
1706 | goto audio_finish_load_track_exit; | ||
1403 | } | 1707 | } |
1404 | 1708 | ||
1405 | enum data_type type = TYPE_PACKET_AUDIO; | 1709 | #ifdef HAVE_ALBUMART |
1710 | /* Try to load album art for the track */ | ||
1711 | if (!audio_load_albumart(info, track_id3)) | ||
1712 | { | ||
1713 | /* No space for album art on buffer, not an error */ | ||
1714 | filling = STATE_FULL; | ||
1715 | goto audio_finish_load_track_exit; | ||
1716 | } | ||
1717 | #endif | ||
1406 | 1718 | ||
1407 | switch (track_id3->codectype) { | 1719 | #ifdef HAVE_CODEC_BUFFERING |
1408 | case AFMT_MPA_L1: | 1720 | /* Try to buffer a codec for the track */ |
1409 | case AFMT_MPA_L2: | 1721 | if (info != cur_info && !audio_buffer_codec(info, track_id3)) |
1410 | case AFMT_MPA_L3: | 1722 | { |
1411 | if (offset > 0) { | 1723 | if (info->codec_hid == ERR_BUFFER_FULL) |
1412 | file_offset = offset; | 1724 | { |
1725 | /* No space for codec on buffer, not an error */ | ||
1726 | filling = STATE_FULL; | ||
1727 | logf("buffer is full for now (%s)", __func__); | ||
1413 | } | 1728 | } |
1414 | break; | 1729 | else |
1415 | 1730 | { | |
1416 | case AFMT_WAVPACK: | 1731 | /* This is an error condition, either no codec was found, or |
1417 | if (offset > 0) { | 1732 | reading the codec file failed part way through, either way, |
1418 | file_offset = offset; | 1733 | skip the track */ |
1419 | track_id3->elapsed = track_id3->length / 2; | 1734 | logf("No codec for: %s", track_id3->path); |
1735 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
1420 | } | 1736 | } |
1421 | break; | ||
1422 | 1737 | ||
1423 | case AFMT_NSF: | 1738 | goto audio_finish_load_track_exit; |
1424 | case AFMT_SPC: | ||
1425 | case AFMT_SID: | ||
1426 | logf("Loading atomic %d",track_id3->codectype); | ||
1427 | type = TYPE_ATOMIC_AUDIO; | ||
1428 | break; | ||
1429 | |||
1430 | default: | ||
1431 | /* no special treatment needed */ | ||
1432 | break; | ||
1433 | } | 1739 | } |
1740 | #endif /* HAVE_CODEC_BUFFERING */ | ||
1434 | 1741 | ||
1435 | track_id3->offset = offset; | 1742 | /** Finally, load the audio **/ |
1743 | size_t file_offset = 0; | ||
1744 | track_id3->elapsed = 0; | ||
1745 | |||
1746 | if (track_id3->offset >= info->filesize) | ||
1747 | track_id3->offset = 0; | ||
1748 | |||
1749 | logf("%s: set offset for %s to %lu\n", __func__, | ||
1750 | id3->title, (unsigned long)offset); | ||
1751 | |||
1752 | /* Adjust for resume rewind so we know what to buffer - starting the codec | ||
1753 | calls it again, so we don't save it (and they shouldn't accumulate) */ | ||
1754 | size_t offset = resume_rewind_adjusted_offset(track_id3); | ||
1755 | |||
1756 | enum data_type audiotype = get_audio_base_data_type(track_id3->codectype); | ||
1757 | |||
1758 | if (audiotype == TYPE_ATOMIC_AUDIO) | ||
1759 | logf("Loading atomic %d", track_id3->codectype); | ||
1760 | |||
1761 | if (format_buffers_with_offset(track_id3->codectype)) | ||
1762 | { | ||
1763 | /* This format can begin buffering from any point */ | ||
1764 | file_offset = offset; | ||
1765 | } | ||
1436 | 1766 | ||
1437 | logf("load track: %s", track_id3->path); | 1767 | logf("load track: %s", track_id3->path); |
1438 | 1768 | ||
1439 | if (file_offset > AUDIO_REBUFFER_GUESS_SIZE) | 1769 | if (file_offset > AUDIO_REBUFFER_GUESS_SIZE) |
1770 | { | ||
1771 | /* We can buffer later in the file, adjust the hunt-and-peck margin */ | ||
1440 | file_offset -= AUDIO_REBUFFER_GUESS_SIZE; | 1772 | file_offset -= AUDIO_REBUFFER_GUESS_SIZE; |
1441 | else if (track_id3->first_frame_offset) | 1773 | } |
1442 | file_offset = track_id3->first_frame_offset; | ||
1443 | else | 1774 | else |
1444 | file_offset = 0; | 1775 | { |
1776 | /* No offset given or it is very minimal - begin at the first frame | ||
1777 | according to the metadata */ | ||
1778 | file_offset = track_id3->first_frame_offset; | ||
1779 | } | ||
1445 | 1780 | ||
1446 | tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type, | 1781 | int hid = bufopen(track_id3->path, file_offset, audiotype, NULL); |
1447 | NULL); | ||
1448 | 1782 | ||
1449 | /* No space left, not an error */ | 1783 | if (hid >= 0) |
1450 | if (tracks[track_widx].audio_hid == ERR_BUFFER_FULL) | ||
1451 | { | 1784 | { |
1452 | filling = STATE_FULL; | 1785 | info->audio_hid = hid; |
1453 | logf("buffer is full for now (load audio)"); | 1786 | |
1454 | return; | 1787 | if (info == cur_info) |
1788 | { | ||
1789 | /* This is the current track to decode - should be started now */ | ||
1790 | trackstat = LOAD_TRACK_READY; | ||
1791 | } | ||
1455 | } | 1792 | } |
1456 | else if (tracks[track_widx].audio_hid < 0) | 1793 | else |
1457 | { | 1794 | { |
1458 | /* another error, do not continue either */ | 1795 | /* Buffer could be full but not properly so if this is the only |
1459 | logf("Could not add audio data handle"); | 1796 | track! */ |
1460 | return; | 1797 | if (hid == ERR_BUFFER_FULL && audio_track_count() > 1) |
1798 | { | ||
1799 | filling = STATE_FULL; | ||
1800 | logf("Buffer is full for now (%s)", __func__); | ||
1801 | } | ||
1802 | else | ||
1803 | { | ||
1804 | /* Nothing to play if no audio handle - skip this */ | ||
1805 | logf("Could not add audio data handle"); | ||
1806 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
1807 | } | ||
1461 | } | 1808 | } |
1462 | 1809 | ||
1463 | /* All required data is now available for the codec -- unless the | 1810 | audio_finish_load_track_exit: |
1464 | autoresume feature is in effect. In the latter case, the codec | 1811 | if (trackstat < LOAD_TRACK_OK) |
1465 | must wait until after PLAYBACK_EVENT_TRACK_BUFFER, which may | ||
1466 | generate a resume position. */ | ||
1467 | #ifdef HAVE_TAGCACHE | ||
1468 | if (!global_settings.autoresume_enable || offset) | ||
1469 | #endif | ||
1470 | tracks[track_widx].taginfo_ready = true; | ||
1471 | |||
1472 | if (start_play) | ||
1473 | { | 1812 | { |
1474 | ci.curpos=file_offset; | 1813 | playlist_skip_entry(NULL, playlist_peek_offset); |
1475 | buf_request_buffer_handle(tracks[track_widx].audio_hid); | 1814 | track_info_close(info); |
1476 | } | 1815 | track_list_unalloc_track(); |
1477 | 1816 | ||
1478 | send_event(PLAYBACK_EVENT_TRACK_BUFFER, track_id3); | 1817 | if (playlist_peek(playlist_peek_offset, NULL, 0)) |
1818 | playlist_next(0); | ||
1479 | 1819 | ||
1480 | #ifdef HAVE_TAGCACHE | 1820 | playlist_peek_offset--; |
1481 | /* In case the autoresume feature has been enabled, finally all | 1821 | } |
1482 | required data is available for the codec. */ | ||
1483 | if (global_settings.autoresume_enable && !offset) | ||
1484 | tracks[track_widx].taginfo_ready = true; | ||
1485 | #endif | ||
1486 | |||
1487 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | ||
1488 | 1822 | ||
1489 | /* load next track */ | 1823 | if (filling != STATE_FULL) |
1490 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); | 1824 | { |
1491 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | 1825 | /* Load next track - error or not */ |
1826 | in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | ||
1827 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); | ||
1828 | audio_queue_post(Q_AUDIO_FILL_BUFFER, 0); | ||
1829 | } | ||
1830 | else | ||
1831 | { | ||
1832 | /* Full */ | ||
1833 | trackstat = LOAD_TRACK_ERR_FINISH_FULL; | ||
1834 | } | ||
1492 | 1835 | ||
1493 | return; | 1836 | return trackstat; |
1494 | } | 1837 | } |
1495 | 1838 | ||
1496 | static void audio_fill_file_buffer(bool start_play, size_t offset) | 1839 | /* Start a new track load */ |
1840 | static int audio_fill_file_buffer(void) | ||
1497 | { | 1841 | { |
1498 | trigger_cpu_boost(); | 1842 | if (play_status == PLAY_STOPPED) |
1843 | return LOAD_TRACK_ERR_FAILED; | ||
1499 | 1844 | ||
1500 | /* No need to rebuffer if there are track skips pending, | 1845 | trigger_cpu_boost(); |
1501 | * however don't cancel buffering on skipping while filling. */ | ||
1502 | if (ci.new_track != 0 && filling != STATE_FILLING) | ||
1503 | return; | ||
1504 | filling = STATE_FILLING; | ||
1505 | 1846 | ||
1506 | /* Must reset the buffer before use if trashed or voice only - voice | 1847 | /* Must reset the buffer before use if trashed or voice only - voice |
1507 | file size shouldn't have changed so we can go straight from | 1848 | file size shouldn't have changed so we can go straight from |
@@ -1511,772 +1852,1848 @@ static void audio_fill_file_buffer(bool start_play, size_t offset) | |||
1511 | 1852 | ||
1512 | logf("Starting buffer fill"); | 1853 | logf("Starting buffer fill"); |
1513 | 1854 | ||
1514 | if (!start_play) | 1855 | int trackstat = audio_load_track(); |
1515 | audio_clear_track_entries(); | ||
1516 | 1856 | ||
1517 | /* Save the current resume position once. */ | 1857 | if (trackstat >= LOAD_TRACK_OK) |
1518 | playlist_update_resume_info(audio_current_track()); | 1858 | { |
1859 | if (track_list_current(0) == track_list_user_current(0)) | ||
1860 | playlist_next(0); | ||
1519 | 1861 | ||
1520 | audio_load_track(offset, start_play); | 1862 | if (filling == STATE_FULL && !track_list_user_current(1)) |
1863 | { | ||
1864 | /* There are no user tracks on the buffer after this therefore | ||
1865 | this is the next track */ | ||
1866 | audio_update_and_announce_next_track(id3_get(UNBUFFERED_ID3)); | ||
1867 | } | ||
1868 | } | ||
1869 | |||
1870 | return trackstat; | ||
1521 | } | 1871 | } |
1522 | 1872 | ||
1523 | static void audio_rebuffer(void) | 1873 | /* Discard unwanted tracks and start refill from after the specified playlist |
1874 | offset */ | ||
1875 | static int audio_reset_and_rebuffer( | ||
1876 | enum track_clear_action action, int peek_offset) | ||
1524 | { | 1877 | { |
1525 | logf("Forcing rebuffer"); | 1878 | logf("Forcing rebuffer: 0x%X, %d", flags, peek_offset); |
1526 | 1879 | ||
1527 | clear_track_info(CUR_TI); | 1880 | id3_write_locked(UNBUFFERED_ID3, NULL); |
1528 | 1881 | ||
1529 | /* Reset track pointers */ | 1882 | /* Remove unwanted tracks - caller must have ensured codec isn't using |
1530 | track_widx = track_ridx; | 1883 | any */ |
1531 | audio_clear_track_entries(); | 1884 | track_list_clear(action); |
1532 | 1885 | ||
1533 | /* Reset a possibly interrupted track load */ | 1886 | /* Refill at specified position (-1 starts at index offset 0) */ |
1534 | track_load_started = false; | 1887 | playlist_peek_offset = peek_offset; |
1535 | 1888 | ||
1536 | /* Fill the buffer */ | 1889 | /* Fill the buffer */ |
1537 | last_peek_offset = -1; | 1890 | return audio_fill_file_buffer(); |
1538 | ci.curpos = 0; | 1891 | } |
1539 | 1892 | ||
1540 | if (!CUR_TI->taginfo_ready) | 1893 | /* Handle buffering events |
1541 | memset(thistrack_id3, 0, sizeof(struct mp3entry)); | 1894 | (Q_AUDIO_BUFFERING) */ |
1895 | static void audio_on_buffering(int event) | ||
1896 | { | ||
1897 | enum track_clear_action action; | ||
1898 | int peek_offset; | ||
1899 | |||
1900 | if (track_list_empty()) | ||
1901 | return; | ||
1902 | |||
1903 | switch (event) | ||
1904 | { | ||
1905 | case BUFFER_EVENT_BUFFER_LOW: | ||
1906 | if (filling != STATE_FULL && filling != STATE_END_OF_PLAYLIST) | ||
1907 | return; /* Should be nothing left to fill */ | ||
1908 | |||
1909 | /* Clear old tracks and continue buffering where it left off */ | ||
1910 | action = TRACK_LIST_KEEP_NEW; | ||
1911 | peek_offset = playlist_peek_offset; | ||
1912 | break; | ||
1542 | 1913 | ||
1543 | audio_fill_file_buffer(false, 0); | 1914 | case BUFFER_EVENT_REBUFFER: |
1915 | /* Remove all but the currently decoding track and redo buffering | ||
1916 | after that */ | ||
1917 | action = TRACK_LIST_KEEP_CURRENT; | ||
1918 | peek_offset = (skip_pending == TRACK_SKIP_AUTO) ? 1 : 0; | ||
1919 | break; | ||
1920 | |||
1921 | default: | ||
1922 | return; | ||
1923 | } | ||
1924 | |||
1925 | switch (skip_pending) | ||
1926 | { | ||
1927 | case TRACK_SKIP_NONE: | ||
1928 | case TRACK_SKIP_AUTO: | ||
1929 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
1930 | audio_reset_and_rebuffer(action, peek_offset); | ||
1931 | break; | ||
1932 | |||
1933 | case TRACK_SKIP_AUTO_END_PLAYLIST: | ||
1934 | /* Already finished */ | ||
1935 | break; | ||
1936 | |||
1937 | default: | ||
1938 | /* Invalid */ | ||
1939 | logf("Buffering call, inv. state: %d", (int)skip_pending); | ||
1940 | } | ||
1544 | } | 1941 | } |
1545 | 1942 | ||
1546 | /* Called on request from the codec to get a new track. This is the codec part | 1943 | /* Handle starting the next track load |
1547 | of the track transition. */ | 1944 | (Q_AUDIO_FILL_BUFFER) */ |
1548 | static void audio_last_track(bool automatic) | 1945 | static void audio_on_fill_buffer(void) |
1549 | { | 1946 | { |
1550 | if (automatic) | 1947 | audio_handle_track_load_status(audio_fill_file_buffer()); |
1948 | } | ||
1949 | |||
1950 | /* Handle posted load track finish event | ||
1951 | (Q_AUDIO_FINISH_LOAD_TRACK) */ | ||
1952 | static void audio_on_finish_load_track(int id3_hid) | ||
1953 | { | ||
1954 | struct track_info *info = track_list_last(0); | ||
1955 | |||
1956 | if (!info || !buf_is_handle(id3_hid)) | ||
1957 | return; | ||
1958 | |||
1959 | if (info == track_list_user_current(1)) | ||
1551 | { | 1960 | { |
1552 | ci.new_track = 0; | 1961 | /* Just loaded the metadata right after the current position */ |
1553 | automatic_skip = false; | 1962 | audio_update_and_announce_next_track(bufgetid3(info->id3_hid)); |
1963 | } | ||
1554 | 1964 | ||
1555 | if (filling != STATE_ENDING) | 1965 | if (audio_finish_load_track(info) != LOAD_TRACK_READY) |
1966 | return; /* Not current track */ | ||
1967 | |||
1968 | bool is_user_current = info == track_list_user_current(0); | ||
1969 | |||
1970 | if (is_user_current) | ||
1971 | { | ||
1972 | /* Copy cuesheet */ | ||
1973 | buf_read_cuesheet(info->cuesheet_hid); | ||
1974 | } | ||
1975 | |||
1976 | if (audio_start_codec(automatic_skip)) | ||
1977 | { | ||
1978 | if (is_user_current) | ||
1556 | { | 1979 | { |
1557 | /* Monitor remaining PCM before stopping */ | 1980 | /* Be sure all tagtree info is synchronized; it will be needed for the |
1558 | filling = STATE_ENDING; | 1981 | track finish event - the sync will happen when finalizing a track |
1559 | pcmbuf_monitor_track_change(true); | 1982 | change otherwise */ |
1560 | } | 1983 | bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3)); |
1561 | 1984 | ||
1562 | codec_stop(); | 1985 | playing_id3_sync(info, -1); |
1986 | |||
1987 | if (!was_valid) | ||
1988 | { | ||
1989 | /* Playing id3 hadn't been updated yet because no valid track | ||
1990 | was yet available - treat like the first track */ | ||
1991 | audio_playlist_track_change(); | ||
1992 | } | ||
1993 | } | ||
1563 | } | 1994 | } |
1564 | else | 1995 | else |
1565 | { | 1996 | { |
1997 | audio_handle_track_load_status(LOAD_TRACK_ERR_START_CODEC); | ||
1998 | } | ||
1999 | } | ||
2000 | |||
2001 | /* Called when handles other than metadata handles have finished buffering | ||
2002 | (Q_AUDIO_HANDLE_FINISHED) */ | ||
2003 | static void audio_on_handle_finished(int hid) | ||
2004 | { | ||
2005 | /* Right now, only audio handles should end up calling this */ | ||
2006 | if (filling == STATE_END_OF_PLAYLIST) | ||
2007 | { | ||
2008 | struct track_info *info = track_list_last(0); | ||
2009 | |||
2010 | /* Really we don't know which order the handles will actually complete | ||
2011 | to zero bytes remaining since another thread is doing it - be sure | ||
2012 | it's the right one */ | ||
2013 | if (info && info->audio_hid == hid) | ||
2014 | { | ||
2015 | /* This was the last track in the playlist and we now have all the | ||
2016 | data we need */ | ||
2017 | filling_is_finished(); | ||
2018 | } | ||
2019 | } | ||
2020 | } | ||
2021 | |||
2022 | /* Called to make an outstanding track skip the current track and to send the | ||
2023 | transition events */ | ||
2024 | static void audio_finalise_track_change(bool delayed) | ||
2025 | { | ||
2026 | switch (skip_pending) | ||
2027 | { | ||
2028 | case TRACK_SKIP_NONE: /* Manual skip */ | ||
2029 | break; | ||
2030 | |||
2031 | case TRACK_SKIP_AUTO: | ||
2032 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
2033 | { | ||
2034 | int playlist_delta = skip_pending == TRACK_SKIP_AUTO ? 1 : 0; | ||
2035 | audio_playlist_track_finish(); | ||
2036 | |||
2037 | if (!playlist_peek(playlist_delta, NULL, 0)) | ||
2038 | { | ||
2039 | /* Track ended up rejected - push things ahead like the codec blew | ||
2040 | it (because it was never started and now we're here where it | ||
2041 | should have been decoding the next track by now) - next, a | ||
2042 | directory change or end of playback will most likely happen */ | ||
2043 | skip_pending = TRACK_SKIP_NONE; | ||
2044 | audio_handle_track_load_status(LOAD_TRACK_ERR_START_CODEC); | ||
2045 | return; | ||
2046 | } | ||
2047 | |||
2048 | if (!playlist_delta) | ||
2049 | break; | ||
2050 | |||
2051 | playlist_peek_offset -= playlist_delta; | ||
2052 | if (playlist_next(playlist_delta) >= 0) | ||
2053 | break; | ||
2054 | /* What!? Disappear? Hopeless bleak despair */ | ||
2055 | } | ||
2056 | /* Fallthrough */ | ||
2057 | case TRACK_SKIP_AUTO_END_PLAYLIST: | ||
2058 | default: /* Invalid */ | ||
2059 | filling = STATE_ENDED; | ||
1566 | audio_stop_playback(); | 2060 | audio_stop_playback(); |
2061 | return; | ||
2062 | } | ||
2063 | |||
2064 | struct track_info *info = track_list_current(0); | ||
2065 | struct mp3entry *track_id3 = NULL; | ||
2066 | |||
2067 | id3_mutex_lock(); | ||
2068 | |||
2069 | /* Update the current cuesheet if any and enabled */ | ||
2070 | if (info) | ||
2071 | { | ||
2072 | buf_read_cuesheet(info->cuesheet_hid); | ||
2073 | track_id3 = bufgetid3(info->id3_hid); | ||
2074 | } | ||
2075 | |||
2076 | id3_write(PLAYING_ID3, track_id3); | ||
2077 | |||
2078 | if (delayed) | ||
2079 | { | ||
2080 | /* Delayed skip where codec is ahead of user's current track */ | ||
2081 | struct mp3entry *ci_id3 = id3_get(CODEC_ID3); | ||
2082 | struct mp3entry *ply_id3 = id3_get(PLAYING_ID3); | ||
2083 | ply_id3->elapsed = ci_id3->elapsed; | ||
2084 | ply_id3->offset = ci_id3->offset; | ||
1567 | } | 2085 | } |
2086 | |||
2087 | /* The skip is technically over */ | ||
2088 | skip_pending = TRACK_SKIP_NONE; | ||
2089 | |||
2090 | /* Sync the next track information */ | ||
2091 | info = track_list_current(1); | ||
2092 | |||
2093 | id3_write(NEXTTRACK_ID3, info ? bufgetid3(info->id3_hid) : | ||
2094 | id3_get(UNBUFFERED_ID3)); | ||
2095 | |||
2096 | id3_mutex_unlock(); | ||
2097 | |||
2098 | audio_playlist_track_change(); | ||
1568 | } | 2099 | } |
1569 | 2100 | ||
1570 | static void audio_check_new_track(void) | 2101 | /* Actually begin a transition and take care of the codec change - may complete |
2102 | it now or ask pcmbuf for notification depending on the type and what pcmbuf | ||
2103 | has to say */ | ||
2104 | static void audio_begin_track_change(bool auto_skip, int trackstat) | ||
1571 | { | 2105 | { |
1572 | int track_count; | 2106 | /* Even if the new track is bad, the old track must be finished off */ |
1573 | int old_track_ridx; | 2107 | bool finalised = pcmbuf_start_track_change(auto_skip); |
1574 | int i, idx; | ||
1575 | bool forward; | ||
1576 | struct mp3entry *temp; | ||
1577 | 2108 | ||
1578 | if (ci.new_track == 0) | 2109 | if (finalised) |
1579 | { | 2110 | { |
1580 | ci.new_track++; | 2111 | /* pcmbuf says that the transition happens now - complete it */ |
1581 | automatic_skip = true; | 2112 | audio_finalise_track_change(false); |
2113 | |||
2114 | if (play_status == PLAY_STOPPED) | ||
2115 | return; /* Stopped us */ | ||
2116 | } | ||
2117 | |||
2118 | if (!auto_skip) | ||
2119 | audio_clear_paused_pcm(); | ||
2120 | |||
2121 | if (trackstat >= LOAD_TRACK_OK) | ||
2122 | { | ||
2123 | struct track_info *info = track_list_current(0); | ||
2124 | |||
2125 | if (info->audio_hid < 0) | ||
2126 | return; | ||
2127 | |||
2128 | /* Everything needed for the codec is ready - start it */ | ||
2129 | if (audio_start_codec(auto_skip)) | ||
2130 | { | ||
2131 | if (finalised) | ||
2132 | playing_id3_sync(info, -1); | ||
2133 | return; | ||
2134 | } | ||
2135 | |||
2136 | trackstat = LOAD_TRACK_ERR_START_CODEC; | ||
1582 | } | 2137 | } |
1583 | 2138 | ||
1584 | track_count = audio_track_count(); | 2139 | audio_handle_track_load_status(trackstat); |
1585 | old_track_ridx = track_ridx; | 2140 | } |
2141 | |||
2142 | /* Transition to end-of-playlist state and begin wait for PCM to finish */ | ||
2143 | static void audio_monitor_end_of_playlist(void) | ||
2144 | { | ||
2145 | skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST; | ||
2146 | filling = STATE_ENDING; | ||
2147 | pcmbuf_monitor_track_change(true); | ||
2148 | } | ||
2149 | |||
2150 | /* Codec has completed decoding the track | ||
2151 | (usually Q_AUDIO_CODEC_COMPLETE) */ | ||
2152 | static void audio_on_codec_complete(int status) | ||
2153 | { | ||
2154 | logf("%s(%d)", __func__, status); | ||
1586 | 2155 | ||
1587 | /* Now it's good time to send track finish events. */ | 2156 | if (play_status == PLAY_STOPPED) |
1588 | send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3); | 2157 | return; |
1589 | /* swap the mp3entry pointers */ | ||
1590 | temp = thistrack_id3; | ||
1591 | thistrack_id3 = othertrack_id3; | ||
1592 | othertrack_id3 = temp; | ||
1593 | ci.id3 = thistrack_id3; | ||
1594 | memset(thistrack_id3, 0, sizeof(struct mp3entry)); | ||
1595 | 2158 | ||
1596 | if (dir_skip) | 2159 | /* If it didn't notify us first, don't expect "seek complete" message |
2160 | since the codec can't post it now - do things like it would have | ||
2161 | done */ | ||
2162 | audio_complete_codec_seek(); | ||
2163 | |||
2164 | if (play_status == PLAY_PAUSED || skip_pending != TRACK_SKIP_NONE) | ||
1597 | { | 2165 | { |
1598 | dir_skip = false; | 2166 | /* Old-hay on the ip-skay - codec has completed decoding |
1599 | /* regardless of the return value we need to rebuffer. | 2167 | |
1600 | if it fails the old playlist will resume, else the | 2168 | Paused: We're not sounding it, so just remember that it happened |
1601 | next dir will start playing */ | 2169 | and the resume will begin the transition |
1602 | playlist_next_dir(ci.new_track); | 2170 | |
1603 | ci.new_track = 0; | 2171 | Skipping: There was already a skip in progress, remember it and |
1604 | audio_rebuffer(); | 2172 | allow no further progress until the PCM from the previous |
1605 | goto skip_done; | 2173 | song has finished |
2174 | */ | ||
2175 | codec_skip_pending = true; | ||
2176 | codec_skip_status = status; | ||
2177 | return; | ||
1606 | } | 2178 | } |
1607 | 2179 | ||
1608 | if (new_playlist) | 2180 | codec_skip_pending = false; |
1609 | ci.new_track = 0; | ||
1610 | 2181 | ||
1611 | /* If the playlist isn't that big */ | 2182 | if (status >= 0) |
1612 | if (automatic_skip) | ||
1613 | { | 2183 | { |
1614 | while (!playlist_check(ci.new_track)) | 2184 | /* Normal automatic skip */ |
2185 | ab_end_of_track_report(); | ||
2186 | } | ||
2187 | |||
2188 | int trackstat = LOAD_TRACK_OK; | ||
2189 | |||
2190 | automatic_skip = true; | ||
2191 | skip_pending = TRACK_SKIP_AUTO; | ||
2192 | |||
2193 | /* Does this track have an entry allocated? */ | ||
2194 | struct track_info *info = track_list_advance_current(1); | ||
2195 | |||
2196 | if (!info || info->audio_hid < 0) | ||
2197 | { | ||
2198 | bool end_of_playlist = false; | ||
2199 | |||
2200 | if (info) | ||
1615 | { | 2201 | { |
1616 | if (ci.new_track >= 0) | 2202 | /* Track load is not complete - it might have stopped on a |
2203 | full buffer without reaching the audio handle or we just | ||
2204 | arrived at it early | ||
2205 | |||
2206 | If this type is atomic and we couldn't get the audio, | ||
2207 | perhaps it would need to wrap to make the allocation and | ||
2208 | handles are in the way - to maximize the liklihood it can | ||
2209 | be allocated, clear all handles to reset the buffer and | ||
2210 | its indexes to 0 - for packet audio, this should not be an | ||
2211 | issue and a pointless full reload of all the track's | ||
2212 | metadata may be avoided */ | ||
2213 | |||
2214 | struct mp3entry *track_id3 = bufgetid3(info->id3_hid); | ||
2215 | |||
2216 | if (track_id3 && | ||
2217 | get_audio_base_data_type(track_id3->codectype) | ||
2218 | == TYPE_PACKET_AUDIO) | ||
1617 | { | 2219 | { |
1618 | audio_last_track(true); | 2220 | /* Continue filling after this track */ |
2221 | audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1); | ||
2222 | audio_begin_track_change(true, trackstat); | ||
1619 | return; | 2223 | return; |
1620 | } | 2224 | } |
1621 | ci.new_track++; | 2225 | /* else rebuffer at this track; status applies to the track we |
2226 | want */ | ||
2227 | } | ||
2228 | else if (!playlist_peek(1, NULL, 0)) | ||
2229 | { | ||
2230 | /* Play sequence is complete - directory change or other playlist | ||
2231 | resequencing - the playlist must now be advanced in order to | ||
2232 | continue since a peek ahead to the next track is not possible */ | ||
2233 | skip_pending = TRACK_SKIP_AUTO_NEW_PLAYLIST; | ||
2234 | end_of_playlist = playlist_next(1) < 0; | ||
2235 | } | ||
2236 | |||
2237 | if (!end_of_playlist) | ||
2238 | { | ||
2239 | trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, | ||
2240 | skip_pending == TRACK_SKIP_AUTO ? 0 : -1); | ||
2241 | |||
2242 | if (trackstat == LOAD_TRACK_ERR_NO_MORE) | ||
2243 | { | ||
2244 | /* Failed to find anything afterall - do playlist switchover | ||
2245 | instead */ | ||
2246 | skip_pending = TRACK_SKIP_AUTO_NEW_PLAYLIST; | ||
2247 | end_of_playlist = playlist_next(1) < 0; | ||
2248 | } | ||
2249 | } | ||
2250 | |||
2251 | if (end_of_playlist) | ||
2252 | { | ||
2253 | audio_monitor_end_of_playlist(); | ||
2254 | return; | ||
2255 | } | ||
2256 | } | ||
2257 | |||
2258 | audio_begin_track_change(true, trackstat); | ||
2259 | } | ||
2260 | |||
2261 | /* Called when codec completes seek operation | ||
2262 | (usually Q_AUDIO_CODEC_SEEK_COMPLETE) */ | ||
2263 | static void audio_on_codec_seek_complete(void) | ||
2264 | { | ||
2265 | logf("%s()", __func__); | ||
2266 | audio_complete_codec_seek(); | ||
2267 | codec_go(); | ||
2268 | } | ||
2269 | |||
2270 | /* Called when PCM track change has completed | ||
2271 | (Q_AUDIO_TRACK_CHANGED) */ | ||
2272 | static void audio_on_track_changed(void) | ||
2273 | { | ||
2274 | /* Finish whatever is pending so that the WPS is in sync */ | ||
2275 | audio_finalise_track_change(true); | ||
2276 | |||
2277 | if (codec_skip_pending) | ||
2278 | { | ||
2279 | /* Codec got ahead completing a short track - complete the | ||
2280 | codec's skip and begin the next */ | ||
2281 | codec_skip_pending = false; | ||
2282 | audio_on_codec_complete(codec_skip_status); | ||
2283 | } | ||
2284 | } | ||
2285 | |||
2286 | /* Begin playback from an idle state, transition to a new playlist or | ||
2287 | invalidate the buffer and resume (if playing). | ||
2288 | (usually Q_AUDIO_PLAY, Q_AUDIO_REMAKE_AUDIO_BUFFER) */ | ||
2289 | static void audio_start_playback(size_t offset, unsigned int flags) | ||
2290 | { | ||
2291 | enum play_status old_status = play_status; | ||
2292 | |||
2293 | if (flags & AUDIO_START_NEWBUF) | ||
2294 | { | ||
2295 | /* Mark the buffer dirty - if not playing, it will be reset next | ||
2296 | time */ | ||
2297 | if (buffer_state == AUDIOBUF_STATE_INITIALIZED) | ||
2298 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | ||
2299 | } | ||
2300 | |||
2301 | if (old_status != PLAY_STOPPED) | ||
2302 | { | ||
2303 | logf("%s(%lu): skipping", __func__, (unsigned long)offset); | ||
2304 | |||
2305 | halt_decoding_track(true); | ||
2306 | |||
2307 | automatic_skip = false; | ||
2308 | ff_rw_mode = false; | ||
2309 | |||
2310 | if (flags & AUDIO_START_RESTART) | ||
2311 | { | ||
2312 | /* Clear out some stuff to resume the current track where it | ||
2313 | left off */ | ||
2314 | pcmbuf_play_stop(); | ||
2315 | offset = id3_get(PLAYING_ID3)->offset; | ||
2316 | track_list_clear(TRACK_LIST_CLEAR_ALL); | ||
2317 | } | ||
2318 | else | ||
2319 | { | ||
2320 | /* This is more-or-less treated as manual track transition */ | ||
2321 | /* Save resume information for current track */ | ||
2322 | audio_playlist_track_finish(); | ||
2323 | track_list_clear(TRACK_LIST_CLEAR_ALL); | ||
2324 | |||
2325 | /* Indicate manual track change */ | ||
2326 | pcmbuf_start_track_change(false); | ||
2327 | audio_clear_paused_pcm(); | ||
2328 | wipe_track_metadata(true); | ||
1622 | } | 2329 | } |
2330 | |||
2331 | /* Set after track finish event in case skip was in progress */ | ||
2332 | skip_pending = TRACK_SKIP_NONE; | ||
2333 | } | ||
2334 | else | ||
2335 | { | ||
2336 | if (flags & AUDIO_START_RESTART) | ||
2337 | return; /* Must already be playing */ | ||
2338 | |||
2339 | /* Cold playback start from a stopped state */ | ||
2340 | logf("%s(%lu): starting", __func__, offset); | ||
2341 | |||
2342 | /* Set audio parameters */ | ||
2343 | #if INPUT_SRC_CAPS != 0 | ||
2344 | audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); | ||
2345 | audio_set_output_source(AUDIO_SRC_PLAYBACK); | ||
2346 | #endif | ||
2347 | #ifndef PLATFORM_HAS_VOLUME_CHANGE | ||
2348 | sound_set_volume(global_settings.volume); | ||
2349 | #endif | ||
2350 | /* Update our state */ | ||
2351 | play_status = PLAY_PLAYING; | ||
2352 | } | ||
2353 | |||
2354 | /* Start fill from beginning of playlist */ | ||
2355 | playlist_peek_offset = -1; | ||
2356 | buf_set_base_handle(-1); | ||
2357 | |||
2358 | /* Officially playing */ | ||
2359 | queue_reply(&audio_queue, 1); | ||
2360 | |||
2361 | /* Add these now - finish event for the first id3 will most likely be sent | ||
2362 | immediately */ | ||
2363 | add_event(BUFFER_EVENT_REBUFFER, false, buffer_event_rebuffer_callback); | ||
2364 | add_event(BUFFER_EVENT_FINISHED, false, buffer_event_finished_callback); | ||
2365 | |||
2366 | if (old_status == PLAY_STOPPED) | ||
2367 | { | ||
2368 | /* Send coldstart event */ | ||
2369 | send_event(PLAYBACK_EVENT_START_PLAYBACK, NULL); | ||
1623 | } | 2370 | } |
1624 | 2371 | ||
1625 | /* Update the playlist */ | 2372 | /* Fill the buffer */ |
1626 | last_peek_offset -= ci.new_track; | 2373 | int trackstat = audio_fill_file_buffer(); |
2374 | |||
2375 | if (trackstat >= LOAD_TRACK_OK) | ||
2376 | { | ||
2377 | /* This is the currently playing track - get metadata, stat */ | ||
2378 | playing_id3_sync(track_list_current(0), offset); | ||
1627 | 2379 | ||
1628 | if (playlist_next(ci.new_track) < 0) | 2380 | if (valid_mp3entry(id3_get(PLAYING_ID3))) |
2381 | { | ||
2382 | /* Only if actually changing tracks... */ | ||
2383 | if (!(flags & AUDIO_START_RESTART)) | ||
2384 | audio_playlist_track_change(); | ||
2385 | } | ||
2386 | } | ||
2387 | else | ||
1629 | { | 2388 | { |
1630 | /* End of list */ | 2389 | /* Found nothing playable */ |
1631 | audio_last_track(automatic_skip); | 2390 | audio_handle_track_load_status(trackstat); |
2391 | } | ||
2392 | } | ||
2393 | |||
2394 | /* Stop playback and enter an idle state | ||
2395 | (usually Q_AUDIO_STOP) */ | ||
2396 | static void audio_stop_playback(void) | ||
2397 | { | ||
2398 | logf("%s()", __func__); | ||
2399 | |||
2400 | if (play_status == PLAY_STOPPED) | ||
2401 | return; | ||
2402 | |||
2403 | /* Stop the codec and unload it */ | ||
2404 | halt_decoding_track(true); | ||
2405 | pcmbuf_play_stop(); | ||
2406 | codec_unload(); | ||
2407 | |||
2408 | /* Save resume information - "filling" might have been set to | ||
2409 | "STATE_ENDED" by caller in order to facilitate end of playlist */ | ||
2410 | audio_playlist_track_finish(); | ||
2411 | |||
2412 | skip_pending = TRACK_SKIP_NONE; | ||
2413 | automatic_skip = false; | ||
2414 | |||
2415 | /* Close all tracks and mark them NULL */ | ||
2416 | remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback); | ||
2417 | remove_event(BUFFER_EVENT_FINISHED, buffer_event_finished_callback); | ||
2418 | remove_event(BUFFER_EVENT_BUFFER_LOW, buffer_event_buffer_low_callback); | ||
2419 | |||
2420 | track_list_clear(TRACK_LIST_CLEAR_ALL); | ||
2421 | |||
2422 | /* Update our state */ | ||
2423 | ff_rw_mode = false; | ||
2424 | play_status = PLAY_STOPPED; | ||
2425 | |||
2426 | wipe_track_metadata(true); | ||
2427 | |||
2428 | /* Go idle */ | ||
2429 | filling = STATE_IDLE; | ||
2430 | cancel_cpu_boost(); | ||
2431 | } | ||
2432 | |||
2433 | /* Pause the playback of the current track | ||
2434 | (Q_AUDIO_PAUSE) */ | ||
2435 | static void audio_on_pause(bool pause) | ||
2436 | { | ||
2437 | logf("%s(%s)", __func__, pause ? "true" : "false"); | ||
2438 | |||
2439 | if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED)) | ||
1632 | return; | 2440 | return; |
2441 | |||
2442 | if (!ff_rw_mode) | ||
2443 | { | ||
2444 | /* Not in ff/rw mode - may set the state (otherwise this could make | ||
2445 | old data play because seek hasn't completed and cleared it) */ | ||
2446 | pcmbuf_pause(pause); | ||
1633 | } | 2447 | } |
1634 | 2448 | ||
1635 | if (new_playlist) | 2449 | play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; |
2450 | |||
2451 | if (!pause && codec_skip_pending) | ||
1636 | { | 2452 | { |
1637 | ci.new_track = 1; | 2453 | /* Actually do the skip that is due - resets the status flag */ |
1638 | new_playlist = false; | 2454 | audio_on_codec_complete(codec_skip_status); |
1639 | } | 2455 | } |
2456 | } | ||
2457 | |||
2458 | /* Skip a certain number of tracks forwards or backwards | ||
2459 | (Q_AUDIO_SKIP) */ | ||
2460 | static void audio_on_skip(void) | ||
2461 | { | ||
2462 | id3_mutex_lock(); | ||
2463 | |||
2464 | /* Eat the delta to keep it synced, even if not playing */ | ||
2465 | int toskip = skip_offset; | ||
2466 | skip_offset = 0; | ||
1640 | 2467 | ||
1641 | /* Save a pointer to the old track to allow later clearing */ | 2468 | logf("%s(): %d", __func__, toskip); |
1642 | prev_ti = CUR_TI; | 2469 | |
2470 | id3_mutex_unlock(); | ||
2471 | |||
2472 | if (play_status == PLAY_STOPPED) | ||
2473 | return; | ||
1643 | 2474 | ||
1644 | for (i = 0; i < ci.new_track; i++) | 2475 | /* Force codec to abort this track */ |
2476 | halt_decoding_track(true); | ||
2477 | |||
2478 | /* Kill the ff/rw halt */ | ||
2479 | ff_rw_mode = false; | ||
2480 | |||
2481 | /* Manual skip */ | ||
2482 | automatic_skip = false; | ||
2483 | |||
2484 | /* If there was an auto skip in progress, there will be residual | ||
2485 | advancement of the playlist and/or track list so compensation will be | ||
2486 | required in order to end up in the right spot */ | ||
2487 | int track_list_delta = toskip; | ||
2488 | int playlist_delta = toskip; | ||
2489 | |||
2490 | if (skip_pending != TRACK_SKIP_NONE) | ||
1645 | { | 2491 | { |
1646 | idx = (track_ridx + i) & MAX_TRACK_MASK; | 2492 | if (skip_pending != TRACK_SKIP_AUTO_END_PLAYLIST) |
1647 | struct mp3entry *id3 = bufgetid3(tracks[idx].id3_hid); | 2493 | track_list_delta--; |
1648 | ssize_t offset = buf_handle_offset(tracks[idx].audio_hid); | 2494 | |
1649 | if (!id3 || offset < 0 || (unsigned)offset > id3->first_frame_offset) | 2495 | if (skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST) |
2496 | playlist_delta--; | ||
2497 | } | ||
2498 | |||
2499 | audio_playlist_track_finish(); | ||
2500 | skip_pending = TRACK_SKIP_NONE; | ||
2501 | |||
2502 | /* Update the playlist current track now */ | ||
2503 | while (playlist_next(playlist_delta) < 0) | ||
2504 | { | ||
2505 | /* Manual skip out of range (because the playlist wasn't updated | ||
2506 | yet by us and so the check in audio_skip returned 'ok') - bring | ||
2507 | back into range */ | ||
2508 | int d = toskip < 0 ? 1 : -1; | ||
2509 | |||
2510 | while (!playlist_check(playlist_delta)) | ||
1650 | { | 2511 | { |
1651 | /* We don't have all the audio data for that track, so clear it, | 2512 | if (playlist_delta == d) |
1652 | but keep the metadata. */ | ||
1653 | if (tracks[idx].audio_hid >= 0 && bufclose(tracks[idx].audio_hid)) | ||
1654 | { | 2513 | { |
1655 | tracks[idx].audio_hid = -1; | 2514 | /* Had to move the opposite direction to correct, which is |
1656 | tracks[idx].filesize = 0; | 2515 | wrong - this is the end */ |
2516 | filling = STATE_ENDED; | ||
2517 | audio_stop_playback(); | ||
2518 | return; | ||
1657 | } | 2519 | } |
2520 | |||
2521 | playlist_delta += d; | ||
2522 | track_list_delta += d; | ||
1658 | } | 2523 | } |
1659 | } | 2524 | } |
1660 | 2525 | ||
1661 | /* Move to the new track */ | 2526 | /* Adjust things by how much the playlist was manually moved */ |
1662 | track_ridx = (track_ridx + ci.new_track) & MAX_TRACK_MASK; | 2527 | playlist_peek_offset -= playlist_delta; |
1663 | buf_set_base_handle(CUR_TI->audio_hid); | ||
1664 | 2528 | ||
1665 | if (automatic_skip) | 2529 | struct track_info *info = track_list_advance_current(track_list_delta); |
1666 | { | 2530 | int trackstat = LOAD_TRACK_OK; |
1667 | wps_offset = -ci.new_track; | ||
1668 | } | ||
1669 | 2531 | ||
1670 | /* If it is not safe to even skip this many track entries */ | 2532 | if (!info || info->audio_hid < 0) |
1671 | if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK) | ||
1672 | { | 2533 | { |
1673 | ci.new_track = 0; | 2534 | /* We don't know the next track thus we know we don't have it */ |
1674 | audio_rebuffer(); | 2535 | trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); |
1675 | goto skip_done; | ||
1676 | } | 2536 | } |
1677 | 2537 | ||
1678 | forward = ci.new_track > 0; | 2538 | audio_begin_track_change(false, trackstat); |
1679 | ci.new_track = 0; | 2539 | } |
2540 | |||
2541 | /* Skip to the next/previous directory | ||
2542 | (Q_AUDIO_DIR_SKIP) */ | ||
2543 | static void audio_on_dir_skip(int direction) | ||
2544 | { | ||
2545 | logf("%s(%d)", __func__, direction); | ||
2546 | |||
2547 | id3_mutex_lock(); | ||
2548 | skip_offset = 0; | ||
2549 | id3_mutex_unlock(); | ||
2550 | |||
2551 | if (play_status == PLAY_STOPPED) | ||
2552 | return; | ||
2553 | |||
2554 | /* Force codec to abort this track */ | ||
2555 | halt_decoding_track(true); | ||
2556 | |||
2557 | /* Kill the ff/rw halt */ | ||
2558 | ff_rw_mode = false; | ||
2559 | |||
2560 | /* Manual skip */ | ||
2561 | automatic_skip = false; | ||
2562 | |||
2563 | audio_playlist_track_finish(); | ||
2564 | |||
2565 | /* Unless automatic and gapless, skips do not pend */ | ||
2566 | skip_pending = TRACK_SKIP_NONE; | ||
2567 | |||
2568 | /* Regardless of the return value we need to rebuffer. If it fails the old | ||
2569 | playlist will resume, else the next dir will start playing. */ | ||
2570 | playlist_next_dir(direction); | ||
2571 | |||
2572 | wipe_track_metadata(false); | ||
1680 | 2573 | ||
1681 | /* If the target track is clearly not in memory */ | 2574 | int trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); |
1682 | if (CUR_TI->filesize == 0 || !CUR_TI->taginfo_ready) | 2575 | |
2576 | if (trackstat == LOAD_TRACK_ERR_NO_MORE) | ||
1683 | { | 2577 | { |
1684 | audio_rebuffer(); | 2578 | /* The day the music died - finish-off whatever is playing and call it |
1685 | goto skip_done; | 2579 | quits */ |
2580 | audio_monitor_end_of_playlist(); | ||
2581 | return; | ||
1686 | } | 2582 | } |
1687 | 2583 | ||
1688 | /* When skipping backwards, it is possible that we've found a track that's | 2584 | audio_begin_track_change(false, trackstat); |
1689 | * buffered, but which is around the track-wrap and therefore not the track | 2585 | } |
1690 | * we are looking for */ | 2586 | |
1691 | if (!forward) | 2587 | /* Enter seek mode in order to start a seek |
2588 | (Q_AUDIO_PRE_FF_REWIND) */ | ||
2589 | static void audio_on_pre_ff_rewind(void) | ||
2590 | { | ||
2591 | logf("%s()", __func__); | ||
2592 | |||
2593 | if (play_status == PLAY_STOPPED || ff_rw_mode) | ||
2594 | return; | ||
2595 | |||
2596 | ff_rw_mode = true; | ||
2597 | |||
2598 | if (play_status == PLAY_PAUSED) | ||
2599 | return; | ||
2600 | |||
2601 | pcmbuf_pause(true); | ||
2602 | } | ||
2603 | |||
2604 | /* Seek the playback of the current track to the specified time | ||
2605 | (Q_AUDIO_FF_REWIND) */ | ||
2606 | static void audio_on_ff_rewind(long time) | ||
2607 | { | ||
2608 | logf("%s(%ld)", __func__, time); | ||
2609 | |||
2610 | if (play_status == PLAY_STOPPED) | ||
2611 | return; | ||
2612 | |||
2613 | enum track_skip_type pending = skip_pending; | ||
2614 | |||
2615 | switch (pending) | ||
2616 | { | ||
2617 | case TRACK_SKIP_NONE: /* The usual case */ | ||
2618 | case TRACK_SKIP_AUTO: /* Have to back it out (fun!) */ | ||
2619 | case TRACK_SKIP_AUTO_END_PLAYLIST: /* Still have the last codec used */ | ||
1692 | { | 2620 | { |
1693 | int cur_idx = track_ridx; | 2621 | struct mp3entry *id3 = id3_get(PLAYING_ID3); |
1694 | bool taginfo_ready = true; | 2622 | struct mp3entry *ci_id3 = id3_get(CODEC_ID3); |
1695 | /* We've wrapped the buffer backwards if new > old */ | 2623 | |
1696 | bool wrap = track_ridx > old_track_ridx; | 2624 | automatic_skip = false; |
2625 | |||
2626 | /* Send event before clobbering the time */ | ||
2627 | /* FIXME: Nasty, but the tagtree expects this so that rewinding and | ||
2628 | then skipping back to this track resumes properly. Something else | ||
2629 | should be sent. We're not _really_ finishing the track are we? */ | ||
2630 | if (time == 0) | ||
2631 | send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); | ||
2632 | |||
2633 | /* Prevent user codec time update - coerce to something that is | ||
2634 | innocuous concerning lookaheads */ | ||
2635 | if (pending == TRACK_SKIP_NONE) | ||
2636 | skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST; | ||
2637 | |||
2638 | id3->elapsed = time; | ||
2639 | queue_reply(&audio_queue, 1); | ||
2640 | |||
2641 | bool haltres = halt_decoding_track(pending == TRACK_SKIP_AUTO); | ||
2642 | |||
2643 | /* Need this set in case ff/rw mode + error but _after_ the codec | ||
2644 | halt that will reset it */ | ||
2645 | codec_seeking = true; | ||
1697 | 2646 | ||
1698 | while (1) | 2647 | if (pending == TRACK_SKIP_AUTO) |
1699 | { | 2648 | { |
1700 | cur_idx = (cur_idx + 1) & MAX_TRACK_MASK; | 2649 | if (!track_list_advance_current(-1)) |
2650 | { | ||
2651 | /* Not in list - must rebuffer at the current playlist index */ | ||
2652 | if (audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1) | ||
2653 | < LOAD_TRACK_OK) | ||
2654 | { | ||
2655 | /* Codec is stopped */ | ||
2656 | break; | ||
2657 | } | ||
2658 | } | ||
2659 | } | ||
1701 | 2660 | ||
1702 | /* if we've advanced past the wrap when cur_idx is zeroed */ | 2661 | /* Set after audio_fill_file_buffer to disable playing id3 clobber if |
1703 | if (!cur_idx) | 2662 | rebuffer is needed */ |
1704 | wrap = false; | 2663 | skip_pending = TRACK_SKIP_NONE; |
2664 | struct track_info *cur_info = track_list_current(0); | ||
1705 | 2665 | ||
1706 | /* if we aren't still on the wrap and we've caught the old track */ | 2666 | /* Track must complete the loading _now_ since a codec and audio |
1707 | if (!(wrap || cur_idx < old_track_ridx)) | 2667 | handle are needed in order to do the seek */ |
1708 | break; | 2668 | if (cur_info->audio_hid < 0 && |
2669 | audio_finish_load_track(cur_info) != LOAD_TRACK_READY) | ||
2670 | { | ||
2671 | /* Call above should push any load sequence - no need for | ||
2672 | halt_decoding_track here if no skip was pending here because | ||
2673 | there would not be a codec started if no audio handle was yet | ||
2674 | opened */ | ||
2675 | break; | ||
2676 | } | ||
1709 | 2677 | ||
1710 | /* If we hit a track in between without valid tag info, bail */ | 2678 | if (pending == TRACK_SKIP_AUTO) |
1711 | if (!tracks[cur_idx].taginfo_ready) | 2679 | { |
2680 | if (!bufreadid3(cur_info->id3_hid, ci_id3) || | ||
2681 | !audio_init_codec(cur_info, ci_id3)) | ||
1712 | { | 2682 | { |
1713 | taginfo_ready = false; | 2683 | /* We should have still been able to get it - skip it and move |
2684 | onto the next one - like it or not this track is borken */ | ||
1714 | break; | 2685 | break; |
1715 | } | 2686 | } |
2687 | |||
2688 | /* Set the codec API to the correct metadata and track info */ | ||
2689 | ci.audio_hid = cur_info->audio_hid; | ||
2690 | ci.filesize = cur_info->filesize; | ||
2691 | buf_set_base_handle(cur_info->audio_hid); | ||
1716 | } | 2692 | } |
1717 | if (!taginfo_ready) | 2693 | |
2694 | if (!haltres) | ||
1718 | { | 2695 | { |
1719 | audio_rebuffer(); | 2696 | /* If codec must be (re)started, reset the offset */ |
2697 | ci_id3->offset = 0; | ||
1720 | } | 2698 | } |
1721 | } | ||
1722 | 2699 | ||
1723 | skip_done: | 2700 | codec_seek(time); |
1724 | audio_update_trackinfo(); | 2701 | return; |
1725 | pcmbuf_start_track_change(automatic_skip); | 2702 | } |
1726 | 2703 | ||
1727 | if (get_codec_base_type(codec_loaded()) == | 2704 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: |
1728 | get_codec_base_type(thistrack_id3->codectype)) | ||
1729 | { | 2705 | { |
1730 | /* codec is the same base type */ | 2706 | /* We cannot do this because the playlist must be reversed by one |
1731 | logf("New track loaded"); | 2707 | and it doesn't always return the same song when going backwards |
1732 | codec_ack_msg(Q_CODEC_REQUEST_COMPLETE, false); | 2708 | across boundaries as forwards (either because of randomization |
2709 | or inconsistency in deciding what the previous track should be), | ||
2710 | therefore the whole operation would often end up as nonsense - | ||
2711 | lock out seeking for a couple seconds */ | ||
2712 | |||
2713 | /* Sure as heck cancel seek mode too! */ | ||
2714 | audio_ff_rewind_end(); | ||
2715 | return; | ||
2716 | } | ||
2717 | |||
2718 | default: | ||
2719 | /* Won't see this */ | ||
2720 | return; | ||
1733 | } | 2721 | } |
1734 | else | 2722 | |
2723 | if (play_status == PLAY_STOPPED) | ||
1735 | { | 2724 | { |
1736 | /* a codec change is required */ | 2725 | /* Playback ended because of an error completing a track load */ |
1737 | logf("New codec: %d/%d", thistrack_id3->codectype, codec_loaded()); | 2726 | return; |
1738 | codec_ack_msg(Q_CODEC_REQUEST_COMPLETE, true); | ||
1739 | codec_load(tracks[track_ridx].codec_hid, thistrack_id3->codectype); | ||
1740 | tracks[track_ridx].codec_hid = -1; /* Codec thread will close it */ | ||
1741 | } | 2727 | } |
2728 | |||
2729 | /* Always fake it as a codec start error which will handle mode | ||
2730 | cancellations and skip to the next track */ | ||
2731 | audio_handle_track_load_status(LOAD_TRACK_ERR_START_CODEC); | ||
1742 | } | 2732 | } |
1743 | 2733 | ||
1744 | unsigned long audio_prev_elapsed(void) | 2734 | /* Invalidates all but currently playing track |
2735 | (Q_AUDIO_FLUSH) */ | ||
2736 | static void audio_on_audio_flush(void) | ||
1745 | { | 2737 | { |
1746 | return prev_track_elapsed; | 2738 | logf("%s", __func__); |
2739 | |||
2740 | if (track_list_empty()) | ||
2741 | return; /* Nothing to flush out */ | ||
2742 | |||
2743 | switch (skip_pending) | ||
2744 | { | ||
2745 | case TRACK_SKIP_NONE: | ||
2746 | case TRACK_SKIP_AUTO_END_PLAYLIST: | ||
2747 | /* Remove all but the currently playing track from the list and | ||
2748 | refill after that */ | ||
2749 | track_list_clear(TRACK_LIST_KEEP_CURRENT); | ||
2750 | playlist_peek_offset = 0; | ||
2751 | id3_write_locked(UNBUFFERED_ID3, NULL); | ||
2752 | audio_update_and_announce_next_track(NULL); | ||
2753 | |||
2754 | /* Ignore return since it's about the next track, not this one */ | ||
2755 | audio_fill_file_buffer(); | ||
2756 | |||
2757 | if (skip_pending == TRACK_SKIP_NONE) | ||
2758 | break; | ||
2759 | |||
2760 | /* There's now a track after this one now - convert to auto skip - | ||
2761 | no skip should pend right now because multiple flush messages can | ||
2762 | be fired which would cause a restart in the below cases */ | ||
2763 | skip_pending = TRACK_SKIP_NONE; | ||
2764 | audio_clear_track_notifications(); | ||
2765 | audio_queue_post(Q_AUDIO_CODEC_COMPLETE, CODEC_OK); | ||
2766 | break; | ||
2767 | |||
2768 | case TRACK_SKIP_AUTO: | ||
2769 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
2770 | /* Precisely removing what it already decoded for the next track is | ||
2771 | not possible so a restart is required in order to continue the | ||
2772 | currently playing track without the now invalid future track | ||
2773 | playing */ | ||
2774 | audio_start_playback(0, AUDIO_START_RESTART); | ||
2775 | break; | ||
2776 | |||
2777 | default: /* Nothing else is a state */ | ||
2778 | break; | ||
2779 | } | ||
1747 | } | 2780 | } |
1748 | 2781 | ||
1749 | void audio_set_prev_elapsed(unsigned long setting) | 2782 | #ifdef AUDIO_HAVE_RECORDING |
2783 | /* Load the requested encoder type | ||
2784 | (Q_AUDIO_LOAD_ENCODER) */ | ||
2785 | static void audio_on_load_encoder(int afmt) | ||
1750 | { | 2786 | { |
1751 | prev_track_elapsed = setting; | 2787 | bool res = true; |
2788 | |||
2789 | if (play_status != PLAY_STOPPED) | ||
2790 | audio_stop_playback(); /* Can't load both types at once */ | ||
2791 | else | ||
2792 | codec_unload(); /* Encoder still loaded, stop and unload it */ | ||
2793 | |||
2794 | if (afmt != AFMT_UNKNOWN) | ||
2795 | { | ||
2796 | res = codec_load(-1, afmt | CODEC_TYPE_ENCODER); | ||
2797 | if (res) | ||
2798 | codec_go(); /* These are run immediately */ | ||
2799 | } | ||
2800 | |||
2801 | queue_reply(&audio_queue, res); | ||
1752 | } | 2802 | } |
2803 | #endif /* AUDIO_HAVE_RECORDING */ | ||
1753 | 2804 | ||
1754 | /* Stop the codec and reset the PCM buffer */ | 2805 | static void audio_thread(void) |
1755 | static void audio_stop_codec_flush(void) | ||
1756 | { | 2806 | { |
1757 | bool pcm_playing; | 2807 | struct queue_event ev; |
1758 | 2808 | ||
1759 | pcmbuf_pause(true); | 2809 | pcm_postinit(); |
1760 | 2810 | ||
1761 | codec_stop(); | 2811 | filling = STATE_IDLE; |
1762 | 2812 | ||
1763 | pcm_play_lock(); | 2813 | while (1) |
2814 | { | ||
2815 | switch (filling) | ||
2816 | { | ||
2817 | /* Active states */ | ||
2818 | case STATE_FULL: | ||
2819 | case STATE_END_OF_PLAYLIST: | ||
2820 | if (buf_get_watermark() == 0) | ||
2821 | { | ||
2822 | /* End of buffering for now, let's calculate the watermark, | ||
2823 | register for a low buffer event and unboost */ | ||
2824 | audio_update_filebuf_watermark(0); | ||
2825 | add_event(BUFFER_EVENT_BUFFER_LOW, true, | ||
2826 | buffer_event_buffer_low_callback); | ||
2827 | } | ||
2828 | /* Fall-through */ | ||
2829 | case STATE_FINISHED: | ||
2830 | /* All data was buffered */ | ||
2831 | cancel_cpu_boost(); | ||
2832 | /* Fall-through */ | ||
2833 | case STATE_FILLING: | ||
2834 | case STATE_ENDING: | ||
2835 | if (audio_pcmbuf_track_change_scan()) | ||
2836 | { | ||
2837 | /* Transfer notification to audio queue event */ | ||
2838 | ev.id = Q_AUDIO_TRACK_CHANGED; | ||
2839 | ev.data = 1; | ||
2840 | } | ||
2841 | else | ||
2842 | { | ||
2843 | /* If doing auto skip, poll pcmbuf track notifications a bit | ||
2844 | faster to promply detect the transition */ | ||
2845 | queue_wait_w_tmo(&audio_queue, &ev, | ||
2846 | skip_pending == TRACK_SKIP_NONE ? | ||
2847 | HZ/2 : HZ/10); | ||
2848 | } | ||
2849 | break; | ||
1764 | 2850 | ||
1765 | pcm_playing = pcm_is_playing(); | 2851 | /* Idle states */ |
2852 | default: | ||
2853 | queue_wait(&audio_queue, &ev); | ||
1766 | 2854 | ||
1767 | pcmbuf_play_stop(); | 2855 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
1768 | queue_clear(&pcmbuf_queue); | 2856 | switch (ev.id) |
2857 | { | ||
2858 | #ifdef AUDIO_HAVE_RECORDING | ||
2859 | /* Must monitor the encoder message for recording so it can remove | ||
2860 | it if we process the insertion before it does. It cannot simply | ||
2861 | be removed from under recording however. */ | ||
2862 | case Q_AUDIO_LOAD_ENCODER: | ||
2863 | break; | ||
2864 | #endif | ||
2865 | case SYS_USB_DISCONNECTED: | ||
2866 | filling = STATE_IDLE; | ||
2867 | break; | ||
2868 | |||
2869 | default: | ||
2870 | if (filling == STATE_USB) | ||
2871 | continue; | ||
2872 | } | ||
2873 | #endif /* CONFIG_PLATFORM */ | ||
2874 | } | ||
2875 | |||
2876 | switch (ev.id) | ||
2877 | { | ||
2878 | /** Codec and track change messages **/ | ||
2879 | case Q_AUDIO_CODEC_COMPLETE: | ||
2880 | /* Codec is done processing track and has gone idle */ | ||
2881 | LOGFQUEUE("audio < Q_AUDIO_CODEC_COMPLETE: %ld", (long)ev.data); | ||
2882 | audio_on_codec_complete(ev.data); | ||
2883 | break; | ||
2884 | |||
2885 | case Q_AUDIO_CODEC_SEEK_COMPLETE: | ||
2886 | /* Codec is done seeking */ | ||
2887 | LOGFQUEUE("audio < Q_AUDIO_SEEK_COMPLETE"); | ||
2888 | audio_on_codec_seek_complete(); | ||
2889 | break; | ||
2890 | |||
2891 | case Q_AUDIO_TRACK_CHANGED: | ||
2892 | /* PCM track change done */ | ||
2893 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); | ||
2894 | audio_on_track_changed(); | ||
2895 | break; | ||
2896 | |||
2897 | /** Control messages **/ | ||
2898 | case Q_AUDIO_PLAY: | ||
2899 | LOGFQUEUE("audio < Q_AUDIO_PLAY"); | ||
2900 | audio_start_playback(ev.data, 0); | ||
2901 | break; | ||
2902 | |||
2903 | case Q_AUDIO_STOP: | ||
2904 | LOGFQUEUE("audio < Q_AUDIO_STOP"); | ||
2905 | audio_stop_playback(); | ||
2906 | if (ev.data != 0) | ||
2907 | queue_clear(&audio_queue); | ||
2908 | break; | ||
2909 | |||
2910 | case Q_AUDIO_PAUSE: | ||
2911 | LOGFQUEUE("audio < Q_AUDIO_PAUSE"); | ||
2912 | audio_on_pause(ev.data); | ||
2913 | break; | ||
2914 | |||
2915 | case Q_AUDIO_SKIP: | ||
2916 | LOGFQUEUE("audio < Q_AUDIO_SKIP"); | ||
2917 | audio_on_skip(); | ||
2918 | break; | ||
1769 | 2919 | ||
1770 | if (pcm_playing) | 2920 | case Q_AUDIO_DIR_SKIP: |
1771 | pcmbuf_pause(paused); | 2921 | LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP"); |
2922 | audio_on_dir_skip(ev.data); | ||
2923 | break; | ||
2924 | |||
2925 | case Q_AUDIO_PRE_FF_REWIND: | ||
2926 | LOGFQUEUE("audio < Q_AUDIO_PRE_FF_REWIND"); | ||
2927 | audio_on_pre_ff_rewind(); | ||
2928 | break; | ||
2929 | |||
2930 | case Q_AUDIO_FF_REWIND: | ||
2931 | LOGFQUEUE("audio < Q_AUDIO_FF_REWIND"); | ||
2932 | audio_on_ff_rewind(ev.data); | ||
2933 | break; | ||
2934 | |||
2935 | case Q_AUDIO_FLUSH: | ||
2936 | LOGFQUEUE("audio < Q_AUDIO_FLUSH: %d", (int)ev.data); | ||
2937 | audio_on_audio_flush(); | ||
2938 | break; | ||
1772 | 2939 | ||
1773 | pcm_play_unlock(); | 2940 | /** Buffering messages **/ |
2941 | case Q_AUDIO_BUFFERING: | ||
2942 | /* some buffering event */ | ||
2943 | LOGFQUEUE("audio < Q_AUDIO_BUFFERING: %d", (int)ev.data); | ||
2944 | audio_on_buffering(ev.data); | ||
2945 | break; | ||
2946 | |||
2947 | case Q_AUDIO_FILL_BUFFER: | ||
2948 | /* continue buffering next track */ | ||
2949 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); | ||
2950 | audio_on_fill_buffer(); | ||
2951 | break; | ||
2952 | |||
2953 | case Q_AUDIO_FINISH_LOAD_TRACK: | ||
2954 | /* metadata is buffered */ | ||
2955 | LOGFQUEUE("audio < Q_AUDIO_FINISH_LOAD_TRACK"); | ||
2956 | audio_on_finish_load_track(ev.data); | ||
2957 | break; | ||
2958 | |||
2959 | case Q_AUDIO_HANDLE_FINISHED: | ||
2960 | /* some other type is buffered */ | ||
2961 | LOGFQUEUE("audio < Q_AUDIO_HANDLE_FINISHED"); | ||
2962 | audio_on_handle_finished(ev.data); | ||
2963 | break; | ||
2964 | |||
2965 | /** Miscellaneous messages **/ | ||
2966 | case Q_AUDIO_REMAKE_AUDIO_BUFFER: | ||
2967 | /* buffer needs to be reinitialized */ | ||
2968 | LOGFQUEUE("audio < Q_AUDIO_REMAKE_AUDIO_BUFFER"); | ||
2969 | audio_start_playback(0, AUDIO_START_RESTART | AUDIO_START_NEWBUF); | ||
2970 | break; | ||
2971 | |||
2972 | #ifdef HAVE_DISK_STORAGE | ||
2973 | case Q_AUDIO_UPDATE_WATERMARK: | ||
2974 | /* buffering watermark needs updating */ | ||
2975 | LOGFQUEUE("audio < Q_AUDIO_UPDATE_WATERMARK: %d", (int)ev.data); | ||
2976 | audio_update_filebuf_watermark(ev.data); | ||
2977 | break; | ||
2978 | #endif /* HAVE_DISK_STORAGE */ | ||
2979 | |||
2980 | #ifdef AUDIO_HAVE_RECORDING | ||
2981 | case Q_AUDIO_LOAD_ENCODER: | ||
2982 | /* load an encoder for recording */ | ||
2983 | LOGFQUEUE("audio < Q_AUDIO_LOAD_ENCODER: %d", (int)ev.data); | ||
2984 | audio_on_load_encoder(ev.data); | ||
2985 | break; | ||
2986 | #endif /* AUDIO_HAVE_RECORDING */ | ||
2987 | |||
2988 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
2989 | case SYS_USB_CONNECTED: | ||
2990 | LOGFQUEUE("audio < SYS_USB_CONNECTED"); | ||
2991 | audio_stop_playback(); | ||
2992 | #ifdef PLAYBACK_VOICE | ||
2993 | voice_stop(); | ||
2994 | #endif | ||
2995 | filling = STATE_USB; | ||
2996 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
2997 | break; | ||
2998 | #endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) */ | ||
2999 | |||
3000 | case SYS_TIMEOUT: | ||
3001 | LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); | ||
3002 | break; | ||
3003 | |||
3004 | default: | ||
3005 | /* LOGFQUEUE("audio < default : %08lX", ev.id); */ | ||
3006 | break; | ||
3007 | } /* end switch */ | ||
3008 | } /* end while */ | ||
1774 | } | 3009 | } |
1775 | 3010 | ||
1776 | static void audio_stop_playback(void) | 3011 | |
3012 | /* --- Buffering callbacks --- */ | ||
3013 | |||
3014 | /* Called when fullness is below the watermark level */ | ||
3015 | static void buffer_event_buffer_low_callback(void *data) | ||
1777 | { | 3016 | { |
1778 | if (playing) | 3017 | logf("low buffer callback"); |
1779 | { | 3018 | LOGFQUEUE("buffering > audio Q_AUDIO_BUFFERING: buffer low"); |
1780 | /* If we were playing, save resume information */ | 3019 | audio_queue_post(Q_AUDIO_BUFFERING, BUFFER_EVENT_BUFFER_LOW); |
1781 | struct mp3entry *id3 = NULL; | 3020 | (void)data; |
3021 | } | ||
1782 | 3022 | ||
1783 | if (!ci.stop_codec) | 3023 | /* Called when handles must be discarded in order to buffer new data */ |
1784 | id3 = audio_current_track(); | 3024 | static void buffer_event_rebuffer_callback(void *data) |
3025 | { | ||
3026 | logf("rebuffer callback"); | ||
3027 | LOGFQUEUE("buffering > audio Q_AUDIO_BUFFERING: rebuffer"); | ||
3028 | audio_queue_post(Q_AUDIO_BUFFERING, BUFFER_EVENT_REBUFFER); | ||
3029 | (void)data; | ||
3030 | } | ||
1785 | 3031 | ||
1786 | /* Save the current playing spot, or NULL if the playlist has ended */ | 3032 | /* A handle has completed buffering and all required data is available */ |
1787 | playlist_update_resume_info(id3); | 3033 | static void buffer_event_finished_callback(void *data) |
3034 | { | ||
3035 | int hid = *(const int *)data; | ||
3036 | const enum data_type htype = buf_handle_data_type(hid); | ||
1788 | 3037 | ||
1789 | /* Now it's good time to send track finish events. Do this | 3038 | logf("handle %d finished buffering (type:%u)", hid, (unsigned)htype); |
1790 | only if this hasn't been done already as part of a track | ||
1791 | switch. */ | ||
1792 | if (id3 == thistrack_id3) | ||
1793 | send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3); | ||
1794 | 3039 | ||
1795 | /* TODO: Create auto bookmark too? */ | 3040 | /* Limit queue traffic */ |
3041 | switch (htype) | ||
3042 | { | ||
3043 | case TYPE_ID3: | ||
3044 | /* The metadata handle for the last loaded track has been buffered. | ||
3045 | We can ask the audio thread to load the rest of the track's data. */ | ||
3046 | LOGFQUEUE("buffering > audio Q_AUDIO_FINISH_LOAD_TRACK: %d", hid); | ||
3047 | audio_queue_post(Q_AUDIO_FINISH_LOAD_TRACK, hid); | ||
3048 | break; | ||
1796 | 3049 | ||
1797 | prev_track_elapsed = othertrack_id3->elapsed; | 3050 | case TYPE_PACKET_AUDIO: |
3051 | /* Strip any useless trailing tags that are left. */ | ||
3052 | strip_tags(hid); | ||
3053 | /* Fall-through */ | ||
3054 | case TYPE_ATOMIC_AUDIO: | ||
3055 | LOGFQUEUE("buffering > audio Q_AUDIO_HANDLE_FINISHED: %d", hid); | ||
3056 | audio_queue_post(Q_AUDIO_HANDLE_FINISHED, hid); | ||
3057 | break; | ||
1798 | 3058 | ||
1799 | remove_event(BUFFER_EVENT_BUFFER_LOW, buffering_low_buffer_callback); | 3059 | default: |
3060 | /* Don't care to know about these */ | ||
3061 | break; | ||
1800 | } | 3062 | } |
3063 | } | ||
1801 | 3064 | ||
1802 | audio_stop_codec_flush(); | ||
1803 | paused = false; | ||
1804 | playing = false; | ||
1805 | track_load_started = false; | ||
1806 | 3065 | ||
1807 | filling = STATE_IDLE; | 3066 | /** -- Codec callbacks -- **/ |
3067 | |||
3068 | /* Update elapsed times with latency-adjusted values */ | ||
3069 | void audio_codec_update_elapsed(unsigned long value) | ||
3070 | { | ||
3071 | #ifdef AB_REPEAT_ENABLE | ||
3072 | ab_position_report(value); | ||
3073 | #endif | ||
3074 | |||
3075 | unsigned long latency = pcmbuf_get_latency(); | ||
3076 | |||
3077 | if (LIKELY(value >= latency)) | ||
3078 | { | ||
3079 | unsigned long elapsed = value - latency; | ||
3080 | |||
3081 | if (elapsed > value || elapsed < value - 2) | ||
3082 | value = elapsed; | ||
3083 | } | ||
3084 | else | ||
3085 | { | ||
3086 | value = 0; | ||
3087 | } | ||
1808 | 3088 | ||
1809 | /* Mark all entries null. */ | 3089 | /* Track codec: used later when updating the playing at the user |
1810 | audio_clear_track_entries(); | 3090 | transition */ |
3091 | id3_get(CODEC_ID3)->elapsed = value; | ||
1811 | 3092 | ||
1812 | /* Close all tracks */ | 3093 | /* If a skip is pending, the PCM buffer is updating the time on the |
1813 | audio_release_tracks(); | 3094 | previous song */ |
3095 | if (LIKELY(skip_pending == TRACK_SKIP_NONE)) | ||
3096 | id3_get(PLAYING_ID3)->elapsed = value; | ||
1814 | } | 3097 | } |
1815 | 3098 | ||
1816 | static void audio_play_start(size_t offset) | 3099 | /* Update offsets with latency-adjusted values */ |
3100 | void audio_codec_update_offset(size_t value) | ||
1817 | { | 3101 | { |
1818 | int i; | 3102 | struct mp3entry *ci_id3 = id3_get(CODEC_ID3); |
3103 | unsigned long latency = pcmbuf_get_latency() * ci_id3->bitrate / 8; | ||
1819 | 3104 | ||
1820 | send_event(PLAYBACK_EVENT_START_PLAYBACK, NULL); | 3105 | if (LIKELY(value >= latency)) |
1821 | #if INPUT_SRC_CAPS != 0 | 3106 | { |
1822 | audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); | 3107 | value -= latency; |
1823 | audio_set_output_source(AUDIO_SRC_PLAYBACK); | 3108 | } |
1824 | #endif | 3109 | else |
3110 | { | ||
3111 | value = 0; | ||
3112 | } | ||
1825 | 3113 | ||
1826 | paused = false; | 3114 | /* Track codec: used later when updating the playing id3 at the user |
1827 | audio_stop_codec_flush(); | 3115 | transition */ |
3116 | ci_id3->offset = value; | ||
1828 | 3117 | ||
1829 | playing = true; | 3118 | /* If a skip is pending, the PCM buffer is updating the time on the |
1830 | track_load_started = false; | 3119 | previous song */ |
3120 | if (LIKELY(skip_pending == TRACK_SKIP_NONE)) | ||
3121 | id3_get(PLAYING_ID3)->offset = value; | ||
3122 | } | ||
1831 | 3123 | ||
1832 | ci.new_track = 0; | ||
1833 | ci.seek_time = 0; | ||
1834 | wps_offset = 0; | ||
1835 | 3124 | ||
1836 | #ifndef PLATFORM_HAS_VOLUME_CHANGE | 3125 | /** --- Pcmbuf callbacks --- **/ |
1837 | sound_set_volume(global_settings.volume); | ||
1838 | #endif | ||
1839 | track_widx = track_ridx = 0; | ||
1840 | buf_set_base_handle(-1); | ||
1841 | 3126 | ||
1842 | /* Clear all track entries. */ | 3127 | /* Between the codec and PCM track change, we need to keep updating the |
1843 | for (i = 0; i < MAX_TRACK; i++) { | 3128 | * "elapsed" value of the previous (to the codec, but current to the |
1844 | clear_track_info(&tracks[i]); | 3129 | * user/PCM/WPS) track, so that the progressbar reaches the end. */ |
3130 | void audio_pcmbuf_position_callback(unsigned int time) | ||
3131 | { | ||
3132 | struct mp3entry *id3 = id3_get(PLAYING_ID3); | ||
3133 | |||
3134 | time += id3->elapsed; | ||
3135 | |||
3136 | id3->elapsed = MIN(time, id3->length); | ||
3137 | } | ||
3138 | |||
3139 | /* Post message from pcmbuf that the end of the previous track has just | ||
3140 | * been played */ | ||
3141 | void audio_pcmbuf_track_change(bool pcmbuf) | ||
3142 | { | ||
3143 | if (pcmbuf) | ||
3144 | { | ||
3145 | /* Notify of the change in special-purpose semaphore object */ | ||
3146 | LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED"); | ||
3147 | audio_pcmbuf_track_change_post(); | ||
1845 | } | 3148 | } |
3149 | else | ||
3150 | { | ||
3151 | /* Safe to post directly to the queue */ | ||
3152 | LOGFQUEUE("pcmbuf > audio Q_AUDIO_TRACK_CHANGED"); | ||
3153 | audio_queue_post(Q_AUDIO_TRACK_CHANGED, 0); | ||
3154 | } | ||
3155 | } | ||
1846 | 3156 | ||
1847 | last_peek_offset = -1; | 3157 | /* May pcmbuf start PCM playback when the buffer is full enough? */ |
3158 | bool audio_pcmbuf_may_play(void) | ||
3159 | { | ||
3160 | return play_status == PLAY_PLAYING && !ff_rw_mode; | ||
3161 | } | ||
1848 | 3162 | ||
1849 | /* Officially playing */ | ||
1850 | queue_reply(&audio_queue, 1); | ||
1851 | 3163 | ||
1852 | audio_fill_file_buffer(true, offset); | 3164 | /** -- External interfaces -- **/ |
1853 | 3165 | ||
1854 | add_event(BUFFER_EVENT_BUFFER_LOW, false, buffering_low_buffer_callback); | 3166 | /* Return the playback and recording status */ |
3167 | int audio_status(void) | ||
3168 | { | ||
3169 | unsigned int ret = play_status; | ||
3170 | |||
3171 | #ifdef AUDIO_HAVE_RECORDING | ||
3172 | /* Do this here for constitency with mpeg.c version */ | ||
3173 | ret |= pcm_rec_status(); | ||
3174 | #endif | ||
1855 | 3175 | ||
1856 | LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED"); | 3176 | return (int)ret; |
1857 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | ||
1858 | } | 3177 | } |
1859 | 3178 | ||
3179 | /* Clear all accumulated audio errors for playback and recording */ | ||
3180 | void audio_error_clear(void) | ||
3181 | { | ||
3182 | #ifdef AUDIO_HAVE_RECORDING | ||
3183 | pcm_rec_error_clear(); | ||
3184 | #endif | ||
3185 | } | ||
1860 | 3186 | ||
1861 | /* Invalidates all but currently playing track. */ | 3187 | /* Get a copy of the id3 data for the for current track + offset + skip delta */ |
1862 | static void audio_invalidate_tracks(void) | 3188 | bool audio_peek_track(struct mp3entry *id3, int offset) |
1863 | { | 3189 | { |
1864 | if (audio_have_tracks()) | 3190 | bool retval = false; |
3191 | |||
3192 | id3_mutex_lock(); | ||
3193 | |||
3194 | if (play_status != PLAY_STOPPED) | ||
1865 | { | 3195 | { |
1866 | last_peek_offset = 0; | 3196 | id3->path[0] = '\0'; /* Null path means it should be filled now */ |
1867 | track_widx = track_ridx; | 3197 | retval = audio_get_track_metadata(offset + skip_offset, id3) && |
3198 | id3->path[0] != '\0'; | ||
3199 | } | ||
1868 | 3200 | ||
1869 | /* Mark all other entries null (also buffered wrong metadata). */ | 3201 | id3_mutex_unlock(); |
1870 | audio_clear_track_entries(); | ||
1871 | 3202 | ||
1872 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | 3203 | return retval; |
3204 | } | ||
3205 | |||
3206 | /* Return the mp3entry for the currently playing track */ | ||
3207 | struct mp3entry * audio_current_track(void) | ||
3208 | { | ||
3209 | struct mp3entry *id3; | ||
1873 | 3210 | ||
1874 | audio_fill_file_buffer(false, 0); | 3211 | id3_mutex_lock(); |
1875 | send_event(PLAYBACK_EVENT_TRACK_CHANGE, thistrack_id3); | 3212 | |
3213 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
3214 | if (skip_offset != 0) | ||
3215 | { | ||
3216 | /* This is a peekahead */ | ||
3217 | id3 = id3_get(PLAYING_PEEK_ID3); | ||
3218 | audio_peek_track(id3, 0); | ||
1876 | } | 3219 | } |
3220 | else | ||
3221 | #endif | ||
3222 | { | ||
3223 | /* Normal case */ | ||
3224 | id3 = id3_get(PLAYING_ID3); | ||
3225 | audio_get_track_metadata(0, id3); | ||
3226 | } | ||
3227 | |||
3228 | id3_mutex_unlock(); | ||
3229 | |||
3230 | return id3; | ||
1877 | } | 3231 | } |
1878 | 3232 | ||
1879 | static void audio_new_playlist(void) | 3233 | /* Obtains the mp3entry for the next track from the current */ |
3234 | struct mp3entry * audio_next_track(void) | ||
1880 | { | 3235 | { |
1881 | /* Prepare to start a new fill from the beginning of the playlist */ | 3236 | struct mp3entry *id3 = id3_get(NEXTTRACK_ID3); |
1882 | last_peek_offset = -1; | ||
1883 | 3237 | ||
1884 | /* Signal the codec to initiate a track change forward */ | 3238 | id3_mutex_lock(); |
1885 | new_playlist = true; | ||
1886 | ci.new_track = 1; | ||
1887 | 3239 | ||
1888 | if (audio_have_tracks()) | 3240 | #ifdef AUDIO_FAST_SKIP_PREVIEW |
3241 | if (skip_offset != 0) | ||
3242 | { | ||
3243 | /* This is a peekahead */ | ||
3244 | if (!audio_peek_track(id3, 1)) | ||
3245 | id3 = NULL; | ||
3246 | } | ||
3247 | else | ||
3248 | #endif | ||
1889 | { | 3249 | { |
1890 | if (paused) | 3250 | /* Normal case */ |
1891 | skipped_during_pause = true; | 3251 | if (!audio_get_track_metadata(1, id3)) |
1892 | track_widx = track_ridx; | 3252 | id3 = NULL; |
1893 | audio_clear_track_entries(); | 3253 | } |
1894 | 3254 | ||
1895 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | 3255 | id3_mutex_unlock(); |
1896 | 3256 | ||
1897 | /* Mark the current track as invalid to prevent skipping back to it */ | 3257 | return id3; |
1898 | CUR_TI->taginfo_ready = false; | 3258 | } |
1899 | } | ||
1900 | 3259 | ||
1901 | /* Officially playing */ | 3260 | /* Start playback at the specified offset */ |
1902 | queue_reply(&audio_queue, 1); | 3261 | void audio_play(long offset) |
3262 | { | ||
3263 | logf("audio_play"); | ||
1903 | 3264 | ||
1904 | audio_fill_file_buffer(false, 0); | 3265 | #ifdef PLAYBACK_VOICE |
3266 | /* Truncate any existing voice output so we don't have spelling | ||
3267 | * etc. over the first part of the played track */ | ||
3268 | talk_force_shutup(); | ||
3269 | #endif | ||
3270 | |||
3271 | LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset); | ||
3272 | audio_queue_send(Q_AUDIO_PLAY, offset); | ||
1905 | } | 3273 | } |
1906 | 3274 | ||
1907 | /* Called on manual track skip */ | 3275 | /* Stop playback if playing */ |
1908 | static void audio_initiate_track_change(long direction) | 3276 | void audio_stop(void) |
1909 | { | 3277 | { |
1910 | logf("audio_initiate_track_change(%ld)", direction); | 3278 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP"); |
3279 | audio_queue_send(Q_AUDIO_STOP, 0); | ||
3280 | } | ||
1911 | 3281 | ||
1912 | ci.new_track += direction; | 3282 | /* Pause playback if playing */ |
1913 | wps_offset -= direction; | 3283 | void audio_pause(void) |
1914 | if (paused) | 3284 | { |
1915 | skipped_during_pause = true; | 3285 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE"); |
3286 | audio_queue_send(Q_AUDIO_PAUSE, true); | ||
3287 | } | ||
3288 | |||
3289 | /* This sends a stop message and the audio thread will dump all its | ||
3290 | subsequent messages */ | ||
3291 | void audio_hard_stop(void) | ||
3292 | { | ||
3293 | /* Stop playback */ | ||
3294 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1"); | ||
3295 | audio_queue_send(Q_AUDIO_STOP, 1); | ||
3296 | #ifdef PLAYBACK_VOICE | ||
3297 | voice_stop(); | ||
3298 | #endif | ||
1916 | } | 3299 | } |
1917 | 3300 | ||
1918 | /* Called on manual dir skip */ | 3301 | /* Resume playback if paused */ |
1919 | static void audio_initiate_dir_change(long direction) | 3302 | void audio_resume(void) |
1920 | { | 3303 | { |
1921 | dir_skip = true; | 3304 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE resume"); |
1922 | ci.new_track = direction; | 3305 | audio_queue_send(Q_AUDIO_PAUSE, false); |
1923 | if (paused) | ||
1924 | skipped_during_pause = true; | ||
1925 | } | 3306 | } |
1926 | 3307 | ||
1927 | /* Called when PCM track change is complete */ | 3308 | /* Skip the specified number of tracks forward or backward from the current */ |
1928 | static void audio_finalise_track_change(void) | 3309 | void audio_skip(int offset) |
1929 | { | 3310 | { |
1930 | logf("audio_finalise_track_change"); | 3311 | id3_mutex_lock(); |
3312 | |||
3313 | /* If offset has to be backed-out to stay in range, no skip is done */ | ||
3314 | int accum = skip_offset + offset; | ||
1931 | 3315 | ||
1932 | if (automatic_skip) | 3316 | while (offset != 0 && !playlist_check(accum)) |
1933 | { | 3317 | { |
1934 | wps_offset = 0; | 3318 | offset += offset < 0 ? 1 : -1; |
1935 | automatic_skip = false; | 3319 | accum = skip_offset + offset; |
3320 | } | ||
1936 | 3321 | ||
1937 | /* Invalidate prevtrack_id3 */ | 3322 | if (offset != 0) |
1938 | memset(othertrack_id3, 0, sizeof(struct mp3entry)); | 3323 | { |
3324 | /* Accumulate net manual skip count since the audio thread last | ||
3325 | processed one */ | ||
3326 | skip_offset = accum; | ||
1939 | 3327 | ||
1940 | if (prev_ti && prev_ti->audio_hid < 0) | 3328 | if (global_settings.beep) |
1941 | { | 3329 | pcmbuf_beep(2000, 100, 2500*global_settings.beep); |
1942 | /* No audio left so we clear all the track info. */ | 3330 | |
1943 | clear_track_info(prev_ti); | 3331 | LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", offset); |
1944 | } | 3332 | |
3333 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
3334 | /* Do this before posting so that the audio thread can correct us | ||
3335 | when things settle down - additionally, if audio gets a message | ||
3336 | and the delta is zero, the Q_AUDIO_SKIP handler (audio_on_skip) | ||
3337 | handler a skip event with the correct info but doesn't skip */ | ||
3338 | send_event(PLAYBACK_EVENT_TRACK_SKIP, NULL); | ||
3339 | #endif /* AUDIO_FAST_SKIP_PREVIEW */ | ||
3340 | |||
3341 | /* Playback only needs the final state even if more than one is | ||
3342 | processed because it wasn't removed in time */ | ||
3343 | queue_remove_from_head(&audio_queue, Q_AUDIO_SKIP); | ||
3344 | audio_queue_post(Q_AUDIO_SKIP, 0); | ||
1945 | } | 3345 | } |
1946 | send_event(PLAYBACK_EVENT_TRACK_CHANGE, thistrack_id3); | 3346 | else |
1947 | playlist_update_resume_info(audio_current_track()); | 3347 | { |
3348 | /* No more tracks */ | ||
3349 | if (global_settings.beep) | ||
3350 | pcmbuf_beep(1000, 100, 1500*global_settings.beep); | ||
3351 | } | ||
3352 | |||
3353 | id3_mutex_unlock(); | ||
1948 | } | 3354 | } |
1949 | 3355 | ||
1950 | static void audio_seek_complete(void) | 3356 | /* Skip one track forward from the current */ |
3357 | void audio_next(void) | ||
1951 | { | 3358 | { |
1952 | logf("audio_seek_complete"); | 3359 | audio_skip(1); |
3360 | } | ||
1953 | 3361 | ||
1954 | if (!playing) | 3362 | /* Skip one track backward from the current */ |
1955 | return; | 3363 | void audio_prev(void) |
3364 | { | ||
3365 | audio_skip(-1); | ||
3366 | } | ||
1956 | 3367 | ||
1957 | /* If seeking-while-playing, pcm_is_paused() is true. | 3368 | /* Move one directory forward */ |
1958 | * If seeking-while-paused, audio_status PAUSE is true. | 3369 | void audio_next_dir(void) |
1959 | * A seamless seek skips this section. */ | 3370 | { |
1960 | ci.seek_time = 0; | 3371 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP 1"); |
3372 | audio_queue_post(Q_AUDIO_DIR_SKIP, 1); | ||
3373 | } | ||
1961 | 3374 | ||
1962 | pcm_play_lock(); | 3375 | /* Move one directory backward */ |
3376 | void audio_prev_dir(void) | ||
3377 | { | ||
3378 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP -1"); | ||
3379 | audio_queue_post(Q_AUDIO_DIR_SKIP, -1); | ||
3380 | } | ||
1963 | 3381 | ||
1964 | if (pcm_is_paused() || paused) | 3382 | /* Pause playback in order to start a seek that flushes the old audio */ |
1965 | { | 3383 | void audio_pre_ff_rewind(void) |
1966 | /* Clear the buffer */ | 3384 | { |
1967 | pcmbuf_play_stop(); | 3385 | LOGFQUEUE("audio > audio Q_AUDIO_PRE_FF_REWIND"); |
3386 | audio_queue_post(Q_AUDIO_PRE_FF_REWIND, 0); | ||
3387 | } | ||
1968 | 3388 | ||
1969 | /* If seeking-while-playing, resume PCM playback */ | 3389 | /* Seek to the new time in the current track */ |
1970 | if (!paused) | 3390 | void audio_ff_rewind(long time) |
1971 | pcmbuf_pause(false); | 3391 | { |
1972 | } | 3392 | LOGFQUEUE("audio > audio Q_AUDIO_FF_REWIND"); |
3393 | audio_queue_post(Q_AUDIO_FF_REWIND, time); | ||
3394 | } | ||
1973 | 3395 | ||
1974 | pcm_play_unlock(); | 3396 | /* Clear all but the currently playing track then rebuffer */ |
3397 | void audio_flush_and_reload_tracks(void) | ||
3398 | { | ||
3399 | LOGFQUEUE("audio > audio Q_AUDIO_FLUSH"); | ||
3400 | audio_queue_post(Q_AUDIO_FLUSH, 0); | ||
1975 | } | 3401 | } |
1976 | 3402 | ||
1977 | static void audio_codec_status_message(long reason, int status) | 3403 | /* Return the pointer to the main audio buffer, optionally preserving |
3404 | voicing */ | ||
3405 | unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) | ||
1978 | { | 3406 | { |
1979 | /* TODO: Push the errors up to the normal UI somewhere */ | 3407 | unsigned char *buf, *end; |
1980 | switch (reason) | 3408 | |
3409 | if (audio_is_initialized) | ||
1981 | { | 3410 | { |
1982 | case Q_CODEC_LOAD_DISK: | 3411 | audio_hard_stop(); |
1983 | case Q_CODEC_LOAD: | 3412 | } |
1984 | if (!playing) | 3413 | /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */ |
1985 | return; | ||
1986 | 3414 | ||
1987 | if (status < 0) | 3415 | if (buffer_size == NULL) |
3416 | { | ||
3417 | /* Special case for talk_init to use since it already knows it's | ||
3418 | trashed */ | ||
3419 | buffer_state = AUDIOBUF_STATE_TRASHED; | ||
3420 | return NULL; | ||
3421 | } | ||
3422 | |||
3423 | if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED | ||
3424 | || !talk_voice_required()) | ||
3425 | { | ||
3426 | logf("get buffer: talk, audio"); | ||
3427 | /* Ok to use everything from audiobuf to audiobufend - voice is loaded, | ||
3428 | the talk buffer is not needed because voice isn't being used, or | ||
3429 | could be AUDIOBUF_STATE_TRASHED already. If state is | ||
3430 | AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't written | ||
3431 | without the caller knowing what's going on. Changing certain settings | ||
3432 | may move it to a worse condition but the memory in use by something | ||
3433 | else will remain undisturbed. | ||
3434 | */ | ||
3435 | if (buffer_state != AUDIOBUF_STATE_TRASHED) | ||
1988 | { | 3436 | { |
1989 | splash(HZ*2, "Codec failure"); | 3437 | talk_buffer_steal(); |
1990 | audio_check_new_track(); | 3438 | buffer_state = AUDIOBUF_STATE_TRASHED; |
1991 | } | 3439 | } |
1992 | break; | ||
1993 | 3440 | ||
1994 | #ifdef AUDIO_HAVE_RECORDING | 3441 | buf = audiobuf; |
1995 | case Q_ENCODER_LOAD_DISK: | 3442 | end = audiobufend; |
1996 | if (status < 0) | ||
1997 | splash(HZ*2, "Encoder failure"); | ||
1998 | break; | ||
1999 | #endif /* AUDIO_HAVE_RECORDING */ | ||
2000 | } | 3443 | } |
3444 | else | ||
3445 | { | ||
3446 | /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or | ||
3447 | still AUDIOBUF_STATE_INITIALIZED */ | ||
3448 | /* Skip talk buffer and move pcm buffer to end to maximize available | ||
3449 | contiguous memory - no audio running means voice will not need the | ||
3450 | swap space */ | ||
3451 | logf("get buffer: audio"); | ||
3452 | buf = audiobuf + talk_get_bufsize(); | ||
3453 | end = audiobufend - pcmbuf_init(audiobufend); | ||
3454 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | ||
3455 | } | ||
3456 | |||
3457 | *buffer_size = end - buf; | ||
3458 | |||
3459 | return buf; | ||
2001 | } | 3460 | } |
2002 | 3461 | ||
2003 | /* | 3462 | #ifdef HAVE_RECORDING |
2004 | * Layout audio buffer as follows - iram buffer depends on target: | 3463 | /* Stop audio, voice and obtain all available buffer space */ |
2005 | * [|SWAP:iram][|TALK]|FILE|GUARD|PCM|[SWAP:dram[|iram]|] | 3464 | unsigned char * audio_get_recording_buffer(size_t *buffer_size) |
2006 | */ | ||
2007 | static void audio_reset_buffer(void) | ||
2008 | { | 3465 | { |
2009 | /* see audio_get_recording_buffer if this is modified */ | 3466 | audio_hard_stop(); |
2010 | logf("audio_reset_buffer"); | 3467 | talk_buffer_steal(); |
2011 | 3468 | ||
2012 | /* If the setup of anything allocated before the file buffer is | 3469 | unsigned char *end = audiobufend; |
2013 | changed, do check the adjustments after the buffer_alloc call | 3470 | buffer_state = AUDIOBUF_STATE_TRASHED; |
2014 | as it will likely be affected and need sliding over */ | 3471 | *buffer_size = end - audiobuf; |
2015 | 3472 | ||
2016 | /* Initially set up file buffer as all space available */ | 3473 | return (unsigned char *)audiobuf; |
2017 | filebuf = audiobuf + talk_get_bufsize(); | 3474 | } |
2018 | filebuflen = audiobufend - filebuf; | 3475 | #endif /* HAVE_RECORDING */ |
2019 | 3476 | ||
2020 | ALIGN_BUFFER(filebuf, filebuflen, sizeof (intptr_t)); | 3477 | /* Restore audio buffer to a particular state (one more valid than the current |
3478 | state) */ | ||
3479 | bool audio_restore_playback(int type) | ||
3480 | { | ||
3481 | switch (type) | ||
3482 | { | ||
3483 | case AUDIO_WANT_PLAYBACK: | ||
3484 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) | ||
3485 | audio_reset_buffer(); | ||
3486 | return true; | ||
3487 | case AUDIO_WANT_VOICE: | ||
3488 | if (buffer_state == AUDIOBUF_STATE_TRASHED) | ||
3489 | audio_reset_buffer(); | ||
3490 | return true; | ||
3491 | default: | ||
3492 | return false; | ||
3493 | } | ||
3494 | } | ||
2021 | 3495 | ||
2022 | /* Subtract whatever the pcm buffer says it used plus the guard buffer */ | 3496 | /* Has the playback buffer been completely claimed? */ |
2023 | size_t pcmbuf_size = pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE; | 3497 | bool audio_buffer_state_trashed(void) |
3498 | { | ||
3499 | return buffer_state == AUDIOBUF_STATE_TRASHED; | ||
3500 | } | ||
2024 | 3501 | ||
2025 | /* Make sure filebuflen is a pointer sized multiple after adjustment */ | ||
2026 | pcmbuf_size = ALIGN_UP(pcmbuf_size, sizeof (intptr_t)); | ||
2027 | 3502 | ||
2028 | if(pcmbuf_size > filebuflen) | 3503 | /** --- Miscellaneous public interfaces --- **/ |
2029 | panicf("%s(): EOM (%zu > %zu)", __func__, pcmbuf_size, filebuflen); | ||
2030 | 3504 | ||
2031 | filebuflen -= pcmbuf_size; | 3505 | #ifdef HAVE_ALBUMART |
2032 | buffering_reset(filebuf, filebuflen); | 3506 | /* Return which album art handle is current for the user in the given slot */ |
3507 | int playback_current_aa_hid(int slot) | ||
3508 | { | ||
3509 | if ((unsigned)slot < MAX_MULTIPLE_AA) | ||
3510 | { | ||
3511 | struct track_info *info = track_list_user_current(skip_offset); | ||
2033 | 3512 | ||
2034 | /* Clear any references to the file buffer */ | 3513 | if (!info && abs(skip_offset) <= 1) |
2035 | buffer_state = AUDIOBUF_STATE_INITIALIZED; | 3514 | { |
3515 | /* Give the actual position a go */ | ||
3516 | info = track_list_user_current(0); | ||
3517 | } | ||
2036 | 3518 | ||
2037 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | 3519 | if (info) |
2038 | /* Make sure everything adds up - yes, some info is a bit redundant but | 3520 | return info->aa_hid[slot]; |
2039 | aids viewing and the sumation of certain variables should add up to | ||
2040 | the location of others. */ | ||
2041 | { | ||
2042 | size_t pcmbufsize; | ||
2043 | const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize); | ||
2044 | logf("fbuf: %08X", (unsigned)filebuf); | ||
2045 | logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); | ||
2046 | logf("gbuf: %08X", (unsigned)(filebuf + filebuflen)); | ||
2047 | logf("gbufe: %08X", (unsigned)(filebuf + filebuflen + GUARD_BUFSIZE)); | ||
2048 | logf("pcmb: %08X", (unsigned)pcmbuf); | ||
2049 | logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize)); | ||
2050 | } | 3521 | } |
2051 | #endif | 3522 | |
3523 | return ERR_HANDLE_NOT_FOUND; | ||
2052 | } | 3524 | } |
2053 | 3525 | ||
2054 | static void audio_thread(void) | 3526 | /* Find an album art slot that doesn't match the dimensions of another that |
3527 | is already claimed - increment the use count if it is */ | ||
3528 | int playback_claim_aa_slot(struct dim *dim) | ||
2055 | { | 3529 | { |
2056 | struct queue_event ev; | 3530 | int i; |
2057 | |||
2058 | pcm_postinit(); | ||
2059 | |||
2060 | audio_thread_ready = true; | ||
2061 | 3531 | ||
2062 | while (1) | 3532 | /* First try to find a slot already having the size to reuse it since we |
3533 | don't want albumart of the same size buffered multiple times */ | ||
3534 | FOREACH_ALBUMART(i) | ||
2063 | { | 3535 | { |
2064 | switch (filling) { | 3536 | struct albumart_slot *slot = &albumart_slots[i]; |
2065 | case STATE_IDLE: | ||
2066 | queue_wait(&audio_queue, &ev); | ||
2067 | break; | ||
2068 | 3537 | ||
2069 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 3538 | if (slot->dim.width == dim->width && |
2070 | case STATE_USB: | 3539 | slot->dim.height == dim->height) |
2071 | queue_wait(&audio_queue, &ev); | 3540 | { |
2072 | switch (ev.id) { | 3541 | slot->used++; |
2073 | #ifdef AUDIO_HAVE_RECORDING | 3542 | return i; |
2074 | /* Must monitor the encoder message for recording so it can | 3543 | } |
2075 | remove it if we process the insertion before it does. It | 3544 | } |
2076 | cannot simply be removed from under recording however. */ | ||
2077 | case Q_AUDIO_LOAD_ENCODER: | ||
2078 | break; | ||
2079 | #endif | ||
2080 | case SYS_USB_DISCONNECTED: | ||
2081 | filling = STATE_IDLE; | ||
2082 | default: | ||
2083 | continue; | ||
2084 | } | ||
2085 | break; | ||
2086 | #endif /* CONFIG_PLATFORM */ | ||
2087 | 3545 | ||
2088 | default: | 3546 | /* Size is new, find a free slot */ |
2089 | /* End of buffering, let's calculate the watermark and | 3547 | FOREACH_ALBUMART(i) |
2090 | unboost */ | 3548 | { |
2091 | set_filebuf_watermark(); | 3549 | if (!albumart_slots[i].used) |
2092 | cancel_cpu_boost(); | 3550 | { |
2093 | /* Fall-through */ | 3551 | albumart_slots[i].used++; |
2094 | case STATE_FILLING: | 3552 | albumart_slots[i].dim = *dim; |
2095 | case STATE_ENDING: | 3553 | return i; |
2096 | if (!pcmbuf_queue_scan(&ev)) | ||
2097 | queue_wait_w_tmo(&audio_queue, &ev, HZ/2); | ||
2098 | break; | ||
2099 | } | 3554 | } |
3555 | } | ||
2100 | 3556 | ||
2101 | switch (ev.id) { | 3557 | /* Sorry, no free slot */ |
3558 | return -1; | ||
3559 | } | ||
2102 | 3560 | ||
2103 | case Q_AUDIO_FILL_BUFFER: | 3561 | /* Invalidate the albumart_slot - decrement the use count if > 0 */ |
2104 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER %d", (int)ev.data); | 3562 | void playback_release_aa_slot(int slot) |
2105 | audio_fill_file_buffer((bool)ev.data, 0); | 3563 | { |
2106 | break; | 3564 | if ((unsigned)slot < MAX_MULTIPLE_AA) |
3565 | { | ||
3566 | struct albumart_slot *aa_slot = &albumart_slots[slot]; | ||
2107 | 3567 | ||
2108 | case Q_AUDIO_FINISH_LOAD: | 3568 | if (aa_slot->used > 0) |
2109 | LOGFQUEUE("audio < Q_AUDIO_FINISH_LOAD"); | 3569 | aa_slot->used--; |
2110 | audio_finish_load_track(); | 3570 | } |
2111 | buf_set_base_handle(CUR_TI->audio_hid); | 3571 | } |
2112 | break; | 3572 | #endif /* HAVE_ALBUMART */ |
2113 | 3573 | ||
2114 | case Q_AUDIO_PLAY: | ||
2115 | LOGFQUEUE("audio < Q_AUDIO_PLAY"); | ||
2116 | if (playing && ev.data <= 0) | ||
2117 | audio_new_playlist(); | ||
2118 | else | ||
2119 | { | ||
2120 | audio_stop_playback(); | ||
2121 | audio_play_start((size_t)ev.data); | ||
2122 | } | ||
2123 | break; | ||
2124 | 3574 | ||
2125 | case Q_AUDIO_STOP: | 3575 | #ifdef HAVE_RECORDING |
2126 | LOGFQUEUE("audio < Q_AUDIO_STOP"); | 3576 | /* Load an encoder and run it */ |
2127 | if (playing) | 3577 | bool audio_load_encoder(int afmt) |
2128 | audio_stop_playback(); | 3578 | { |
2129 | if (ev.data != 0) | 3579 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
2130 | queue_clear(&audio_queue); | 3580 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: %d", afmt); |
2131 | break; | 3581 | return audio_queue_send(Q_AUDIO_LOAD_ENCODER, afmt) != 0; |
3582 | #else | ||
3583 | (void)afmt; | ||
3584 | return true; | ||
3585 | #endif | ||
3586 | } | ||
2132 | 3587 | ||
2133 | case Q_AUDIO_PAUSE: | 3588 | /* Stop an encoder and unload it */ |
2134 | LOGFQUEUE("audio < Q_AUDIO_PAUSE"); | 3589 | void audio_remove_encoder(void) |
2135 | if (!(bool) ev.data && skipped_during_pause | 3590 | { |
2136 | #ifdef HAVE_CROSSFADE | 3591 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
2137 | && !pcmbuf_is_crossfade_active() | 3592 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: NULL"); |
3593 | audio_queue_send(Q_AUDIO_LOAD_ENCODER, AFMT_UNKNOWN); | ||
2138 | #endif | 3594 | #endif |
2139 | ) | 3595 | } |
2140 | pcmbuf_play_stop(); /* Flush old track on resume after skip */ | 3596 | #endif /* HAVE_RECORDING */ |
2141 | skipped_during_pause = false; | ||
2142 | if (!playing) | ||
2143 | break; | ||
2144 | pcmbuf_pause((bool)ev.data); | ||
2145 | paused = (bool)ev.data; | ||
2146 | break; | ||
2147 | 3597 | ||
2148 | case Q_AUDIO_SKIP: | 3598 | /* Is an automatic skip in progress? If called outside transistion callbacks, |
2149 | LOGFQUEUE("audio < Q_AUDIO_SKIP"); | 3599 | indicates the last skip type at the time it was processed and isn't very |
2150 | audio_initiate_track_change((long)ev.data); | 3600 | meaningful. */ |
2151 | break; | 3601 | bool audio_automatic_skip(void) |
3602 | { | ||
3603 | return automatic_skip; | ||
3604 | } | ||
2152 | 3605 | ||
2153 | case Q_AUDIO_PRE_FF_REWIND: | 3606 | /* Would normally calculate byte offset from an elapsed time but is not |
2154 | LOGFQUEUE("audio < Q_AUDIO_PRE_FF_REWIND"); | 3607 | used on SWCODEC */ |
2155 | if (!playing) | 3608 | int audio_get_file_pos(void) |
2156 | break; | 3609 | { |
2157 | pcmbuf_pause(true); | 3610 | return 0; |
2158 | break; | 3611 | } |
2159 | 3612 | ||
2160 | case Q_AUDIO_FF_REWIND: | 3613 | /* Return the elasped time of the track previous to the current */ |
2161 | LOGFQUEUE("audio < Q_AUDIO_FF_REWIND"); | 3614 | unsigned long audio_prev_elapsed(void) |
2162 | if (!playing) | 3615 | { |
2163 | break; | 3616 | return prev_track_elapsed; |
3617 | } | ||
2164 | 3618 | ||
2165 | if (filling == STATE_ENDING) | 3619 | /* Is the audio thread ready to accept commands? */ |
2166 | { | 3620 | bool audio_is_thread_ready(void) |
2167 | /* Temp workaround: There is no codec available */ | 3621 | { |
2168 | if (!paused) | 3622 | return filling != STATE_BOOT; |
2169 | pcmbuf_pause(false); | 3623 | } |
2170 | break; | ||
2171 | } | ||
2172 | 3624 | ||
2173 | if ((long)ev.data == 0) | 3625 | /* Return total file buffer length after accounting for the talk buf */ |
2174 | { | 3626 | size_t audio_get_filebuflen(void) |
2175 | /* About to restart the track - send track finish | 3627 | { |
2176 | events if not already done. */ | 3628 | return buf_length(); |
2177 | if (thistrack_id3 == audio_current_track()) | 3629 | } |
2178 | send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3); | ||
2179 | } | ||
2180 | 3630 | ||
2181 | if (automatic_skip) | 3631 | /* How many tracks exist on the buffer - full or partial */ |
2182 | { | 3632 | int audio_track_count(void) |
2183 | /* An automatic track skip is in progress. Finalize it, | 3633 | __attribute__((alias("track_list_count"))); |
2184 | then go back to the previous track */ | ||
2185 | audio_finalise_track_change(); | ||
2186 | ci.new_track = -1; | ||
2187 | } | ||
2188 | ci.seek_time = (long)ev.data+1; | ||
2189 | break; | ||
2190 | 3634 | ||
2191 | case Q_AUDIO_CHECK_NEW_TRACK: | 3635 | /* Return total ringbuffer space occupied - ridx to widx */ |
2192 | LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK"); | 3636 | long audio_filebufused(void) |
2193 | audio_check_new_track(); | 3637 | { |
2194 | break; | 3638 | return buf_used(); |
3639 | } | ||
2195 | 3640 | ||
2196 | case Q_AUDIO_DIR_SKIP: | ||
2197 | LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP"); | ||
2198 | audio_initiate_dir_change(ev.data); | ||
2199 | break; | ||
2200 | 3641 | ||
2201 | case Q_AUDIO_FLUSH: | 3642 | /** -- Settings -- **/ |
2202 | LOGFQUEUE("audio < Q_AUDIO_FLUSH"); | ||
2203 | audio_invalidate_tracks(); | ||
2204 | break; | ||
2205 | 3643 | ||
2206 | case Q_AUDIO_TRACK_CHANGED: | 3644 | /* Enable or disable cuesheet support and allocate/don't allocate the |
2207 | /* PCM track change done */ | 3645 | extra associated resources */ |
2208 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); | 3646 | void audio_set_cuesheet(int enable) |
2209 | /* Set new playlist position for resuming. */ | 3647 | { |
2210 | playlist_update_resume_index(); | 3648 | if (play_status == PLAY_STOPPED || !enable != !get_current_cuesheet()) |
2211 | if (filling != STATE_ENDING) | 3649 | { |
2212 | audio_finalise_track_change(); | 3650 | LOGFQUEUE("audio >| audio Q_AUDIO_REMAKE_AUDIO_BUFFER"); |
2213 | else if (playing) | 3651 | audio_queue_send(Q_AUDIO_REMAKE_AUDIO_BUFFER, 0); |
2214 | audio_stop_playback(); | 3652 | } |
2215 | break; | 3653 | } |
2216 | 3654 | ||
2217 | case Q_AUDIO_SEEK_COMPLETE: | 3655 | #ifdef HAVE_DISK_STORAGE |
2218 | /* Codec seek done */ | 3656 | /* Set the audio antiskip buffer margin by index */ |
2219 | LOGFQUEUE("audio < Q_AUDIO_SEEK_COMPLETE"); | 3657 | void audio_set_buffer_margin(int setting) |
2220 | audio_seek_complete(); | 3658 | { |
2221 | codec_ack_msg(Q_AUDIO_SEEK_COMPLETE, false); | 3659 | static const unsigned short lookup[] = |
2222 | break; | 3660 | { 5, 15, 30, 60, 120, 180, 300, 600 }; |
2223 | 3661 | ||
2224 | case Q_CODEC_LOAD: | 3662 | if ((unsigned)setting >= ARRAYLEN(lookup)) |
2225 | case Q_CODEC_LOAD_DISK: | 3663 | setting = 0; |
2226 | #ifdef AUDIO_HAVE_RECORDING | ||
2227 | case Q_ENCODER_LOAD_DISK: | ||
2228 | #endif | ||
2229 | /* These are received when a codec has finished normally or | ||
2230 | upon a codec error */ | ||
2231 | audio_codec_status_message(ev.id, ev.data); | ||
2232 | break; | ||
2233 | 3664 | ||
2234 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 3665 | logf("buffer margin: %u", (unsigned)lookup[setting]); |
2235 | case SYS_USB_CONNECTED: | ||
2236 | LOGFQUEUE("audio < SYS_USB_CONNECTED"); | ||
2237 | if (playing) | ||
2238 | audio_stop_playback(); | ||
2239 | #ifdef PLAYBACK_VOICE | ||
2240 | voice_stop(); | ||
2241 | #endif | ||
2242 | filling = STATE_USB; | ||
2243 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
2244 | break; | ||
2245 | #endif | ||
2246 | 3666 | ||
2247 | #ifdef AUDIO_HAVE_RECORDING | 3667 | LOGFQUEUE("audio > audio Q_AUDIO_UPDATE_WATERMARK: %u", |
2248 | case Q_AUDIO_LOAD_ENCODER: | 3668 | (unsigned)lookup[setting]); |
2249 | if (playing) | 3669 | audio_queue_post(Q_AUDIO_UPDATE_WATERMARK, lookup[setting]); |
2250 | audio_stop_playback(); | 3670 | } |
2251 | else | 3671 | #endif /* HAVE_DISK_STORAGE */ |
2252 | codec_stop(); /* If encoder still loaded, stop it */ | ||
2253 | 3672 | ||
2254 | if (ev.data == AFMT_UNKNOWN) | 3673 | #ifdef HAVE_CROSSFADE |
2255 | break; | 3674 | /* Take necessary steps to enable or disable the crossfade setting */ |
3675 | void audio_set_crossfade(int enable) | ||
3676 | { | ||
3677 | /* Tell it the next setting to use */ | ||
3678 | pcmbuf_request_crossfade_enable(enable); | ||
2256 | 3679 | ||
2257 | queue_reply(&audio_queue, | 3680 | /* Return if size hasn't changed or this is too early to determine |
2258 | codec_load(-1, ev.data | CODEC_TYPE_ENCODER)); | 3681 | which in the second case there's no way we could be playing |
2259 | break; | 3682 | anything at all */ |
2260 | #endif /* AUDIO_HAVE_RECORDING */ | 3683 | if (!pcmbuf_is_same_size()) |
3684 | { | ||
3685 | LOGFQUEUE("audio >| audio Q_AUDIO_REMAKE_AUDIO_BUFFER"); | ||
3686 | audio_queue_send(Q_AUDIO_REMAKE_AUDIO_BUFFER, 0); | ||
3687 | } | ||
3688 | } | ||
3689 | #endif /* HAVE_CROSSFADE */ | ||
2261 | 3690 | ||
2262 | case SYS_TIMEOUT: | ||
2263 | LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); | ||
2264 | break; | ||
2265 | 3691 | ||
2266 | default: | 3692 | /** -- Startup -- **/ |
2267 | /* LOGFQUEUE("audio < default : %08lX", ev.id); */ | ||
2268 | break; | ||
2269 | } /* end switch */ | ||
2270 | } /* end while */ | ||
2271 | } | ||
2272 | 3693 | ||
2273 | /* Initialize the audio system - called from init() in main.c. | 3694 | /* Initialize the audio system - called from init() in main.c */ |
2274 | * Last function because of all the references to internal symbols | ||
2275 | */ | ||
2276 | void audio_init(void) | 3695 | void audio_init(void) |
2277 | { | 3696 | { |
2278 | unsigned int audio_thread_id; | ||
2279 | |||
2280 | /* Can never do this twice */ | 3697 | /* Can never do this twice */ |
2281 | if (audio_is_initialized) | 3698 | if (audio_is_initialized) |
2282 | { | 3699 | { |
@@ -2290,31 +3707,20 @@ void audio_init(void) | |||
2290 | to send messages. Thread creation will be delayed however so nothing | 3707 | to send messages. Thread creation will be delayed however so nothing |
2291 | starts running until ready if something yields such as talk_init. */ | 3708 | starts running until ready if something yields such as talk_init. */ |
2292 | queue_init(&audio_queue, true); | 3709 | queue_init(&audio_queue, true); |
2293 | queue_init(&pcmbuf_queue, false); | 3710 | |
3711 | mutex_init(&id3_mutex); | ||
2294 | 3712 | ||
2295 | pcm_init(); | 3713 | pcm_init(); |
2296 | 3714 | ||
2297 | codec_init_codec_api(); | 3715 | codec_init_codec_api(); |
2298 | 3716 | ||
2299 | thistrack_id3 = &mp3entry_buf[0]; | ||
2300 | othertrack_id3 = &mp3entry_buf[1]; | ||
2301 | |||
2302 | /* cuesheet support */ | ||
2303 | if (global_settings.cuesheet) | ||
2304 | curr_cue = (struct cuesheet*)buffer_alloc(sizeof(struct cuesheet)); | ||
2305 | |||
2306 | /* initialize the buffer */ | ||
2307 | filebuf = audiobuf; | ||
2308 | |||
2309 | /* audio_reset_buffer must to know the size of voice buffer so init | ||
2310 | talk first */ | ||
2311 | talk_init(); | ||
2312 | |||
2313 | make_codec_thread(); | 3717 | make_codec_thread(); |
2314 | 3718 | ||
3719 | /* This thread does buffer, so match its priority */ | ||
2315 | audio_thread_id = create_thread(audio_thread, audio_stack, | 3720 | audio_thread_id = create_thread(audio_thread, audio_stack, |
2316 | sizeof(audio_stack), CREATE_THREAD_FROZEN, | 3721 | sizeof(audio_stack), CREATE_THREAD_FROZEN, |
2317 | audio_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) | 3722 | audio_thread_name |
3723 | IF_PRIO(, MIN(PRIORITY_BUFFERING, PRIORITY_USER_INTERFACE)) | ||
2318 | IF_COP(, CPU)); | 3724 | IF_COP(, CPU)); |
2319 | 3725 | ||
2320 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list, | 3726 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list, |
@@ -2324,39 +3730,21 @@ void audio_init(void) | |||
2324 | voice_thread_init(); | 3730 | voice_thread_init(); |
2325 | #endif | 3731 | #endif |
2326 | 3732 | ||
3733 | /* audio_reset_buffer must to know the size of voice buffer so init | ||
3734 | talk first */ | ||
3735 | talk_init(); | ||
3736 | |||
2327 | #ifdef HAVE_CROSSFADE | 3737 | #ifdef HAVE_CROSSFADE |
2328 | /* Set crossfade setting for next buffer init which should be about... */ | 3738 | /* Set crossfade setting for next buffer init which should be about... */ |
2329 | pcmbuf_request_crossfade_enable(global_settings.crossfade); | 3739 | pcmbuf_request_crossfade_enable(global_settings.crossfade); |
2330 | #endif | 3740 | #endif |
2331 | 3741 | ||
2332 | /* initialize the buffering system */ | 3742 | /* Initialize the buffering system */ |
2333 | 3743 | track_list_init(); | |
2334 | buffering_init(); | 3744 | buffering_init(); |
2335 | /* ...now! Set up the buffers */ | 3745 | /* ...now! Set up the buffers */ |
2336 | audio_reset_buffer(); | 3746 | audio_reset_buffer(); |
2337 | 3747 | ||
2338 | int i; | ||
2339 | for(i = 0; i < MAX_TRACK; i++) | ||
2340 | { | ||
2341 | tracks[i].audio_hid = -1; | ||
2342 | tracks[i].id3_hid = -1; | ||
2343 | tracks[i].codec_hid = -1; | ||
2344 | tracks[i].cuesheet_hid = -1; | ||
2345 | } | ||
2346 | #ifdef HAVE_ALBUMART | ||
2347 | FOREACH_ALBUMART(i) | ||
2348 | { | ||
2349 | int j; | ||
2350 | for (j = 0; j < MAX_TRACK; j++) | ||
2351 | { | ||
2352 | tracks[j].aa_hid[i] = -1; | ||
2353 | } | ||
2354 | } | ||
2355 | #endif | ||
2356 | |||
2357 | add_event(BUFFER_EVENT_REBUFFER, false, buffering_handle_rebuffer_callback); | ||
2358 | add_event(BUFFER_EVENT_FINISHED, false, buffering_handle_finished_callback); | ||
2359 | |||
2360 | /* Probably safe to say */ | 3748 | /* Probably safe to say */ |
2361 | audio_is_initialized = true; | 3749 | audio_is_initialized = true; |
2362 | 3750 | ||
@@ -2365,26 +3753,10 @@ void audio_init(void) | |||
2365 | audio_set_buffer_margin(global_settings.buffer_margin); | 3753 | audio_set_buffer_margin(global_settings.buffer_margin); |
2366 | #endif | 3754 | #endif |
2367 | 3755 | ||
2368 | /* it's safe to let the threads run now */ | 3756 | /* It's safe to let the threads run now */ |
2369 | #ifdef PLAYBACK_VOICE | 3757 | #ifdef PLAYBACK_VOICE |
2370 | voice_thread_resume(); | 3758 | voice_thread_resume(); |
2371 | #endif | 3759 | #endif |
2372 | codec_thread_resume(); | 3760 | codec_thread_resume(); |
2373 | thread_thaw(audio_thread_id); | 3761 | thread_thaw(audio_thread_id); |
2374 | |||
2375 | } /* audio_init */ | ||
2376 | |||
2377 | bool audio_is_thread_ready(void) | ||
2378 | { | ||
2379 | return audio_thread_ready; | ||
2380 | } | ||
2381 | |||
2382 | size_t audio_get_filebuflen(void) | ||
2383 | { | ||
2384 | return filebuflen; | ||
2385 | } | ||
2386 | |||
2387 | int get_audio_hid() | ||
2388 | { | ||
2389 | return CUR_TI->audio_hid; | ||
2390 | } | 3762 | } |