diff options
-rw-r--r-- | apps/buffering.c | 6 | ||||
-rw-r--r-- | apps/buffering.h | 3 | ||||
-rw-r--r-- | apps/playback.c | 798 | ||||
-rw-r--r-- | firmware/export/system.h | 5 |
4 files changed, 483 insertions, 329 deletions
diff --git a/apps/buffering.c b/apps/buffering.c index 09b164ea4f..9bc7d730c5 100644 --- a/apps/buffering.c +++ b/apps/buffering.c | |||
@@ -43,6 +43,8 @@ | |||
43 | /* #define LOGF_ENABLE */ | 43 | /* #define LOGF_ENABLE */ |
44 | #include "logf.h" | 44 | #include "logf.h" |
45 | 45 | ||
46 | #define BUF_MAX_HANDLES 384 | ||
47 | |||
46 | /* macros to enable logf for queues | 48 | /* macros to enable logf for queues |
47 | logging on SYS_TIMEOUT can be disabled */ | 49 | logging on SYS_TIMEOUT can be disabled */ |
48 | #ifdef SIMULATOR | 50 | #ifdef SIMULATOR |
@@ -1120,6 +1122,10 @@ bool bufclose(int handle_id) | |||
1120 | return true; | 1122 | return true; |
1121 | } | 1123 | } |
1122 | #endif | 1124 | #endif |
1125 | if (handle_id <= 0) { | ||
1126 | return true; | ||
1127 | } | ||
1128 | |||
1123 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id); | 1129 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id); |
1124 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); | 1130 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); |
1125 | } | 1131 | } |
diff --git a/apps/buffering.h b/apps/buffering.h index 5a1369a31d..4d4cb39df3 100644 --- a/apps/buffering.h +++ b/apps/buffering.h | |||
@@ -36,6 +36,7 @@ enum data_type { | |||
36 | TYPE_ATOMIC_AUDIO, | 36 | TYPE_ATOMIC_AUDIO, |
37 | TYPE_CUESHEET, | 37 | TYPE_CUESHEET, |
38 | TYPE_BITMAP, | 38 | TYPE_BITMAP, |
39 | TYPE_RAW_ATOMIC, | ||
39 | }; | 40 | }; |
40 | 41 | ||
41 | /* Error return values */ | 42 | /* Error return values */ |
@@ -74,8 +75,6 @@ bool buffering_reset(char *buf, size_t buflen); | |||
74 | * NOTE: Tail operations are only legal when the end of the file is buffered. | 75 | * NOTE: Tail operations are only legal when the end of the file is buffered. |
75 | ****************************************************************************/ | 76 | ****************************************************************************/ |
76 | 77 | ||
77 | #define BUF_MAX_HANDLES 256 | ||
78 | |||
79 | int bufopen(const char *file, size_t offset, enum data_type type, | 78 | int bufopen(const char *file, size_t offset, enum data_type type, |
80 | void *user_data); | 79 | void *user_data); |
81 | int bufalloc(const void *src, size_t size, enum data_type type); | 80 | int bufalloc(const void *src, size_t size, enum data_type type); |
diff --git a/apps/playback.c b/apps/playback.c index c2fc30f0a1..356aad794a 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -73,7 +73,9 @@ | |||
73 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) | 73 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) |
74 | 74 | ||
75 | /* Define LOGF_ENABLE to enable logf output in this file */ | 75 | /* Define LOGF_ENABLE to enable logf output in this file */ |
76 | /* #define LOGF_ENABLE */ | 76 | #if 0 |
77 | #define LOGF_ENABLE | ||
78 | #endif | ||
77 | #include "logf.h" | 79 | #include "logf.h" |
78 | 80 | ||
79 | /* Macros to enable logf for queues | 81 | /* Macros to enable logf for queues |
@@ -193,64 +195,71 @@ static enum filling_state | |||
193 | } filling = STATE_IDLE; | 195 | } filling = STATE_IDLE; |
194 | 196 | ||
195 | /* Track info - holds information about each track in the buffer */ | 197 | /* Track info - holds information about each track in the buffer */ |
198 | #ifdef HAVE_ALBUMART | ||
199 | #define TRACK_INFO_AA MAX_MULTIPLE_AA | ||
200 | #else | ||
201 | #define TRACK_INFO_AA 0 | ||
202 | #endif | ||
203 | |||
204 | #ifdef HAVE_CODEC_BUFFERING | ||
205 | #define TRACK_INFO_CODEC 1 | ||
206 | #else | ||
207 | #define TRACK_INFO_CODEC 0 | ||
208 | #endif | ||
209 | |||
210 | #define TRACK_INFO_HANDLES (3 + TRACK_INFO_AA + TRACK_INFO_CODEC) | ||
211 | |||
196 | struct track_info | 212 | struct track_info |
197 | { | 213 | { |
214 | int self_hid; /* handle for the info on buffer */ | ||
215 | |||
198 | /* In per-track allocated order: */ | 216 | /* In per-track allocated order: */ |
199 | int id3_hid; /* Metadata handle ID */ | 217 | union { |
200 | int cuesheet_hid; /* Parsed cuesheet handle ID */ | 218 | int handle[TRACK_INFO_HANDLES]; /* array mirror for efficient wipe/close */ |
219 | struct { | ||
220 | int id3_hid; /* Metadata handle ID */ | ||
221 | int cuesheet_hid; /* Parsed cuesheet handle ID */ | ||
201 | #ifdef HAVE_ALBUMART | 222 | #ifdef HAVE_ALBUMART |
202 | int aa_hid[MAX_MULTIPLE_AA];/* Album art handle IDs */ | 223 | int aa_hid[MAX_MULTIPLE_AA]; /* Album art handle IDs */ |
203 | #endif | 224 | #endif |
204 | #ifdef HAVE_CODEC_BUFFERING | 225 | #ifdef HAVE_CODEC_BUFFERING |
205 | int codec_hid; /* Buffered codec handle ID */ | 226 | int codec_hid; /* Buffered codec handle ID */ |
206 | #endif | 227 | #endif |
207 | int audio_hid; /* Main audio data handle ID */ | 228 | int audio_hid; /* Main audio data handle ID */ |
208 | size_t filesize; /* File total length on disk | 229 | }; }; |
209 | TODO: This should be stored | 230 | off_t filesize; /* File total length on disk |
210 | in the handle or the | 231 | TODO: This should be stored |
211 | id3 and would use less | 232 | in the handle or the |
212 | ram */ | 233 | id3 and would use less |
234 | ram */ | ||
213 | }; | 235 | }; |
214 | 236 | ||
215 | /* Track list - holds info about all buffered tracks */ | 237 | /* On-buffer info format; includes links */ |
216 | #if MEMORYSIZE >= 32 | 238 | struct track_buf_info |
217 | #define TRACK_LIST_LEN 128 /* Must be 2^int(+n) */ | 239 | { |
218 | #elif MEMORYSIZE >= 16 | 240 | int link[2]; /* prev/next handles */ |
219 | #define TRACK_LIST_LEN 64 | 241 | struct track_info info; |
220 | #elif MEMORYSIZE >= 8 | 242 | }; |
221 | #define TRACK_LIST_LEN 32 | ||
222 | #else | ||
223 | #define TRACK_LIST_LEN 16 | ||
224 | #endif | ||
225 | 243 | ||
226 | #define TRACK_LIST_MASK (TRACK_LIST_LEN-1) | 244 | #define FOR_EACH_TRACK_INFO_HANDLE(i) \ |
245 | for (int i = 0; i < TRACK_INFO_HANDLES; i++) | ||
227 | 246 | ||
228 | static struct | 247 | static struct |
229 | { | 248 | { |
230 | /* read, write and current are maintained unwrapped, limited only by the | 249 | /* TODO: perhaps cache -1/+1 delta handles if speed ever matters much |
231 | unsigned int range and wrap-safe comparisons are used */ | 250 | because those lookups are common; also could cache a few recent |
232 | 251 | acccesses */ | |
233 | /* NOTE: there appears to be a bug in arm-elf-eabi-gcc 4.4.4 for ARMv4 where | 252 | int first_hid; /* handle of first track in list */ |
234 | if 'end' follows 'start' in this structure, track_list_count performs | 253 | int current_hid; /* handle of track delta 0 */ |
235 | 'start - end' rather than 'end - start', giving negative count values... | 254 | int last_hid; /* handle of last track in list */ |
236 | so leave it this way for now! */ | 255 | int in_progress_hid; /* track in process of loading */ |
237 | unsigned int end; /* Next open position */ | 256 | unsigned int count; /* number of tracks in list */ |
238 | unsigned int start; /* First track in list */ | ||
239 | unsigned int current; /* Currently decoding track */ | ||
240 | struct track_info tracks[TRACK_LIST_LEN]; /* Buffered track information */ | ||
241 | } track_list; /* (A, O-) */ | 257 | } track_list; /* (A, O-) */ |
242 | 258 | ||
243 | 259 | ||
244 | /* Playlist steps from playlist position to next track to be buffered */ | 260 | /* Playlist steps from playlist position to next track to be buffered */ |
245 | static int playlist_peek_offset = 0; | 261 | static int playlist_peek_offset = 0; |
246 | 262 | ||
247 | /* Metadata handle of track load in progress (meaning all handles have not | ||
248 | yet been opened for the track, id3 always exists or the track does not) | ||
249 | |||
250 | Tracks are keyed by their metadata handles if track list pointers are | ||
251 | insufficient to make comparisons */ | ||
252 | static int in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | ||
253 | |||
254 | #ifdef HAVE_DISK_STORAGE | 263 | #ifdef HAVE_DISK_STORAGE |
255 | /* Buffer margin A.K.A. anti-skip buffer (in seconds) */ | 264 | /* Buffer margin A.K.A. anti-skip buffer (in seconds) */ |
256 | static size_t buffer_margin = 5; | 265 | static size_t buffer_margin = 5; |
@@ -443,150 +452,225 @@ static void id3_write_locked(enum audio_id3_types id3_num, | |||
443 | 452 | ||
444 | /** --- Track info --- **/ | 453 | /** --- Track info --- **/ |
445 | 454 | ||
446 | /* Close a handle and mark it invalid */ | 455 | static void track_info_close_handle(int *hidp) |
447 | static void track_info_close_handle(int *hid_p) | ||
448 | { | 456 | { |
449 | int hid = *hid_p; | 457 | bufclose(*hidp); |
450 | 458 | *hidp = ERR_HANDLE_NOT_FOUND; | |
451 | /* bufclose returns true if the handle is not found, or if it is closed | ||
452 | * successfully, so these checks are safe on non-existant handles */ | ||
453 | if (hid >= 0) | ||
454 | bufclose(hid); | ||
455 | |||
456 | /* Always reset to "no handle" in case it was something else */ | ||
457 | *hid_p = ERR_HANDLE_NOT_FOUND; | ||
458 | } | 459 | } |
459 | 460 | ||
460 | /* Close all handles in a struct track_info and clear it */ | 461 | /* Invalidate all members to initial values - does not close handles or sync */ |
461 | static void track_info_close(struct track_info *info) | 462 | static void track_info_wipe(struct track_info *infop) |
462 | { | 463 | { |
463 | /* Close them in the order they are allocated on the buffer to speed up | 464 | /* don't touch ->self_hid */ |
464 | the handle searching */ | ||
465 | track_info_close_handle(&info->id3_hid); | ||
466 | track_info_close_handle(&info->cuesheet_hid); | ||
467 | #ifdef HAVE_ALBUMART | ||
468 | FOREACH_ALBUMART(i) | ||
469 | track_info_close_handle(&info->aa_hid[i]); | ||
470 | #endif | ||
471 | #ifdef HAVE_CODEC_BUFFERING | ||
472 | track_info_close_handle(&info->codec_hid); | ||
473 | #endif | ||
474 | track_info_close_handle(&info->audio_hid); | ||
475 | info->filesize = 0; | ||
476 | } | ||
477 | 465 | ||
478 | /* Invalidate all members to initial values - does not close handles */ | 466 | FOR_EACH_TRACK_INFO_HANDLE(i) |
479 | static void track_info_wipe(struct track_info * info) | 467 | infop->handle[i] = ERR_HANDLE_NOT_FOUND; |
480 | { | ||
481 | info->id3_hid = ERR_HANDLE_NOT_FOUND; | ||
482 | info->cuesheet_hid = ERR_HANDLE_NOT_FOUND; | ||
483 | #ifdef HAVE_ALBUMART | ||
484 | FOREACH_ALBUMART(i) | ||
485 | info->aa_hid[i] = ERR_HANDLE_NOT_FOUND; | ||
486 | #endif | ||
487 | #ifdef HAVE_CODEC_BUFFERING | ||
488 | info->codec_hid = ERR_HANDLE_NOT_FOUND; | ||
489 | #endif | ||
490 | info->audio_hid = ERR_HANDLE_NOT_FOUND; | ||
491 | info->filesize = 0; | ||
492 | } | ||
493 | 468 | ||
469 | infop->filesize = 0; | ||
470 | } | ||
494 | 471 | ||
495 | /** --- Track list --- **/ | 472 | /** --- Track list --- **/ |
496 | 473 | ||
474 | /* Clear tracks in the list, optionally preserving the current track - | ||
475 | returns 'false' if the operation was changed */ | ||
476 | enum track_clear_action | ||
477 | { | ||
478 | TRACK_LIST_CLEAR_ALL = 0, /* Clear all tracks */ | ||
479 | TRACK_LIST_KEEP_CURRENT, /* Keep current only; clear before + after */ | ||
480 | TRACK_LIST_KEEP_NEW /* Keep current and those that follow */ | ||
481 | }; | ||
482 | |||
497 | /* Initialize the track list */ | 483 | /* Initialize the track list */ |
498 | static void INIT_ATTR track_list_init(void) | 484 | static void INIT_ATTR track_list_init(void) |
499 | { | 485 | { |
500 | int i; | 486 | track_list.first_hid = 0; |
501 | for (i = 0; i < TRACK_LIST_LEN; i++) | 487 | track_list.current_hid = 0; |
502 | track_info_wipe(&track_list.tracks[i]); | 488 | track_list.last_hid = 0; |
503 | 489 | track_list.in_progress_hid = 0; | |
504 | track_list.start = track_list.end = track_list.current; | 490 | track_list.count = 0; |
505 | } | 491 | } |
506 | 492 | ||
507 | /* Return number of items allocated in the list */ | 493 | /* Return number of items allocated in the list */ |
508 | static unsigned int track_list_count(void) | 494 | static inline unsigned int track_list_count(void) |
509 | { | 495 | { |
510 | return track_list.end - track_list.start; | 496 | return track_list.count; |
511 | } | 497 | } |
512 | 498 | ||
513 | /* Return true if the list is empty */ | 499 | /* Return true if the list is empty */ |
514 | static inline bool track_list_empty(void) | 500 | static inline bool track_list_empty(void) |
515 | { | 501 | { |
516 | return track_list.end == track_list.start; | 502 | return track_list.count == 0; |
517 | } | 503 | } |
518 | 504 | ||
519 | /* Returns true if the list is holding the maximum number of items */ | 505 | /* Returns a pointer to the track info data on the buffer */ |
520 | static bool track_list_full(void) | 506 | static struct track_buf_info * track_buf_info_get(int hid) |
521 | { | 507 | { |
522 | return track_list.end - track_list.start >= TRACK_LIST_LEN; | 508 | void *p; |
509 | ssize_t size = bufgetdata(hid, sizeof (struct track_buf_info), &p); | ||
510 | return size == (ssize_t)sizeof (struct track_buf_info) ? p : NULL; | ||
523 | } | 511 | } |
524 | 512 | ||
525 | /* Test if the index is within the allocated range */ | 513 | /* Synchronize the buffer object with the cached track info */ |
526 | static bool track_list_in_range(int pos) | 514 | static bool track_info_sync(const struct track_info *infop) |
527 | { | 515 | { |
528 | return (int)(pos - track_list.start) >= 0 && | 516 | struct track_buf_info *tbip = track_buf_info_get(infop->self_hid); |
529 | (int)(pos - track_list.end) < 0; | 517 | if (!tbip) |
530 | } | 518 | return false; |
531 | 519 | ||
532 | static struct track_info * track_list_entry(int pos) | 520 | tbip->info = *infop; |
533 | { | 521 | return true; |
534 | return &track_list.tracks[pos & TRACK_LIST_MASK]; | ||
535 | } | 522 | } |
536 | 523 | ||
537 | /* Return the info of the last allocation plus an offset, NULL if result is | 524 | /* Return track info a given offset from the info referenced by hid and |
538 | out of bounds */ | 525 | * place a copy into *infop, if provided */ |
539 | static struct track_info * track_list_last(int offset) | 526 | static struct track_buf_info * |
527 | track_list_get_info_from(int hid, int offset, struct track_info *infop) | ||
540 | { | 528 | { |
541 | /* Last is before the end since the end isn't inclusive */ | 529 | int sgn = SGN(offset); |
542 | unsigned int pos = track_list.end + offset - 1; | 530 | struct track_buf_info *tbip; |
543 | 531 | ||
544 | if (!track_list_in_range(pos)) | 532 | while (1) |
545 | return NULL; | 533 | { |
534 | if (!(tbip = track_buf_info_get(hid))) | ||
535 | break; | ||
546 | 536 | ||
547 | return track_list_entry(pos); | 537 | if (!offset) |
548 | } | 538 | break; |
549 | 539 | ||
550 | /* Allocate space at the end for another track if not full */ | 540 | if ((hid = tbip->link[(unsigned)(sgn + 1) / 2]) <= 0) |
551 | static struct track_info * track_list_alloc_track(void) | 541 | { |
552 | { | 542 | tbip = NULL; |
553 | if (track_list_full()) | 543 | break; |
554 | return NULL; | 544 | } |
555 | 545 | ||
556 | return track_list_entry(track_list.end++); | 546 | offset -= sgn; |
547 | } | ||
548 | |||
549 | if (infop) | ||
550 | { | ||
551 | if (tbip) | ||
552 | { | ||
553 | *infop = tbip->info; | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | track_info_wipe(infop); | ||
558 | infop->self_hid = ERR_HANDLE_NOT_FOUND; | ||
559 | } | ||
560 | } | ||
561 | |||
562 | return tbip; | ||
557 | } | 563 | } |
558 | 564 | ||
559 | /* Remove the last track entry allocated in order to support backing out | 565 | /* Commit the track info to the buffer updated with the provided source info */ |
560 | of a track load */ | 566 | static bool track_list_commit_buf_info(struct track_buf_info *tbip, |
561 | static void track_list_unalloc_track(void) | 567 | const struct track_info *src_infop) |
562 | { | 568 | { |
563 | if (track_list_empty()) | 569 | /* Leaves the list unmodified if anything fails */ |
564 | return; | 570 | if (tbip->link[1] != ERR_HANDLE_NOT_FOUND) |
571 | return false; | ||
565 | 572 | ||
566 | track_list.end--; | 573 | int hid = tbip->info.self_hid; |
574 | int last_hid = track_list.last_hid; | ||
575 | struct track_buf_info *last_tbip = NULL; | ||
567 | 576 | ||
568 | if (track_list.current == track_list.end && | 577 | if (last_hid > 0 && !(last_tbip = track_buf_info_get(last_hid))) |
569 | track_list.current != track_list.start) | 578 | return false; |
579 | |||
580 | tbip->info = *src_infop; | ||
581 | |||
582 | /* Insert last */ | ||
583 | tbip->link[0] = last_hid; | ||
584 | tbip->link[1] = 0; /* "In list" */ | ||
585 | |||
586 | if (last_tbip) | ||
587 | { | ||
588 | last_tbip->link[1] = hid; | ||
589 | } | ||
590 | else | ||
570 | { | 591 | { |
571 | /* Current _must_ remain within bounds */ | 592 | track_list.first_hid = hid; |
572 | track_list.current--; | 593 | track_list.current_hid = hid; |
573 | } | 594 | } |
595 | |||
596 | track_list.last_hid = hid; | ||
597 | track_list.count++; | ||
598 | return true; | ||
574 | } | 599 | } |
575 | 600 | ||
576 | /* Return current track plus an offset, NULL if result is out of bounds */ | 601 | /* Free the track buffer entry and possibly remove it from the list if it |
577 | static struct track_info * track_list_current(int offset) | 602 | was succesfully added at some point */ |
603 | static void track_list_free_buf_info(struct track_buf_info *tbip) | ||
578 | { | 604 | { |
579 | unsigned int pos = track_list.current + offset; | 605 | int hid = tbip->info.self_hid; |
606 | int next_hid = tbip->link[1]; | ||
580 | 607 | ||
581 | if (!track_list_in_range(pos)) | 608 | if (next_hid != ERR_HANDLE_NOT_FOUND) |
582 | return NULL; | 609 | { |
610 | int prev_hid = tbip->link[0]; | ||
611 | struct track_buf_info *prev_tbip = NULL; | ||
612 | struct track_buf_info *next_tbip = NULL; | ||
613 | |||
614 | if ((prev_hid > 0 && !(prev_tbip = track_buf_info_get(prev_hid))) || | ||
615 | (next_hid > 0 && !(next_tbip = track_buf_info_get(next_hid)))) | ||
616 | { | ||
617 | return; | ||
618 | } | ||
619 | |||
620 | if (prev_tbip) | ||
621 | { | ||
622 | prev_tbip->link[1] = next_hid; | ||
623 | } | ||
624 | else | ||
625 | { | ||
626 | /* Was the first track; new first track is next one */ | ||
627 | track_list.first_hid = next_hid; | ||
628 | |||
629 | if (hid == track_list.current_hid) | ||
630 | { | ||
631 | /* Was the current track; new current track is next one */ | ||
632 | track_list.current_hid = next_hid; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | if (next_tbip) | ||
637 | { | ||
638 | next_tbip->link[0] = prev_hid; | ||
639 | } | ||
640 | else | ||
641 | { | ||
642 | /* Was the last track; new last track is previous one */ | ||
643 | track_list.last_hid = prev_hid; | ||
583 | 644 | ||
584 | return track_list_entry(pos); | 645 | if (hid == track_list.current_hid) |
646 | { | ||
647 | /* Was the current track; new current track is previous one */ | ||
648 | track_list.current_hid = prev_hid; | ||
649 | } | ||
650 | } | ||
651 | |||
652 | track_list.count--; | ||
653 | } | ||
654 | |||
655 | /* No movement allowed during bufclose calls */ | ||
656 | buf_pin_handle(hid, true); | ||
657 | |||
658 | FOR_EACH_TRACK_INFO_HANDLE(i) | ||
659 | bufclose(tbip->info.handle[i]); | ||
660 | |||
661 | /* Finally, the handle itself */ | ||
662 | bufclose(hid); | ||
663 | } | ||
664 | |||
665 | /* Return current track plus an offset */ | ||
666 | static bool track_list_current(int offset, struct track_info *infop) | ||
667 | { | ||
668 | return !!track_list_get_info_from(track_list.current_hid, offset, infop); | ||
585 | } | 669 | } |
586 | 670 | ||
587 | /* Return current based upon what's intended that the user sees - not | 671 | /* Return current based upon what's intended that the user sees - not |
588 | necessarily where decoding is taking place */ | 672 | necessarily where decoding is taking place */ |
589 | static struct track_info * track_list_user_current(int offset) | 673 | static bool track_list_user_current(int offset, struct track_info *infop) |
590 | { | 674 | { |
591 | if (skip_pending == TRACK_SKIP_AUTO || | 675 | if (skip_pending == TRACK_SKIP_AUTO || |
592 | skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST) | 676 | skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST) |
@@ -594,91 +678,114 @@ static struct track_info * track_list_user_current(int offset) | |||
594 | offset--; | 678 | offset--; |
595 | } | 679 | } |
596 | 680 | ||
597 | return track_list_current(offset); | 681 | return !!track_list_get_info_from(track_list.current_hid, offset, infop); |
598 | } | 682 | } |
599 | 683 | ||
600 | /* Advance current track by an offset, return false if result is out of | 684 | /* Advance current track by an offset, return false if result is out of |
601 | bounds */ | 685 | bounds */ |
602 | static struct track_info * track_list_advance_current(int offset) | 686 | static bool track_list_advance_current(int offset, struct track_info *infop) |
603 | { | 687 | { |
604 | unsigned int pos = track_list.current + offset; | 688 | struct track_buf_info *new_bufinfop = |
689 | track_list_get_info_from(track_list.current_hid, offset, infop); | ||
605 | 690 | ||
606 | if (!track_list_in_range(pos)) | 691 | if (!new_bufinfop) |
607 | return NULL; | 692 | return false; |
608 | 693 | ||
609 | track_list.current = pos; | 694 | track_list.current_hid = new_bufinfop->info.self_hid; |
610 | return track_list_entry(pos); | 695 | return true; |
611 | } | 696 | } |
612 | 697 | ||
613 | /* Clear tracks in the list, optionally preserving the current track - | 698 | /* Return the info of the last allocation plus an offset, NULL if result is |
614 | returns 'false' if the operation was changed */ | 699 | out of bounds */ |
615 | enum track_clear_action | 700 | static bool track_list_last(int offset, struct track_info *infop) |
616 | { | 701 | { |
617 | TRACK_LIST_CLEAR_ALL = 0, /* Clear all tracks */ | 702 | return !!track_list_get_info_from(track_list.last_hid, offset, infop); |
618 | TRACK_LIST_KEEP_CURRENT, /* Keep current only; clear before + after */ | 703 | } |
619 | TRACK_LIST_KEEP_NEW /* Keep current and those that follow */ | 704 | |
620 | }; | 705 | /* Allocate a new struct track_info on the buffer; does not add to list */ |
706 | static bool track_list_alloc_info(struct track_info *infop) | ||
707 | { | ||
708 | int hid = bufalloc(NULL, sizeof (struct track_buf_info), TYPE_RAW_ATOMIC); | ||
709 | |||
710 | track_info_wipe(infop); | ||
711 | |||
712 | struct track_buf_info *tbip = track_buf_info_get(hid); | ||
713 | if (!tbip) | ||
714 | { | ||
715 | infop->self_hid = ERR_HANDLE_NOT_FOUND; | ||
716 | bufclose(hid); | ||
717 | return false; | ||
718 | } | ||
719 | |||
720 | infop->self_hid = hid; | ||
721 | |||
722 | tbip->link[0] = 0; | ||
723 | tbip->link[1] = ERR_HANDLE_NOT_FOUND; /* "Not in list" */ | ||
724 | tbip->info.self_hid = hid; | ||
725 | track_info_wipe(&tbip->info); | ||
726 | |||
727 | return true; | ||
728 | } | ||
729 | |||
730 | /* Actually commit the track info to the track list */ | ||
731 | static bool track_list_commit_info(const struct track_info *infop) | ||
732 | { | ||
733 | struct track_buf_info *tbip = track_buf_info_get(infop->self_hid); | ||
734 | if (!tbip) | ||
735 | return false; | ||
736 | |||
737 | return track_list_commit_buf_info(tbip, infop); | ||
738 | } | ||
739 | |||
740 | /* Free the track entry and possibly remove it from the list if it was | ||
741 | succesfully added at some point */ | ||
742 | static void track_list_free_info(struct track_info *infop) | ||
743 | { | ||
744 | struct track_buf_info *tbip = track_buf_info_get(infop->self_hid); | ||
745 | if (!tbip) | ||
746 | return; | ||
747 | |||
748 | track_list_free_buf_info(tbip); | ||
749 | } | ||
621 | 750 | ||
622 | static void track_list_clear(enum track_clear_action action) | 751 | /* Close all open handles in the range except the for the current track |
752 | if preserving that */ | ||
753 | static void track_list_clear(unsigned int action) | ||
623 | { | 754 | { |
624 | logf("%s(%d)", __func__, (int)action); | 755 | logf("%s:action=%u", __func__, action); |
625 | 756 | ||
626 | /* Don't care now since rebuffering is imminent */ | 757 | /* Don't care now since rebuffering is imminent */ |
627 | buf_set_watermark(0); | 758 | buf_set_watermark(0); |
628 | 759 | ||
629 | if (action != TRACK_LIST_CLEAR_ALL) | 760 | if (action != TRACK_LIST_CLEAR_ALL) |
630 | { | 761 | { |
631 | struct track_info *cur = track_list_current(0); | 762 | struct track_info info; |
632 | 763 | if (!track_list_current(0, &info) || info.id3_hid < 0) | |
633 | if (!cur || cur->id3_hid < 0) | ||
634 | action = TRACK_LIST_CLEAR_ALL; /* Nothing worthwhile keeping */ | 764 | action = TRACK_LIST_CLEAR_ALL; /* Nothing worthwhile keeping */ |
635 | } | 765 | } |
636 | 766 | ||
637 | /* Noone should see this progressing */ | 767 | int hid = track_list.first_hid; |
638 | int start = track_list.start; | 768 | int current_hid = track_list.current_hid; |
639 | int current = track_list.current; | 769 | int last_hid = action == TRACK_LIST_KEEP_NEW ? current_hid : 0; |
640 | int end = track_list.end; | ||
641 | 770 | ||
642 | track_list.start = current; | 771 | while (hid != last_hid) |
643 | |||
644 | switch (action) | ||
645 | { | 772 | { |
646 | case TRACK_LIST_CLEAR_ALL: | 773 | struct track_buf_info *tbip = track_buf_info_get(hid); |
647 | /* Result: .start = .current, .end = .current */ | 774 | if (!tbip) |
648 | track_list.end = current; | 775 | break; |
649 | break; | ||
650 | 776 | ||
651 | case TRACK_LIST_KEEP_CURRENT: | 777 | int next_hid = tbip->link[1]; |
652 | /* Result: .start = .current, .end = .current + 1 */ | ||
653 | track_list.end = current + 1; | ||
654 | break; | ||
655 | 778 | ||
656 | case TRACK_LIST_KEEP_NEW: | 779 | if (action != TRACK_LIST_KEEP_CURRENT || hid != current_hid) |
657 | /* Result: .start = .current, .end = .end */ | ||
658 | end = current; | ||
659 | break; | ||
660 | } | ||
661 | |||
662 | /* Close all open handles in the range except the for the current track | ||
663 | if preserving that */ | ||
664 | while (start != end) | ||
665 | { | ||
666 | if (action != TRACK_LIST_KEEP_CURRENT || start != current) | ||
667 | { | 780 | { |
668 | struct track_info *info = | ||
669 | &track_list.tracks[start & TRACK_LIST_MASK]; | ||
670 | |||
671 | /* If this is the in-progress load, abort it */ | 781 | /* If this is the in-progress load, abort it */ |
672 | if (in_progress_id3_hid >= 0 && | 782 | if (hid == track_list.in_progress_hid) |
673 | info->id3_hid == in_progress_id3_hid) | 783 | track_list.in_progress_hid = 0; |
674 | { | ||
675 | in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | ||
676 | } | ||
677 | 784 | ||
678 | track_info_close(info); | 785 | track_list_free_buf_info(tbip); |
679 | } | 786 | } |
680 | 787 | ||
681 | start++; | 788 | hid = next_hid; |
682 | } | 789 | } |
683 | } | 790 | } |
684 | 791 | ||
@@ -961,11 +1068,11 @@ static void audio_update_filebuf_watermark(int seconds) | |||
961 | #endif | 1068 | #endif |
962 | 1069 | ||
963 | /* Watermark is a function of the bitrate of the last track in the buffer */ | 1070 | /* Watermark is a function of the bitrate of the last track in the buffer */ |
1071 | struct track_info info; | ||
964 | struct mp3entry *id3 = NULL; | 1072 | struct mp3entry *id3 = NULL; |
965 | struct track_info *info = track_list_last(0); | ||
966 | 1073 | ||
967 | if (info) | 1074 | if (track_list_last(0, &info)) |
968 | id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | 1075 | id3 = valid_mp3entry(bufgetid3(info.id3_hid)); |
969 | 1076 | ||
970 | if (id3) | 1077 | if (id3) |
971 | { | 1078 | { |
@@ -980,20 +1087,20 @@ static void audio_update_filebuf_watermark(int seconds) | |||
980 | track that fits, in which case we should avoid constant buffer | 1087 | track that fits, in which case we should avoid constant buffer |
981 | low events */ | 1088 | low events */ |
982 | if (track_list_count() > 1) | 1089 | if (track_list_count() > 1) |
983 | bytes = info->filesize + 1; | 1090 | bytes = info.filesize + 1; |
984 | } | 1091 | } |
985 | } | 1092 | } |
986 | else | 1093 | else |
987 | { | 1094 | { |
988 | /* Then set the minimum - this should not occur anyway */ | 1095 | /* Then set the minimum - this should not occur anyway */ |
989 | logf("fwmark: No id3 for last track (s%u/c%u/e%u)", | 1096 | logf("fwmark: No id3 for last track (f=%d:c=%d:l=%d)", |
990 | track_list.start, track_list.current, track_list.end); | 1097 | track_list.first_hid, track_list.current_hid, track_list.last_hid); |
991 | } | 1098 | } |
992 | 1099 | ||
993 | /* Actually setting zero disables the notification and we use that | 1100 | /* Actually setting zero disables the notification and we use that |
994 | to detect that it has been reset */ | 1101 | to detect that it has been reset */ |
995 | buf_set_watermark(MAX(bytes, 1)); | 1102 | buf_set_watermark(MAX(bytes, 1)); |
996 | logf("fwmark: %lu", (unsigned long)bytes); | 1103 | logf("fwmark: %zu", bytes); |
997 | } | 1104 | } |
998 | 1105 | ||
999 | 1106 | ||
@@ -1126,12 +1233,12 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next | |||
1126 | 1233 | ||
1127 | /* Bring the user current mp3entry up to date and set a new offset for the | 1234 | /* Bring the user current mp3entry up to date and set a new offset for the |
1128 | buffered metadata */ | 1235 | buffered metadata */ |
1129 | static void playing_id3_sync(struct track_info *user_info, | 1236 | static void playing_id3_sync(struct track_info *user_infop, |
1130 | unsigned long elapsed, unsigned long offset) | 1237 | unsigned long elapsed, unsigned long offset) |
1131 | { | 1238 | { |
1132 | id3_mutex_lock(); | 1239 | id3_mutex_lock(); |
1133 | 1240 | ||
1134 | struct mp3entry *id3 = bufgetid3(user_info->id3_hid); | 1241 | struct mp3entry *id3 = bufgetid3(user_infop->id3_hid); |
1135 | struct mp3entry *playing_id3 = id3_get(PLAYING_ID3); | 1242 | struct mp3entry *playing_id3 = id3_get(PLAYING_ID3); |
1136 | 1243 | ||
1137 | pcm_play_lock(); | 1244 | pcm_play_lock(); |
@@ -1290,13 +1397,13 @@ static bool audio_get_track_metadata(int offset, struct mp3entry *id3) | |||
1290 | if (id3->path[0] != '\0') | 1397 | if (id3->path[0] != '\0') |
1291 | return true; /* Already filled */ | 1398 | return true; /* Already filled */ |
1292 | 1399 | ||
1293 | struct track_info *info = track_list_user_current(offset); | 1400 | struct track_info info; |
1294 | 1401 | ||
1295 | if (!info) | 1402 | if (!track_list_user_current(offset, &info)) |
1296 | { | 1403 | { |
1297 | struct mp3entry *ub_id3 = id3_get(UNBUFFERED_ID3); | 1404 | struct mp3entry *ub_id3 = id3_get(UNBUFFERED_ID3); |
1298 | 1405 | ||
1299 | if (offset > 0 && track_list_user_current(offset - 1)) | 1406 | if (offset > 0 && track_list_user_current(offset - 1, NULL)) |
1300 | { | 1407 | { |
1301 | /* Try the unbuffered id3 since we're moving forward */ | 1408 | /* Try the unbuffered id3 since we're moving forward */ |
1302 | if (ub_id3->path[0] != '\0') | 1409 | if (ub_id3->path[0] != '\0') |
@@ -1306,7 +1413,7 @@ static bool audio_get_track_metadata(int offset, struct mp3entry *id3) | |||
1306 | } | 1413 | } |
1307 | } | 1414 | } |
1308 | } | 1415 | } |
1309 | else if (bufreadid3(info->id3_hid, id3)) | 1416 | else if (bufreadid3(info.id3_hid, id3)) |
1310 | { | 1417 | { |
1311 | id3->cuesheet = NULL; | 1418 | id3->cuesheet = NULL; |
1312 | return true; | 1419 | return true; |
@@ -1348,7 +1455,7 @@ static void resume_rewind_adjust_progress(const struct mp3entry *id3, | |||
1348 | } | 1455 | } |
1349 | 1456 | ||
1350 | /* Get the codec into ram and initialize it - keep it if it's ready */ | 1457 | /* Get the codec into ram and initialize it - keep it if it's ready */ |
1351 | static bool audio_init_codec(struct track_info *track_info, | 1458 | static bool audio_init_codec(struct track_info *track_infop, |
1352 | struct mp3entry *track_id3) | 1459 | struct mp3entry *track_id3) |
1353 | { | 1460 | { |
1354 | int codt_loaded = get_audio_base_codec_type(codec_loaded()); | 1461 | int codt_loaded = get_audio_base_codec_type(codec_loaded()); |
@@ -1366,8 +1473,9 @@ static bool audio_init_codec(struct track_info *track_info, | |||
1366 | /* Close any buffered codec (we could have skipped directly to a | 1473 | /* Close any buffered codec (we could have skipped directly to a |
1367 | format transistion that is the same format as the current track | 1474 | format transistion that is the same format as the current track |
1368 | and the buffered one is no longer needed) */ | 1475 | and the buffered one is no longer needed) */ |
1369 | track_info_close_handle(&track_info->codec_hid); | 1476 | track_info_close_handle(&track_infop->codec_hid); |
1370 | #endif | 1477 | track_info_sync(track_infop); |
1478 | #endif /* HAVE_CODEC_BUFFERING */ | ||
1371 | return true; | 1479 | return true; |
1372 | } | 1480 | } |
1373 | else | 1481 | else |
@@ -1383,12 +1491,13 @@ static bool audio_init_codec(struct track_info *track_info, | |||
1383 | #ifdef HAVE_CODEC_BUFFERING | 1491 | #ifdef HAVE_CODEC_BUFFERING |
1384 | /* Codec thread will close the handle even if it fails and will load from | 1492 | /* Codec thread will close the handle even if it fails and will load from |
1385 | storage if hid is not valid or the buffer load fails */ | 1493 | storage if hid is not valid or the buffer load fails */ |
1386 | hid = track_info->codec_hid; | 1494 | hid = track_infop->codec_hid; |
1387 | track_info->codec_hid = ERR_HANDLE_NOT_FOUND; | 1495 | track_infop->codec_hid = ERR_HANDLE_NOT_FOUND; |
1496 | track_info_sync(track_infop); | ||
1388 | #endif | 1497 | #endif |
1389 | 1498 | ||
1390 | return codec_load(hid, track_id3->codectype); | 1499 | return codec_load(hid, track_id3->codectype); |
1391 | (void)track_info; /* When codec buffering isn't supported */ | 1500 | (void)track_infop; /* When codec buffering isn't supported */ |
1392 | } | 1501 | } |
1393 | 1502 | ||
1394 | #ifdef HAVE_TAGCACHE | 1503 | #ifdef HAVE_TAGCACHE |
@@ -1452,17 +1561,19 @@ static bool autoresumable(struct mp3entry *id3) | |||
1452 | /* Start the codec for the current track scheduled to be decoded */ | 1561 | /* Start the codec for the current track scheduled to be decoded */ |
1453 | static bool audio_start_codec(bool auto_skip) | 1562 | static bool audio_start_codec(bool auto_skip) |
1454 | { | 1563 | { |
1455 | struct track_info *info = track_list_current(0); | 1564 | struct track_info info; |
1456 | struct mp3entry *cur_id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | 1565 | track_list_current(0, &info); |
1566 | |||
1567 | struct mp3entry *cur_id3 = valid_mp3entry(bufgetid3(info.id3_hid)); | ||
1457 | 1568 | ||
1458 | if (!cur_id3) | 1569 | if (!cur_id3) |
1459 | return false; | 1570 | return false; |
1460 | 1571 | ||
1461 | buf_pin_handle(info->id3_hid, true); | 1572 | buf_pin_handle(info.id3_hid, true); |
1462 | 1573 | ||
1463 | if (!audio_init_codec(info, cur_id3)) | 1574 | if (!audio_init_codec(&info, cur_id3)) |
1464 | { | 1575 | { |
1465 | buf_pin_handle(info->id3_hid, false); | 1576 | buf_pin_handle(info.id3_hid, false); |
1466 | return false; | 1577 | return false; |
1467 | } | 1578 | } |
1468 | 1579 | ||
@@ -1523,9 +1634,9 @@ static bool audio_start_codec(bool auto_skip) | |||
1523 | /* Update the codec API with the metadata and track info */ | 1634 | /* Update the codec API with the metadata and track info */ |
1524 | id3_write(CODEC_ID3, cur_id3); | 1635 | id3_write(CODEC_ID3, cur_id3); |
1525 | 1636 | ||
1526 | ci.audio_hid = info->audio_hid; | 1637 | ci.audio_hid = info.audio_hid; |
1527 | ci.filesize = info->filesize; | 1638 | ci.filesize = info.filesize; |
1528 | buf_set_base_handle(info->audio_hid); | 1639 | buf_set_base_handle(info.audio_hid); |
1529 | 1640 | ||
1530 | /* All required data is now available for the codec */ | 1641 | /* All required data is now available for the codec */ |
1531 | codec_go(); | 1642 | codec_go(); |
@@ -1538,7 +1649,7 @@ static bool audio_start_codec(bool auto_skip) | |||
1538 | send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, 0, cur_id3); | 1649 | send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, 0, cur_id3); |
1539 | } | 1650 | } |
1540 | 1651 | ||
1541 | buf_pin_handle(info->id3_hid, false); | 1652 | buf_pin_handle(info.id3_hid, false); |
1542 | return true; | 1653 | return true; |
1543 | 1654 | ||
1544 | (void)auto_skip; /* ifndef HAVE_TAGCACHE */ | 1655 | (void)auto_skip; /* ifndef HAVE_TAGCACHE */ |
@@ -1549,13 +1660,13 @@ static bool audio_start_codec(bool auto_skip) | |||
1549 | 1660 | ||
1550 | /* Load and parse a cuesheet for the file - returns false if the buffer | 1661 | /* Load and parse a cuesheet for the file - returns false if the buffer |
1551 | is full */ | 1662 | is full */ |
1552 | static bool audio_load_cuesheet(struct track_info *info, | 1663 | static bool audio_load_cuesheet(struct track_info *infop, |
1553 | struct mp3entry *track_id3) | 1664 | struct mp3entry *track_id3) |
1554 | { | 1665 | { |
1555 | struct cuesheet *cue = get_current_cuesheet(); | 1666 | struct cuesheet *cue = get_current_cuesheet(); |
1556 | track_id3->cuesheet = NULL; | 1667 | track_id3->cuesheet = NULL; |
1557 | 1668 | ||
1558 | if (cue && info->cuesheet_hid == ERR_HANDLE_NOT_FOUND) | 1669 | if (cue && infop->cuesheet_hid == ERR_HANDLE_NOT_FOUND) |
1559 | { | 1670 | { |
1560 | /* If error other than a full buffer, then mark it "unsupported" to | 1671 | /* If error other than a full buffer, then mark it "unsupported" to |
1561 | avoid reloading attempt */ | 1672 | avoid reloading attempt */ |
@@ -1595,7 +1706,7 @@ static bool audio_load_cuesheet(struct track_info *info, | |||
1595 | if (hid < 0) | 1706 | if (hid < 0) |
1596 | logf("Cuesheet loading failed"); | 1707 | logf("Cuesheet loading failed"); |
1597 | 1708 | ||
1598 | info->cuesheet_hid = hid; | 1709 | infop->cuesheet_hid = hid; |
1599 | } | 1710 | } |
1600 | } | 1711 | } |
1601 | 1712 | ||
@@ -1604,13 +1715,13 @@ static bool audio_load_cuesheet(struct track_info *info, | |||
1604 | 1715 | ||
1605 | #ifdef HAVE_ALBUMART | 1716 | #ifdef HAVE_ALBUMART |
1606 | /* Load any album art for the file - returns false if the buffer is full */ | 1717 | /* Load any album art for the file - returns false if the buffer is full */ |
1607 | static bool audio_load_albumart(struct track_info *info, | 1718 | static bool audio_load_albumart(struct track_info *infop, |
1608 | struct mp3entry *track_id3) | 1719 | struct mp3entry *track_id3) |
1609 | { | 1720 | { |
1610 | FOREACH_ALBUMART(i) | 1721 | FOREACH_ALBUMART(i) |
1611 | { | 1722 | { |
1612 | struct bufopen_bitmap_data user_data; | 1723 | struct bufopen_bitmap_data user_data; |
1613 | int *aa_hid = &info->aa_hid[i]; | 1724 | int *aa_hid = &infop->aa_hid[i]; |
1614 | int hid = ERR_UNSUPPORTED_TYPE; | 1725 | int hid = ERR_UNSUPPORTED_TYPE; |
1615 | 1726 | ||
1616 | /* albumart_slots may change during a yield of bufopen, | 1727 | /* albumart_slots may change during a yield of bufopen, |
@@ -1656,6 +1767,11 @@ static bool audio_load_albumart(struct track_info *info, | |||
1656 | logf("Album art loading failed"); | 1767 | logf("Album art loading failed"); |
1657 | hid = ERR_UNSUPPORTED_TYPE; | 1768 | hid = ERR_UNSUPPORTED_TYPE; |
1658 | } | 1769 | } |
1770 | else | ||
1771 | { | ||
1772 | logf("Loaded album art:%dx%d", user_data.dim->width, | ||
1773 | user_data.dim->height); | ||
1774 | } | ||
1659 | 1775 | ||
1660 | *aa_hid = hid; | 1776 | *aa_hid = hid; |
1661 | } | 1777 | } |
@@ -1668,14 +1784,16 @@ static bool audio_load_albumart(struct track_info *info, | |||
1668 | #ifdef HAVE_CODEC_BUFFERING | 1784 | #ifdef HAVE_CODEC_BUFFERING |
1669 | /* Load a codec for the file onto the buffer - assumes we're working from the | 1785 | /* Load a codec for the file onto the buffer - assumes we're working from the |
1670 | currently loading track - not called for the current track */ | 1786 | currently loading track - not called for the current track */ |
1671 | static bool audio_buffer_codec(struct track_info *track_info, | 1787 | static bool audio_buffer_codec(struct track_info *track_infop, |
1672 | struct mp3entry *track_id3) | 1788 | struct mp3entry *track_id3) |
1673 | { | 1789 | { |
1674 | /* This will not be the current track -> it cannot be the first and the | 1790 | /* This will not be the current track -> it cannot be the first and the |
1675 | current track cannot be ahead of buffering -> there is a previous | 1791 | current track cannot be ahead of buffering -> there is a previous |
1676 | track entry which is either current or ahead of the current */ | 1792 | track entry which is either current or ahead of the current */ |
1677 | struct track_info *prev_info = track_list_last(-1); | 1793 | struct track_info prev_info; |
1678 | struct mp3entry *prev_id3 = bufgetid3(prev_info->id3_hid); | 1794 | track_list_last(-1, &prev_info); |
1795 | |||
1796 | struct mp3entry *prev_id3 = bufgetid3(prev_info.id3_hid); | ||
1679 | 1797 | ||
1680 | /* If the previous codec is the same as this one, there is no need to | 1798 | /* If the previous codec is the same as this one, there is no need to |
1681 | put another copy of it on the file buffer (in other words, only | 1799 | put another copy of it on the file buffer (in other words, only |
@@ -1701,11 +1819,11 @@ static bool audio_buffer_codec(struct track_info *track_info, | |||
1701 | char codec_path[MAX_PATH+1]; /* Full path to codec */ | 1819 | char codec_path[MAX_PATH+1]; /* Full path to codec */ |
1702 | codec_get_full_path(codec_path, codec_fn); | 1820 | codec_get_full_path(codec_path, codec_fn); |
1703 | 1821 | ||
1704 | track_info->codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL); | 1822 | track_infop->codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL); |
1705 | 1823 | ||
1706 | if (track_info->codec_hid >= 0) | 1824 | if (track_infop->codec_hid > 0) |
1707 | { | 1825 | { |
1708 | logf("Buffered codec: %d", track_info->codec_hid); | 1826 | logf("Buffered codec: %d", track_infop->codec_hid); |
1709 | return true; | 1827 | return true; |
1710 | } | 1828 | } |
1711 | 1829 | ||
@@ -1726,18 +1844,18 @@ static bool audio_buffer_codec(struct track_info *track_info, | |||
1726 | */ | 1844 | */ |
1727 | static int audio_load_track(void) | 1845 | static int audio_load_track(void) |
1728 | { | 1846 | { |
1729 | if (in_progress_id3_hid >= 0) | 1847 | struct track_info info; |
1848 | |||
1849 | if (track_list.in_progress_hid > 0) | ||
1730 | { | 1850 | { |
1731 | /* There must be an info pointer if the in-progress id3 is even there */ | 1851 | /* There must be an info pointer if the in-progress id3 is even there */ |
1732 | struct track_info *info = track_list_last(0); | 1852 | if (track_list_last(0, &info) && info.self_hid == track_list.in_progress_hid) |
1733 | |||
1734 | if (info->id3_hid == in_progress_id3_hid) | ||
1735 | { | 1853 | { |
1736 | if (filling == STATE_FILLING) | 1854 | if (filling == STATE_FILLING) |
1737 | { | 1855 | { |
1738 | /* Haven't finished the metadata but the notification is | 1856 | /* Haven't finished the metadata but the notification is |
1739 | anticipated to come soon */ | 1857 | anticipated to come soon */ |
1740 | logf("%s(): in progress ok: %d", __func__, info->id3_hid); | 1858 | logf("%s:in progress:id=%d", __func__, info.self_hid); |
1741 | return LOAD_TRACK_OK; | 1859 | return LOAD_TRACK_OK; |
1742 | } | 1860 | } |
1743 | else if (filling == STATE_FULL) | 1861 | else if (filling == STATE_FULL) |
@@ -1745,33 +1863,32 @@ static int audio_load_track(void) | |||
1745 | /* Buffer was full trying to complete the load after the | 1863 | /* Buffer was full trying to complete the load after the |
1746 | metadata finished, so attempt to continue - older handles | 1864 | metadata finished, so attempt to continue - older handles |
1747 | should have been cleared already */ | 1865 | should have been cleared already */ |
1748 | logf("%s(): finishing load: %d", __func__, info->id3_hid); | 1866 | logf("%s:finished:id=%d", __func__, info.self_hid); |
1749 | filling = STATE_FILLING; | 1867 | filling = STATE_FILLING; |
1750 | buffer_event_finished_callback(BUFFER_EVENT_FINISHED, &info->id3_hid); | 1868 | buffer_event_finished_callback(BUFFER_EVENT_FINISHED, &info.id3_hid); |
1751 | return LOAD_TRACK_OK; | 1869 | return LOAD_TRACK_OK; |
1752 | } | 1870 | } |
1753 | } | 1871 | } |
1754 | 1872 | ||
1755 | /* Some old, stray buffering message */ | 1873 | /* Some old, stray buffering message */ |
1756 | logf("%s(): already in progress: %d", __func__, info->id3_hid); | 1874 | logf("%s:busy:id=%d", __func__, info.self_hid); |
1757 | return LOAD_TRACK_ERR_BUSY; | 1875 | return LOAD_TRACK_ERR_BUSY; |
1758 | } | 1876 | } |
1759 | 1877 | ||
1760 | filling = STATE_FILLING; | 1878 | filling = STATE_FILLING; |
1761 | 1879 | ||
1762 | struct track_info *info = track_list_alloc_track(); | 1880 | if (!track_list_alloc_info(&info)) |
1763 | if (info == NULL) | ||
1764 | { | 1881 | { |
1765 | /* List is full so stop buffering tracks - however, attempt to obtain | 1882 | /* List is full so stop buffering tracks - however, attempt to obtain |
1766 | metadata as the unbuffered id3 */ | 1883 | metadata as the unbuffered id3 */ |
1767 | logf("No free tracks"); | 1884 | logf("buffer full:alloc"); |
1768 | filling = STATE_FULL; | 1885 | filling = STATE_FULL; |
1769 | } | 1886 | } |
1770 | 1887 | ||
1771 | playlist_peek_offset++; | 1888 | playlist_peek_offset++; |
1772 | 1889 | ||
1773 | logf("Buffering track: s%u/c%u/e%u/p%d", | 1890 | logf("Buffering track:f=%d:c=%d:l=%d:pk=%d", |
1774 | track_list.start, track_list.current, track_list.end, | 1891 | track_list.first_hid, track_list.current_hid, track_list.last_hid, |
1775 | playlist_peek_offset); | 1892 | playlist_peek_offset); |
1776 | 1893 | ||
1777 | /* Get track name from current playlist read position */ | 1894 | /* Get track name from current playlist read position */ |
@@ -1781,7 +1898,6 @@ static int audio_load_track(void) | |||
1781 | 1898 | ||
1782 | while (1) | 1899 | while (1) |
1783 | { | 1900 | { |
1784 | |||
1785 | trackname = playlist_peek(playlist_peek_offset, name_buf, | 1901 | trackname = playlist_peek(playlist_peek_offset, name_buf, |
1786 | sizeof (name_buf)); | 1902 | sizeof (name_buf)); |
1787 | 1903 | ||
@@ -1809,16 +1925,14 @@ static int audio_load_track(void) | |||
1809 | id3_write_locked(UNBUFFERED_ID3, NULL); | 1925 | id3_write_locked(UNBUFFERED_ID3, NULL); |
1810 | 1926 | ||
1811 | if (filling != STATE_FULL) | 1927 | if (filling != STATE_FULL) |
1812 | track_list_unalloc_track(); /* Free this entry */ | 1928 | track_list_free_info(&info); /* Free this entry */ |
1813 | 1929 | ||
1814 | playlist_peek_offset--; /* Maintain at last index */ | 1930 | playlist_peek_offset--; /* Maintain at last index */ |
1815 | 1931 | ||
1816 | /* We can end up here after the real last track signals its completion | 1932 | /* We can end up here after the real last track signals its completion |
1817 | and miss the transition to STATE_FINISHED esp. if dropping the last | 1933 | and miss the transition to STATE_FINISHED esp. if dropping the last |
1818 | songs of a playlist late in their load (2nd stage) */ | 1934 | songs of a playlist late in their load (2nd stage) */ |
1819 | info = track_list_last(0); | 1935 | if (track_list_last(0, &info) && buf_handle_remaining(info.audio_hid) == 0) |
1820 | |||
1821 | if (info && buf_handle_remaining(info->audio_hid) == 0) | ||
1822 | filling_is_finished(); | 1936 | filling_is_finished(); |
1823 | else | 1937 | else |
1824 | filling = STATE_END_OF_PLAYLIST; | 1938 | filling = STATE_END_OF_PLAYLIST; |
@@ -1828,7 +1942,7 @@ static int audio_load_track(void) | |||
1828 | 1942 | ||
1829 | /* Successfully opened the file - get track metadata */ | 1943 | /* Successfully opened the file - get track metadata */ |
1830 | if (filling == STATE_FULL || | 1944 | if (filling == STATE_FULL || |
1831 | (info->id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL)) < 0) | 1945 | (info.id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL)) < 0) |
1832 | { | 1946 | { |
1833 | /* Buffer or track list is full */ | 1947 | /* Buffer or track list is full */ |
1834 | struct mp3entry *ub_id3; | 1948 | struct mp3entry *ub_id3; |
@@ -1843,7 +1957,7 @@ static int audio_load_track(void) | |||
1843 | 1957 | ||
1844 | if (filling != STATE_FULL) | 1958 | if (filling != STATE_FULL) |
1845 | { | 1959 | { |
1846 | track_list_unalloc_track(); | 1960 | track_list_free_info(&info); |
1847 | filling = STATE_FULL; | 1961 | filling = STATE_FULL; |
1848 | } | 1962 | } |
1849 | 1963 | ||
@@ -1852,9 +1966,17 @@ static int audio_load_track(void) | |||
1852 | } | 1966 | } |
1853 | else | 1967 | else |
1854 | { | 1968 | { |
1969 | info.filesize = filesize(fd); | ||
1970 | |||
1971 | if (!track_list_commit_info(&info)) | ||
1972 | { | ||
1973 | track_list_free_info(&info); | ||
1974 | track_list.in_progress_hid = 0; | ||
1975 | return LOAD_TRACK_ERR_FAILED; | ||
1976 | } | ||
1977 | |||
1855 | /* Successful load initiation */ | 1978 | /* Successful load initiation */ |
1856 | info->filesize = filesize(fd); | 1979 | track_list.in_progress_hid = info.self_hid; |
1857 | in_progress_id3_hid = info->id3_hid; /* Remember what's in-progress */ | ||
1858 | } | 1980 | } |
1859 | 1981 | ||
1860 | close(fd); | 1982 | close(fd); |
@@ -1865,22 +1987,24 @@ static int audio_load_track(void) | |||
1865 | can load the codec, the album art and finally the audio data. | 1987 | can load the codec, the album art and finally the audio data. |
1866 | This is called on the audio thread after the buffering thread calls the | 1988 | This is called on the audio thread after the buffering thread calls the |
1867 | buffering_handle_finished_callback callback. */ | 1989 | buffering_handle_finished_callback callback. */ |
1868 | static int audio_finish_load_track(struct track_info *info) | 1990 | static int audio_finish_load_track(struct track_info *infop) |
1869 | { | 1991 | { |
1870 | int trackstat = LOAD_TRACK_OK; | 1992 | int trackstat = LOAD_TRACK_OK; |
1871 | 1993 | ||
1872 | if (info->id3_hid != in_progress_id3_hid) | 1994 | if (infop->self_hid != track_list.in_progress_hid) |
1873 | { | 1995 | { |
1874 | /* We must not be here if not! */ | 1996 | /* We must not be here if not! */ |
1875 | logf("%s: wrong track %d/%d", __func__, info->id3_hid, | 1997 | logf("%s:wrong track:hids=%d!=%d", __func__, infop->self_hid, |
1876 | in_progress_id3_hid); | 1998 | track_list.in_progress_hid); |
1877 | return LOAD_TRACK_ERR_BUSY; | 1999 | return LOAD_TRACK_ERR_BUSY; |
1878 | } | 2000 | } |
1879 | 2001 | ||
1880 | /* The current track for decoding (there is always one if the list is | 2002 | /* The current track for decoding (there is always one if the list is |
1881 | populated) */ | 2003 | populated) */ |
1882 | struct track_info *cur_info = track_list_current(0); | 2004 | struct track_info cur_info; |
1883 | struct mp3entry *track_id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | 2005 | track_list_current(0, &cur_info); |
2006 | |||
2007 | struct mp3entry *track_id3 = valid_mp3entry(bufgetid3(infop->id3_hid)); | ||
1884 | 2008 | ||
1885 | if (!track_id3) | 2009 | if (!track_id3) |
1886 | { | 2010 | { |
@@ -1892,7 +2016,7 @@ static int audio_finish_load_track(struct track_info *info) | |||
1892 | } | 2016 | } |
1893 | 2017 | ||
1894 | /* Try to load a cuesheet for the track */ | 2018 | /* Try to load a cuesheet for the track */ |
1895 | if (!audio_load_cuesheet(info, track_id3)) | 2019 | if (!audio_load_cuesheet(infop, track_id3)) |
1896 | { | 2020 | { |
1897 | /* No space for cuesheet on buffer, not an error */ | 2021 | /* No space for cuesheet on buffer, not an error */ |
1898 | filling = STATE_FULL; | 2022 | filling = STATE_FULL; |
@@ -1901,7 +2025,7 @@ static int audio_finish_load_track(struct track_info *info) | |||
1901 | 2025 | ||
1902 | #ifdef HAVE_ALBUMART | 2026 | #ifdef HAVE_ALBUMART |
1903 | /* Try to load album art for the track */ | 2027 | /* Try to load album art for the track */ |
1904 | if (!audio_load_albumart(info, track_id3)) | 2028 | if (!audio_load_albumart(infop, track_id3)) |
1905 | { | 2029 | { |
1906 | /* No space for album art on buffer, not an error */ | 2030 | /* No space for album art on buffer, not an error */ |
1907 | filling = STATE_FULL; | 2031 | filling = STATE_FULL; |
@@ -1912,7 +2036,9 @@ static int audio_finish_load_track(struct track_info *info) | |||
1912 | /* All handles available to external routines are ready - audio and codec | 2036 | /* All handles available to external routines are ready - audio and codec |
1913 | information is private */ | 2037 | information is private */ |
1914 | 2038 | ||
1915 | if (info == track_list_user_current(0)) | 2039 | struct track_info user_cur; |
2040 | track_list_user_current(0, &user_cur); | ||
2041 | if (infop->self_hid == user_cur.self_hid) | ||
1916 | { | 2042 | { |
1917 | /* Send only when the track handles could not all be opened ahead of | 2043 | /* Send only when the track handles could not all be opened ahead of |
1918 | time for the user's current track - otherwise everything is ready | 2044 | time for the user's current track - otherwise everything is ready |
@@ -1923,13 +2049,14 @@ static int audio_finish_load_track(struct track_info *info) | |||
1923 | 2049 | ||
1924 | #ifdef HAVE_CODEC_BUFFERING | 2050 | #ifdef HAVE_CODEC_BUFFERING |
1925 | /* Try to buffer a codec for the track */ | 2051 | /* Try to buffer a codec for the track */ |
1926 | if (info != cur_info && !audio_buffer_codec(info, track_id3)) | 2052 | if (infop->self_hid != cur_info.self_hid |
2053 | && !audio_buffer_codec(infop, track_id3)) | ||
1927 | { | 2054 | { |
1928 | if (info->codec_hid == ERR_BUFFER_FULL) | 2055 | if (infop->codec_hid == ERR_BUFFER_FULL) |
1929 | { | 2056 | { |
1930 | /* No space for codec on buffer, not an error */ | 2057 | /* No space for codec on buffer, not an error */ |
1931 | filling = STATE_FULL; | 2058 | filling = STATE_FULL; |
1932 | logf("buffer is full for now (%s)", __func__); | 2059 | logf("%s:STATE_FULL", __func__); |
1933 | } | 2060 | } |
1934 | else | 2061 | else |
1935 | { | 2062 | { |
@@ -1950,7 +2077,7 @@ static int audio_finish_load_track(struct track_info *info) | |||
1950 | if (track_id3->elapsed > track_id3->length) | 2077 | if (track_id3->elapsed > track_id3->length) |
1951 | track_id3->elapsed = 0; | 2078 | track_id3->elapsed = 0; |
1952 | 2079 | ||
1953 | if (track_id3->offset >= info->filesize) | 2080 | if ((off_t)track_id3->offset >= infop->filesize) |
1954 | track_id3->offset = 0; | 2081 | track_id3->offset = 0; |
1955 | 2082 | ||
1956 | logf("%s: set offset for %s to %lu\n", __func__, | 2083 | logf("%s: set offset for %s to %lu\n", __func__, |
@@ -1994,9 +2121,8 @@ static int audio_finish_load_track(struct track_info *info) | |||
1994 | 2121 | ||
1995 | if (hid >= 0) | 2122 | if (hid >= 0) |
1996 | { | 2123 | { |
1997 | info->audio_hid = hid; | 2124 | infop->audio_hid = hid; |
1998 | 2125 | if (infop->self_hid == cur_info.self_hid) | |
1999 | if (info == cur_info) | ||
2000 | { | 2126 | { |
2001 | /* This is the current track to decode - should be started now */ | 2127 | /* This is the current track to decode - should be started now */ |
2002 | trackstat = LOAD_TRACK_READY; | 2128 | trackstat = LOAD_TRACK_READY; |
@@ -2020,11 +2146,16 @@ static int audio_finish_load_track(struct track_info *info) | |||
2020 | } | 2146 | } |
2021 | 2147 | ||
2022 | audio_finish_load_track_exit: | 2148 | audio_finish_load_track_exit: |
2149 | if (trackstat >= LOAD_TRACK_OK && !track_info_sync(infop)) | ||
2150 | { | ||
2151 | logf("Track info sync failed"); | ||
2152 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
2153 | } | ||
2154 | |||
2023 | if (trackstat < LOAD_TRACK_OK) | 2155 | if (trackstat < LOAD_TRACK_OK) |
2024 | { | 2156 | { |
2025 | playlist_skip_entry(NULL, playlist_peek_offset); | 2157 | playlist_skip_entry(NULL, playlist_peek_offset); |
2026 | track_info_close(info); | 2158 | track_list_free_info(infop); |
2027 | track_list_unalloc_track(); | ||
2028 | 2159 | ||
2029 | if (playlist_peek(playlist_peek_offset, NULL, 0)) | 2160 | if (playlist_peek(playlist_peek_offset, NULL, 0)) |
2030 | playlist_next(0); | 2161 | playlist_next(0); |
@@ -2035,7 +2166,7 @@ audio_finish_load_track_exit: | |||
2035 | if (filling != STATE_FULL) | 2166 | if (filling != STATE_FULL) |
2036 | { | 2167 | { |
2037 | /* Load next track - error or not */ | 2168 | /* Load next track - error or not */ |
2038 | in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | 2169 | track_list.in_progress_hid = 0; |
2039 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); | 2170 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); |
2040 | audio_queue_post(Q_AUDIO_FILL_BUFFER, 0); | 2171 | audio_queue_post(Q_AUDIO_FILL_BUFFER, 0); |
2041 | } | 2172 | } |
@@ -2071,10 +2202,14 @@ static int audio_fill_file_buffer(void) | |||
2071 | 2202 | ||
2072 | if (trackstat >= LOAD_TRACK_OK) | 2203 | if (trackstat >= LOAD_TRACK_OK) |
2073 | { | 2204 | { |
2074 | if (track_list_current(0) == track_list_user_current(0)) | 2205 | struct track_info info, user_cur; |
2206 | track_list_current(0, &info); | ||
2207 | track_list_user_current(0, &user_cur); | ||
2208 | |||
2209 | if (info.self_hid == user_cur.self_hid) | ||
2075 | playlist_next(0); | 2210 | playlist_next(0); |
2076 | 2211 | ||
2077 | if (filling == STATE_FULL && !track_list_user_current(1)) | 2212 | if (filling == STATE_FULL && !track_list_user_current(1, NULL)) |
2078 | { | 2213 | { |
2079 | /* There are no user tracks on the buffer after this therefore | 2214 | /* There are no user tracks on the buffer after this therefore |
2080 | this is the next track */ | 2215 | this is the next track */ |
@@ -2166,26 +2301,28 @@ static void audio_on_fill_buffer(void) | |||
2166 | (Q_AUDIO_FINISH_LOAD_TRACK) */ | 2301 | (Q_AUDIO_FINISH_LOAD_TRACK) */ |
2167 | static void audio_on_finish_load_track(int id3_hid) | 2302 | static void audio_on_finish_load_track(int id3_hid) |
2168 | { | 2303 | { |
2169 | struct track_info *info = track_list_last(0); | 2304 | struct track_info info, user_cur; |
2170 | 2305 | ||
2171 | if (!info || !buf_is_handle(id3_hid)) | 2306 | if (!buf_is_handle(id3_hid) || !track_list_last(0, &info)) |
2172 | return; | 2307 | return; |
2173 | 2308 | ||
2174 | if (info == track_list_user_current(1)) | 2309 | track_list_user_current(1, &user_cur); |
2310 | if (info.self_hid == user_cur.self_hid) | ||
2175 | { | 2311 | { |
2176 | /* Just loaded the metadata right after the current position */ | 2312 | /* Just loaded the metadata right after the current position */ |
2177 | audio_update_and_announce_next_track(bufgetid3(info->id3_hid)); | 2313 | audio_update_and_announce_next_track(bufgetid3(info.id3_hid)); |
2178 | } | 2314 | } |
2179 | 2315 | ||
2180 | if (audio_finish_load_track(info) != LOAD_TRACK_READY) | 2316 | if (audio_finish_load_track(&info) != LOAD_TRACK_READY) |
2181 | return; /* Not current track */ | 2317 | return; /* Not current track */ |
2182 | 2318 | ||
2183 | bool is_user_current = info == track_list_user_current(0); | 2319 | track_list_user_current(0, &user_cur); |
2320 | bool is_user_current = info.self_hid == user_cur.self_hid; | ||
2184 | 2321 | ||
2185 | if (is_user_current) | 2322 | if (is_user_current) |
2186 | { | 2323 | { |
2187 | /* Copy cuesheet */ | 2324 | /* Copy cuesheet */ |
2188 | buf_read_cuesheet(info->cuesheet_hid); | 2325 | buf_read_cuesheet(info.cuesheet_hid); |
2189 | } | 2326 | } |
2190 | 2327 | ||
2191 | if (audio_start_codec(track_event_flags & TEF_AUTO_SKIP)) | 2328 | if (audio_start_codec(track_event_flags & TEF_AUTO_SKIP)) |
@@ -2197,7 +2334,7 @@ static void audio_on_finish_load_track(int id3_hid) | |||
2197 | change otherwise */ | 2334 | change otherwise */ |
2198 | bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3)); | 2335 | bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3)); |
2199 | 2336 | ||
2200 | playing_id3_sync(info, -1, -1); | 2337 | playing_id3_sync(&info, -1, -1); |
2201 | 2338 | ||
2202 | if (!was_valid) | 2339 | if (!was_valid) |
2203 | { | 2340 | { |
@@ -2220,12 +2357,12 @@ static void audio_on_handle_finished(int hid) | |||
2220 | /* Right now, only audio handles should end up calling this */ | 2357 | /* Right now, only audio handles should end up calling this */ |
2221 | if (filling == STATE_END_OF_PLAYLIST) | 2358 | if (filling == STATE_END_OF_PLAYLIST) |
2222 | { | 2359 | { |
2223 | struct track_info *info = track_list_last(0); | 2360 | struct track_info info; |
2224 | 2361 | ||
2225 | /* Really we don't know which order the handles will actually complete | 2362 | /* Really we don't know which order the handles will actually complete |
2226 | to zero bytes remaining since another thread is doing it - be sure | 2363 | to zero bytes remaining since another thread is doing it - be sure |
2227 | it's the right one */ | 2364 | it's the right one */ |
2228 | if (info && info->audio_hid == hid) | 2365 | if (track_list_last(0, &info) && info.audio_hid == hid) |
2229 | { | 2366 | { |
2230 | /* This was the last track in the playlist and we now have all the | 2367 | /* This was the last track in the playlist and we now have all the |
2231 | data we need */ | 2368 | data we need */ |
@@ -2276,16 +2413,17 @@ static void audio_finalise_track_change(void) | |||
2276 | return; | 2413 | return; |
2277 | } | 2414 | } |
2278 | 2415 | ||
2279 | struct track_info *info = track_list_current(0); | 2416 | struct track_info info; |
2417 | bool have_info = track_list_current(0, &info); | ||
2280 | struct mp3entry *track_id3 = NULL; | 2418 | struct mp3entry *track_id3 = NULL; |
2281 | 2419 | ||
2282 | id3_mutex_lock(); | 2420 | id3_mutex_lock(); |
2283 | 2421 | ||
2284 | /* Update the current cuesheet if any and enabled */ | 2422 | /* Update the current cuesheet if any and enabled */ |
2285 | if (info) | 2423 | if (have_info) |
2286 | { | 2424 | { |
2287 | buf_read_cuesheet(info->cuesheet_hid); | 2425 | buf_read_cuesheet(info.cuesheet_hid); |
2288 | track_id3 = bufgetid3(info->id3_hid); | 2426 | track_id3 = bufgetid3(info.id3_hid); |
2289 | } | 2427 | } |
2290 | 2428 | ||
2291 | id3_write(PLAYING_ID3, track_id3); | 2429 | id3_write(PLAYING_ID3, track_id3); |
@@ -2294,10 +2432,10 @@ static void audio_finalise_track_change(void) | |||
2294 | skip_pending = TRACK_SKIP_NONE; | 2432 | skip_pending = TRACK_SKIP_NONE; |
2295 | 2433 | ||
2296 | /* Sync the next track information */ | 2434 | /* Sync the next track information */ |
2297 | info = track_list_current(1); | 2435 | have_info = track_list_current(1, &info); |
2298 | 2436 | ||
2299 | id3_write(NEXTTRACK_ID3, info ? bufgetid3(info->id3_hid) : | 2437 | id3_write(NEXTTRACK_ID3, have_info ? bufgetid3(info.id3_hid) : |
2300 | id3_get(UNBUFFERED_ID3)); | 2438 | id3_get(UNBUFFERED_ID3)); |
2301 | 2439 | ||
2302 | id3_mutex_unlock(); | 2440 | id3_mutex_unlock(); |
2303 | 2441 | ||
@@ -2326,17 +2464,19 @@ static void audio_begin_track_change(enum pcm_track_change_type type, | |||
2326 | 2464 | ||
2327 | if (trackstat >= LOAD_TRACK_OK) | 2465 | if (trackstat >= LOAD_TRACK_OK) |
2328 | { | 2466 | { |
2329 | struct track_info *info = track_list_current(0); | 2467 | struct track_info info; |
2330 | 2468 | if (track_list_current(0, &info)) | |
2331 | if (info->audio_hid < 0) | ||
2332 | return; | ||
2333 | |||
2334 | /* Everything needed for the codec is ready - start it */ | ||
2335 | if (audio_start_codec(auto_skip)) | ||
2336 | { | 2469 | { |
2337 | if (!auto_skip) | 2470 | if (info.audio_hid < 0) |
2338 | playing_id3_sync(info, -1, -1); | 2471 | return; |
2339 | return; | 2472 | |
2473 | /* Everything needed for the codec is ready - start it */ | ||
2474 | if (audio_start_codec(auto_skip)) | ||
2475 | { | ||
2476 | if (!auto_skip) | ||
2477 | playing_id3_sync(&info, -1, -1); | ||
2478 | return; | ||
2479 | } | ||
2340 | } | 2480 | } |
2341 | 2481 | ||
2342 | trackstat = LOAD_TRACK_ERR_START_CODEC; | 2482 | trackstat = LOAD_TRACK_ERR_START_CODEC; |
@@ -2398,13 +2538,14 @@ static void audio_on_codec_complete(int status) | |||
2398 | skip_pending = TRACK_SKIP_AUTO; | 2538 | skip_pending = TRACK_SKIP_AUTO; |
2399 | 2539 | ||
2400 | /* Does this track have an entry allocated? */ | 2540 | /* Does this track have an entry allocated? */ |
2401 | struct track_info *info = track_list_advance_current(1); | 2541 | struct track_info info; |
2542 | bool have_track = track_list_advance_current(1, &info); | ||
2402 | 2543 | ||
2403 | if (!info || info->audio_hid < 0) | 2544 | if (!have_track || info.audio_hid < 0) |
2404 | { | 2545 | { |
2405 | bool end_of_playlist = false; | 2546 | bool end_of_playlist = false; |
2406 | 2547 | ||
2407 | if (info) | 2548 | if (have_track) |
2408 | { | 2549 | { |
2409 | /* Track load is not complete - it might have stopped on a | 2550 | /* Track load is not complete - it might have stopped on a |
2410 | full buffer without reaching the audio handle or we just | 2551 | full buffer without reaching the audio handle or we just |
@@ -2418,7 +2559,7 @@ static void audio_on_codec_complete(int status) | |||
2418 | issue and a pointless full reload of all the track's | 2559 | issue and a pointless full reload of all the track's |
2419 | metadata may be avoided */ | 2560 | metadata may be avoided */ |
2420 | 2561 | ||
2421 | struct mp3entry *track_id3 = bufgetid3(info->id3_hid); | 2562 | struct mp3entry *track_id3 = bufgetid3(info.id3_hid); |
2422 | 2563 | ||
2423 | if (track_id3 && !rbcodec_format_is_atomic(track_id3->codectype)) | 2564 | if (track_id3 && !rbcodec_format_is_atomic(track_id3->codectype)) |
2424 | { | 2565 | { |
@@ -2595,7 +2736,9 @@ static void audio_start_playback(const struct audio_resume_info *resume_info, | |||
2595 | if (trackstat >= LOAD_TRACK_OK) | 2736 | if (trackstat >= LOAD_TRACK_OK) |
2596 | { | 2737 | { |
2597 | /* This is the currently playing track - get metadata, stat */ | 2738 | /* This is the currently playing track - get metadata, stat */ |
2598 | playing_id3_sync(track_list_current(0), resume.elapsed, resume.offset); | 2739 | struct track_info info; |
2740 | track_list_current(0, &info); | ||
2741 | playing_id3_sync(&info, resume.elapsed, resume.offset); | ||
2599 | 2742 | ||
2600 | if (valid_mp3entry(id3_get(PLAYING_ID3))) | 2743 | if (valid_mp3entry(id3_get(PLAYING_ID3))) |
2601 | { | 2744 | { |
@@ -2765,10 +2908,11 @@ static void audio_on_skip(void) | |||
2765 | /* Adjust things by how much the playlist was manually moved */ | 2908 | /* Adjust things by how much the playlist was manually moved */ |
2766 | playlist_peek_offset -= playlist_delta; | 2909 | playlist_peek_offset -= playlist_delta; |
2767 | 2910 | ||
2768 | struct track_info *info = track_list_advance_current(track_list_delta); | ||
2769 | int trackstat = LOAD_TRACK_OK; | 2911 | int trackstat = LOAD_TRACK_OK; |
2770 | 2912 | ||
2771 | if (!info || info->audio_hid < 0) | 2913 | struct track_info info; |
2914 | if (!track_list_advance_current(track_list_delta, &info) | ||
2915 | || info.audio_hid < 0) | ||
2772 | { | 2916 | { |
2773 | /* We don't know the next track thus we know we don't have it */ | 2917 | /* We don't know the next track thus we know we don't have it */ |
2774 | trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); | 2918 | trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); |
@@ -2883,31 +3027,30 @@ static void audio_on_ff_rewind(long time) | |||
2883 | /* If in transition, key will have changed - sync to it */ | 3027 | /* If in transition, key will have changed - sync to it */ |
2884 | position_key = pcmbuf_get_position_key(); | 3028 | position_key = pcmbuf_get_position_key(); |
2885 | 3029 | ||
2886 | if (pending == TRACK_SKIP_AUTO) | 3030 | if (pending == TRACK_SKIP_AUTO && !track_list_advance_current(-1, NULL)) |
2887 | { | 3031 | { |
2888 | if (!track_list_advance_current(-1)) | 3032 | /* Not in list - must rebuffer at the current playlist index */ |
3033 | if (audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1) | ||
3034 | < LOAD_TRACK_OK) | ||
2889 | { | 3035 | { |
2890 | /* Not in list - must rebuffer at the current playlist index */ | 3036 | /* Codec is stopped */ |
2891 | if (audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1) | 3037 | break; |
2892 | < LOAD_TRACK_OK) | ||
2893 | { | ||
2894 | /* Codec is stopped */ | ||
2895 | break; | ||
2896 | } | ||
2897 | } | 3038 | } |
2898 | } | 3039 | } |
2899 | 3040 | ||
2900 | /* Set after audio_fill_file_buffer to disable playing id3 clobber if | 3041 | /* Set after audio_fill_file_buffer to disable playing id3 clobber if |
2901 | rebuffer is needed */ | 3042 | rebuffer is needed */ |
2902 | skip_pending = TRACK_SKIP_NONE; | 3043 | skip_pending = TRACK_SKIP_NONE; |
2903 | struct track_info *cur_info = track_list_current(0); | 3044 | |
3045 | struct track_info cur_info; | ||
3046 | track_list_current(0, &cur_info); | ||
2904 | 3047 | ||
2905 | /* Track must complete the loading _now_ since a codec and audio | 3048 | /* Track must complete the loading _now_ since a codec and audio |
2906 | handle are needed in order to do the seek */ | 3049 | handle are needed in order to do the seek */ |
2907 | bool finish_load = cur_info->audio_hid < 0; | 3050 | bool finish_load = cur_info.audio_hid < 0; |
2908 | 3051 | ||
2909 | if (finish_load && | 3052 | if (finish_load && |
2910 | audio_finish_load_track(cur_info) != LOAD_TRACK_READY) | 3053 | audio_finish_load_track(&cur_info) != LOAD_TRACK_READY) |
2911 | { | 3054 | { |
2912 | /* Call above should push any load sequence - no need for | 3055 | /* Call above should push any load sequence - no need for |
2913 | halt_decoding_track here if no skip was pending here because | 3056 | halt_decoding_track here if no skip was pending here because |
@@ -2918,8 +3061,8 @@ static void audio_on_ff_rewind(long time) | |||
2918 | 3061 | ||
2919 | if (pending == TRACK_SKIP_AUTO || finish_load) | 3062 | if (pending == TRACK_SKIP_AUTO || finish_load) |
2920 | { | 3063 | { |
2921 | if (!bufreadid3(cur_info->id3_hid, ci_id3) || | 3064 | if (!bufreadid3(cur_info.id3_hid, ci_id3) || |
2922 | !audio_init_codec(cur_info, ci_id3)) | 3065 | !audio_init_codec(&cur_info, ci_id3)) |
2923 | { | 3066 | { |
2924 | /* We should have still been able to get it - skip it and move | 3067 | /* We should have still been able to get it - skip it and move |
2925 | onto the next one - like it or not this track is broken */ | 3068 | onto the next one - like it or not this track is broken */ |
@@ -2927,9 +3070,9 @@ static void audio_on_ff_rewind(long time) | |||
2927 | } | 3070 | } |
2928 | 3071 | ||
2929 | /* Set the codec API to the correct metadata and track info */ | 3072 | /* Set the codec API to the correct metadata and track info */ |
2930 | ci.audio_hid = cur_info->audio_hid; | 3073 | ci.audio_hid = cur_info.audio_hid; |
2931 | ci.filesize = cur_info->filesize; | 3074 | ci.filesize = cur_info.filesize; |
2932 | buf_set_base_handle(cur_info->audio_hid); | 3075 | buf_set_base_handle(cur_info.audio_hid); |
2933 | } | 3076 | } |
2934 | 3077 | ||
2935 | if (!haltres) | 3078 | if (!haltres) |
@@ -3568,16 +3711,17 @@ int playback_current_aa_hid(int slot) | |||
3568 | { | 3711 | { |
3569 | if ((unsigned)slot < MAX_MULTIPLE_AA) | 3712 | if ((unsigned)slot < MAX_MULTIPLE_AA) |
3570 | { | 3713 | { |
3571 | struct track_info *info = track_list_user_current(skip_offset); | 3714 | struct track_info user_cur; |
3715 | bool have_info = track_list_user_current(skip_offset, &user_cur); | ||
3572 | 3716 | ||
3573 | if (!info && abs(skip_offset) <= 1) | 3717 | if (!have_info && abs(skip_offset) <= 1) |
3574 | { | 3718 | { |
3575 | /* Give the actual position a go */ | 3719 | /* Give the actual position a go */ |
3576 | info = track_list_user_current(0); | 3720 | have_info = track_list_user_current(0, &user_cur); |
3577 | } | 3721 | } |
3578 | 3722 | ||
3579 | if (info) | 3723 | if (have_info) |
3580 | return info->aa_hid[slot]; | 3724 | return user_cur.aa_hid[slot]; |
3581 | } | 3725 | } |
3582 | 3726 | ||
3583 | return ERR_HANDLE_NOT_FOUND; | 3727 | return ERR_HANDLE_NOT_FOUND; |
diff --git a/firmware/export/system.h b/firmware/export/system.h index c7f5a8112c..f26b3d7f56 100644 --- a/firmware/export/system.h +++ b/firmware/export/system.h | |||
@@ -97,6 +97,11 @@ int get_cpu_boost_counter(void); | |||
97 | #define MAX(a, b) (((a)>(b))?(a):(b)) | 97 | #define MAX(a, b) (((a)>(b))?(a):(b)) |
98 | #endif | 98 | #endif |
99 | 99 | ||
100 | #ifndef SGN | ||
101 | #define SGN(a) \ | ||
102 | ({ typeof (a) ___a = (a); (___a > 0) - (___a < 0); }) | ||
103 | #endif | ||
104 | |||
100 | /* return number of elements in array a */ | 105 | /* return number of elements in array a */ |
101 | #define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0])) | 106 | #define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0])) |
102 | 107 | ||