summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2017-12-08 13:01:25 -0500
committerMichael Sevakis <jethead71@rockbox.org>2017-12-09 17:05:59 -0500
commitc1a01beded5103df32ea2e3ec596e80de740cf2e (patch)
tree244e086f06172bb1957c45e1c508a1de5ece6964
parente86ea6bdb9976d5142ba924565dbdf96bf9826a7 (diff)
downloadrockbox-c1a01beded5103df32ea2e3ec596e80de740cf2e.tar.gz
rockbox-c1a01beded5103df32ea2e3ec596e80de740cf2e.zip
Playback: Move internal track list onto buffer
Does away the statically-allocated track list which frees quite a fair amount of in-RAM size. There's no compile-time hard track limit. Recommended TODO (but not right away): Have data small enough use the handle structure as its buffer data area. Almost the entire handle structure is unused for simple allocations without any associated filesystem path. Change-Id: I74a4561e5a837e049811ac421722ec00dadc0d50
-rw-r--r--apps/buffering.c6
-rw-r--r--apps/buffering.h3
-rw-r--r--apps/playback.c798
-rw-r--r--firmware/export/system.h5
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
79int bufopen(const char *file, size_t offset, enum data_type type, 78int bufopen(const char *file, size_t offset, enum data_type type,
80 void *user_data); 79 void *user_data);
81int bufalloc(const void *src, size_t size, enum data_type type); 80int 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
196struct track_info 212struct 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 238struct 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
228static struct 247static 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 */
245static int playlist_peek_offset = 0; 261static 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 */
252static 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) */
256static size_t buffer_margin = 5; 265static 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 */ 455static void track_info_close_handle(int *hidp)
447static 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 */
461static void track_info_close(struct track_info *info) 462static 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)
479static 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 */
476enum 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 */
498static void INIT_ATTR track_list_init(void) 484static 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 */
508static unsigned int track_list_count(void) 494static 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 */
514static inline bool track_list_empty(void) 500static 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 */
520static bool track_list_full(void) 506static 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 */
526static bool track_list_in_range(int pos) 514static 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
532static 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 */
539static struct track_info * track_list_last(int offset) 526static 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)
551static 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 */ 566static bool track_list_commit_buf_info(struct track_buf_info *tbip,
561static 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
577static struct track_info * track_list_current(int offset) 602 was succesfully added at some point */
603static 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 */
666static 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 */
589static struct track_info * track_list_user_current(int offset) 673static 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 */
602static struct track_info * track_list_advance_current(int offset) 686static 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 */
615enum track_clear_action 700static 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 */
706static 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 */
731static 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 */
742static 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
622static 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 */
753static 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 */
1129static void playing_id3_sync(struct track_info *user_info, 1236static 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 */
1351static bool audio_init_codec(struct track_info *track_info, 1458static 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 */
1453static bool audio_start_codec(bool auto_skip) 1562static 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 */
1552static bool audio_load_cuesheet(struct track_info *info, 1663static 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 */
1607static bool audio_load_albumart(struct track_info *info, 1718static 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 */
1671static bool audio_buffer_codec(struct track_info *track_info, 1787static 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*/
1727static int audio_load_track(void) 1845static 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. */
1868static int audio_finish_load_track(struct track_info *info) 1990static 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
2022audio_finish_load_track_exit: 2148audio_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) */
2167static void audio_on_finish_load_track(int id3_hid) 2302static 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