diff options
Diffstat (limited to 'apps/playback.c')
-rw-r--r-- | apps/playback.c | 1291 |
1 files changed, 383 insertions, 908 deletions
diff --git a/apps/playback.c b/apps/playback.c index 03bbb9ddd2..0cda680c0b 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include "settings.h" | 46 | #include "settings.h" |
47 | #include "codecs.h" | 47 | #include "codecs.h" |
48 | #include "audio.h" | 48 | #include "audio.h" |
49 | #include "buffering.h" | ||
49 | #include "mp3_playback.h" | 50 | #include "mp3_playback.h" |
50 | #include "usb.h" | 51 | #include "usb.h" |
51 | #include "status.h" | 52 | #include "status.h" |
@@ -91,8 +92,6 @@ | |||
91 | #define AUDIO_DEFAULT_WATERMARK (1024*512) | 92 | #define AUDIO_DEFAULT_WATERMARK (1024*512) |
92 | /* amount of data to read in one read() call */ | 93 | /* amount of data to read in one read() call */ |
93 | #define AUDIO_DEFAULT_FILECHUNK (1024*32) | 94 | #define AUDIO_DEFAULT_FILECHUNK (1024*32) |
94 | /* point at which the file buffer will fight for CPU time */ | ||
95 | #define AUDIO_FILEBUF_CRITICAL (1024*128) | ||
96 | /* amount of guess-space to allow for codecs that must hunt and peck | 95 | /* amount of guess-space to allow for codecs that must hunt and peck |
97 | * for their correct seeek target, 32k seems a good size */ | 96 | * for their correct seeek target, 32k seems a good size */ |
98 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) | 97 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) |
@@ -135,16 +134,12 @@ enum { | |||
135 | Q_AUDIO_SKIP, | 134 | Q_AUDIO_SKIP, |
136 | Q_AUDIO_PRE_FF_REWIND, | 135 | Q_AUDIO_PRE_FF_REWIND, |
137 | Q_AUDIO_FF_REWIND, | 136 | Q_AUDIO_FF_REWIND, |
138 | Q_AUDIO_REBUFFER_SEEK, | ||
139 | Q_AUDIO_CHECK_NEW_TRACK, | 137 | Q_AUDIO_CHECK_NEW_TRACK, |
140 | Q_AUDIO_FLUSH, | 138 | Q_AUDIO_FLUSH, |
141 | Q_AUDIO_TRACK_CHANGED, | 139 | Q_AUDIO_TRACK_CHANGED, |
142 | Q_AUDIO_DIR_SKIP, | 140 | Q_AUDIO_DIR_SKIP, |
143 | Q_AUDIO_POSTINIT, | 141 | Q_AUDIO_POSTINIT, |
144 | Q_AUDIO_FILL_BUFFER, | 142 | Q_AUDIO_FILL_BUFFER, |
145 | #if MEM > 8 | ||
146 | Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA, | ||
147 | #endif | ||
148 | Q_CODEC_REQUEST_COMPLETE, | 143 | Q_CODEC_REQUEST_COMPLETE, |
149 | Q_CODEC_REQUEST_FAILED, | 144 | Q_CODEC_REQUEST_FAILED, |
150 | 145 | ||
@@ -196,7 +191,6 @@ bool audio_is_initialized = false; | |||
196 | static volatile bool audio_codec_loaded NOCACHEBSS_ATTR = false; /* Codec loaded? (C/A-) */ | 191 | static volatile bool audio_codec_loaded NOCACHEBSS_ATTR = false; /* Codec loaded? (C/A-) */ |
197 | static volatile bool playing NOCACHEBSS_ATTR = false; /* Is audio playing? (A) */ | 192 | static volatile bool playing NOCACHEBSS_ATTR = false; /* Is audio playing? (A) */ |
198 | static volatile bool paused NOCACHEBSS_ATTR = false; /* Is audio paused? (A/C-) */ | 193 | static volatile bool paused NOCACHEBSS_ATTR = false; /* Is audio paused? (A/C-) */ |
199 | static volatile bool filling IDATA_ATTR = false; /* Is file buffer refilling? (A/C-) */ | ||
200 | 194 | ||
201 | /* Ring buffer where compressed audio and codecs are loaded */ | 195 | /* Ring buffer where compressed audio and codecs are loaded */ |
202 | static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ | 196 | static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ |
@@ -204,8 +198,6 @@ static unsigned char *malloc_buf = NULL; /* Start of malloc buffer (A/C-) */ | |||
204 | /* FIXME: make filebuflen static */ | 198 | /* FIXME: make filebuflen static */ |
205 | size_t filebuflen = 0; /* Size of buffer (A/C-) */ | 199 | size_t filebuflen = 0; /* Size of buffer (A/C-) */ |
206 | /* FIXME: make buf_ridx (C/A-) */ | 200 | /* FIXME: make buf_ridx (C/A-) */ |
207 | static volatile size_t buf_ridx IDATA_ATTR = 0; /* Buffer read position (A/C)*/ | ||
208 | static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-) */ | ||
209 | 201 | ||
210 | /* Possible arrangements of the buffer */ | 202 | /* Possible arrangements of the buffer */ |
211 | #define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ | 203 | #define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ |
@@ -213,29 +205,18 @@ static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-) | |||
213 | #define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */ | 205 | #define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */ |
214 | static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ | 206 | static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ |
215 | 207 | ||
216 | /* Compressed ring buffer helper macros */ | 208 | static struct mp3entry prevtrack_id3; |
217 | /* Buffer pointer (p) plus value (v), wrapped if necessary */ | 209 | static struct mp3entry curtrack_id3; |
218 | #define RINGBUF_ADD(p,v) ((p+v)<filebuflen ? p+v : p+v-filebuflen) | 210 | static struct mp3entry nexttrack_id3; |
219 | /* Buffer pointer (p) minus value (v), wrapped if necessary */ | ||
220 | #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+filebuflen-v) | ||
221 | /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ | ||
222 | #define RINGBUF_ADD_CROSS(p1,v,p2) \ | ||
223 | ((p1<p2)?(int)(p1+v)-(int)p2:(int)(p1+v-p2)-(int)filebuflen) | ||
224 | /* Bytes available in the buffer */ | ||
225 | #define FILEBUFUSED RINGBUF_SUB(buf_widx, buf_ridx) | ||
226 | 211 | ||
227 | /* Track info structure about songs in the file buffer (A/C-) */ | 212 | /* Track info structure about songs in the file buffer (A/C-) */ |
228 | struct track_info { | 213 | struct track_info { |
229 | struct mp3entry id3; /* TAG metadata */ | 214 | int audio_hid; /* The ID for the track's buffer handle */ |
230 | char *codecbuf; /* Pointer to codec buffer */ | 215 | int id3_hid; /* The ID for the track's metadata handle */ |
231 | size_t codecsize; /* Codec length in bytes */ | 216 | int codec_hid; /* The ID for the track's codec handle */ |
232 | bool has_codec; /* Does this track have a codec on the buffer */ | ||
233 | 217 | ||
234 | size_t buf_idx; /* Pointer to the track's buffer */ | 218 | size_t codecsize; /* Codec length in bytes */ |
235 | size_t filerem; /* Remaining bytes of file NOT in buffer */ | ||
236 | size_t filesize; /* File total length */ | 219 | size_t filesize; /* File total length */ |
237 | size_t start_pos; /* Position to first bytes of file in buffer */ | ||
238 | volatile size_t available; /* Available bytes to read from buffer */ | ||
239 | 220 | ||
240 | bool taginfo_ready; /* Is metadata read */ | 221 | bool taginfo_ready; /* Is metadata read */ |
241 | 222 | ||
@@ -246,7 +227,6 @@ static struct track_info tracks[MAX_TRACK]; | |||
246 | static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ | 227 | static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ |
247 | static int track_widx = 0; /* Track being buffered (A) */ | 228 | static int track_widx = 0; /* Track being buffered (A) */ |
248 | 229 | ||
249 | static struct track_info *prev_ti = NULL; /* Previous track info pointer (A/C-) */ | ||
250 | #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ | 230 | #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ |
251 | 231 | ||
252 | /* Set by the audio thread when the current track information has updated | 232 | /* Set by the audio thread when the current track information has updated |
@@ -256,8 +236,6 @@ static bool track_changed = false; | |||
256 | /* Information used only for filling the buffer */ | 236 | /* Information used only for filling the buffer */ |
257 | /* Playlist steps from playing track to next track to be buffered (A) */ | 237 | /* Playlist steps from playing track to next track to be buffered (A) */ |
258 | static int last_peek_offset = 0; | 238 | static int last_peek_offset = 0; |
259 | /* Partially loaded track file handle to continue buffering (A) */ | ||
260 | static int current_fd = -1; | ||
261 | 239 | ||
262 | /* Scrobbler support */ | 240 | /* Scrobbler support */ |
263 | static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ | 241 | static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ |
@@ -278,19 +256,12 @@ void (*track_buffer_callback)(struct mp3entry *id3, bool last_track) = NULL; | |||
278 | /* When a track's buffer has been overwritten or cleared */ | 256 | /* When a track's buffer has been overwritten or cleared */ |
279 | void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track) = NULL; | 257 | void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track) = NULL; |
280 | 258 | ||
281 | /* Configuration */ | ||
282 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill (A/C) FIXME */ | ||
283 | static size_t conf_filechunk = 0; /* Largest chunk the codec accepts (A/C) FIXME */ | ||
284 | static size_t conf_preseek = 0; /* Codec pre-seek margin (A/C) FIXME */ | ||
285 | static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ | 259 | static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ |
286 | #if MEM > 8 | ||
287 | static size_t high_watermark = 0; /* High watermark for rebuffer (A/V/other) */ | ||
288 | #endif | ||
289 | 260 | ||
290 | /* Multiple threads */ | 261 | /* Multiple threads */ |
291 | static void set_current_codec(int codec_idx); | 262 | static void set_current_codec(int codec_idx); |
292 | /* Set the watermark to trigger buffer fill (A/C) FIXME */ | 263 | /* Set the watermark to trigger buffer fill (A/C) FIXME */ |
293 | static void set_filebuf_watermark(int seconds); | 264 | static void set_filebuf_watermark(int seconds, size_t max); |
294 | 265 | ||
295 | /* Audio thread */ | 266 | /* Audio thread */ |
296 | static struct event_queue audio_queue NOCACHEBSS_ATTR; | 267 | static struct event_queue audio_queue NOCACHEBSS_ATTR; |
@@ -306,6 +277,7 @@ static void audio_reset_buffer(void); | |||
306 | /* Codec thread */ | 277 | /* Codec thread */ |
307 | extern struct codec_api ci; | 278 | extern struct codec_api ci; |
308 | static struct event_queue codec_queue NOCACHEBSS_ATTR; | 279 | static struct event_queue codec_queue NOCACHEBSS_ATTR; |
280 | static struct queue_sender_list codec_queue_sender_list; | ||
309 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] | 281 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] |
310 | IBSS_ATTR; | 282 | IBSS_ATTR; |
311 | static const char codec_thread_name[] = "codec"; | 283 | static const char codec_thread_name[] = "codec"; |
@@ -372,6 +344,73 @@ static void voice_stop(void); | |||
372 | 344 | ||
373 | #endif /* PLAYBACK_VOICE */ | 345 | #endif /* PLAYBACK_VOICE */ |
374 | 346 | ||
347 | |||
348 | /* --- Helper functions --- */ | ||
349 | |||
350 | struct mp3entry *bufgetid3(int handle_id) | ||
351 | { | ||
352 | if (handle_id < 0) | ||
353 | return NULL; | ||
354 | |||
355 | struct mp3entry *id3; | ||
356 | ssize_t ret = bufgetdata(handle_id, 0, (void *)&id3); | ||
357 | |||
358 | if (ret < 0 || ret != sizeof(struct mp3entry)) | ||
359 | return NULL; | ||
360 | |||
361 | return id3; | ||
362 | } | ||
363 | |||
364 | void *bufgetcodec(struct track_info *track) | ||
365 | { | ||
366 | void *ptr; | ||
367 | ssize_t ret = bufgetdata(track->codec_hid, track->codecsize, &ptr); | ||
368 | |||
369 | if (ret == -2) { | ||
370 | buf_request_buffer_handle(CUR_TI->audio_hid); | ||
371 | } | ||
372 | |||
373 | while (ret == -2) { | ||
374 | sleep(1); | ||
375 | ret = bufgetdata(track->codec_hid, track->codecsize, &ptr); | ||
376 | } | ||
377 | |||
378 | if (ret < 0) | ||
379 | return NULL; | ||
380 | else | ||
381 | return ptr; | ||
382 | } | ||
383 | |||
384 | bool clear_track_info(struct track_info *track) | ||
385 | { | ||
386 | if (!track) | ||
387 | return false; | ||
388 | |||
389 | if (track->codec_hid > 0) { | ||
390 | if (bufclose(track->codec_hid)) | ||
391 | track->codec_hid = 0; | ||
392 | else | ||
393 | return false; | ||
394 | } | ||
395 | |||
396 | if (track->id3_hid > 0) { | ||
397 | if (bufclose(track->id3_hid)) | ||
398 | track->id3_hid = 0; | ||
399 | else | ||
400 | return false; | ||
401 | } | ||
402 | |||
403 | if (track->audio_hid > 0) { | ||
404 | if (bufclose(track->audio_hid)) | ||
405 | track->audio_hid = 0; | ||
406 | else | ||
407 | return false; | ||
408 | } | ||
409 | |||
410 | memset(track, 0, sizeof(struct track_info)); | ||
411 | return true; | ||
412 | } | ||
413 | |||
375 | /* --- External interfaces --- */ | 414 | /* --- External interfaces --- */ |
376 | 415 | ||
377 | void mp3_play_data(const unsigned char* start, int size, | 416 | void mp3_play_data(const unsigned char* start, int size, |
@@ -620,8 +659,12 @@ struct mp3entry* audio_current_track(void) | |||
620 | cur_idx = track_ridx + offset; | 659 | cur_idx = track_ridx + offset; |
621 | cur_idx &= MAX_TRACK_MASK; | 660 | cur_idx &= MAX_TRACK_MASK; |
622 | 661 | ||
623 | if (tracks[cur_idx].taginfo_ready) | 662 | if (cur_idx == track_ridx && *curtrack_id3.path) |
624 | return &tracks[cur_idx].id3; | 663 | return &curtrack_id3; |
664 | else if (offset == -1 && *prevtrack_id3.path) | ||
665 | return &prevtrack_id3; | ||
666 | else if (tracks[cur_idx].id3_hid > 0) | ||
667 | return bufgetid3(tracks[cur_idx].id3_hid); | ||
625 | 668 | ||
626 | memset(&temp_id3, 0, sizeof(struct mp3entry)); | 669 | memset(&temp_id3, 0, sizeof(struct mp3entry)); |
627 | 670 | ||
@@ -653,13 +696,16 @@ struct mp3entry* audio_next_track(void) | |||
653 | if (!audio_have_tracks()) | 696 | if (!audio_have_tracks()) |
654 | return NULL; | 697 | return NULL; |
655 | 698 | ||
699 | if (wps_offset == -1 && *prevtrack_id3.path) | ||
700 | return &curtrack_id3; | ||
701 | |||
656 | next_idx++; | 702 | next_idx++; |
657 | next_idx &= MAX_TRACK_MASK; | 703 | next_idx &= MAX_TRACK_MASK; |
658 | 704 | ||
659 | if (!tracks[next_idx].taginfo_ready) | 705 | if (tracks[next_idx].id3_hid <= 0) |
660 | return NULL; | 706 | return NULL; |
661 | 707 | ||
662 | return &tracks[next_idx].id3; | 708 | return &nexttrack_id3; |
663 | } | 709 | } |
664 | 710 | ||
665 | bool audio_has_changed_track(void) | 711 | bool audio_has_changed_track(void) |
@@ -818,8 +864,8 @@ void audio_set_buffer_margin(int setting) | |||
818 | { | 864 | { |
819 | static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; | 865 | static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; |
820 | buffer_margin = lookup[setting]; | 866 | buffer_margin = lookup[setting]; |
821 | logf("buffer margin: %ld", buffer_margin); | 867 | logf("buffer margin: %d", buffer_margin); |
822 | set_filebuf_watermark(buffer_margin); | 868 | set_filebuf_watermark(buffer_margin, 0); |
823 | } | 869 | } |
824 | #endif | 870 | #endif |
825 | 871 | ||
@@ -851,7 +897,7 @@ void audio_set_crossfade(int enable) | |||
851 | if (was_playing) | 897 | if (was_playing) |
852 | { | 898 | { |
853 | /* Store the track resume position */ | 899 | /* Store the track resume position */ |
854 | offset = CUR_TI->id3.offset; | 900 | offset = curtrack_id3.offset; |
855 | gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK)); | 901 | gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK)); |
856 | } | 902 | } |
857 | 903 | ||
@@ -997,16 +1043,16 @@ void voice_wait(void) | |||
997 | #endif | 1043 | #endif |
998 | } | 1044 | } |
999 | 1045 | ||
1000 | static void set_filebuf_watermark(int seconds) | 1046 | static void set_filebuf_watermark(int seconds, size_t max) |
1001 | { | 1047 | { |
1002 | size_t bytes; | 1048 | size_t bytes; |
1003 | 1049 | ||
1004 | if (!filebuf) | 1050 | if (!filebuf) |
1005 | return; /* Audio buffers not yet set up */ | 1051 | return; /* Audio buffers not yet set up */ |
1006 | 1052 | ||
1007 | bytes = MAX(CUR_TI->id3.bitrate * seconds * (1000/8), conf_watermark); | 1053 | bytes = MAX(curtrack_id3.bitrate * seconds * (1000/8), max); |
1008 | bytes = MIN(bytes, filebuflen / 2); | 1054 | bytes = MIN(bytes, filebuflen / 2); |
1009 | conf_watermark = bytes; | 1055 | buf_set_conf(BUFFERING_SET_WATERMARK, bytes); |
1010 | } | 1056 | } |
1011 | 1057 | ||
1012 | const char * get_codec_filename(int cod_spec) | 1058 | const char * get_codec_filename(int cod_spec) |
@@ -1422,15 +1468,15 @@ static void codec_pcmbuf_position_callback(size_t size) | |||
1422 | { | 1468 | { |
1423 | /* This is called from an ISR, so be quick */ | 1469 | /* This is called from an ISR, so be quick */ |
1424 | unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY + | 1470 | unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY + |
1425 | prev_ti->id3.elapsed; | 1471 | prevtrack_id3.elapsed; |
1426 | 1472 | ||
1427 | if (time >= prev_ti->id3.length) | 1473 | if (time >= prevtrack_id3.length) |
1428 | { | 1474 | { |
1429 | pcmbuf_set_position_callback(NULL); | 1475 | pcmbuf_set_position_callback(NULL); |
1430 | prev_ti->id3.elapsed = prev_ti->id3.length; | 1476 | prevtrack_id3.elapsed = prevtrack_id3.length; |
1431 | } | 1477 | } |
1432 | else | 1478 | else |
1433 | prev_ti->id3.elapsed = time; | 1479 | prevtrack_id3.elapsed = time; |
1434 | } | 1480 | } |
1435 | 1481 | ||
1436 | static void codec_set_elapsed_callback(unsigned int value) | 1482 | static void codec_set_elapsed_callback(unsigned int value) |
@@ -1445,11 +1491,11 @@ static void codec_set_elapsed_callback(unsigned int value) | |||
1445 | 1491 | ||
1446 | latency = pcmbuf_get_latency(); | 1492 | latency = pcmbuf_get_latency(); |
1447 | if (value < latency) | 1493 | if (value < latency) |
1448 | CUR_TI->id3.elapsed = 0; | 1494 | curtrack_id3.elapsed = 0; |
1449 | else if (value - latency > CUR_TI->id3.elapsed || | 1495 | else if (value - latency > curtrack_id3.elapsed || |
1450 | value - latency < CUR_TI->id3.elapsed - 2) | 1496 | value - latency < curtrack_id3.elapsed - 2) |
1451 | { | 1497 | { |
1452 | CUR_TI->id3.elapsed = value - latency; | 1498 | curtrack_id3.elapsed = value - latency; |
1453 | } | 1499 | } |
1454 | } | 1500 | } |
1455 | 1501 | ||
@@ -1460,68 +1506,48 @@ static void codec_set_offset_callback(size_t value) | |||
1460 | if (ci.seek_time) | 1506 | if (ci.seek_time) |
1461 | return; | 1507 | return; |
1462 | 1508 | ||
1463 | latency = pcmbuf_get_latency() * CUR_TI->id3.bitrate / 8; | 1509 | latency = pcmbuf_get_latency() * curtrack_id3.bitrate / 8; |
1464 | if (value < latency) | 1510 | if (value < latency) |
1465 | CUR_TI->id3.offset = 0; | 1511 | curtrack_id3.offset = 0; |
1466 | else | 1512 | else |
1467 | CUR_TI->id3.offset = value - latency; | 1513 | curtrack_id3.offset = value - latency; |
1468 | } | 1514 | } |
1469 | 1515 | ||
1470 | static void codec_advance_buffer_counters(size_t amount) | 1516 | static void codec_advance_buffer_counters(size_t amount) |
1471 | { | 1517 | { |
1472 | buf_ridx = RINGBUF_ADD(buf_ridx, amount); | 1518 | bufadvance(CUR_TI->audio_hid, amount); |
1473 | ci.curpos += amount; | 1519 | ci.curpos += amount; |
1474 | CUR_TI->available -= amount; | ||
1475 | |||
1476 | /* Start buffer filling as necessary. */ | ||
1477 | if (!pcmbuf_is_lowdata() && !filling) | ||
1478 | { | ||
1479 | if (FILEBUFUSED < conf_watermark && playing && !playlist_end) | ||
1480 | { | ||
1481 | LOGFQUEUE("codec > audio Q_AUDIO_FILL_BUFFER"); | ||
1482 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | ||
1483 | } | ||
1484 | } | ||
1485 | } | 1520 | } |
1486 | 1521 | ||
1487 | /* copy up-to size bytes into ptr and return the actual size copied */ | 1522 | /* copy up-to size bytes into ptr and return the actual size copied */ |
1488 | static size_t codec_filebuf_callback(void *ptr, size_t size) | 1523 | static size_t codec_filebuf_callback(void *ptr, size_t size) |
1489 | { | 1524 | { |
1490 | char *buf = (char *)ptr; | 1525 | ssize_t copy_n; |
1491 | size_t copy_n; | ||
1492 | size_t part_n; | ||
1493 | 1526 | ||
1494 | if (ci.stop_codec || !playing) | 1527 | if (ci.stop_codec || !playing) |
1495 | return 0; | 1528 | return 0; |
1496 | 1529 | ||
1497 | /* The ammount to copy is the lesser of the requested amount and the | 1530 | copy_n = bufread(CUR_TI->audio_hid, size, ptr); |
1498 | * amount left of the current track (both on disk and already loaded) */ | ||
1499 | copy_n = MIN(size, CUR_TI->available + CUR_TI->filerem); | ||
1500 | 1531 | ||
1501 | /* Nothing requested OR nothing left */ | 1532 | /* Nothing requested OR nothing left */ |
1502 | if (copy_n == 0) | 1533 | if (copy_n == 0) |
1503 | return 0; | 1534 | return 0; |
1504 | 1535 | ||
1536 | |||
1537 | if (copy_n == -2) | ||
1538 | { | ||
1539 | buf_request_buffer_handle(CUR_TI->audio_hid); | ||
1540 | } | ||
1541 | |||
1505 | /* Let the disk buffer catch fill until enough data is available */ | 1542 | /* Let the disk buffer catch fill until enough data is available */ |
1506 | while (copy_n > CUR_TI->available) | 1543 | while (copy_n == -2) |
1507 | { | 1544 | { |
1508 | if (!filling) | ||
1509 | { | ||
1510 | LOGFQUEUE("codec > audio Q_AUDIO_FILL_BUFFER"); | ||
1511 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | ||
1512 | } | ||
1513 | |||
1514 | sleep(1); | 1545 | sleep(1); |
1546 | |||
1515 | if (ci.stop_codec || ci.new_track) | 1547 | if (ci.stop_codec || ci.new_track) |
1516 | return 0; | 1548 | return 0; |
1517 | } | ||
1518 | 1549 | ||
1519 | /* Copy as much as possible without wrapping */ | 1550 | copy_n = bufread(CUR_TI->audio_hid, size, ptr); |
1520 | part_n = MIN(copy_n, filebuflen - buf_ridx); | ||
1521 | memcpy(buf, &filebuf[buf_ridx], part_n); | ||
1522 | /* Copy the rest in the case of a wrap */ | ||
1523 | if (part_n < copy_n) { | ||
1524 | memcpy(&buf[part_n], &filebuf[0], copy_n - part_n); | ||
1525 | } | 1551 | } |
1526 | 1552 | ||
1527 | /* Update read and other position pointers */ | 1553 | /* Update read and other position pointers */ |
@@ -1533,7 +1559,9 @@ static size_t codec_filebuf_callback(void *ptr, size_t size) | |||
1533 | 1559 | ||
1534 | static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) | 1560 | static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) |
1535 | { | 1561 | { |
1536 | size_t short_n, copy_n, buf_rem; | 1562 | size_t copy_n = reqsize; |
1563 | ssize_t ret; | ||
1564 | void *ptr; | ||
1537 | 1565 | ||
1538 | if (!playing) | 1566 | if (!playing) |
1539 | { | 1567 | { |
@@ -1541,48 +1569,38 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
1541 | return NULL; | 1569 | return NULL; |
1542 | } | 1570 | } |
1543 | 1571 | ||
1544 | copy_n = MIN(reqsize, CUR_TI->available + CUR_TI->filerem); | 1572 | ret = bufgetdata(CUR_TI->audio_hid, reqsize, &ptr); |
1545 | if (copy_n == 0) | 1573 | if (ret >= 0) |
1574 | copy_n = MIN((size_t)ret, reqsize); | ||
1575 | |||
1576 | if (copy_n == 0) | ||
1546 | { | 1577 | { |
1547 | *realsize = 0; | 1578 | *realsize = 0; |
1548 | return NULL; | 1579 | return NULL; |
1549 | } | 1580 | } |
1550 | 1581 | ||
1551 | while (copy_n > CUR_TI->available) | 1582 | if (ret == -2) |
1583 | { | ||
1584 | buf_request_buffer_handle(CUR_TI->audio_hid); | ||
1585 | } | ||
1586 | |||
1587 | /* Let the disk buffer catch fill until enough data is available */ | ||
1588 | while (ret == -2) | ||
1552 | { | 1589 | { |
1553 | if (!filling) | ||
1554 | { | ||
1555 | LOGFQUEUE("codec > audio Q_AUDIO_FILL_BUFFER"); | ||
1556 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | ||
1557 | } | ||
1558 | |||
1559 | sleep(1); | 1590 | sleep(1); |
1591 | |||
1560 | if (ci.stop_codec || ci.new_track) | 1592 | if (ci.stop_codec || ci.new_track) |
1561 | { | 1593 | { |
1562 | *realsize = 0; | 1594 | *realsize = 0; |
1563 | return NULL; | 1595 | return NULL; |
1564 | } | 1596 | } |
1597 | ret = bufgetdata(CUR_TI->audio_hid, reqsize, &ptr); | ||
1565 | } | 1598 | } |
1566 | 1599 | copy_n = MIN((size_t)ret, reqsize); | |
1567 | /* How much is left at the end of the file buffer before wrap? */ | ||
1568 | buf_rem = filebuflen - buf_ridx; | ||
1569 | |||
1570 | /* If we can't satisfy the request without wrapping */ | ||
1571 | if (buf_rem < copy_n) | ||
1572 | { | ||
1573 | /* How short are we? */ | ||
1574 | short_n = copy_n - buf_rem; | ||
1575 | |||
1576 | /* If we can fudge it with the guardbuf */ | ||
1577 | if (short_n < GUARD_BUFSIZE) | ||
1578 | memcpy(&filebuf[filebuflen], &filebuf[0], short_n); | ||
1579 | else | ||
1580 | copy_n = buf_rem; | ||
1581 | } | ||
1582 | 1600 | ||
1583 | *realsize = copy_n; | 1601 | *realsize = copy_n; |
1584 | 1602 | ||
1585 | return (char *)&filebuf[buf_ridx]; | 1603 | return ptr; |
1586 | } /* codec_request_buffer_callback */ | 1604 | } /* codec_request_buffer_callback */ |
1587 | 1605 | ||
1588 | static int get_codec_base_type(int type) | 1606 | static int get_codec_base_type(int type) |
@@ -1599,50 +1617,13 @@ static int get_codec_base_type(int type) | |||
1599 | 1617 | ||
1600 | static void codec_advance_buffer_callback(size_t amount) | 1618 | static void codec_advance_buffer_callback(size_t amount) |
1601 | { | 1619 | { |
1602 | if (amount > CUR_TI->available + CUR_TI->filerem) | ||
1603 | amount = CUR_TI->available + CUR_TI->filerem; | ||
1604 | |||
1605 | while (amount > CUR_TI->available && filling) | ||
1606 | sleep(1); | ||
1607 | |||
1608 | if (amount > CUR_TI->available) | ||
1609 | { | ||
1610 | intptr_t result = Q_CODEC_REQUEST_FAILED; | ||
1611 | |||
1612 | if (!ci.stop_codec) | ||
1613 | { | ||
1614 | LOGFQUEUE("codec >| audio Q_AUDIO_REBUFFER_SEEK"); | ||
1615 | result = queue_send(&audio_queue, Q_AUDIO_REBUFFER_SEEK, | ||
1616 | ci.curpos + amount); | ||
1617 | } | ||
1618 | |||
1619 | switch (result) | ||
1620 | { | ||
1621 | case Q_CODEC_REQUEST_FAILED: | ||
1622 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED"); | ||
1623 | ci.stop_codec = true; | ||
1624 | return; | ||
1625 | |||
1626 | case Q_CODEC_REQUEST_COMPLETE: | ||
1627 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE"); | ||
1628 | return; | ||
1629 | |||
1630 | default: | ||
1631 | LOGFQUEUE("codec |< default"); | ||
1632 | ci.stop_codec = true; | ||
1633 | return; | ||
1634 | } | ||
1635 | } | ||
1636 | |||
1637 | codec_advance_buffer_counters(amount); | 1620 | codec_advance_buffer_counters(amount); |
1638 | |||
1639 | codec_set_offset_callback(ci.curpos); | 1621 | codec_set_offset_callback(ci.curpos); |
1640 | } | 1622 | } |
1641 | 1623 | ||
1642 | static void codec_advance_buffer_loc_callback(void *ptr) | 1624 | static void codec_advance_buffer_loc_callback(void *ptr) |
1643 | { | 1625 | { |
1644 | size_t amount = (size_t)ptr - (size_t)&filebuf[buf_ridx]; | 1626 | size_t amount = buf_get_offset(CUR_TI->audio_hid, ptr); |
1645 | |||
1646 | codec_advance_buffer_callback(amount); | 1627 | codec_advance_buffer_callback(amount); |
1647 | } | 1628 | } |
1648 | 1629 | ||
@@ -1705,7 +1686,7 @@ static off_t codec_mp3_get_filepos_callback(int newtime) | |||
1705 | { | 1686 | { |
1706 | off_t newpos; | 1687 | off_t newpos; |
1707 | 1688 | ||
1708 | CUR_TI->id3.elapsed = newtime; | 1689 | curtrack_id3.elapsed = newtime; |
1709 | newpos = codec_get_file_pos(); | 1690 | newpos = codec_get_file_pos(); |
1710 | 1691 | ||
1711 | return newpos; | 1692 | return newpos; |
@@ -1729,79 +1710,31 @@ static void codec_seek_complete_callback(void) | |||
1729 | 1710 | ||
1730 | static bool codec_seek_buffer_callback(size_t newpos) | 1711 | static bool codec_seek_buffer_callback(size_t newpos) |
1731 | { | 1712 | { |
1732 | int difference; | ||
1733 | |||
1734 | logf("codec_seek_buffer_callback"); | 1713 | logf("codec_seek_buffer_callback"); |
1735 | 1714 | ||
1736 | if (newpos >= CUR_TI->filesize) | 1715 | int ret = bufseek(CUR_TI->audio_hid, newpos); |
1737 | newpos = CUR_TI->filesize - 1; | 1716 | if (ret == 0) { |
1738 | 1717 | ci.curpos = newpos; | |
1739 | difference = newpos - ci.curpos; | ||
1740 | if (difference >= 0) | ||
1741 | { | ||
1742 | /* Seeking forward */ | ||
1743 | logf("seek: +%d", difference); | ||
1744 | codec_advance_buffer_callback(difference); | ||
1745 | return true; | 1718 | return true; |
1746 | } | 1719 | } |
1747 | 1720 | else { | |
1748 | /* Seeking backward */ | 1721 | return false; |
1749 | difference = -difference; | ||
1750 | if (ci.curpos - difference < 0) | ||
1751 | difference = ci.curpos; | ||
1752 | |||
1753 | /* We need to reload the song. */ | ||
1754 | if (newpos < CUR_TI->start_pos) | ||
1755 | { | ||
1756 | intptr_t result = Q_CODEC_REQUEST_FAILED; | ||
1757 | |||
1758 | if (!ci.stop_codec) | ||
1759 | { | ||
1760 | LOGFQUEUE("codec >| audio Q_AUDIO_REBUFFER_SEEK"); | ||
1761 | result = queue_send(&audio_queue, Q_AUDIO_REBUFFER_SEEK, | ||
1762 | newpos); | ||
1763 | } | ||
1764 | |||
1765 | switch (result) | ||
1766 | { | ||
1767 | case Q_CODEC_REQUEST_COMPLETE: | ||
1768 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE"); | ||
1769 | return true; | ||
1770 | |||
1771 | case Q_CODEC_REQUEST_FAILED: | ||
1772 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED"); | ||
1773 | ci.stop_codec = true; | ||
1774 | return false; | ||
1775 | |||
1776 | default: | ||
1777 | LOGFQUEUE("codec |< default"); | ||
1778 | return false; | ||
1779 | } | ||
1780 | } | 1722 | } |
1781 | |||
1782 | /* Seeking inside buffer space. */ | ||
1783 | logf("seek: -%d", difference); | ||
1784 | CUR_TI->available += difference; | ||
1785 | buf_ridx = RINGBUF_SUB(buf_ridx, (unsigned)difference); | ||
1786 | ci.curpos -= difference; | ||
1787 | |||
1788 | return true; | ||
1789 | } | 1723 | } |
1790 | 1724 | ||
1791 | static void codec_configure_callback(int setting, intptr_t value) | 1725 | static void codec_configure_callback(int setting, intptr_t value) |
1792 | { | 1726 | { |
1793 | switch (setting) { | 1727 | switch (setting) { |
1794 | case CODEC_SET_FILEBUF_WATERMARK: | 1728 | case CODEC_SET_FILEBUF_WATERMARK: |
1795 | conf_watermark = value; | 1729 | set_filebuf_watermark(buffer_margin, value); |
1796 | set_filebuf_watermark(buffer_margin); | ||
1797 | break; | 1730 | break; |
1798 | 1731 | ||
1799 | case CODEC_SET_FILEBUF_CHUNKSIZE: | 1732 | case CODEC_SET_FILEBUF_CHUNKSIZE: |
1800 | conf_filechunk = value; | 1733 | buf_set_conf(BUFFERING_SET_CHUNKSIZE, value); |
1801 | break; | 1734 | break; |
1802 | 1735 | ||
1803 | case CODEC_SET_FILEBUF_PRESEEK: | 1736 | case CODEC_SET_FILEBUF_PRESEEK: |
1804 | conf_preseek = value; | 1737 | buf_set_conf(BUFFERING_SET_PRESEEK, value); |
1805 | break; | 1738 | break; |
1806 | 1739 | ||
1807 | default: | 1740 | default: |
@@ -1811,7 +1744,6 @@ static void codec_configure_callback(int setting, intptr_t value) | |||
1811 | 1744 | ||
1812 | static void codec_track_changed(void) | 1745 | static void codec_track_changed(void) |
1813 | { | 1746 | { |
1814 | automatic_skip = false; | ||
1815 | LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED"); | 1747 | LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED"); |
1816 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | 1748 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); |
1817 | } | 1749 | } |
@@ -1824,28 +1756,12 @@ static void codec_pcmbuf_track_changed_callback(void) | |||
1824 | 1756 | ||
1825 | static void codec_discard_codec_callback(void) | 1757 | static void codec_discard_codec_callback(void) |
1826 | { | 1758 | { |
1827 | if (CUR_TI->has_codec) | 1759 | if (CUR_TI->codec_hid > 0) |
1828 | { | 1760 | { |
1829 | CUR_TI->has_codec = false; | 1761 | bufclose(CUR_TI->codec_hid); |
1830 | buf_ridx = RINGBUF_ADD(buf_ridx, CUR_TI->codecsize); | 1762 | CUR_TI->codec_hid = 0; |
1763 | CUR_TI->codecsize = 0; | ||
1831 | } | 1764 | } |
1832 | |||
1833 | #if 0 | ||
1834 | /* Check if a buffer desync has happened, log it and stop playback. */ | ||
1835 | if (buf_ridx != CUR_TI->buf_idx) | ||
1836 | { | ||
1837 | int offset = CUR_TI->buf_idx - buf_ridx; | ||
1838 | size_t new_used = FILEBUFUSED - offset; | ||
1839 | |||
1840 | logf("Buf off :%d=%d-%d", offset, CUR_TI->buf_idx, buf_ridx); | ||
1841 | logf("Used off:%d",FILEBUFUSED - new_used); | ||
1842 | |||
1843 | /* This is a fatal internal error and it's not safe to | ||
1844 | * continue playback. */ | ||
1845 | ci.stop_codec = true; | ||
1846 | queue_post(&audio_queue, Q_AUDIO_STOP, 0); | ||
1847 | } | ||
1848 | #endif | ||
1849 | } | 1765 | } |
1850 | 1766 | ||
1851 | static inline void codec_gapless_track_change(void) { | 1767 | static inline void codec_gapless_track_change(void) { |
@@ -1899,7 +1815,7 @@ static bool codec_load_next_track(void) | |||
1899 | { | 1815 | { |
1900 | intptr_t result = Q_CODEC_REQUEST_FAILED; | 1816 | intptr_t result = Q_CODEC_REQUEST_FAILED; |
1901 | 1817 | ||
1902 | prev_track_elapsed = CUR_TI->id3.elapsed; | 1818 | prev_track_elapsed = curtrack_id3.elapsed; |
1903 | 1819 | ||
1904 | if (ci.seek_time) | 1820 | if (ci.seek_time) |
1905 | codec_seek_complete_callback(); | 1821 | codec_seek_complete_callback(); |
@@ -1950,13 +1866,18 @@ static bool codec_request_next_track_callback(void) | |||
1950 | if (ci.stop_codec || !playing) | 1866 | if (ci.stop_codec || !playing) |
1951 | return false; | 1867 | return false; |
1952 | 1868 | ||
1953 | prev_codectype = get_codec_base_type(CUR_TI->id3.codectype); | 1869 | prev_codectype = get_codec_base_type(curtrack_id3.codectype); |
1954 | 1870 | ||
1955 | if (!codec_load_next_track()) | 1871 | if (!codec_load_next_track()) |
1956 | return false; | 1872 | return false; |
1957 | 1873 | ||
1874 | /* Seek to the beginning of the new track because if the struct mp3entry was | ||
1875 | buffered, "elapsed" might not be zero (if the track has been played | ||
1876 | already but not unbuffered) */ | ||
1877 | codec_seek_buffer_callback(0); | ||
1878 | |||
1958 | /* Check if the next codec is the same file. */ | 1879 | /* Check if the next codec is the same file. */ |
1959 | if (prev_codectype == get_codec_base_type(CUR_TI->id3.codectype)) | 1880 | if (prev_codectype == get_codec_base_type(curtrack_id3.codectype)) |
1960 | { | 1881 | { |
1961 | logf("New track loaded"); | 1882 | logf("New track loaded"); |
1962 | codec_discard_codec_callback(); | 1883 | codec_discard_codec_callback(); |
@@ -1964,7 +1885,7 @@ static bool codec_request_next_track_callback(void) | |||
1964 | } | 1885 | } |
1965 | else | 1886 | else |
1966 | { | 1887 | { |
1967 | logf("New codec:%d/%d", CUR_TI->id3.codectype, prev_codectype); | 1888 | logf("New codec:%d/%d", curtrack_id3.codectype, prev_codectype); |
1968 | return false; | 1889 | return false; |
1969 | } | 1890 | } |
1970 | } | 1891 | } |
@@ -1982,6 +1903,7 @@ static void codec_thread(void) | |||
1982 | switch (ev.id) { | 1903 | switch (ev.id) { |
1983 | case Q_CODEC_LOAD_DISK: | 1904 | case Q_CODEC_LOAD_DISK: |
1984 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); | 1905 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); |
1906 | queue_reply(&codec_queue, 1); | ||
1985 | audio_codec_loaded = true; | 1907 | audio_codec_loaded = true; |
1986 | #ifdef PLAYBACK_VOICE | 1908 | #ifdef PLAYBACK_VOICE |
1987 | /* Don't sent messages to voice codec if it's already swapped | 1909 | /* Don't sent messages to voice codec if it's already swapped |
@@ -2004,7 +1926,7 @@ static void codec_thread(void) | |||
2004 | 1926 | ||
2005 | case Q_CODEC_LOAD: | 1927 | case Q_CODEC_LOAD: |
2006 | LOGFQUEUE("codec < Q_CODEC_LOAD"); | 1928 | LOGFQUEUE("codec < Q_CODEC_LOAD"); |
2007 | if (!CUR_TI->has_codec) { | 1929 | if (CUR_TI->codec_hid <= 0) { |
2008 | logf("Codec slot is empty!"); | 1930 | logf("Codec slot is empty!"); |
2009 | /* Wait for the pcm buffer to go empty */ | 1931 | /* Wait for the pcm buffer to go empty */ |
2010 | while (pcm_is_playing()) | 1932 | while (pcm_is_playing()) |
@@ -2028,8 +1950,8 @@ static void codec_thread(void) | |||
2028 | #endif | 1950 | #endif |
2029 | set_current_codec(CODEC_IDX_AUDIO); | 1951 | set_current_codec(CODEC_IDX_AUDIO); |
2030 | ci.stop_codec = false; | 1952 | ci.stop_codec = false; |
2031 | wrap = (size_t)&filebuf[filebuflen] - (size_t)CUR_TI->codecbuf; | 1953 | wrap = (size_t)&filebuf[filebuflen] - (size_t)bufgetcodec(CUR_TI); |
2032 | status = codec_load_ram(CUR_TI->codecbuf, CUR_TI->codecsize, | 1954 | status = codec_load_ram(bufgetcodec(CUR_TI), CUR_TI->codecsize, |
2033 | &filebuf[0], wrap, &ci); | 1955 | &filebuf[0], wrap, &ci); |
2034 | #ifdef PLAYBACK_VOICE | 1956 | #ifdef PLAYBACK_VOICE |
2035 | semaphore_release(&sem_codecthread); | 1957 | semaphore_release(&sem_codecthread); |
@@ -2098,7 +2020,7 @@ static void codec_thread(void) | |||
2098 | logf("Codec failure"); | 2020 | logf("Codec failure"); |
2099 | gui_syncsplash(HZ*2, "Codec failure"); | 2021 | gui_syncsplash(HZ*2, "Codec failure"); |
2100 | } | 2022 | } |
2101 | 2023 | ||
2102 | if (!codec_load_next_track()) | 2024 | if (!codec_load_next_track()) |
2103 | { | 2025 | { |
2104 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); | 2026 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); |
@@ -2116,8 +2038,8 @@ static void codec_thread(void) | |||
2116 | * triggering the WPS exit */ | 2038 | * triggering the WPS exit */ |
2117 | while(pcm_is_playing()) | 2039 | while(pcm_is_playing()) |
2118 | { | 2040 | { |
2119 | CUR_TI->id3.elapsed = | 2041 | curtrack_id3.elapsed = |
2120 | CUR_TI->id3.length - pcmbuf_get_latency(); | 2042 | curtrack_id3.length - pcmbuf_get_latency(); |
2121 | sleep(1); | 2043 | sleep(1); |
2122 | } | 2044 | } |
2123 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); | 2045 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); |
@@ -2126,8 +2048,8 @@ static void codec_thread(void) | |||
2126 | break; | 2048 | break; |
2127 | } | 2049 | } |
2128 | } | 2050 | } |
2129 | 2051 | ||
2130 | if (CUR_TI->has_codec) | 2052 | if (CUR_TI->codec_hid > 0) |
2131 | { | 2053 | { |
2132 | LOGFQUEUE("codec > codec Q_CODEC_LOAD"); | 2054 | LOGFQUEUE("codec > codec Q_CODEC_LOAD"); |
2133 | queue_post(&codec_queue, Q_CODEC_LOAD, 0); | 2055 | queue_post(&codec_queue, Q_CODEC_LOAD, 0); |
@@ -2135,7 +2057,7 @@ static void codec_thread(void) | |||
2135 | else | 2057 | else |
2136 | { | 2058 | { |
2137 | const char *codec_fn = | 2059 | const char *codec_fn = |
2138 | get_codec_filename(CUR_TI->id3.codectype); | 2060 | get_codec_filename(curtrack_id3.codectype); |
2139 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); | 2061 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); |
2140 | queue_post(&codec_queue, Q_CODEC_LOAD_DISK, | 2062 | queue_post(&codec_queue, Q_CODEC_LOAD_DISK, |
2141 | (intptr_t)codec_fn); | 2063 | (intptr_t)codec_fn); |
@@ -2171,11 +2093,6 @@ static void codec_thread(void) | |||
2171 | 2093 | ||
2172 | /* --- Audio thread --- */ | 2094 | /* --- Audio thread --- */ |
2173 | 2095 | ||
2174 | static bool audio_filebuf_is_lowdata(void) | ||
2175 | { | ||
2176 | return FILEBUFUSED < AUDIO_FILEBUF_CRITICAL; | ||
2177 | } | ||
2178 | |||
2179 | static bool audio_have_tracks(void) | 2096 | static bool audio_have_tracks(void) |
2180 | { | 2097 | { |
2181 | return track_ridx != track_widx || CUR_TI->filesize; | 2098 | return track_ridx != track_widx || CUR_TI->filesize; |
@@ -2208,142 +2125,41 @@ int audio_track_count(void) | |||
2208 | 2125 | ||
2209 | long audio_filebufused(void) | 2126 | long audio_filebufused(void) |
2210 | { | 2127 | { |
2211 | return (long) FILEBUFUSED; | 2128 | return (long) buf_used(); |
2212 | } | ||
2213 | |||
2214 | /* Count the data BETWEEN the selected tracks */ | ||
2215 | static size_t audio_buffer_count_tracks(int from_track, int to_track) | ||
2216 | { | ||
2217 | size_t amount = 0; | ||
2218 | bool need_wrap = to_track < from_track; | ||
2219 | |||
2220 | while (1) | ||
2221 | { | ||
2222 | if (++from_track >= MAX_TRACK) | ||
2223 | { | ||
2224 | from_track -= MAX_TRACK; | ||
2225 | need_wrap = false; | ||
2226 | } | ||
2227 | |||
2228 | if (from_track >= to_track && !need_wrap) | ||
2229 | break; | ||
2230 | |||
2231 | amount += tracks[from_track].codecsize + tracks[from_track].filesize; | ||
2232 | } | ||
2233 | return amount; | ||
2234 | } | ||
2235 | |||
2236 | static bool audio_buffer_wind_forward(int new_track_ridx, int old_track_ridx) | ||
2237 | { | ||
2238 | size_t amount; | ||
2239 | |||
2240 | /* Start with the remainder of the previously playing track */ | ||
2241 | amount = tracks[old_track_ridx].filesize - ci.curpos; | ||
2242 | /* Then collect all data from tracks in between them */ | ||
2243 | amount += audio_buffer_count_tracks(old_track_ridx, new_track_ridx); | ||
2244 | logf("bwf:%ldB", (long) amount); | ||
2245 | |||
2246 | if (amount > FILEBUFUSED) | ||
2247 | return false; | ||
2248 | |||
2249 | /* Wind the buffer to the beginning of the target track or its codec */ | ||
2250 | buf_ridx = RINGBUF_ADD(buf_ridx, amount); | ||
2251 | |||
2252 | return true; | ||
2253 | } | 2129 | } |
2254 | 2130 | ||
2255 | static bool audio_buffer_wind_backward(int new_track_ridx, int old_track_ridx) | 2131 | static void audio_update_trackinfo(void) |
2256 | { | 2132 | { |
2257 | /* Available buffer data */ | 2133 | if (CUR_TI->id3_hid > 0) |
2258 | size_t buf_back; | 2134 | copy_mp3entry(&curtrack_id3, bufgetid3(CUR_TI->id3_hid)); |
2259 | /* Start with the previously playing track's data and our data */ | ||
2260 | size_t amount; | ||
2261 | |||
2262 | amount = ci.curpos; | ||
2263 | buf_back = RINGBUF_SUB(buf_ridx, buf_widx); | ||
2264 | |||
2265 | /* If we're not just resetting the current track */ | ||
2266 | if (new_track_ridx != old_track_ridx) | ||
2267 | { | ||
2268 | /* Need to wind to before the old track's codec and our filesize */ | ||
2269 | amount += tracks[old_track_ridx].codecsize; | ||
2270 | amount += tracks[new_track_ridx].filesize; | ||
2271 | 2135 | ||
2272 | /* Rewind the old track to its beginning */ | 2136 | CUR_TI->taginfo_ready = (CUR_TI->id3_hid > 0); |
2273 | tracks[old_track_ridx].available = | ||
2274 | tracks[old_track_ridx].filesize - tracks[old_track_ridx].filerem; | ||
2275 | } | ||
2276 | |||
2277 | /* If the codec was ever buffered */ | ||
2278 | if (tracks[new_track_ridx].codecsize) | ||
2279 | { | ||
2280 | /* Add the codec to the needed size */ | ||
2281 | amount += tracks[new_track_ridx].codecsize; | ||
2282 | tracks[new_track_ridx].has_codec = true; | ||
2283 | } | ||
2284 | |||
2285 | /* Then collect all data from tracks between new and old */ | ||
2286 | amount += audio_buffer_count_tracks(new_track_ridx, old_track_ridx); | ||
2287 | |||
2288 | /* Do we have space to make this skip? */ | ||
2289 | if (amount > buf_back) | ||
2290 | return false; | ||
2291 | 2137 | ||
2292 | logf("bwb:%ldB",amount); | 2138 | int next_idx = track_ridx + 1; |
2293 | 2139 | next_idx &= MAX_TRACK_MASK; | |
2294 | /* Rewind the buffer to the beginning of the target track or its codec */ | ||
2295 | buf_ridx = RINGBUF_SUB(buf_ridx, amount); | ||
2296 | 2140 | ||
2297 | /* Reset to the beginning of the new track */ | 2141 | if (tracks[next_idx].id3_hid > 0) |
2298 | tracks[new_track_ridx].available = tracks[new_track_ridx].filesize; | 2142 | copy_mp3entry(&nexttrack_id3, bufgetid3(tracks[next_idx].id3_hid)); |
2299 | 2143 | ||
2300 | return true; | 2144 | tracks[next_idx].taginfo_ready = (tracks[next_idx].id3_hid > 0); |
2301 | } | ||
2302 | 2145 | ||
2303 | static void audio_update_trackinfo(void) | ||
2304 | { | ||
2305 | ci.filesize = CUR_TI->filesize; | 2146 | ci.filesize = CUR_TI->filesize; |
2306 | CUR_TI->id3.elapsed = 0; | 2147 | curtrack_id3.elapsed = 0; |
2307 | CUR_TI->id3.offset = 0; | 2148 | curtrack_id3.offset = 0; |
2308 | ci.id3 = &CUR_TI->id3; | 2149 | ci.id3 = &curtrack_id3; |
2309 | ci.curpos = 0; | 2150 | ci.curpos = 0; |
2310 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | 2151 | ci.taginfo_ready = &CUR_TI->taginfo_ready; |
2311 | } | 2152 | } |
2312 | 2153 | ||
2313 | /* Yield to codecs for as long as possible if they are in need of data | ||
2314 | * return true if the caller should break to let the audio thread process | ||
2315 | * new events */ | ||
2316 | static bool audio_yield_codecs(void) | ||
2317 | { | ||
2318 | yield(); | ||
2319 | |||
2320 | if (!queue_empty(&audio_queue)) | ||
2321 | return true; | ||
2322 | |||
2323 | while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata()) | ||
2324 | && !ci.stop_codec && playing && !audio_filebuf_is_lowdata()) | ||
2325 | { | ||
2326 | if (filling) | ||
2327 | yield(); | ||
2328 | else | ||
2329 | sleep(2); | ||
2330 | |||
2331 | if (!queue_empty(&audio_queue)) | ||
2332 | return true; | ||
2333 | } | ||
2334 | |||
2335 | return false; | ||
2336 | } | ||
2337 | |||
2338 | static void audio_clear_track_entries(bool clear_unbuffered) | 2154 | static void audio_clear_track_entries(bool clear_unbuffered) |
2339 | { | 2155 | { |
2340 | int cur_idx = track_widx; | 2156 | int cur_idx = track_widx; |
2341 | int last_idx = -1; | 2157 | int last_idx = -1; |
2342 | 2158 | ||
2343 | logf("Clearing tracks:%d/%d, %d", track_ridx, track_widx, clear_unbuffered); | 2159 | logf("Clearing tracks:%d/%d, %d", track_ridx, track_widx, clear_unbuffered); |
2344 | 2160 | ||
2345 | /* Loop over all tracks from write-to-read */ | 2161 | /* Loop over all tracks from write-to-read */ |
2346 | while (1) | 2162 | while (1) |
2347 | { | 2163 | { |
2348 | cur_idx++; | 2164 | cur_idx++; |
2349 | cur_idx &= MAX_TRACK_MASK; | 2165 | cur_idx &= MAX_TRACK_MASK; |
@@ -2353,21 +2169,21 @@ static void audio_clear_track_entries(bool clear_unbuffered) | |||
2353 | 2169 | ||
2354 | /* If the track is buffered, conditionally clear/notify, | 2170 | /* If the track is buffered, conditionally clear/notify, |
2355 | * otherwise clear the track if that option is selected */ | 2171 | * otherwise clear the track if that option is selected */ |
2356 | if (tracks[cur_idx].event_sent) | 2172 | if (tracks[cur_idx].event_sent) |
2357 | { | 2173 | { |
2358 | if (last_idx >= 0) | 2174 | if (last_idx >= 0) |
2359 | { | 2175 | { |
2360 | /* If there is an unbuffer callback, call it, otherwise, | 2176 | /* If there is an unbuffer callback, call it, otherwise, |
2361 | * just clear the track */ | 2177 | * just clear the track */ |
2362 | if (track_unbuffer_callback) | 2178 | if (track_unbuffer_callback && tracks[last_idx].id3_hid > 0) |
2363 | track_unbuffer_callback(&tracks[last_idx].id3, false); | 2179 | track_unbuffer_callback(bufgetid3(tracks[last_idx].id3_hid), false); |
2364 | 2180 | ||
2365 | memset(&tracks[last_idx], 0, sizeof(struct track_info)); | 2181 | clear_track_info(&tracks[last_idx]); |
2366 | } | 2182 | } |
2367 | last_idx = cur_idx; | 2183 | last_idx = cur_idx; |
2368 | } | 2184 | } |
2369 | else if (clear_unbuffered) | 2185 | else if (clear_unbuffered) |
2370 | memset(&tracks[cur_idx], 0, sizeof(struct track_info)); | 2186 | clear_track_info(&tracks[cur_idx]); |
2371 | } | 2187 | } |
2372 | 2188 | ||
2373 | /* We clear the previous instance of a buffered track throughout | 2189 | /* We clear the previous instance of a buffered track throughout |
@@ -2375,203 +2191,51 @@ static void audio_clear_track_entries(bool clear_unbuffered) | |||
2375 | * the last track here */ | 2191 | * the last track here */ |
2376 | if (last_idx >= 0) | 2192 | if (last_idx >= 0) |
2377 | { | 2193 | { |
2378 | if (track_unbuffer_callback) | 2194 | if (track_unbuffer_callback && tracks[last_idx].id3_hid > 0) |
2379 | track_unbuffer_callback(&tracks[last_idx].id3, true); | 2195 | track_unbuffer_callback(bufgetid3(tracks[last_idx].id3_hid), true); |
2380 | memset(&tracks[last_idx], 0, sizeof(struct track_info)); | 2196 | clear_track_info(&tracks[last_idx]); |
2381 | } | 2197 | } |
2382 | } | 2198 | } |
2383 | 2199 | ||
2384 | /* FIXME: This code should be made more generic and move to metadata.c */ | 2200 | static bool audio_release_tracks(void) |
2385 | static void audio_strip_tags(void) | ||
2386 | { | 2201 | { |
2387 | int i; | 2202 | int i, cur_idx; |
2388 | static const unsigned char tag[] = "TAG"; | ||
2389 | static const unsigned char apetag[] = "APETAGEX"; | ||
2390 | size_t tag_idx; | ||
2391 | size_t cur_idx; | ||
2392 | size_t len, version; | ||
2393 | 2203 | ||
2394 | tag_idx = RINGBUF_SUB(buf_widx, 128); | 2204 | logf("releasing all tracks"); |
2395 | 2205 | ||
2396 | if (FILEBUFUSED > 128 && tag_idx > buf_ridx) | 2206 | for(i = 0; i < MAX_TRACKS; i++) |
2397 | { | 2207 | { |
2398 | cur_idx = tag_idx; | 2208 | cur_idx = (track_ridx + i) & MAX_TRACK_MASK; |
2399 | for(i = 0;i < 3;i++) | 2209 | if (!clear_track_info(&tracks[cur_idx])) |
2400 | { | 2210 | return false; |
2401 | if(filebuf[cur_idx] != tag[i]) | ||
2402 | goto strip_ape_tag; | ||
2403 | |||
2404 | cur_idx = RINGBUF_ADD(cur_idx, 1); | ||
2405 | } | ||
2406 | |||
2407 | /* Skip id3v1 tag */ | ||
2408 | logf("Skipping ID3v1 tag"); | ||
2409 | buf_widx = tag_idx; | ||
2410 | tracks[track_widx].available -= 128; | ||
2411 | tracks[track_widx].filesize -= 128; | ||
2412 | } | ||
2413 | |||
2414 | strip_ape_tag: | ||
2415 | /* Check for APE tag (look for the APE tag footer) */ | ||
2416 | tag_idx = RINGBUF_SUB(buf_widx, 32); | ||
2417 | |||
2418 | if (FILEBUFUSED > 32 && tag_idx > buf_ridx) | ||
2419 | { | ||
2420 | cur_idx = tag_idx; | ||
2421 | for(i = 0;i < 8;i++) | ||
2422 | { | ||
2423 | if(filebuf[cur_idx] != apetag[i]) | ||
2424 | return; | ||
2425 | |||
2426 | cur_idx = RINGBUF_ADD(cur_idx, 1); | ||
2427 | } | ||
2428 | |||
2429 | /* Read the version and length from the footer */ | ||
2430 | version = filebuf[tag_idx+8] | (filebuf[tag_idx+9] << 8) | | ||
2431 | (filebuf[tag_idx+10] << 16) | (filebuf[tag_idx+11] << 24); | ||
2432 | len = filebuf[tag_idx+12] | (filebuf[tag_idx+13] << 8) | | ||
2433 | (filebuf[tag_idx+14] << 16) | (filebuf[tag_idx+15] << 24); | ||
2434 | if (version == 2000) | ||
2435 | len += 32; /* APEv2 has a 32 byte header */ | ||
2436 | |||
2437 | /* Skip APE tag */ | ||
2438 | if (FILEBUFUSED > len) | ||
2439 | { | ||
2440 | logf("Skipping APE tag (%ldB)", len); | ||
2441 | buf_widx = RINGBUF_SUB(buf_widx, len); | ||
2442 | tracks[track_widx].available -= len; | ||
2443 | tracks[track_widx].filesize -= len; | ||
2444 | } | ||
2445 | } | ||
2446 | } | ||
2447 | |||
2448 | /* Returns true if a whole file is read, false otherwise */ | ||
2449 | static bool audio_read_file(size_t minimum) | ||
2450 | { | ||
2451 | bool ret_val = false; | ||
2452 | |||
2453 | /* If we're called and no file is open, this is an error */ | ||
2454 | if (current_fd < 0) | ||
2455 | { | ||
2456 | logf("Bad fd in arf"); | ||
2457 | /* Give some hope of miraculous recovery by forcing a track reload */ | ||
2458 | tracks[track_widx].filesize = 0; | ||
2459 | /* Stop this buffering run */ | ||
2460 | return ret_val; | ||
2461 | } | ||
2462 | |||
2463 | trigger_cpu_boost(); | ||
2464 | while (tracks[track_widx].filerem > 0) | ||
2465 | { | ||
2466 | size_t copy_n; | ||
2467 | int overlap; | ||
2468 | int rc; | ||
2469 | |||
2470 | /* copy_n is the largest chunk that is safe to read */ | ||
2471 | copy_n = MIN(conf_filechunk, filebuflen - buf_widx); | ||
2472 | |||
2473 | /* buf_widx == buf_ridx is defined as buffer empty, not buffer full */ | ||
2474 | if (RINGBUF_ADD_CROSS(buf_widx,copy_n,buf_ridx) >= 0) | ||
2475 | break; | ||
2476 | |||
2477 | /* rc is the actual amount read */ | ||
2478 | rc = read(current_fd, &filebuf[buf_widx], copy_n); | ||
2479 | |||
2480 | if (rc < 0) | ||
2481 | { | ||
2482 | logf("File ended %ldB early", tracks[track_widx].filerem); | ||
2483 | tracks[track_widx].filesize -= tracks[track_widx].filerem; | ||
2484 | tracks[track_widx].filerem = 0; | ||
2485 | break; | ||
2486 | } | ||
2487 | |||
2488 | /* How much of the playing track did we overwrite */ | ||
2489 | if (buf_widx == CUR_TI->buf_idx) | ||
2490 | { | ||
2491 | /* Special handling; zero or full overlap? */ | ||
2492 | if (track_widx == track_ridx && CUR_TI->available == 0) | ||
2493 | overlap = 0; | ||
2494 | else | ||
2495 | overlap = rc; | ||
2496 | } | ||
2497 | else | ||
2498 | overlap = RINGBUF_ADD_CROSS(buf_widx,rc,CUR_TI->buf_idx); | ||
2499 | |||
2500 | if ((unsigned)rc > tracks[track_widx].filerem) | ||
2501 | { | ||
2502 | logf("Bad: rc-filerem=%ld, fixing", rc-tracks[track_widx].filerem); | ||
2503 | tracks[track_widx].filesize += rc - tracks[track_widx].filerem; | ||
2504 | tracks[track_widx].filerem = rc; | ||
2505 | } | ||
2506 | |||
2507 | /* Advance buffer */ | ||
2508 | buf_widx = RINGBUF_ADD(buf_widx, rc); | ||
2509 | tracks[track_widx].available += rc; | ||
2510 | tracks[track_widx].filerem -= rc; | ||
2511 | |||
2512 | /* If we write into the playing track, adjust it's buffer info */ | ||
2513 | if (overlap > 0) | ||
2514 | { | ||
2515 | CUR_TI->buf_idx += overlap; | ||
2516 | CUR_TI->start_pos += overlap; | ||
2517 | } | ||
2518 | |||
2519 | /* For a rebuffer, fill at least this minimum */ | ||
2520 | if (minimum > (unsigned)rc) | ||
2521 | minimum -= rc; | ||
2522 | /* Let the codec process up to the watermark */ | ||
2523 | /* Break immediately if this is a quick buffer, or there is an event */ | ||
2524 | else if (minimum || audio_yield_codecs()) | ||
2525 | { | ||
2526 | /* Exit quickly, but don't stop the overall buffering process */ | ||
2527 | ret_val = true; | ||
2528 | break; | ||
2529 | } | ||
2530 | } | 2211 | } |
2531 | 2212 | ||
2532 | if (tracks[track_widx].filerem == 0) | 2213 | return true; |
2533 | { | ||
2534 | logf("Finished buf:%ldB", tracks[track_widx].filesize); | ||
2535 | close(current_fd); | ||
2536 | current_fd = -1; | ||
2537 | audio_strip_tags(); | ||
2538 | |||
2539 | track_widx++; | ||
2540 | track_widx &= MAX_TRACK_MASK; | ||
2541 | |||
2542 | tracks[track_widx].filesize = 0; | ||
2543 | return true; | ||
2544 | } | ||
2545 | else | ||
2546 | { | ||
2547 | logf("%s buf:%ldB", ret_val?"Quick":"Partially", | ||
2548 | tracks[track_widx].filesize - tracks[track_widx].filerem); | ||
2549 | return ret_val; | ||
2550 | } | ||
2551 | } | 2214 | } |
2552 | 2215 | ||
2553 | static bool audio_loadcodec(bool start_play) | 2216 | static bool audio_loadcodec(bool start_play) |
2554 | { | 2217 | { |
2555 | size_t size = 0; | ||
2556 | int fd; | 2218 | int fd; |
2557 | int rc; | ||
2558 | size_t copy_n; | ||
2559 | int prev_track; | 2219 | int prev_track; |
2560 | char codec_path[MAX_PATH]; /* Full path to codec */ | 2220 | char codec_path[MAX_PATH]; /* Full path to codec */ |
2561 | 2221 | ||
2222 | if (tracks[track_widx].id3_hid <= 0) { | ||
2223 | return false; | ||
2224 | } | ||
2225 | |||
2562 | const char * codec_fn = | 2226 | const char * codec_fn = |
2563 | get_codec_filename(tracks[track_widx].id3.codectype); | 2227 | get_codec_filename(bufgetid3(tracks[track_widx].id3_hid)->codectype); |
2564 | if (codec_fn == NULL) | 2228 | if (codec_fn == NULL) |
2565 | return false; | 2229 | return false; |
2566 | 2230 | ||
2567 | tracks[track_widx].has_codec = false; | 2231 | tracks[track_widx].codec_hid = 0; |
2568 | 2232 | ||
2569 | if (start_play) | 2233 | if (start_play) |
2570 | { | 2234 | { |
2571 | /* Load the codec directly from disk and save some memory. */ | 2235 | /* Load the codec directly from disk and save some memory. */ |
2572 | track_ridx = track_widx; | 2236 | track_ridx = track_widx; |
2573 | ci.filesize = CUR_TI->filesize; | 2237 | ci.filesize = CUR_TI->filesize; |
2574 | ci.id3 = &CUR_TI->id3; | 2238 | ci.id3 = &curtrack_id3; |
2575 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | 2239 | ci.taginfo_ready = &CUR_TI->taginfo_ready; |
2576 | ci.curpos = 0; | 2240 | ci.curpos = 0; |
2577 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); | 2241 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); |
@@ -2584,11 +2248,13 @@ static bool audio_loadcodec(bool start_play) | |||
2584 | if (track_widx != track_ridx) | 2248 | if (track_widx != track_ridx) |
2585 | { | 2249 | { |
2586 | prev_track = (track_widx - 1) & MAX_TRACK_MASK; | 2250 | prev_track = (track_widx - 1) & MAX_TRACK_MASK; |
2587 | 2251 | ||
2588 | /* If the previous codec is the same as this one, there is no need | 2252 | /* If the previous codec is the same as this one, there is no need |
2589 | * to put another copy of it on the file buffer */ | 2253 | * to put another copy of it on the file buffer */ |
2590 | if (get_codec_base_type(tracks[track_widx].id3.codectype) == | 2254 | if (get_codec_base_type( |
2591 | get_codec_base_type(tracks[prev_track].id3.codectype) | 2255 | bufgetid3(tracks[track_widx].id3_hid)->codectype) == |
2256 | get_codec_base_type( | ||
2257 | bufgetid3(tracks[prev_track].id3_hid)->codectype) | ||
2592 | && audio_codec_loaded) | 2258 | && audio_codec_loaded) |
2593 | { | 2259 | { |
2594 | logf("Reusing prev. codec"); | 2260 | logf("Reusing prev. codec"); |
@@ -2607,39 +2273,17 @@ static bool audio_loadcodec(bool start_play) | |||
2607 | } | 2273 | } |
2608 | 2274 | ||
2609 | tracks[track_widx].codecsize = filesize(fd); | 2275 | tracks[track_widx].codecsize = filesize(fd); |
2610 | 2276 | ||
2611 | /* Never load a partial codec */ | 2277 | tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC); |
2612 | if (RINGBUF_ADD_CROSS(buf_widx,tracks[track_widx].codecsize,buf_ridx) >= 0) | 2278 | if (tracks[track_widx].codec_hid < 0) |
2613 | { | 2279 | { |
2614 | logf("Not enough space"); | 2280 | logf("Not enough space"); |
2615 | close(fd); | 2281 | close(fd); |
2616 | return false; | 2282 | return false; |
2617 | } | 2283 | } |
2618 | 2284 | ||
2619 | while (size < tracks[track_widx].codecsize) | ||
2620 | { | ||
2621 | copy_n = MIN(conf_filechunk, filebuflen - buf_widx); | ||
2622 | rc = read(fd, &filebuf[buf_widx], copy_n); | ||
2623 | if (rc < 0) | ||
2624 | { | ||
2625 | close(fd); | ||
2626 | /* This is an error condition, likely the codec file is corrupt */ | ||
2627 | logf("Partial codec loaded"); | ||
2628 | /* Must undo the buffer write of the partial codec */ | ||
2629 | buf_widx = RINGBUF_SUB(buf_widx, size); | ||
2630 | tracks[track_widx].codecsize = 0; | ||
2631 | return false; | ||
2632 | } | ||
2633 | |||
2634 | buf_widx = RINGBUF_ADD(buf_widx, rc); | ||
2635 | |||
2636 | size += rc; | ||
2637 | } | ||
2638 | |||
2639 | tracks[track_widx].has_codec = true; | ||
2640 | |||
2641 | close(fd); | 2285 | close(fd); |
2642 | logf("Done: %ldB", size); | 2286 | logf("Loaded codec"); |
2643 | 2287 | ||
2644 | return true; | 2288 | return true; |
2645 | } | 2289 | } |
@@ -2700,94 +2344,99 @@ static void audio_set_elapsed(struct mp3entry* id3) | |||
2700 | } | 2344 | } |
2701 | } | 2345 | } |
2702 | 2346 | ||
2703 | static bool audio_load_track(int offset, bool start_play, bool rebuffer) | 2347 | /* Load one track by making the appropriate bufopen calls. Return true if |
2348 | everything required was loaded correctly, false if not. */ | ||
2349 | static bool audio_load_track(int offset, bool start_play) | ||
2704 | { | 2350 | { |
2705 | char *trackname; | 2351 | char *trackname; |
2706 | off_t size; | ||
2707 | char msgbuf[80]; | 2352 | char msgbuf[80]; |
2353 | int fd = -1; | ||
2354 | int file_offset = 0; | ||
2355 | struct mp3entry id3; | ||
2708 | 2356 | ||
2709 | /* Stop buffer filling if there is no free track entries. | 2357 | /* Stop buffer filling if there is no free track entries. |
2710 | Don't fill up the last track entry (we wan't to store next track | 2358 | Don't fill up the last track entry (we wan't to store next track |
2711 | metadata there). */ | 2359 | metadata there). */ |
2712 | if (!audio_have_free_tracks()) | 2360 | if (!audio_have_free_tracks()) |
2713 | { | 2361 | { |
2714 | logf("No free tracks"); | 2362 | logf("No free tracks"); |
2715 | return false; | 2363 | return false; |
2716 | } | 2364 | } |
2717 | 2365 | ||
2718 | if (current_fd >= 0) | ||
2719 | { | ||
2720 | logf("Nonzero fd in alt"); | ||
2721 | close(current_fd); | ||
2722 | current_fd = -1; | ||
2723 | } | ||
2724 | |||
2725 | last_peek_offset++; | 2366 | last_peek_offset++; |
2726 | peek_again: | 2367 | peek_again: |
2727 | logf("Buffering track:%d/%d", track_widx, track_ridx); | 2368 | logf("Buffering track:%d/%d", track_widx, track_ridx); |
2728 | /* Get track name from current playlist read position. */ | 2369 | /* Get track name from current playlist read position. */ |
2729 | while ((trackname = playlist_peek(last_peek_offset)) != NULL) | 2370 | while ((trackname = playlist_peek(last_peek_offset)) != NULL) |
2730 | { | 2371 | { |
2731 | /* Handle broken playlists. */ | 2372 | /* Handle broken playlists. */ |
2732 | current_fd = open(trackname, O_RDONLY); | 2373 | fd = open(trackname, O_RDONLY); |
2733 | if (current_fd < 0) | 2374 | if (fd < 0) |
2734 | { | 2375 | { |
2735 | logf("Open failed"); | 2376 | logf("Open failed"); |
2736 | /* Skip invalid entry from playlist. */ | 2377 | /* Skip invalid entry from playlist. */ |
2737 | playlist_skip_entry(NULL, last_peek_offset); | 2378 | playlist_skip_entry(NULL, last_peek_offset); |
2738 | } | 2379 | } |
2739 | else | 2380 | else |
2740 | break; | 2381 | break; |
2741 | } | 2382 | } |
2742 | 2383 | ||
2743 | if (!trackname) | 2384 | if (!trackname) |
2744 | { | 2385 | { |
2745 | logf("End-of-playlist"); | 2386 | logf("End-of-playlist"); |
2746 | playlist_end = true; | 2387 | playlist_end = true; |
2747 | return false; | 2388 | return false; |
2748 | } | 2389 | } |
2749 | 2390 | ||
2750 | /* Initialize track entry. */ | 2391 | tracks[track_widx].filesize = filesize(fd); |
2751 | size = filesize(current_fd); | ||
2752 | tracks[track_widx].filerem = size; | ||
2753 | tracks[track_widx].filesize = size; | ||
2754 | tracks[track_widx].available = 0; | ||
2755 | 2392 | ||
2756 | /* Set default values */ | 2393 | /* Set default values */ |
2757 | if (start_play) | 2394 | if (start_play) |
2758 | { | 2395 | { |
2759 | int last_codec = current_codec; | 2396 | int last_codec = current_codec; |
2760 | 2397 | ||
2761 | set_current_codec(CODEC_IDX_AUDIO); | 2398 | set_current_codec(CODEC_IDX_AUDIO); |
2762 | conf_watermark = AUDIO_DEFAULT_WATERMARK; | 2399 | buf_set_conf(BUFFERING_SET_WATERMARK, AUDIO_DEFAULT_WATERMARK); |
2763 | conf_filechunk = AUDIO_DEFAULT_FILECHUNK; | 2400 | buf_set_conf(BUFFERING_SET_CHUNKSIZE, AUDIO_DEFAULT_FILECHUNK); |
2764 | conf_preseek = AUDIO_REBUFFER_GUESS_SIZE; | 2401 | buf_set_conf(BUFFERING_SET_PRESEEK, AUDIO_REBUFFER_GUESS_SIZE); |
2765 | dsp_configure(DSP_RESET, 0); | 2402 | dsp_configure(DSP_RESET, 0); |
2766 | set_current_codec(last_codec); | 2403 | set_current_codec(last_codec); |
2404 | |||
2405 | track_changed = true; | ||
2406 | playlist_update_resume_info(audio_current_track()); | ||
2767 | } | 2407 | } |
2768 | 2408 | ||
2769 | /* Get track metadata if we don't already have it. */ | 2409 | /* Get track metadata if we don't already have it. */ |
2770 | if (!tracks[track_widx].taginfo_ready) | 2410 | if (tracks[track_widx].id3_hid <= 0) |
2771 | { | 2411 | { |
2772 | if (get_metadata(&(tracks[track_widx].id3),current_fd,trackname)) | 2412 | if (get_metadata(&id3, fd, trackname)) |
2773 | { | 2413 | { |
2774 | tracks[track_widx].taginfo_ready = true; | 2414 | tracks[track_widx].id3_hid = bufalloc(&id3, sizeof(struct mp3entry), |
2775 | if (start_play) | 2415 | TYPE_ID3); |
2416 | tracks[track_widx].taginfo_ready = (tracks[track_widx].id3_hid > 0); | ||
2417 | |||
2418 | if (tracks[track_widx].id3_hid <= 0) | ||
2419 | { | ||
2420 | last_peek_offset--; | ||
2421 | close(fd); | ||
2422 | return false; | ||
2423 | } | ||
2424 | |||
2425 | if (track_widx == track_ridx) | ||
2426 | copy_mp3entry(&curtrack_id3, &id3); | ||
2427 | else if (track_widx == ((track_ridx + 1) & MAX_TRACK_MASK)) | ||
2428 | copy_mp3entry(&nexttrack_id3, &id3); | ||
2429 | |||
2430 | if (start_play) | ||
2776 | { | 2431 | { |
2777 | track_changed = true; | 2432 | track_changed = true; |
2778 | playlist_update_resume_info(audio_current_track()); | 2433 | playlist_update_resume_info(audio_current_track()); |
2779 | } | 2434 | } |
2780 | } | 2435 | } |
2781 | else | 2436 | else |
2782 | { | 2437 | { |
2783 | logf("mde:%s!",trackname); | 2438 | logf("mde:%s!",trackname); |
2784 | 2439 | ||
2785 | /* Set filesize to zero to indicate no file was loaded. */ | ||
2786 | tracks[track_widx].filesize = 0; | ||
2787 | tracks[track_widx].filerem = 0; | ||
2788 | close(current_fd); | ||
2789 | current_fd = -1; | ||
2790 | |||
2791 | /* Skip invalid entry from playlist. */ | 2440 | /* Skip invalid entry from playlist. */ |
2792 | playlist_skip_entry(NULL, last_peek_offset); | 2441 | playlist_skip_entry(NULL, last_peek_offset); |
2793 | tracks[track_widx].taginfo_ready = false; | 2442 | tracks[track_widx].taginfo_ready = false; |
@@ -2796,6 +2445,9 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2796 | 2445 | ||
2797 | } | 2446 | } |
2798 | 2447 | ||
2448 | close(fd); | ||
2449 | |||
2450 | #if 0 | ||
2799 | if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1) | 2451 | if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1) |
2800 | { | 2452 | { |
2801 | char cuepath[MAX_PATH]; | 2453 | char cuepath[MAX_PATH]; |
@@ -2810,17 +2462,11 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2810 | cue_spoof_id3(curr_cue, &tracks[track_widx].id3); | 2462 | cue_spoof_id3(curr_cue, &tracks[track_widx].id3); |
2811 | } | 2463 | } |
2812 | } | 2464 | } |
2465 | #endif | ||
2813 | 2466 | ||
2814 | /* Load the codec. */ | 2467 | /* Load the codec. */ |
2815 | tracks[track_widx].codecbuf = &filebuf[buf_widx]; | 2468 | if (!audio_loadcodec(start_play)) |
2816 | if (!audio_loadcodec(start_play)) | ||
2817 | { | 2469 | { |
2818 | /* Set filesize to zero to indicate no file was loaded. */ | ||
2819 | tracks[track_widx].filesize = 0; | ||
2820 | tracks[track_widx].filerem = 0; | ||
2821 | close(current_fd); | ||
2822 | current_fd = -1; | ||
2823 | |||
2824 | if (tracks[track_widx].codecsize) | 2470 | if (tracks[track_widx].codecsize) |
2825 | { | 2471 | { |
2826 | /* No space for codec on buffer, not an error */ | 2472 | /* No space for codec on buffer, not an error */ |
@@ -2839,32 +2485,35 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2839 | goto peek_again; | 2485 | goto peek_again; |
2840 | } | 2486 | } |
2841 | 2487 | ||
2842 | tracks[track_widx].start_pos = 0; | 2488 | struct mp3entry *track_id3; |
2843 | set_filebuf_watermark(buffer_margin); | ||
2844 | tracks[track_widx].id3.elapsed = 0; | ||
2845 | 2489 | ||
2846 | if (offset > 0) | 2490 | if (track_widx == track_ridx) |
2491 | track_id3 = &curtrack_id3; | ||
2492 | else if (track_widx == ((track_ridx + 1) & MAX_TRACK_MASK)) | ||
2493 | track_id3 = &nexttrack_id3; | ||
2494 | else | ||
2495 | track_id3 = bufgetid3(tracks[track_widx].id3_hid); | ||
2496 | |||
2497 | set_filebuf_watermark(buffer_margin, 0); | ||
2498 | track_id3->elapsed = 0; | ||
2499 | |||
2500 | if (offset > 0) | ||
2847 | { | 2501 | { |
2848 | switch (tracks[track_widx].id3.codectype) { | 2502 | switch (track_id3->codectype) { |
2849 | case AFMT_MPA_L1: | 2503 | case AFMT_MPA_L1: |
2850 | case AFMT_MPA_L2: | 2504 | case AFMT_MPA_L2: |
2851 | case AFMT_MPA_L3: | 2505 | case AFMT_MPA_L3: |
2852 | lseek(current_fd, offset, SEEK_SET); | 2506 | file_offset = offset; |
2853 | tracks[track_widx].id3.offset = offset; | 2507 | track_id3->offset = offset; |
2854 | audio_set_elapsed(&tracks[track_widx].id3); | 2508 | audio_set_elapsed(track_id3); |
2855 | tracks[track_widx].filerem = size - offset; | ||
2856 | ci.curpos = offset; | 2509 | ci.curpos = offset; |
2857 | tracks[track_widx].start_pos = offset; | ||
2858 | break; | 2510 | break; |
2859 | 2511 | ||
2860 | case AFMT_WAVPACK: | 2512 | case AFMT_WAVPACK: |
2861 | lseek(current_fd, offset, SEEK_SET); | 2513 | file_offset = offset; |
2862 | tracks[track_widx].id3.offset = offset; | 2514 | track_id3->offset = offset; |
2863 | tracks[track_widx].id3.elapsed = | 2515 | track_id3->elapsed = track_id3->length / 2; |
2864 | tracks[track_widx].id3.length / 2; | ||
2865 | tracks[track_widx].filerem = size - offset; | ||
2866 | ci.curpos = offset; | 2516 | ci.curpos = offset; |
2867 | tracks[track_widx].start_pos = offset; | ||
2868 | break; | 2517 | break; |
2869 | 2518 | ||
2870 | case AFMT_OGG_VORBIS: | 2519 | case AFMT_OGG_VORBIS: |
@@ -2875,57 +2524,27 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2875 | case AFMT_AAC: | 2524 | case AFMT_AAC: |
2876 | case AFMT_MPC: | 2525 | case AFMT_MPC: |
2877 | case AFMT_APE: | 2526 | case AFMT_APE: |
2878 | tracks[track_widx].id3.offset = offset; | 2527 | track_id3->offset = offset; |
2879 | break; | 2528 | break; |
2880 | } | 2529 | } |
2881 | } | 2530 | } |
2882 | |||
2883 | logf("alt:%s", trackname); | ||
2884 | tracks[track_widx].buf_idx = buf_widx; | ||
2885 | |||
2886 | return audio_read_file(rebuffer); | ||
2887 | } | ||
2888 | 2531 | ||
2889 | static bool audio_read_next_metadata(void) | 2532 | logf("alt:%s", trackname); |
2890 | { | ||
2891 | int fd; | ||
2892 | char *trackname; | ||
2893 | int next_idx; | ||
2894 | int status; | ||
2895 | |||
2896 | next_idx = track_widx; | ||
2897 | if (tracks[next_idx].taginfo_ready) | ||
2898 | { | ||
2899 | next_idx++; | ||
2900 | next_idx &= MAX_TRACK_MASK; | ||
2901 | |||
2902 | if (tracks[next_idx].taginfo_ready) | ||
2903 | return true; | ||
2904 | } | ||
2905 | 2533 | ||
2906 | trackname = playlist_peek(last_peek_offset + 1); | 2534 | tracks[track_widx].audio_hid = bufopen(trackname, file_offset, TYPE_AUDIO); |
2907 | if (!trackname) | ||
2908 | return false; | ||
2909 | 2535 | ||
2910 | fd = open(trackname, O_RDONLY); | 2536 | if (tracks[track_widx].audio_hid <= 0) |
2911 | if (fd < 0) | ||
2912 | return false; | 2537 | return false; |
2913 | 2538 | ||
2914 | status = get_metadata(&(tracks[next_idx].id3),fd,trackname); | 2539 | if (start_play) |
2915 | /* Preload the glyphs in the tags */ | ||
2916 | if (status) | ||
2917 | { | 2540 | { |
2918 | tracks[next_idx].taginfo_ready = true; | 2541 | buf_request_buffer_handle(tracks[track_widx].audio_hid); |
2919 | if (tracks[next_idx].id3.title) | ||
2920 | lcd_getstringsize(tracks[next_idx].id3.title, NULL, NULL); | ||
2921 | if (tracks[next_idx].id3.artist) | ||
2922 | lcd_getstringsize(tracks[next_idx].id3.artist, NULL, NULL); | ||
2923 | if (tracks[next_idx].id3.album) | ||
2924 | lcd_getstringsize(tracks[next_idx].id3.album, NULL, NULL); | ||
2925 | } | 2542 | } |
2926 | close(fd); | ||
2927 | 2543 | ||
2928 | return status; | 2544 | track_widx++; |
2545 | track_widx &= MAX_TRACK_MASK; | ||
2546 | |||
2547 | return true; | ||
2929 | } | 2548 | } |
2930 | 2549 | ||
2931 | /* Send callback events to notify about new tracks. */ | 2550 | /* Send callback events to notify about new tracks. */ |
@@ -2939,7 +2558,7 @@ static void audio_generate_postbuffer_events(void) | |||
2939 | if (audio_have_tracks()) | 2558 | if (audio_have_tracks()) |
2940 | { | 2559 | { |
2941 | cur_idx = track_ridx; | 2560 | cur_idx = track_ridx; |
2942 | 2561 | ||
2943 | while (1) { | 2562 | while (1) { |
2944 | if (!tracks[cur_idx].event_sent) | 2563 | if (!tracks[cur_idx].event_sent) |
2945 | { | 2564 | { |
@@ -2947,8 +2566,8 @@ static void audio_generate_postbuffer_events(void) | |||
2947 | { | 2566 | { |
2948 | /* Mark the event 'sent' even if we don't really send one */ | 2567 | /* Mark the event 'sent' even if we don't really send one */ |
2949 | tracks[last_idx].event_sent = true; | 2568 | tracks[last_idx].event_sent = true; |
2950 | if (track_buffer_callback) | 2569 | if (track_buffer_callback && tracks[last_idx].id3_hid > 0) |
2951 | track_buffer_callback(&tracks[last_idx].id3, false); | 2570 | track_buffer_callback(bufgetid3(tracks[last_idx].id3_hid), false); |
2952 | } | 2571 | } |
2953 | last_idx = cur_idx; | 2572 | last_idx = cur_idx; |
2954 | } | 2573 | } |
@@ -2961,35 +2580,19 @@ static void audio_generate_postbuffer_events(void) | |||
2961 | if (last_idx >= 0 && !tracks[last_idx].event_sent) | 2580 | if (last_idx >= 0 && !tracks[last_idx].event_sent) |
2962 | { | 2581 | { |
2963 | tracks[last_idx].event_sent = true; | 2582 | tracks[last_idx].event_sent = true; |
2964 | if (track_buffer_callback) | 2583 | if (track_buffer_callback && tracks[last_idx].id3_hid > 0) |
2965 | track_buffer_callback(&tracks[last_idx].id3, true); | 2584 | track_buffer_callback(bufgetid3(tracks[last_idx].id3_hid), true); |
2966 | } | 2585 | } |
2967 | } | 2586 | } |
2968 | } | 2587 | } |
2969 | 2588 | ||
2970 | static bool audio_initialize_buffer_fill(bool clear_tracks) | 2589 | static void low_buffer_callback(void) |
2971 | { | 2590 | { |
2972 | /* Don't initialize if we're already initialized */ | 2591 | LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER"); |
2973 | if (filling) | 2592 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); |
2974 | return true; | ||
2975 | |||
2976 | logf("Starting buffer fill"); | ||
2977 | |||
2978 | /* Set the filling flag true before calling audio_clear_tracks as that | ||
2979 | * function can yield and we start looping. */ | ||
2980 | filling = true; | ||
2981 | |||
2982 | if (clear_tracks) | ||
2983 | audio_clear_track_entries(false); | ||
2984 | |||
2985 | /* Save the current resume position once. */ | ||
2986 | playlist_update_resume_info(audio_current_track()); | ||
2987 | |||
2988 | return true; | ||
2989 | } | 2593 | } |
2990 | 2594 | ||
2991 | static void audio_fill_file_buffer( | 2595 | static void audio_fill_file_buffer(bool start_play, size_t offset) |
2992 | bool start_play, bool rebuffer, size_t offset) | ||
2993 | { | 2596 | { |
2994 | bool had_next_track = audio_next_track() != NULL; | 2597 | bool had_next_track = audio_next_track() != NULL; |
2995 | bool continue_buffering; | 2598 | bool continue_buffering; |
@@ -3000,61 +2603,49 @@ static void audio_fill_file_buffer( | |||
3000 | if (buffer_state != BUFFER_STATE_INITIALIZED) | 2603 | if (buffer_state != BUFFER_STATE_INITIALIZED) |
3001 | audio_reset_buffer(); | 2604 | audio_reset_buffer(); |
3002 | 2605 | ||
3003 | if (!audio_initialize_buffer_fill(!start_play)) | 2606 | logf("Starting buffer fill"); |
3004 | return ; | ||
3005 | 2607 | ||
3006 | /* If we have a partially buffered track, continue loading, | 2608 | if (!start_play) |
3007 | * otherwise load a new track */ | 2609 | audio_clear_track_entries(false); |
3008 | if (tracks[track_widx].filesize > 0) | ||
3009 | continue_buffering = audio_read_file(rebuffer); | ||
3010 | else | ||
3011 | continue_buffering = audio_load_track(offset, start_play, rebuffer); | ||
3012 | 2610 | ||
3013 | if (!had_next_track && audio_next_track()) | 2611 | /* Save the current resume position once. */ |
3014 | track_changed = true; | 2612 | playlist_update_resume_info(audio_current_track()); |
3015 | 2613 | ||
3016 | /* If we're done buffering */ | 2614 | do { |
3017 | if (!continue_buffering) | 2615 | continue_buffering = audio_load_track(offset, start_play); |
3018 | { | 2616 | start_play = false; |
3019 | audio_read_next_metadata(); | 2617 | offset = 0; |
2618 | sleep(1); | ||
2619 | } while (continue_buffering); | ||
3020 | 2620 | ||
3021 | audio_generate_postbuffer_events(); | 2621 | if (!had_next_track && audio_next_track()) |
3022 | filling = false; | 2622 | track_changed = true; |
3023 | } | ||
3024 | #ifndef SIMULATOR | ||
3025 | ata_sleep(); | ||
3026 | #endif | ||
3027 | 2623 | ||
2624 | audio_generate_postbuffer_events(); | ||
2625 | register_buffer_low_callback(low_buffer_callback); | ||
3028 | } | 2626 | } |
3029 | 2627 | ||
3030 | static void audio_rebuffer(void) | 2628 | static void audio_rebuffer(void) |
3031 | { | 2629 | { |
3032 | logf("Forcing rebuffer"); | 2630 | logf("Forcing rebuffer"); |
3033 | 2631 | ||
3034 | /* Stop in progress fill, and clear open file descriptor */ | 2632 | clear_track_info(CUR_TI); |
3035 | if (current_fd >= 0) | ||
3036 | { | ||
3037 | close(current_fd); | ||
3038 | current_fd = -1; | ||
3039 | } | ||
3040 | filling = false; | ||
3041 | 2633 | ||
3042 | /* Reset buffer and track pointers */ | 2634 | /* Reset track pointers */ |
3043 | CUR_TI->buf_idx = buf_ridx = buf_widx = 0; | ||
3044 | track_widx = track_ridx; | 2635 | track_widx = track_ridx; |
3045 | audio_clear_track_entries(true); | 2636 | audio_clear_track_entries(true); |
3046 | CUR_TI->available = 0; | 2637 | |
2638 | /* Just to make sure none were forgotten */ | ||
2639 | audio_release_tracks(); | ||
3047 | 2640 | ||
3048 | /* Fill the buffer */ | 2641 | /* Fill the buffer */ |
3049 | last_peek_offset = -1; | 2642 | last_peek_offset = -1; |
3050 | CUR_TI->filesize = 0; | ||
3051 | CUR_TI->start_pos = 0; | ||
3052 | ci.curpos = 0; | 2643 | ci.curpos = 0; |
3053 | 2644 | ||
3054 | if (!CUR_TI->taginfo_ready) | 2645 | if (!CUR_TI->taginfo_ready) |
3055 | memset(&CUR_TI->id3, 0, sizeof(struct mp3entry)); | 2646 | memset(&curtrack_id3, 0, sizeof(struct mp3entry)); |
3056 | 2647 | ||
3057 | audio_fill_file_buffer(false, true, 0); | 2648 | audio_fill_file_buffer(false, 0); |
3058 | } | 2649 | } |
3059 | 2650 | ||
3060 | static int audio_check_new_track(void) | 2651 | static int audio_check_new_track(void) |
@@ -3069,7 +2660,6 @@ static int audio_check_new_track(void) | |||
3069 | if (playlist_next_dir(ci.new_track)) | 2660 | if (playlist_next_dir(ci.new_track)) |
3070 | { | 2661 | { |
3071 | ci.new_track = 0; | 2662 | ci.new_track = 0; |
3072 | CUR_TI->taginfo_ready = false; | ||
3073 | audio_rebuffer(); | 2663 | audio_rebuffer(); |
3074 | goto skip_done; | 2664 | goto skip_done; |
3075 | } | 2665 | } |
@@ -3115,22 +2705,34 @@ static int audio_check_new_track(void) | |||
3115 | } | 2705 | } |
3116 | 2706 | ||
3117 | /* Save the old track */ | 2707 | /* Save the old track */ |
3118 | prev_ti = CUR_TI; | 2708 | copy_mp3entry(&prevtrack_id3, &curtrack_id3); |
2709 | |||
2710 | int i, idx; | ||
2711 | for (i = 0; i < ci.new_track; i++) | ||
2712 | { | ||
2713 | idx = (track_ridx + i) & MAX_TRACK_MASK; | ||
2714 | if (buf_handle_offset(tracks[idx].audio_hid) > 0) | ||
2715 | clear_track_info(&tracks[idx]); | ||
2716 | } | ||
3119 | 2717 | ||
3120 | /* Move to the new track */ | 2718 | /* Move to the new track */ |
3121 | track_ridx += ci.new_track; | 2719 | track_ridx += ci.new_track; |
3122 | track_ridx &= MAX_TRACK_MASK; | 2720 | track_ridx &= MAX_TRACK_MASK; |
3123 | 2721 | ||
2722 | buf_set_base_handle(CUR_TI->audio_hid); | ||
2723 | |||
3124 | if (automatic_skip) | 2724 | if (automatic_skip) |
2725 | { | ||
3125 | playlist_end = false; | 2726 | playlist_end = false; |
2727 | wps_offset = -ci.new_track; | ||
2728 | } | ||
3126 | 2729 | ||
3127 | track_changed = !automatic_skip; | 2730 | track_changed = true; |
3128 | 2731 | ||
3129 | /* If it is not safe to even skip this many track entries */ | 2732 | /* If it is not safe to even skip this many track entries */ |
3130 | if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK) | 2733 | if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK) |
3131 | { | 2734 | { |
3132 | ci.new_track = 0; | 2735 | ci.new_track = 0; |
3133 | CUR_TI->taginfo_ready = false; | ||
3134 | audio_rebuffer(); | 2736 | audio_rebuffer(); |
3135 | goto skip_done; | 2737 | goto skip_done; |
3136 | } | 2738 | } |
@@ -3146,18 +2748,13 @@ static int audio_check_new_track(void) | |||
3146 | } | 2748 | } |
3147 | 2749 | ||
3148 | /* The track may be in memory, see if it really is */ | 2750 | /* The track may be in memory, see if it really is */ |
3149 | if (forward) | 2751 | if (!forward) |
3150 | { | ||
3151 | if (!audio_buffer_wind_forward(track_ridx, old_track_ridx)) | ||
3152 | audio_rebuffer(); | ||
3153 | } | ||
3154 | else | ||
3155 | { | 2752 | { |
3156 | int cur_idx = track_ridx; | 2753 | int cur_idx = track_ridx; |
3157 | bool taginfo_ready = true; | 2754 | bool taginfo_ready = true; |
3158 | bool wrap = track_ridx > old_track_ridx; | 2755 | bool wrap = track_ridx > old_track_ridx; |
3159 | 2756 | ||
3160 | while (1) | 2757 | while (1) |
3161 | { | 2758 | { |
3162 | cur_idx++; | 2759 | cur_idx++; |
3163 | cur_idx &= MAX_TRACK_MASK; | 2760 | cur_idx &= MAX_TRACK_MASK; |
@@ -3170,19 +2767,9 @@ static int audio_check_new_track(void) | |||
3170 | taginfo_ready = false; | 2767 | taginfo_ready = false; |
3171 | break; | 2768 | break; |
3172 | } | 2769 | } |
3173 | |||
3174 | tracks[cur_idx].available = tracks[cur_idx].filesize; | ||
3175 | if (tracks[cur_idx].codecsize) | ||
3176 | tracks[cur_idx].has_codec = true; | ||
3177 | } | ||
3178 | if (taginfo_ready) | ||
3179 | { | ||
3180 | if (!audio_buffer_wind_backward(track_ridx, old_track_ridx)) | ||
3181 | audio_rebuffer(); | ||
3182 | } | 2770 | } |
3183 | else | 2771 | if (!taginfo_ready) |
3184 | { | 2772 | { |
3185 | CUR_TI->taginfo_ready = false; | ||
3186 | audio_rebuffer(); | 2773 | audio_rebuffer(); |
3187 | } | 2774 | } |
3188 | } | 2775 | } |
@@ -3193,66 +2780,6 @@ skip_done: | |||
3193 | return Q_CODEC_REQUEST_COMPLETE; | 2780 | return Q_CODEC_REQUEST_COMPLETE; |
3194 | } | 2781 | } |
3195 | 2782 | ||
3196 | static int audio_rebuffer_and_seek(size_t newpos) | ||
3197 | { | ||
3198 | size_t real_preseek; | ||
3199 | int fd; | ||
3200 | char *trackname; | ||
3201 | |||
3202 | /* (Re-)open current track's file handle. */ | ||
3203 | trackname = playlist_peek(0); | ||
3204 | fd = open(trackname, O_RDONLY); | ||
3205 | if (fd < 0) | ||
3206 | { | ||
3207 | LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_FAILED"); | ||
3208 | return Q_CODEC_REQUEST_FAILED; | ||
3209 | } | ||
3210 | |||
3211 | if (current_fd >= 0) | ||
3212 | close(current_fd); | ||
3213 | current_fd = fd; | ||
3214 | |||
3215 | playlist_end = false; | ||
3216 | |||
3217 | ci.curpos = newpos; | ||
3218 | |||
3219 | /* Clear codec buffer. */ | ||
3220 | track_widx = track_ridx; | ||
3221 | tracks[track_widx].buf_idx = buf_widx = buf_ridx = 0; | ||
3222 | |||
3223 | last_peek_offset = 0; | ||
3224 | filling = false; | ||
3225 | audio_initialize_buffer_fill(true); | ||
3226 | |||
3227 | /* This may have been tweaked by the id3v1 code */ | ||
3228 | CUR_TI->filesize=filesize(fd); | ||
3229 | if (newpos > conf_preseek) | ||
3230 | { | ||
3231 | CUR_TI->start_pos = newpos - conf_preseek; | ||
3232 | lseek(current_fd, CUR_TI->start_pos, SEEK_SET); | ||
3233 | CUR_TI->filerem = CUR_TI->filesize - CUR_TI->start_pos; | ||
3234 | real_preseek = conf_preseek; | ||
3235 | } | ||
3236 | else | ||
3237 | { | ||
3238 | CUR_TI->start_pos = 0; | ||
3239 | CUR_TI->filerem = CUR_TI->filesize; | ||
3240 | real_preseek = newpos; | ||
3241 | } | ||
3242 | |||
3243 | CUR_TI->available = 0; | ||
3244 | |||
3245 | audio_read_file(real_preseek); | ||
3246 | |||
3247 | /* Account for the data we just read that is 'behind' us now */ | ||
3248 | CUR_TI->available -= real_preseek; | ||
3249 | |||
3250 | buf_ridx = RINGBUF_ADD(buf_ridx, real_preseek); | ||
3251 | |||
3252 | LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_COMPLETE"); | ||
3253 | return Q_CODEC_REQUEST_COMPLETE; | ||
3254 | } | ||
3255 | |||
3256 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, | 2783 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, |
3257 | bool last_track)) | 2784 | bool last_track)) |
3258 | { | 2785 | { |
@@ -3309,7 +2836,7 @@ static void audio_stop_playback(void) | |||
3309 | /* Save the current playing spot, or NULL if the playlist has ended */ | 2836 | /* Save the current playing spot, or NULL if the playlist has ended */ |
3310 | playlist_update_resume_info(id3); | 2837 | playlist_update_resume_info(id3); |
3311 | 2838 | ||
3312 | prev_track_elapsed = CUR_TI->id3.elapsed; | 2839 | prev_track_elapsed = curtrack_id3.elapsed; |
3313 | 2840 | ||
3314 | /* Increment index so runtime info is saved in audio_clear_track_entries(). | 2841 | /* Increment index so runtime info is saved in audio_clear_track_entries(). |
3315 | * Done here, as audio_stop_playback() may be called more than once. | 2842 | * Done here, as audio_stop_playback() may be called more than once. |
@@ -3324,19 +2851,18 @@ static void audio_stop_playback(void) | |||
3324 | } | 2851 | } |
3325 | } | 2852 | } |
3326 | 2853 | ||
3327 | filling = false; | ||
3328 | paused = false; | 2854 | paused = false; |
3329 | audio_stop_codec_flush(); | 2855 | audio_stop_codec_flush(); |
3330 | playing = false; | 2856 | playing = false; |
3331 | 2857 | ||
3332 | if (current_fd >= 0) | 2858 | /* Close all tracks */ |
3333 | { | 2859 | audio_release_tracks(); |
3334 | close(current_fd); | ||
3335 | current_fd = -1; | ||
3336 | } | ||
3337 | 2860 | ||
3338 | /* Mark all entries null. */ | 2861 | /* Mark all entries null. */ |
3339 | audio_clear_track_entries(false); | 2862 | audio_clear_track_entries(false); |
2863 | |||
2864 | memset(&curtrack_id3, 0, sizeof(struct mp3entry)); | ||
2865 | memset(&nexttrack_id3, 0, sizeof(struct mp3entry)); | ||
3340 | } | 2866 | } |
3341 | 2867 | ||
3342 | static void audio_play_start(size_t offset) | 2868 | static void audio_play_start(size_t offset) |
@@ -3358,26 +2884,19 @@ static void audio_play_start(size_t offset) | |||
3358 | ci.new_track = 0; | 2884 | ci.new_track = 0; |
3359 | ci.seek_time = 0; | 2885 | ci.seek_time = 0; |
3360 | wps_offset = 0; | 2886 | wps_offset = 0; |
3361 | |||
3362 | if (current_fd >= 0) | ||
3363 | { | ||
3364 | close(current_fd); | ||
3365 | current_fd = -1; | ||
3366 | } | ||
3367 | 2887 | ||
3368 | sound_set_volume(global_settings.volume); | 2888 | sound_set_volume(global_settings.volume); |
3369 | track_widx = track_ridx = 0; | 2889 | track_widx = track_ridx = 0; |
3370 | buf_ridx = buf_widx = 0; | ||
3371 | 2890 | ||
3372 | /* Mark all entries null. */ | 2891 | /* Mark all entries null. */ |
3373 | memset(tracks, 0, sizeof(struct track_info) * MAX_TRACK); | 2892 | memset(tracks, 0, sizeof(struct track_info) * MAX_TRACK); |
3374 | 2893 | ||
3375 | last_peek_offset = -1; | 2894 | last_peek_offset = -1; |
3376 | 2895 | ||
3377 | /* Officially playing */ | 2896 | /* Officially playing */ |
3378 | queue_reply(&audio_queue, 1); | 2897 | queue_reply(&audio_queue, 1); |
3379 | 2898 | ||
3380 | audio_fill_file_buffer(true, false, offset); | 2899 | audio_fill_file_buffer(true, offset); |
3381 | 2900 | ||
3382 | LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED"); | 2901 | LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED"); |
3383 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | 2902 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); |
@@ -3387,7 +2906,7 @@ static void audio_play_start(size_t offset) | |||
3387 | /* Invalidates all but currently playing track. */ | 2906 | /* Invalidates all but currently playing track. */ |
3388 | static void audio_invalidate_tracks(void) | 2907 | static void audio_invalidate_tracks(void) |
3389 | { | 2908 | { |
3390 | if (audio_have_tracks()) | 2909 | if (audio_have_tracks()) |
3391 | { | 2910 | { |
3392 | last_peek_offset = 0; | 2911 | last_peek_offset = 0; |
3393 | playlist_end = false; | 2912 | playlist_end = false; |
@@ -3396,13 +2915,9 @@ static void audio_invalidate_tracks(void) | |||
3396 | /* Mark all other entries null (also buffered wrong metadata). */ | 2915 | /* Mark all other entries null (also buffered wrong metadata). */ |
3397 | audio_clear_track_entries(true); | 2916 | audio_clear_track_entries(true); |
3398 | 2917 | ||
3399 | /* If the current track is fully buffered, advance the write pointer */ | 2918 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; |
3400 | if (tracks[track_widx].filerem == 0) | ||
3401 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | ||
3402 | 2919 | ||
3403 | buf_widx = RINGBUF_ADD(buf_ridx, CUR_TI->available); | 2920 | audio_fill_file_buffer(false, 0); |
3404 | |||
3405 | audio_read_next_metadata(); | ||
3406 | } | 2921 | } |
3407 | } | 2922 | } |
3408 | 2923 | ||
@@ -3421,18 +2936,10 @@ static void audio_new_playlist(void) | |||
3421 | track_widx++; | 2936 | track_widx++; |
3422 | track_widx &= MAX_TRACK_MASK; | 2937 | track_widx &= MAX_TRACK_MASK; |
3423 | 2938 | ||
3424 | /* Stop reading the current track */ | ||
3425 | CUR_TI->filerem = 0; | ||
3426 | close(current_fd); | ||
3427 | current_fd = -1; | ||
3428 | |||
3429 | /* Mark the current track as invalid to prevent skipping back to it */ | 2939 | /* Mark the current track as invalid to prevent skipping back to it */ |
3430 | CUR_TI->taginfo_ready = false; | 2940 | CUR_TI->taginfo_ready = false; |
3431 | |||
3432 | /* Invalidate the buffer other than the playing track */ | ||
3433 | buf_widx = RINGBUF_ADD(buf_ridx, CUR_TI->available); | ||
3434 | } | 2941 | } |
3435 | 2942 | ||
3436 | /* Signal the codec to initiate a track change forward */ | 2943 | /* Signal the codec to initiate a track change forward */ |
3437 | new_playlist = true; | 2944 | new_playlist = true; |
3438 | ci.new_track = 1; | 2945 | ci.new_track = 1; |
@@ -3440,7 +2947,7 @@ static void audio_new_playlist(void) | |||
3440 | /* Officially playing */ | 2947 | /* Officially playing */ |
3441 | queue_reply(&audio_queue, 1); | 2948 | queue_reply(&audio_queue, 1); |
3442 | 2949 | ||
3443 | audio_fill_file_buffer(false, true, 0); | 2950 | audio_fill_file_buffer(false, 0); |
3444 | } | 2951 | } |
3445 | 2952 | ||
3446 | static void audio_initiate_track_change(long direction) | 2953 | static void audio_initiate_track_change(long direction) |
@@ -3551,10 +3058,7 @@ static void audio_reset_buffer(void) | |||
3551 | will already be line aligned */ | 3058 | will already be line aligned */ |
3552 | filebuflen &= ~3; | 3059 | filebuflen &= ~3; |
3553 | 3060 | ||
3554 | /* Set the high watermark as 75% full...or 25% empty :) */ | 3061 | buffering_init(filebuf, filebuflen); |
3555 | #if MEM > 8 | ||
3556 | high_watermark = 3*filebuflen / 4; | ||
3557 | #endif | ||
3558 | 3062 | ||
3559 | /* Clear any references to the file buffer */ | 3063 | /* Clear any references to the file buffer */ |
3560 | buffer_state = BUFFER_STATE_INITIALIZED; | 3064 | buffer_state = BUFFER_STATE_INITIALIZED; |
@@ -3588,16 +3092,6 @@ static void audio_reset_buffer(void) | |||
3588 | #endif | 3092 | #endif |
3589 | } | 3093 | } |
3590 | 3094 | ||
3591 | #if MEM > 8 | ||
3592 | /* we dont want this rebuffering on targets with little ram | ||
3593 | because the disk may never spin down */ | ||
3594 | static bool ata_fillbuffer_callback(void) | ||
3595 | { | ||
3596 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA, 0); | ||
3597 | return true; | ||
3598 | } | ||
3599 | #endif | ||
3600 | |||
3601 | static void audio_thread(void) | 3095 | static void audio_thread(void) |
3602 | { | 3096 | { |
3603 | struct queue_event ev; | 3097 | struct queue_event ev; |
@@ -3618,41 +3112,17 @@ static void audio_thread(void) | |||
3618 | invalid when an audio codec is loaded */ | 3112 | invalid when an audio codec is loaded */ |
3619 | wait_for_voice_swap_in(); | 3113 | wait_for_voice_swap_in(); |
3620 | #endif | 3114 | #endif |
3621 | 3115 | ||
3622 | while (1) | 3116 | while (1) |
3623 | { | 3117 | { |
3624 | if (filling) | 3118 | queue_wait_w_tmo(&audio_queue, &ev, HZ/2); |
3625 | { | ||
3626 | queue_wait_w_tmo(&audio_queue, &ev, 0); | ||
3627 | if (ev.id == SYS_TIMEOUT) | ||
3628 | ev.id = Q_AUDIO_FILL_BUFFER; | ||
3629 | } | ||
3630 | else | ||
3631 | { | ||
3632 | queue_wait_w_tmo(&audio_queue, &ev, HZ/2); | ||
3633 | #if MEM > 8 | ||
3634 | if (playing && (ev.id == SYS_TIMEOUT) && | ||
3635 | (FILEBUFUSED < high_watermark)) | ||
3636 | register_ata_idle_func(ata_fillbuffer_callback); | ||
3637 | #endif | ||
3638 | } | ||
3639 | 3119 | ||
3640 | switch (ev.id) { | 3120 | switch (ev.id) { |
3641 | #if MEM > 8 | ||
3642 | case Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA: | ||
3643 | /* only fill if the disk is still spining */ | ||
3644 | #ifndef SIMULATOR | ||
3645 | if (!ata_disk_is_active()) | ||
3646 | break; | ||
3647 | #endif | ||
3648 | #endif /* MEM > 8 */ | ||
3649 | /* else fall through to Q_AUDIO_FILL_BUFFER */ | ||
3650 | case Q_AUDIO_FILL_BUFFER: | 3121 | case Q_AUDIO_FILL_BUFFER: |
3651 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); | 3122 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); |
3652 | if (!filling) | 3123 | if (!playing || playlist_end || ci.stop_codec) |
3653 | if (!playing || playlist_end || ci.stop_codec) | 3124 | break; |
3654 | break; | 3125 | audio_fill_file_buffer(false, 0); |
3655 | audio_fill_file_buffer(false, false, 0); | ||
3656 | break; | 3126 | break; |
3657 | 3127 | ||
3658 | case Q_AUDIO_PLAY: | 3128 | case Q_AUDIO_PLAY: |
@@ -3704,11 +3174,6 @@ static void audio_thread(void) | |||
3704 | ci.seek_time = (long)ev.data+1; | 3174 | ci.seek_time = (long)ev.data+1; |
3705 | break; | 3175 | break; |
3706 | 3176 | ||
3707 | case Q_AUDIO_REBUFFER_SEEK: | ||
3708 | LOGFQUEUE("audio < Q_AUDIO_REBUFFER_SEEK"); | ||
3709 | queue_reply(&audio_queue, audio_rebuffer_and_seek(ev.data)); | ||
3710 | break; | ||
3711 | |||
3712 | case Q_AUDIO_CHECK_NEW_TRACK: | 3177 | case Q_AUDIO_CHECK_NEW_TRACK: |
3713 | LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK"); | 3178 | LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK"); |
3714 | queue_reply(&audio_queue, audio_check_new_track()); | 3179 | queue_reply(&audio_queue, audio_check_new_track()); |
@@ -3727,8 +3192,14 @@ static void audio_thread(void) | |||
3727 | 3192 | ||
3728 | case Q_AUDIO_TRACK_CHANGED: | 3193 | case Q_AUDIO_TRACK_CHANGED: |
3729 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); | 3194 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); |
3195 | if (automatic_skip) | ||
3196 | { | ||
3197 | wps_offset = 0; | ||
3198 | automatic_skip = false; | ||
3199 | prevtrack_id3.path[0] = 0; | ||
3200 | } | ||
3730 | if (track_changed_callback) | 3201 | if (track_changed_callback) |
3731 | track_changed_callback(&CUR_TI->id3); | 3202 | track_changed_callback(&curtrack_id3); |
3732 | track_changed = true; | 3203 | track_changed = true; |
3733 | playlist_update_resume_info(audio_current_track()); | 3204 | playlist_update_resume_info(audio_current_track()); |
3734 | break; | 3205 | break; |
@@ -3740,6 +3211,8 @@ static void audio_thread(void) | |||
3740 | audio_stop_playback(); | 3211 | audio_stop_playback(); |
3741 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 3212 | usb_acknowledge(SYS_USB_CONNECTED_ACK); |
3742 | usb_wait_for_disconnect(&audio_queue); | 3213 | usb_wait_for_disconnect(&audio_queue); |
3214 | /* release tracks to make sure all handles are closed */ | ||
3215 | audio_release_tracks(); | ||
3743 | break; | 3216 | break; |
3744 | #endif | 3217 | #endif |
3745 | 3218 | ||
@@ -3749,6 +3222,7 @@ static void audio_thread(void) | |||
3749 | 3222 | ||
3750 | default: | 3223 | default: |
3751 | LOGFQUEUE("audio < default"); | 3224 | LOGFQUEUE("audio < default"); |
3225 | break; | ||
3752 | } /* end switch */ | 3226 | } /* end switch */ |
3753 | } /* end while */ | 3227 | } /* end while */ |
3754 | } | 3228 | } |
@@ -3796,6 +3270,7 @@ void audio_init(void) | |||
3796 | queue_init(&audio_queue, true); | 3270 | queue_init(&audio_queue, true); |
3797 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); | 3271 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); |
3798 | queue_init(&codec_queue, true); | 3272 | queue_init(&codec_queue, true); |
3273 | queue_enable_queue_send(&codec_queue, &codec_queue_sender_list); | ||
3799 | 3274 | ||
3800 | pcm_init(); | 3275 | pcm_init(); |
3801 | 3276 | ||
@@ -3858,7 +3333,7 @@ void audio_init(void) | |||
3858 | 3333 | ||
3859 | audio_thread_p = create_thread(audio_thread, audio_stack, | 3334 | audio_thread_p = create_thread(audio_thread, audio_stack, |
3860 | sizeof(audio_stack), CREATE_THREAD_FROZEN, | 3335 | sizeof(audio_stack), CREATE_THREAD_FROZEN, |
3861 | audio_thread_name IF_PRIO(, PRIORITY_BUFFERING) | 3336 | audio_thread_name IF_PRIO(, PRIORITY_BACKGROUND) |
3862 | IF_COP(, CPU)); | 3337 | IF_COP(, CPU)); |
3863 | 3338 | ||
3864 | #ifdef PLAYBACK_VOICE | 3339 | #ifdef PLAYBACK_VOICE |