summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-07-14 07:59:39 -0400
committerMichael Sevakis <jethead71@rockbox.org>2014-03-10 04:12:30 +0100
commit31b712286721dd606940c7b557d03e3f714b9604 (patch)
tree8c4a4cc32e9000ea721ebb23aa3c0129ca97bf53 /apps
parentdda54b85daa83b7803b4fb189ab45859f96ff3f9 (diff)
downloadrockbox-31b712286721dd606940c7b557d03e3f714b9604.tar.gz
rockbox-31b712286721dd606940c7b557d03e3f714b9604.zip
Implement time-based resume and playback start.
This complements offset-based resume and playback start funcionality. The implementation is global on both HWCODEC and SWCODEC. Basically, if either the specified elapsed or offset are non-zero, it indicates a mid-track resume. To resume by time only, set elapsed to nonzero and offset to zero. To resume by offset only, set offset to nonzero and elapsed to zero. Which one the codec uses and which has priority is up to the codec; however, using an elapsed time covers more cases: * Codecs not able to use an offset such as VGM or other atomic formats * Starting playback at a nonzero elapsed time from a source that contains no offset, such as a cuesheet The change re-versions pretty much everything from tagcache to nvram. Change-Id: Ic7aebb24e99a03ae99585c5e236eba960d163f38 Reviewed-on: http://gerrit.rockbox.org/516 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested: Michael Sevakis <jethead71@rockbox.org>
Diffstat (limited to 'apps')
-rw-r--r--apps/bookmark.c2
-rw-r--r--apps/filetree.c5
-rw-r--r--apps/gui/skin_engine/skin_parser.c6
-rw-r--r--apps/gui/skin_engine/skin_touchsupport.c1
-rw-r--r--apps/misc.c2
-rw-r--r--apps/mpeg.c60
-rw-r--r--apps/onplay.c2
-rw-r--r--apps/playback.c137
-rwxr-xr-xapps/playlist.c21
-rw-r--r--apps/playlist.h6
-rw-r--r--apps/playlist_viewer.c10
-rw-r--r--apps/plugin.h12
-rw-r--r--apps/plugins/alarmclock.c1
-rw-r--r--apps/plugins/alpine_cdc.c4
-rw-r--r--apps/plugins/lib/playback_control.c1
-rw-r--r--apps/plugins/lrcplayer.c1
-rw-r--r--apps/plugins/pictureflow/pictureflow.c2
-rw-r--r--apps/plugins/random_folder_advance_config.c2
-rw-r--r--apps/root_menu.c1
-rw-r--r--apps/settings.c11
-rw-r--r--apps/settings.h2
-rw-r--r--apps/settings_list.c1
-rw-r--r--apps/settings_list.h2
-rw-r--r--apps/tagcache.c12
-rw-r--r--apps/tagcache.h14
-rw-r--r--apps/tagtree.c20
-rw-r--r--apps/tree.c8
-rw-r--r--apps/tree.h4
28 files changed, 241 insertions, 109 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c
index 543e89331a..3234e77d9b 100644
--- a/apps/bookmark.c
+++ b/apps/bookmark.c
@@ -972,7 +972,7 @@ static bool play_bookmark(const char* bookmark)
972 if (!warn_on_pl_erase()) 972 if (!warn_on_pl_erase())
973 return false; 973 return false;
974 return bookmark_play(global_temp_buffer, bm.resume_index, 974 return bookmark_play(global_temp_buffer, bm.resume_index,
975 bm.resume_offset, bm.resume_seed, global_filename); 975 bm.resume_time, bm.resume_offset, bm.resume_seed, global_filename);
976 } 976 }
977 977
978 return false; 978 return false;
diff --git a/apps/filetree.c b/apps/filetree.c
index 2edcaf3a03..319b5f4a77 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -118,7 +118,7 @@ bool ft_play_playlist(char* pathname, char* dirname, char* filename)
118 playlist_shuffle(current_tick, -1); 118 playlist_shuffle(current_tick, -1);
119 } 119 }
120 120
121 playlist_start(0, 0); 121 playlist_start(0, 0, 0);
122 return true; 122 return true;
123 } 123 }
124 124
@@ -498,7 +498,7 @@ int ft_enter(struct tree_context* c)
498 start_index = 0; 498 start_index = 0;
499 } 499 }
500 500
501 playlist_start(start_index, 0); 501 playlist_start(start_index, 0, 0);
502 play = true; 502 play = true;
503 } 503 }
504 break; 504 break;
@@ -705,6 +705,7 @@ int ft_enter(struct tree_context* c)
705 global_status.resume_index = start_index; 705 global_status.resume_index = start_index;
706 global_status.resume_crc32 = 706 global_status.resume_crc32 =
707 playlist_get_filename_crc32(NULL, start_index); 707 playlist_get_filename_crc32(NULL, start_index);
708 global_status.resume_elapsed = 0;
708 global_status.resume_offset = 0; 709 global_status.resume_offset = 0;
709 status_save(); 710 status_save();
710 rc = GO_TO_WPS; 711 rc = GO_TO_WPS;
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index a76a06ac61..57153ed602 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -2414,10 +2414,12 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2414 (((wps_data->last_albumart_height != aa->height) || 2414 (((wps_data->last_albumart_height != aa->height) ||
2415 (wps_data->last_albumart_width != aa->width))))) 2415 (wps_data->last_albumart_width != aa->width)))))
2416 { 2416 {
2417 long offset = audio_current_track()->offset; 2417 struct mp3entry *id3 = audio_current_track();
2418 unsigned long elapsed = id3->elapsed;
2419 unsigned long offset = id3->offset;
2418 audio_stop(); 2420 audio_stop();
2419 if (!(status & AUDIO_STATUS_PAUSE)) 2421 if (!(status & AUDIO_STATUS_PAUSE))
2420 audio_play(offset); 2422 audio_play(elapsed, offset);
2421 } 2423 }
2422 } 2424 }
2423#endif 2425#endif
diff --git a/apps/gui/skin_engine/skin_touchsupport.c b/apps/gui/skin_engine/skin_touchsupport.c
index dbc561500a..7a03e83c36 100644
--- a/apps/gui/skin_engine/skin_touchsupport.c
+++ b/apps/gui/skin_engine/skin_touchsupport.c
@@ -177,6 +177,7 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
177 if (playlist_resume() != -1) 177 if (playlist_resume() != -1)
178 { 178 {
179 playlist_start(global_status.resume_index, 179 playlist_start(global_status.resume_index,
180 global_status.resume_elapsed,
180 global_status.resume_offset); 181 global_status.resume_offset);
181 } 182 }
182 } 183 }
diff --git a/apps/misc.c b/apps/misc.c
index fdbc1fb831..6c589b99e4 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -618,6 +618,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
618 if (resume && playlist_resume() != -1) 618 if (resume && playlist_resume() != -1)
619 { 619 {
620 playlist_start(global_status.resume_index, 620 playlist_start(global_status.resume_index,
621 global_status.resume_elapsed,
621 global_status.resume_offset); 622 global_status.resume_offset);
622 } 623 }
623 resume = false; 624 resume = false;
@@ -657,6 +658,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
657 if (playlist_resume() != -1) 658 if (playlist_resume() != -1)
658 { 659 {
659 playlist_start(global_status.resume_index, 660 playlist_start(global_status.resume_index,
661 global_status.resume_elapsed,
660 global_status.resume_offset); 662 global_status.resume_offset);
661 } 663 }
662 return event; 664 return event;
diff --git a/apps/mpeg.c b/apps/mpeg.c
index c0b2ae0c0e..d3e0e5c137 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -177,6 +177,13 @@ static long low_watermark; /* Dynamic low watermark level */
177static long low_watermark_margin = 0; /* Extra time in seconds for watermark */ 177static long low_watermark_margin = 0; /* Extra time in seconds for watermark */
178static long lowest_watermark_level; /* Debug value to observe the buffer 178static long lowest_watermark_level; /* Debug value to observe the buffer
179 usage */ 179 usage */
180
181struct audio_resume_info
182{
183 unsigned long elapsed;
184 unsigned long offset;
185};
186
180#if CONFIG_CODEC == MAS3587F 187#if CONFIG_CODEC == MAS3587F
181static char recording_filename[MAX_PATH]; /* argument to thread */ 188static char recording_filename[MAX_PATH]; /* argument to thread */
182static char delayed_filename[MAX_PATH]; /* internal copy of above */ 189static char delayed_filename[MAX_PATH]; /* internal copy of above */
@@ -430,10 +437,9 @@ static void set_elapsed(struct mp3entry* id3)
430 id3->elapsed = id3->offset / (id3->bitrate / 8); 437 id3->elapsed = id3->offset / (id3->bitrate / 8);
431} 438}
432 439
433int audio_get_file_pos(void) 440static int audio_get_file_pos_int(struct mp3entry *id3)
434{ 441{
435 int pos = -1; 442 int pos = -1;
436 struct mp3entry *id3 = audio_current_track();
437 443
438 if (id3->vbr) 444 if (id3->vbr)
439 { 445 {
@@ -490,6 +496,12 @@ int audio_get_file_pos(void)
490 return pos; 496 return pos;
491} 497}
492 498
499int audio_get_file_pos(void)
500{
501 struct mp3entry *id3 = audio_current_track();
502 return id3 ? audio_get_file_pos_int(id3) : 0;
503}
504
493unsigned long mpeg_get_last_header(void) 505unsigned long mpeg_get_last_header(void)
494{ 506{
495#ifdef SIMULATOR 507#ifdef SIMULATOR
@@ -545,7 +557,13 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
545 } 557 }
546 /* TODO: Do it without stopping playback, if possible */ 558 /* TODO: Do it without stopping playback, if possible */
547 bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY; 559 bool playing = (audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY;
548 long offset = audio_current_track()->offset; 560 struct mp3entry *id3 = audio_current_track();
561 unsigned long elapsed = 0, offset = 0;
562 if (id3)
563 {
564 elapsed = id3->elapsed;
565 offset = id3->offset;
566 }
549 /* don't call audio_hard_stop() as it frees this handle */ 567 /* don't call audio_hard_stop() as it frees this handle */
550 if (thread_self() == audio_thread_id) 568 if (thread_self() == audio_thread_id)
551 { /* inline case MPEG_STOP (audio_stop()) response 569 { /* inline case MPEG_STOP (audio_stop()) response
@@ -574,7 +592,7 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
574 } 592 }
575 if (playing) 593 if (playing)
576 { /* safe to call even from the audio thread (due to queue_post()) */ 594 { /* safe to call even from the audio thread (due to queue_post()) */
577 audio_play(offset); 595 audio_play(elapsed, offset);
578 } 596 }
579 597
580 return BUFLIB_CB_OK; 598 return BUFLIB_CB_OK;
@@ -1274,7 +1292,7 @@ static void mpeg_thread(void)
1274 int unplayed_space_left; 1292 int unplayed_space_left;
1275 int amount_to_read; 1293 int amount_to_read;
1276 int t1, t2; 1294 int t1, t2;
1277 int start_offset; 1295 unsigned long start_elapsed, start_offset;
1278#if CONFIG_CODEC == MAS3587F 1296#if CONFIG_CODEC == MAS3587F
1279 int amount_to_save; 1297 int amount_to_save;
1280 int save_endpos = 0; 1298 int save_endpos = 0;
@@ -1337,9 +1355,16 @@ static void mpeg_thread(void)
1337 break; 1355 break;
1338 } 1356 }
1339 1357
1340 start_offset = (int)ev.data; 1358 start_elapsed = ((struct audio_resume_info *)ev.data)->elapsed;
1359 start_offset = ((struct audio_resume_info *)ev.data)->offset;
1341 1360
1342 /* mid-song resume? */ 1361 /* mid-song resume? */
1362 if (!start_offset && start_elapsed) {
1363 struct mp3entry *id3 = &get_trackdata(0)->id3;
1364 id3->elapsed = start_elapsed;
1365 start_offset = audio_get_file_pos_int(id3);
1366 }
1367
1343 if (start_offset) { 1368 if (start_offset) {
1344 struct mp3entry* id3 = &get_trackdata(0)->id3; 1369 struct mp3entry* id3 = &get_trackdata(0)->id3;
1345 lseek(mpeg_file, start_offset, SEEK_SET); 1370 lseek(mpeg_file, start_offset, SEEK_SET);
@@ -1506,7 +1531,7 @@ static void mpeg_thread(void)
1506 1531
1507 id3->elapsed = newtime; 1532 id3->elapsed = newtime;
1508 1533
1509 newpos = audio_get_file_pos(); 1534 newpos = audio_get_file_pos_int(id3);
1510 if(newpos < 0) 1535 if(newpos < 0)
1511 { 1536 {
1512 id3->elapsed = oldtime; 1537 id3->elapsed = oldtime;
@@ -2765,7 +2790,7 @@ static void audio_reset_buffer(void)
2765 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize); 2790 audio_reset_buffer_noalloc(core_get_data(audiobuf_handle), bufsize);
2766} 2791}
2767 2792
2768void audio_play(long offset) 2793void audio_play(unsigned long elapsed, unsigned long offset)
2769{ 2794{
2770 audio_reset_buffer(); 2795 audio_reset_buffer();
2771#ifdef SIMULATOR 2796#ifdef SIMULATOR
@@ -2789,15 +2814,28 @@ void audio_play(long offset)
2789 real_mpeg_play(trackname); 2814 real_mpeg_play(trackname);
2790#endif 2815#endif
2791 playlist_next(steps); 2816 playlist_next(steps);
2792 taginfo.offset = offset; 2817 if (!offset && elapsed)
2793 set_elapsed(&taginfo); 2818 {
2819 /* has an elapsed time but no offset; elapsed may take
2820 precedence in this case */
2821 taginfo.elapsed = elapsed;
2822 taginfo.offset = audio_get_file_pos_int(&taginfo);
2823 }
2824 else
2825 {
2826 taginfo.offset = offset;
2827 set_elapsed(&taginfo);
2828 }
2794 is_playing = true; 2829 is_playing = true;
2795 playing = true; 2830 playing = true;
2796 break; 2831 break;
2797 } while(1); 2832 } while(1);
2798#else /* !SIMULATOR */ 2833#else /* !SIMULATOR */
2834 static struct audio_resume_info resume;
2799 is_playing = true; 2835 is_playing = true;
2800 queue_post(&mpeg_queue, MPEG_PLAY, offset); 2836 resume.elapsed = elapsed;
2837 resume.offset = offset;
2838 queue_post(&mpeg_queue, MPEG_PLAY, (intptr_t)&resume);
2801#endif /* !SIMULATOR */ 2839#endif /* !SIMULATOR */
2802 2840
2803 mpeg_errno = 0; 2841 mpeg_errno = 0;
diff --git a/apps/onplay.c b/apps/onplay.c
index 9152d87bf5..7c5f517090 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -223,7 +223,7 @@ static bool add_to_playlist(int position, bool queue)
223 inserted */ 223 inserted */
224 if (global_settings.playlist_shuffle) 224 if (global_settings.playlist_shuffle)
225 playlist_shuffle(current_tick, -1); 225 playlist_shuffle(current_tick, -1);
226 playlist_start(0,0); 226 playlist_start(0, 0, 0);
227 onplay_result = ONPLAY_START_PLAY; 227 onplay_result = ONPLAY_START_PLAY;
228 } 228 }
229 229
diff --git a/apps/playback.c b/apps/playback.c
index 5e234beb36..80a0585b17 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -151,6 +151,12 @@ enum audio_id3_types
151}; 151};
152static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */ 152static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */
153 153
154struct audio_resume_info
155{
156 unsigned long elapsed;
157 unsigned long offset;
158};
159
154/* Peeking functions can yield and mess us up */ 160/* Peeking functions can yield and mess us up */
155static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/ 161static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/
156 162
@@ -325,7 +331,8 @@ enum audio_start_playback_flags
325 AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */ 331 AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */
326}; 332};
327 333
328static void audio_start_playback(size_t offset, unsigned int flags); 334static void audio_start_playback(const struct audio_resume_info *resume_info,
335 unsigned int flags);
329static void audio_stop_playback(void); 336static void audio_stop_playback(void);
330static void buffer_event_buffer_low_callback(void *data); 337static void buffer_event_buffer_low_callback(void *data);
331static void buffer_event_rebuffer_callback(void *data); 338static void buffer_event_rebuffer_callback(void *data);
@@ -792,7 +799,11 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
792 /* codec messages */ 799 /* codec messages */
793 { Q_AUDIO_PLAY, Q_AUDIO_PLAY }, 800 { Q_AUDIO_PLAY, Q_AUDIO_PLAY },
794 }; 801 };
802
803 static struct audio_resume_info resume;
804
795 bool give_up = false; 805 bool give_up = false;
806
796 /* filebuflen is, at this point, the buffering.c buffer size, 807 /* filebuflen is, at this point, the buffering.c buffer size,
797 * i.e. the audiobuf except voice, scratch mem, pcm, ... */ 808 * i.e. the audiobuf except voice, scratch mem, pcm, ... */
798 ssize_t extradata_size = old_size - filebuflen; 809 ssize_t extradata_size = old_size - filebuflen;
@@ -813,7 +824,9 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
813 824
814 825
815 /* TODO: Do it without stopping playback, if possible */ 826 /* TODO: Do it without stopping playback, if possible */
816 long offset = audio_current_track()->offset; 827 struct mp3entry *id3 = audio_current_track();
828 unsigned long elapsed = id3->elapsed;
829 unsigned long offset = id3->offset;
817 /* resume if playing */ 830 /* resume if playing */
818 bool playing = (audio_status() == AUDIO_STATUS_PLAY); 831 bool playing = (audio_status() == AUDIO_STATUS_PLAY);
819 /* There's one problem with stoping and resuming: If it happens in a too 832 /* There's one problem with stoping and resuming: If it happens in a too
@@ -825,10 +838,20 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
825 * queue_post from the last call to get the correct offset. This also 838 * queue_post from the last call to get the correct offset. This also
826 * lets us conviniently remove the queue event so Q_AUDIO_PLAY is only 839 * lets us conviniently remove the queue event so Q_AUDIO_PLAY is only
827 * processed once. */ 840 * processed once. */
828 bool play_queued = queue_peek_ex(&audio_queue, &ev, QPEEK_REMOVE_EVENTS, filter_list); 841 bool play_queued = queue_peek_ex(&audio_queue, &ev, QPEEK_REMOVE_EVENTS,
842 filter_list);
829 843
830 if (playing && offset > 0) /* current id3->offset is king */ 844 if (playing && ev.data != (intptr_t)&resume)
831 ev.data = offset; 845 {
846 resume = *(struct audio_resume_info *)ev.data;
847
848 /* current id3->elapsed/offset are king */
849 if (elapsed > 0)
850 resume.elapsed = elapsed;
851
852 if (offset > 0)
853 resume.offset = offset;
854 }
832 855
833 /* don't call audio_hard_stop() as it frees this handle */ 856 /* don't call audio_hard_stop() as it frees this handle */
834 if (thread_self() == audio_thread_id) 857 if (thread_self() == audio_thread_id)
@@ -867,7 +890,7 @@ static int shrink_callback(int handle, unsigned hints, void* start, size_t old_s
867 if (playing || play_queued) 890 if (playing || play_queued)
868 { 891 {
869 /* post, to make subsequent calls not break the resume position */ 892 /* post, to make subsequent calls not break the resume position */
870 audio_queue_post(Q_AUDIO_PLAY, ev.data); 893 audio_queue_post(Q_AUDIO_PLAY, (intptr_t)&resume);
871 } 894 }
872 895
873 return BUFLIB_CB_OK; 896 return BUFLIB_CB_OK;
@@ -1099,7 +1122,8 @@ static void audio_update_and_announce_next_track(const struct mp3entry *id3_next
1099 1122
1100/* Bring the user current mp3entry up to date and set a new offset for the 1123/* Bring the user current mp3entry up to date and set a new offset for the
1101 buffered metadata */ 1124 buffered metadata */
1102static void playing_id3_sync(struct track_info *user_info, off_t offset) 1125static void playing_id3_sync(struct track_info *user_info,
1126 unsigned long elapsed, unsigned long offset)
1103{ 1127{
1104 id3_mutex_lock(); 1128 id3_mutex_lock();
1105 1129
@@ -1113,9 +1137,14 @@ static void playing_id3_sync(struct track_info *user_info, off_t offset)
1113 1137
1114 id3_write(PLAYING_ID3, id3); 1138 id3_write(PLAYING_ID3, id3);
1115 1139
1116 if (offset < 0) 1140 if (elapsed == (unsigned long)-1)
1117 { 1141 {
1118 playing_id3->elapsed = e; 1142 playing_id3->elapsed = e;
1143 elapsed = 0;
1144 }
1145
1146 if (offset == (unsigned long)-1)
1147 {
1119 playing_id3->offset = o; 1148 playing_id3->offset = o;
1120 offset = 0; 1149 offset = 0;
1121 } 1150 }
@@ -1123,7 +1152,10 @@ static void playing_id3_sync(struct track_info *user_info, off_t offset)
1123 pcm_play_unlock(); 1152 pcm_play_unlock();
1124 1153
1125 if (id3) 1154 if (id3)
1155 {
1156 id3->elapsed = elapsed;
1126 id3->offset = offset; 1157 id3->offset = offset;
1158 }
1127 1159
1128 id3_mutex_unlock(); 1160 id3_mutex_unlock();
1129} 1161}
@@ -1299,19 +1331,16 @@ static bool audio_get_track_metadata(int offset, struct mp3entry *id3)
1299 return false; 1331 return false;
1300} 1332}
1301 1333
1302/* Get a resume rewind adjusted offset from the ID3 */ 1334/* Get resume rewind adjusted progress from the ID3 */
1303static unsigned long resume_rewind_adjusted_offset(const struct mp3entry *id3) 1335static void resume_rewind_adjust_progress(const struct mp3entry *id3,
1336 unsigned long *elapsed,
1337 unsigned long *offset)
1304{ 1338{
1305 unsigned long offset = id3->offset; 1339 unsigned int rewind = MAX(global_settings.resume_rewind, 0);
1306 size_t resume_rewind = global_settings.resume_rewind * 1340 unsigned long d_e = rewind*1000;
1307 id3->bitrate * (1000/8); 1341 *elapsed = id3->elapsed - MIN(id3->elapsed, d_e);
1308 1342 unsigned long d_o = rewind * id3->bitrate * (1000/8);
1309 if (offset < resume_rewind) 1343 *offset = id3->offset - MIN(id3->offset, d_o);
1310 offset = 0;
1311 else
1312 offset -= resume_rewind;
1313
1314 return offset;
1315} 1344}
1316 1345
1317/* Get the codec into ram and initialize it - keep it if it's ready */ 1346/* Get the codec into ram and initialize it - keep it if it's ready */
@@ -1436,7 +1465,7 @@ static bool audio_start_codec(bool auto_skip)
1436#ifdef HAVE_TAGCACHE 1465#ifdef HAVE_TAGCACHE
1437 bool autoresume_enable = global_settings.autoresume_enable; 1466 bool autoresume_enable = global_settings.autoresume_enable;
1438 1467
1439 if (autoresume_enable && !cur_id3->offset) 1468 if (autoresume_enable && !(cur_id3->elapsed || cur_id3->offset))
1440 { 1469 {
1441 /* Resume all manually selected tracks */ 1470 /* Resume all manually selected tracks */
1442 bool resume = !auto_skip; 1471 bool resume = !auto_skip;
@@ -1466,10 +1495,13 @@ static bool audio_start_codec(bool auto_skip)
1466 } 1495 }
1467 1496
1468 if (!resume) 1497 if (!resume)
1498 {
1499 cur_id3->elapsed = 0;
1469 cur_id3->offset = 0; 1500 cur_id3->offset = 0;
1501 }
1470 1502
1471 logf("%s: Set offset for %s to %lX\n", __func__, 1503 logf("%s: Set resume for %s to %lu %lX", __func__,
1472 cur_id3->title, cur_id3->offset); 1504 cur_id3->title, cur_id3->elapsed, cur_id3->offset);
1473 } 1505 }
1474#endif /* HAVE_TAGCACHE */ 1506#endif /* HAVE_TAGCACHE */
1475 1507
@@ -1481,7 +1513,8 @@ static bool audio_start_codec(bool auto_skip)
1481 and back again will cause accumulation of silent rewinds - that's not 1513 and back again will cause accumulation of silent rewinds - that's not
1482 our job to track directly nor could it be in any reasonable way 1514 our job to track directly nor could it be in any reasonable way
1483 */ 1515 */
1484 cur_id3->offset = resume_rewind_adjusted_offset(cur_id3); 1516 resume_rewind_adjust_progress(cur_id3, &cur_id3->elapsed,
1517 &cur_id3->offset);
1485 1518
1486 /* Update the codec API with the metadata and track info */ 1519 /* Update the codec API with the metadata and track info */
1487 id3_write(CODEC_ID3, cur_id3); 1520 id3_write(CODEC_ID3, cur_id3);
@@ -1494,7 +1527,7 @@ static bool audio_start_codec(bool auto_skip)
1494 codec_go(); 1527 codec_go();
1495 1528
1496#ifdef HAVE_TAGCACHE 1529#ifdef HAVE_TAGCACHE
1497 if (!autoresume_enable || cur_id3->offset) 1530 if (!autoresume_enable || cur_id3->elapsed || cur_id3->offset)
1498#endif 1531#endif
1499 { 1532 {
1500 /* Send the "buffer" event now */ 1533 /* Send the "buffer" event now */
@@ -1923,7 +1956,9 @@ static int audio_finish_load_track(struct track_info *info)
1923 1956
1924 /** Finally, load the audio **/ 1957 /** Finally, load the audio **/
1925 size_t file_offset = 0; 1958 size_t file_offset = 0;
1926 track_id3->elapsed = 0; 1959
1960 if (track_id3->elapsed > track_id3->length)
1961 track_id3->elapsed = 0;
1927 1962
1928 if (track_id3->offset >= info->filesize) 1963 if (track_id3->offset >= info->filesize)
1929 track_id3->offset = 0; 1964 track_id3->offset = 0;
@@ -1933,7 +1968,11 @@ static int audio_finish_load_track(struct track_info *info)
1933 1968
1934 /* Adjust for resume rewind so we know what to buffer - starting the codec 1969 /* Adjust for resume rewind so we know what to buffer - starting the codec
1935 calls it again, so we don't save it (and they shouldn't accumulate) */ 1970 calls it again, so we don't save it (and they shouldn't accumulate) */
1936 size_t offset = resume_rewind_adjusted_offset(track_id3); 1971 unsigned long elapsed, offset;
1972 resume_rewind_adjust_progress(track_id3, &elapsed, &offset);
1973
1974 logf("%s: Set resume for %s to %lu %lX", __func__,
1975 id3->title, elapsed, offset);
1937 1976
1938 enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ? 1977 enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ?
1939 TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO; 1978 TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO;
@@ -2168,7 +2207,7 @@ static void audio_on_finish_load_track(int id3_hid)
2168 change otherwise */ 2207 change otherwise */
2169 bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3)); 2208 bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3));
2170 2209
2171 playing_id3_sync(info, -1); 2210 playing_id3_sync(info, -1, -1);
2172 2211
2173 if (!was_valid) 2212 if (!was_valid)
2174 { 2213 {
@@ -2306,7 +2345,7 @@ static void audio_begin_track_change(enum pcm_track_change_type type,
2306 if (audio_start_codec(auto_skip)) 2345 if (audio_start_codec(auto_skip))
2307 { 2346 {
2308 if (!auto_skip) 2347 if (!auto_skip)
2309 playing_id3_sync(info, -1); 2348 playing_id3_sync(info, -1, -1);
2310 return; 2349 return;
2311 } 2350 }
2312 2351
@@ -2455,8 +2494,11 @@ static void audio_on_track_changed(void)
2455/* Begin playback from an idle state, transition to a new playlist or 2494/* Begin playback from an idle state, transition to a new playlist or
2456 invalidate the buffer and resume (if playing). 2495 invalidate the buffer and resume (if playing).
2457 (usually Q_AUDIO_PLAY, Q_AUDIO_REMAKE_AUDIO_BUFFER) */ 2496 (usually Q_AUDIO_PLAY, Q_AUDIO_REMAKE_AUDIO_BUFFER) */
2458static void audio_start_playback(size_t offset, unsigned int flags) 2497static void audio_start_playback(const struct audio_resume_info *resume_info,
2498 unsigned int flags)
2459{ 2499{
2500 struct audio_resume_info resume =
2501 *(resume_info ?: &(struct audio_resume_info){ 0, 0 } );
2460 enum play_status old_status = play_status; 2502 enum play_status old_status = play_status;
2461 2503
2462 if (flags & AUDIO_START_NEWBUF) 2504 if (flags & AUDIO_START_NEWBUF)
@@ -2469,7 +2511,8 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2469 2511
2470 if (old_status != PLAY_STOPPED) 2512 if (old_status != PLAY_STOPPED)
2471 { 2513 {
2472 logf("%s(%lu): skipping", __func__, (unsigned long)offset); 2514 logf("%s(%lu, %lu): skipping", __func__, resume.elapsed,
2515 resume.offset);
2473 2516
2474 halt_decoding_track(true); 2517 halt_decoding_track(true);
2475 2518
@@ -2481,7 +2524,8 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2481 /* Clear out some stuff to resume the current track where it 2524 /* Clear out some stuff to resume the current track where it
2482 left off */ 2525 left off */
2483 pcmbuf_play_stop(); 2526 pcmbuf_play_stop();
2484 offset = id3_get(PLAYING_ID3)->offset; 2527 resume.elapsed = id3_get(PLAYING_ID3)->elapsed;
2528 resume.offset = id3_get(PLAYING_ID3)->offset;
2485 track_list_clear(TRACK_LIST_CLEAR_ALL); 2529 track_list_clear(TRACK_LIST_CLEAR_ALL);
2486 } 2530 }
2487 else 2531 else
@@ -2505,7 +2549,8 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2505 return; /* Must already be playing */ 2549 return; /* Must already be playing */
2506 2550
2507 /* Cold playback start from a stopped state */ 2551 /* Cold playback start from a stopped state */
2508 logf("%s(%lu): starting", __func__, offset); 2552 logf("%s(%lu, %lu): starting", __func__, resume.elapsed,
2553 resume.offset);
2509 2554
2510 /* Set audio parameters */ 2555 /* Set audio parameters */
2511#if INPUT_SRC_CAPS != 0 2556#if INPUT_SRC_CAPS != 0
@@ -2555,7 +2600,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2555 if (trackstat >= LOAD_TRACK_OK) 2600 if (trackstat >= LOAD_TRACK_OK)
2556 { 2601 {
2557 /* This is the currently playing track - get metadata, stat */ 2602 /* This is the currently playing track - get metadata, stat */
2558 playing_id3_sync(track_list_current(0), offset); 2603 playing_id3_sync(track_list_current(0), resume.elapsed, resume.offset);
2559 2604
2560 if (valid_mp3entry(id3_get(PLAYING_ID3))) 2605 if (valid_mp3entry(id3_get(PLAYING_ID3)))
2561 { 2606 {
@@ -2892,7 +2937,9 @@ static void audio_on_ff_rewind(long time)
2892 2937
2893 if (!haltres) 2938 if (!haltres)
2894 { 2939 {
2895 /* If codec must be (re)started, reset the offset */ 2940 /* If codec must be (re)started, reset the resume info so that
2941 it doesn't execute resume procedures */
2942 ci_id3->elapsed = 0;
2896 ci_id3->offset = 0; 2943 ci_id3->offset = 0;
2897 } 2944 }
2898 2945
@@ -2970,7 +3017,7 @@ static void audio_on_audio_flush(void)
2970 not possible so a restart is required in order to continue the 3017 not possible so a restart is required in order to continue the
2971 currently playing track without the now invalid future track 3018 currently playing track without the now invalid future track
2972 playing */ 3019 playing */
2973 audio_start_playback(0, AUDIO_START_RESTART); 3020 audio_start_playback(NULL, AUDIO_START_RESTART);
2974 break; 3021 break;
2975 3022
2976 default: /* Nothing else is a state */ 3023 default: /* Nothing else is a state */
@@ -3008,7 +3055,7 @@ void audio_playback_handler(struct queue_event *ev)
3008 /** Control messages **/ 3055 /** Control messages **/
3009 case Q_AUDIO_PLAY: 3056 case Q_AUDIO_PLAY:
3010 LOGFQUEUE("playback < Q_AUDIO_PLAY"); 3057 LOGFQUEUE("playback < Q_AUDIO_PLAY");
3011 audio_start_playback(ev->data, 0); 3058 audio_start_playback((struct audio_resume_info *)ev->data, 0);
3012 break; 3059 break;
3013 3060
3014#ifdef HAVE_RECORDING 3061#ifdef HAVE_RECORDING
@@ -3082,7 +3129,7 @@ void audio_playback_handler(struct queue_event *ev)
3082 case Q_AUDIO_REMAKE_AUDIO_BUFFER: 3129 case Q_AUDIO_REMAKE_AUDIO_BUFFER:
3083 /* buffer needs to be reinitialized */ 3130 /* buffer needs to be reinitialized */
3084 LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER"); 3131 LOGFQUEUE("playback < Q_AUDIO_REMAKE_AUDIO_BUFFER");
3085 audio_start_playback(0, AUDIO_START_RESTART | AUDIO_START_NEWBUF); 3132 audio_start_playback(NULL, AUDIO_START_RESTART | AUDIO_START_NEWBUF);
3086 if (play_status == PLAY_STOPPED) 3133 if (play_status == PLAY_STOPPED)
3087 return; /* just need to change buffer state */ 3134 return; /* just need to change buffer state */
3088 break; 3135 break;
@@ -3368,8 +3415,8 @@ struct mp3entry * audio_next_track(void)
3368 return id3; 3415 return id3;
3369} 3416}
3370 3417
3371/* Start playback at the specified offset */ 3418/* Start playback at the specified elapsed time or offset */
3372void audio_play(long offset) 3419void audio_play(unsigned long elapsed, unsigned long offset)
3373{ 3420{
3374 logf("audio_play"); 3421 logf("audio_play");
3375 3422
@@ -3379,8 +3426,9 @@ void audio_play(long offset)
3379 talk_force_shutup(); 3426 talk_force_shutup();
3380#endif 3427#endif
3381 3428
3382 LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset); 3429 LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %lu %lX", elapsed, offset);
3383 audio_queue_send(Q_AUDIO_PLAY, offset); 3430 audio_queue_send(Q_AUDIO_PLAY,
3431 (intptr_t)&(struct audio_resume_info){ elapsed, offset });
3384} 3432}
3385 3433
3386/* Stop playback if playing */ 3434/* Stop playback if playing */
@@ -3582,11 +3630,10 @@ void playback_release_aa_slot(int slot)
3582} 3630}
3583#endif /* HAVE_ALBUMART */ 3631#endif /* HAVE_ALBUMART */
3584 3632
3585/* Would normally calculate byte offset from an elapsed time but is not 3633/* Return file byte offset */
3586 used on SWCODEC */
3587int audio_get_file_pos(void) 3634int audio_get_file_pos(void)
3588{ 3635{
3589 return 0; 3636 return id3_get(PLAYING_ID3)->offset;
3590} 3637}
3591 3638
3592/* Return total file buffer length after accounting for the talk buf */ 3639/* Return total file buffer length after accounting for the talk buf */
diff --git a/apps/playlist.c b/apps/playlist.c
index 9c895bfd67..0e73781238 100755
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -621,7 +621,7 @@ static int create_and_play_dir(int direction, bool play_last)
621#if (CONFIG_CODEC == SWCODEC) 621#if (CONFIG_CODEC == SWCODEC)
622 current_playlist.started = true; 622 current_playlist.started = true;
623#else 623#else
624 playlist_start(index, 0); 624 playlist_start(index, 0, 0);
625#endif 625#endif
626 } 626 }
627 627
@@ -2565,7 +2565,8 @@ unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
2565} 2565}
2566 2566
2567/* resume a playlist track with the given crc_32 of the track name. */ 2567/* resume a playlist track with the given crc_32 of the track name. */
2568void playlist_resume_track(int start_index, unsigned int crc, int offset) 2568void playlist_resume_track(int start_index, unsigned int crc,
2569 unsigned long elapsed, unsigned long offset)
2569{ 2570{
2570 int i; 2571 int i;
2571 unsigned int tmp_crc; 2572 unsigned int tmp_crc;
@@ -2573,7 +2574,7 @@ void playlist_resume_track(int start_index, unsigned int crc, int offset)
2573 tmp_crc = playlist_get_filename_crc32(playlist, start_index); 2574 tmp_crc = playlist_get_filename_crc32(playlist, start_index);
2574 if (tmp_crc == crc) 2575 if (tmp_crc == crc)
2575 { 2576 {
2576 playlist_start(start_index, offset); 2577 playlist_start(start_index, elapsed, offset);
2577 return; 2578 return;
2578 } 2579 }
2579 2580
@@ -2582,17 +2583,18 @@ void playlist_resume_track(int start_index, unsigned int crc, int offset)
2582 tmp_crc = playlist_get_filename_crc32(playlist, i); 2583 tmp_crc = playlist_get_filename_crc32(playlist, i);
2583 if (tmp_crc == crc) 2584 if (tmp_crc == crc)
2584 { 2585 {
2585 playlist_start(i, offset); 2586 playlist_start(i, elapsed, offset);
2586 return; 2587 return;
2587 } 2588 }
2588 } 2589 }
2589 2590
2590 /* If we got here the file wasnt found, so start from the beginning */ 2591 /* If we got here the file wasnt found, so start from the beginning */
2591 playlist_start(0,0); 2592 playlist_start(0, 0, 0);
2592} 2593}
2593 2594
2594/* start playing current playlist at specified index/offset */ 2595/* start playing current playlist at specified index/offset */
2595void playlist_start(int start_index, int offset) 2596void playlist_start(int start_index, unsigned long elapsed,
2597 unsigned long offset)
2596{ 2598{
2597 struct playlist_info* playlist = &current_playlist; 2599 struct playlist_info* playlist = &current_playlist;
2598 2600
@@ -2605,7 +2607,7 @@ void playlist_start(int start_index, int offset)
2605 2607
2606 playlist->started = true; 2608 playlist->started = true;
2607 sync_control(playlist, false); 2609 sync_control(playlist, false);
2608 audio_play(offset); 2610 audio_play(elapsed, offset);
2609} 2611}
2610 2612
2611/* Returns false if 'steps' is out of bounds, else true */ 2613/* Returns false if 'steps' is out of bounds, else true */
@@ -2723,7 +2725,7 @@ int playlist_next(int steps)
2723#if CONFIG_CODEC == SWCODEC 2725#if CONFIG_CODEC == SWCODEC
2724 playlist->started = true; 2726 playlist->started = true;
2725#else 2727#else
2726 playlist_start(0, 0); 2728 playlist_start(0, 0, 0);
2727#endif 2729#endif
2728 playlist->index = 0; 2730 playlist->index = 0;
2729 index = 0; 2731 index = 0;
@@ -2801,11 +2803,13 @@ int playlist_update_resume_info(const struct mp3entry* id3)
2801 if (id3) 2803 if (id3)
2802 { 2804 {
2803 if (global_status.resume_index != playlist->index || 2805 if (global_status.resume_index != playlist->index ||
2806 global_status.resume_elapsed != id3->elapsed ||
2804 global_status.resume_offset != id3->offset) 2807 global_status.resume_offset != id3->offset)
2805 { 2808 {
2806 unsigned int crc = crc_32(id3->path, strlen(id3->path), -1); 2809 unsigned int crc = crc_32(id3->path, strlen(id3->path), -1);
2807 global_status.resume_index = playlist->index; 2810 global_status.resume_index = playlist->index;
2808 global_status.resume_crc32 = crc; 2811 global_status.resume_crc32 = crc;
2812 global_status.resume_elapsed = id3->elapsed;
2809 global_status.resume_offset = id3->offset; 2813 global_status.resume_offset = id3->offset;
2810 status_save(); 2814 status_save();
2811 } 2815 }
@@ -2814,6 +2818,7 @@ int playlist_update_resume_info(const struct mp3entry* id3)
2814 { 2818 {
2815 global_status.resume_index = -1; 2819 global_status.resume_index = -1;
2816 global_status.resume_crc32 = -1; 2820 global_status.resume_crc32 = -1;
2821 global_status.resume_elapsed = -1;
2817 global_status.resume_offset = -1; 2822 global_status.resume_offset = -1;
2818 status_save(); 2823 status_save();
2819 } 2824 }
diff --git a/apps/playlist.h b/apps/playlist.h
index d80d8aa2ee..6314e9a6ee 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -133,8 +133,10 @@ int playlist_add(const char *filename);
133int playlist_shuffle(int random_seed, int start_index); 133int playlist_shuffle(int random_seed, int start_index);
134unsigned int playlist_get_filename_crc32(struct playlist_info *playlist, 134unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
135 int index); 135 int index);
136void playlist_resume_track(int start_index, unsigned int crc, int offset); 136void playlist_resume_track(int start_index, unsigned int crc,
137void playlist_start(int start_index, int offset); 137 unsigned long elapsed, unsigned long offset);
138void playlist_start(int start_index, unsigned long elapsed,
139 unsigned long offset);
138bool playlist_check(int steps); 140bool playlist_check(int steps);
139const char *playlist_peek(int steps, char* buf, size_t buf_size); 141const char *playlist_peek(int steps, char* buf, size_t buf_size);
140int playlist_next(int steps); 142int playlist_next(int steps);
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index 6a20bf1aac..d28643ab20 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -458,6 +458,7 @@ static bool update_playlist(bool force)
458 { 458 {
459 global_status.resume_index = -1; 459 global_status.resume_index = -1;
460 global_status.resume_offset = -1; 460 global_status.resume_offset = -1;
461 global_status.resume_elapsed = -1;
461 return false; 462 return false;
462 } 463 }
463 playlist_buffer_load_entries_screen(&viewer.buffer, FORWARD, 464 playlist_buffer_load_entries_screen(&viewer.buffer, FORWARD,
@@ -466,6 +467,7 @@ static bool update_playlist(bool force)
466 { 467 {
467 global_status.resume_index = -1; 468 global_status.resume_index = -1;
468 global_status.resume_offset = -1; 469 global_status.resume_offset = -1;
470 global_status.resume_elapsed = -1;
469 return false; 471 return false;
470 } 472 }
471 } 473 }
@@ -526,7 +528,7 @@ static int onplay_menu(int index)
526 if (current_track->display_index!=viewer.num_tracks || 528 if (current_track->display_index!=viewer.num_tracks ||
527 global_settings.repeat_mode == REPEAT_ALL) 529 global_settings.repeat_mode == REPEAT_ALL)
528 { 530 {
529 audio_play(0); 531 audio_play(0, 0);
530 viewer.current_playing_track = -1; 532 viewer.current_playing_track = -1;
531 } 533 }
532 } 534 }
@@ -773,7 +775,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
773 /* play new track */ 775 /* play new track */
774 if (!global_settings.party_mode) 776 if (!global_settings.party_mode)
775 { 777 {
776 playlist_start(current_track->index, 0); 778 playlist_start(current_track->index, 0, 0);
777 update_playlist(false); 779 update_playlist(false);
778 } 780 }
779 } 781 }
@@ -790,7 +792,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
790 goto exit; 792 goto exit;
791 if (global_settings.playlist_shuffle) 793 if (global_settings.playlist_shuffle)
792 start_index = playlist_shuffle(current_tick, start_index); 794 start_index = playlist_shuffle(current_tick, start_index);
793 playlist_start(start_index, 0); 795 playlist_start(start_index, 0, 0);
794 796
795 /* Our playlist is now the current list */ 797 /* Our playlist is now the current list */
796 if (!playlist_viewer_init(&viewer, NULL, true)) 798 if (!playlist_viewer_init(&viewer, NULL, true))
@@ -937,7 +939,7 @@ bool search_playlist(void)
937 case ACTION_STD_OK: 939 case ACTION_STD_OK:
938 { 940 {
939 int sel = gui_synclist_get_sel_pos(&playlist_lists); 941 int sel = gui_synclist_get_sel_pos(&playlist_lists);
940 playlist_start(found_indicies[sel], 0); 942 playlist_start(found_indicies[sel], 0, 0);
941 exit = 1; 943 exit = 1;
942 } 944 }
943 break; 945 break;
diff --git a/apps/plugin.h b/apps/plugin.h
index 764af4a6b7..ffdfa8fb77 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -160,12 +160,12 @@ void* plugin_get_buffer(size_t *buffer_size);
160#define PLUGIN_MAGIC 0x526F634B /* RocK */ 160#define PLUGIN_MAGIC 0x526F634B /* RocK */
161 161
162/* increase this every time the api struct changes */ 162/* increase this every time the api struct changes */
163#define PLUGIN_API_VERSION 226 163#define PLUGIN_API_VERSION 227
164 164
165/* update this to latest version if a change to the api struct breaks 165/* update this to latest version if a change to the api struct breaks
166 backwards compatibility (and please take the opportunity to sort in any 166 backwards compatibility (and please take the opportunity to sort in any
167 new function which are "waiting" at the end of the function table) */ 167 new function which are "waiting" at the end of the function table) */
168#define PLUGIN_MIN_API_VERSION 226 168#define PLUGIN_MIN_API_VERSION 227
169 169
170/* plugin return codes */ 170/* plugin return codes */
171/* internal returns start at 0x100 to make exit(1..255) work */ 171/* internal returns start at 0x100 to make exit(1..255) work */
@@ -723,8 +723,10 @@ struct plugin_api {
723 /* playback control */ 723 /* playback control */
724 int (*playlist_amount)(void); 724 int (*playlist_amount)(void);
725 int (*playlist_resume)(void); 725 int (*playlist_resume)(void);
726 void (*playlist_resume_track)(int start_index, unsigned int crc, int offset); 726 void (*playlist_resume_track)(int start_index, unsigned int crc,
727 void (*playlist_start)(int start_index, int offset); 727 unsigned long elapsed, unsigned long offset);
728 void (*playlist_start)(int start_index, unsigned long elapsed,
729 unsigned long offset);
728 int (*playlist_add)(const char *filename); 730 int (*playlist_add)(const char *filename);
729 void (*playlist_sync)(struct playlist_info* playlist); 731 void (*playlist_sync)(struct playlist_info* playlist);
730 int (*playlist_remove_all_tracks)(struct playlist_info *playlist); 732 int (*playlist_remove_all_tracks)(struct playlist_info *playlist);
@@ -735,7 +737,7 @@ struct plugin_api {
735 const char *dirname, int position, bool queue, 737 const char *dirname, int position, bool queue,
736 bool recurse); 738 bool recurse);
737 int (*playlist_shuffle)(int random_seed, int start_index); 739 int (*playlist_shuffle)(int random_seed, int start_index);
738 void (*audio_play)(long offset); 740 void (*audio_play)(unsigned long elapsed, unsigned long offset);
739 void (*audio_stop)(void); 741 void (*audio_stop)(void);
740 void (*audio_pause)(void); 742 void (*audio_pause)(void);
741 void (*audio_resume)(void); 743 void (*audio_resume)(void);
diff --git a/apps/plugins/alarmclock.c b/apps/plugins/alarmclock.c
index 79a676003a..ecafceddc7 100644
--- a/apps/plugins/alarmclock.c
+++ b/apps/plugins/alarmclock.c
@@ -109,6 +109,7 @@ static void resume_audio(void)
109 if (rb->playlist_resume() != -1) { 109 if (rb->playlist_resume() != -1) {
110 rb->playlist_resume_track(rb->global_status->resume_index, 110 rb->playlist_resume_track(rb->global_status->resume_index,
111 rb->global_status->resume_crc32, 111 rb->global_status->resume_crc32,
112 rb->global_status->resume_elapsed,
112 rb->global_status->resume_offset); 113 rb->global_status->resume_offset);
113 } 114 }
114 } 115 }
diff --git a/apps/plugins/alpine_cdc.c b/apps/plugins/alpine_cdc.c
index 653c968ffa..28bb8d8b7f 100644
--- a/apps/plugins/alpine_cdc.c
+++ b/apps/plugins/alpine_cdc.c
@@ -997,8 +997,8 @@ void set_play(void)
997 } 997 }
998 else 998 else
999 { 999 {
1000 print_scroll("audio_play(0)"); 1000 print_scroll("audio_play(0, 0)");
1001 rb->audio_play(0); 1001 rb->audio_play(0, 0);
1002 } 1002 }
1003} 1003}
1004 1004
diff --git a/apps/plugins/lib/playback_control.c b/apps/plugins/lib/playback_control.c
index 47921e52f2..1be234f70f 100644
--- a/apps/plugins/lib/playback_control.c
+++ b/apps/plugins/lib/playback_control.c
@@ -39,6 +39,7 @@ static bool play(void)
39 { 39 {
40 rb->playlist_resume_track(rb->global_status->resume_index, 40 rb->playlist_resume_track(rb->global_status->resume_index,
41 rb->global_status->resume_crc32, 41 rb->global_status->resume_crc32,
42 rb->global_status->resume_elapsed,
42 rb->global_status->resume_offset); 43 rb->global_status->resume_offset);
43 } 44 }
44 } 45 }
diff --git a/apps/plugins/lrcplayer.c b/apps/plugins/lrcplayer.c
index 6e0394fa51..392e78e77f 100644
--- a/apps/plugins/lrcplayer.c
+++ b/apps/plugins/lrcplayer.c
@@ -2684,6 +2684,7 @@ static int handle_button(void)
2684 { 2684 {
2685 rb->playlist_resume_track(rb->global_status->resume_index, 2685 rb->playlist_resume_track(rb->global_status->resume_index,
2686 rb->global_status->resume_crc32, 2686 rb->global_status->resume_crc32,
2687 rb->global_status->resume_elapsed,
2687 rb->global_status->resume_offset); 2688 rb->global_status->resume_offset);
2688 } 2689 }
2689 } 2690 }
diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c
index 51fe5ebfc5..bb7cec888f 100644
--- a/apps/plugins/pictureflow/pictureflow.c
+++ b/apps/plugins/pictureflow/pictureflow.c
@@ -2580,7 +2580,7 @@ play:
2580 * if shuffle, we can't predict the playing track easily, and for either 2580 * if shuffle, we can't predict the playing track easily, and for either
2581 * case the track list doesn't get auto scrolled*/ 2581 * case the track list doesn't get auto scrolled*/
2582 if(!append) 2582 if(!append)
2583 rb->playlist_start(position, 0); 2583 rb->playlist_start(position, 0, 0);
2584 old_playlist = center_slide.slide_index; 2584 old_playlist = center_slide.slide_index;
2585 old_shuffle = shuffle; 2585 old_shuffle = shuffle;
2586} 2586}
diff --git a/apps/plugins/random_folder_advance_config.c b/apps/plugins/random_folder_advance_config.c
index 7f6018df4e..0b3532dde0 100644
--- a/apps/plugins/random_folder_advance_config.c
+++ b/apps/plugins/random_folder_advance_config.c
@@ -541,7 +541,7 @@ static int start_shuffled_play(void)
541 } 541 }
542 } 542 }
543 rb->splash(HZ, "Done"); 543 rb->splash(HZ, "Done");
544 rb->playlist_start(0,0); 544 rb->playlist_start(0, 0, 0);
545 return 1; 545 return 1;
546} 546}
547 547
diff --git a/apps/root_menu.c b/apps/root_menu.c
index 259d9bf69c..09c7efad9d 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -306,6 +306,7 @@ static int wpsscrn(void* param)
306 { 306 {
307 playlist_resume_track(global_status.resume_index, 307 playlist_resume_track(global_status.resume_index,
308 global_status.resume_crc32, 308 global_status.resume_crc32,
309 global_status.resume_elapsed,
309 global_status.resume_offset); 310 global_status.resume_offset);
310 ret_val = gui_wps_show(); 311 ret_val = gui_wps_show();
311 } 312 }
diff --git a/apps/settings.c b/apps/settings.c
index 13dcb5cca9..58d58788be 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -738,12 +738,17 @@ void settings_apply_play_freq(int value, bool playback)
738 bool changed = value != prev_setting; 738 bool changed = value != prev_setting;
739 prev_setting = value; 739 prev_setting = value;
740 740
741 long offset = 0; 741 unsigned long elapsed = 0;
742 unsigned long offset = 0;
742 bool playing = changed && !playback && 743 bool playing = changed && !playback &&
743 audio_status() == AUDIO_STATUS_PLAY; 744 audio_status() == AUDIO_STATUS_PLAY;
744 745
745 if (playing) 746 if (playing)
746 offset = audio_current_track()->offset; 747 {
748 struct mp3entry *id3 = audio_current_track();
749 elapsed = id3->elapsed;
750 offset = id3->offset;
751 }
747 752
748 if (changed && !playback) 753 if (changed && !playback)
749 audio_hard_stop(); 754 audio_hard_stop();
@@ -752,7 +757,7 @@ void settings_apply_play_freq(int value, bool playback)
752 mixer_set_frequency(play_sampr[value]); 757 mixer_set_frequency(play_sampr[value]);
753 758
754 if (playing) 759 if (playing)
755 audio_play(offset); 760 audio_play(elapsed, offset);
756} 761}
757#endif /* HAVE_PLAY_FREQ */ 762#endif /* HAVE_PLAY_FREQ */
758 763
diff --git a/apps/settings.h b/apps/settings.h
index 5b876d3e67..60658f6857 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -266,6 +266,7 @@ struct system_status
266{ 266{
267 int resume_index; /* index in playlist (-1 for no active resume) */ 267 int resume_index; /* index in playlist (-1 for no active resume) */
268 uint32_t resume_crc32; /* crc32 of the name of the file */ 268 uint32_t resume_crc32; /* crc32 of the name of the file */
269 uint32_t resume_elapsed; /* elapsed time in last file */
269 uint32_t resume_offset; /* byte offset in mp3 file */ 270 uint32_t resume_offset; /* byte offset in mp3 file */
270 int runtime; /* current runtime since last charge */ 271 int runtime; /* current runtime since last charge */
271 int topruntime; /* top known runtime */ 272 int topruntime; /* top known runtime */
@@ -282,6 +283,7 @@ struct system_status
282#ifdef HAVE_LCD_BITMAP 283#ifdef HAVE_LCD_BITMAP
283 int font_id[NB_SCREENS]; /* font id of the settings font for each screen */ 284 int font_id[NB_SCREENS]; /* font id of the settings font for each screen */
284#endif 285#endif
286
285}; 287};
286 288
287struct user_settings 289struct user_settings
diff --git a/apps/settings_list.c b/apps/settings_list.c
index 39258dff8f..1ef9c62bf0 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -818,6 +818,7 @@ const struct settings_list settings[] = {
818 OFFON_SETTING(0, playlist_shuffle, LANG_SHUFFLE, false, "shuffle", NULL), 818 OFFON_SETTING(0, playlist_shuffle, LANG_SHUFFLE, false, "shuffle", NULL),
819 SYSTEM_SETTING(NVRAM(4), resume_index, -1), 819 SYSTEM_SETTING(NVRAM(4), resume_index, -1),
820 SYSTEM_SETTING(NVRAM(4), resume_crc32, -1), 820 SYSTEM_SETTING(NVRAM(4), resume_crc32, -1),
821 SYSTEM_SETTING(NVRAM(4), resume_elapsed, -1),
821 SYSTEM_SETTING(NVRAM(4), resume_offset, -1), 822 SYSTEM_SETTING(NVRAM(4), resume_offset, -1),
822 CHOICE_SETTING(0, repeat_mode, LANG_REPEAT, REPEAT_OFF, "repeat", 823 CHOICE_SETTING(0, repeat_mode, LANG_REPEAT, REPEAT_OFF, "repeat",
823 "off,all,one,shuffle" 824 "off,all,one,shuffle"
diff --git a/apps/settings_list.h b/apps/settings_list.h
index 66b20ca6ca..2e63220da1 100644
--- a/apps/settings_list.h
+++ b/apps/settings_list.h
@@ -142,7 +142,7 @@ struct custom_setting {
142 142
143#define F_NVRAM_BYTES_MASK 0xE0000 /*0-4 bytes can be stored */ 143#define F_NVRAM_BYTES_MASK 0xE0000 /*0-4 bytes can be stored */
144#define F_NVRAM_MASK_SHIFT 17 144#define F_NVRAM_MASK_SHIFT 17
145#define NVRAM_CONFIG_VERSION 7 145#define NVRAM_CONFIG_VERSION 8
146/* Above define should be bumped if 146/* Above define should be bumped if
147- a new NVRAM setting is added between 2 other NVRAM settings 147- a new NVRAM setting is added between 2 other NVRAM settings
148- number of bytes for a NVRAM setting is changed 148- number of bytes for a NVRAM setting is changed
diff --git a/apps/tagcache.c b/apps/tagcache.c
index 07d8d1d7a2..3ce0247188 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -130,7 +130,7 @@ static long tempbuf_pos;
130static const char *tags_str[] = { "artist", "album", "genre", "title", 130static const char *tags_str[] = { "artist", "album", "genre", "title",
131 "filename", "composer", "comment", "albumartist", "grouping", "year", 131 "filename", "composer", "comment", "albumartist", "grouping", "year",
132 "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating", 132 "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating",
133 "playtime", "lastplayed", "commitid", "mtime", "lastoffset" }; 133 "playtime", "lastplayed", "commitid", "mtime", "lastelapsed", "lastoffset" };
134 134
135/* Status information of the tagcache. */ 135/* Status information of the tagcache. */
136static struct tagcache_stat tc_stat; 136static struct tagcache_stat tc_stat;
@@ -196,7 +196,7 @@ static const char * const tagfile_entry_ec = "ll";
196/** 196/**
197 Note: This should be (1 + TAG_COUNT) amount of l's. 197 Note: This should be (1 + TAG_COUNT) amount of l's.
198 */ 198 */
199static const char * const index_entry_ec = "llllllllllllllllllllll"; 199static const char * const index_entry_ec = "lllllllllllllllllllllll";
200 200
201static const char * const tagcache_header_ec = "lll"; 201static const char * const tagcache_header_ec = "lll";
202static const char * const master_header_ec = "llllll"; 202static const char * const master_header_ec = "llllll";
@@ -1752,6 +1752,10 @@ bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
1752#if CONFIG_CODEC == SWCODEC 1752#if CONFIG_CODEC == SWCODEC
1753 if (global_settings.autoresume_enable) 1753 if (global_settings.autoresume_enable)
1754 { 1754 {
1755 id3->elapsed = get_tag_numeric(entry, tag_lastelapsed, idx_id);
1756 logf("tagcache_fill_tags: Set elapsed for %s to %lX\n",
1757 id3->title, id3->elapsed);
1758
1755 id3->offset = get_tag_numeric(entry, tag_lastoffset, idx_id); 1759 id3->offset = get_tag_numeric(entry, tag_lastoffset, idx_id);
1756 logf("tagcache_fill_tags: Set offset for %s to %lX\n", 1760 logf("tagcache_fill_tags: Set offset for %s to %lX\n",
1757 id3->title, id3->offset); 1761 id3->title, id3->offset);
@@ -2348,6 +2352,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2348 tmpdb_copy_tag(tag_playtime); 2352 tmpdb_copy_tag(tag_playtime);
2349 tmpdb_copy_tag(tag_lastplayed); 2353 tmpdb_copy_tag(tag_lastplayed);
2350 tmpdb_copy_tag(tag_commitid); 2354 tmpdb_copy_tag(tag_commitid);
2355 tmpdb_copy_tag(tag_lastelapsed);
2351 tmpdb_copy_tag(tag_lastoffset); 2356 tmpdb_copy_tag(tag_lastoffset);
2352 2357
2353 /* Avoid processing this entry again. */ 2358 /* Avoid processing this entry again. */
@@ -3419,7 +3424,8 @@ static int parse_changelog_line(int line_n, char *buf, void *parameters)
3419 int idx_id; 3424 int idx_id;
3420 long masterfd = (long)parameters; 3425 long masterfd = (long)parameters;
3421 const int import_tags[] = { tag_playcount, tag_rating, tag_playtime, 3426 const int import_tags[] = { tag_playcount, tag_rating, tag_playtime,
3422 tag_lastplayed, tag_commitid, tag_lastoffset }; 3427 tag_lastplayed, tag_commitid, tag_lastelapsed,
3428 tag_lastoffset };
3423 int i; 3429 int i;
3424 (void)line_n; 3430 (void)line_n;
3425 3431
diff --git a/apps/tagcache.h b/apps/tagcache.h
index dbe1c92d39..e358cc26e0 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -33,7 +33,8 @@
33enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, 33enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
34 tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year, 34 tag_filename, tag_composer, tag_comment, tag_albumartist, tag_grouping, tag_year,
35 tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating, 35 tag_discnumber, tag_tracknumber, tag_bitrate, tag_length, tag_playcount, tag_rating,
36 tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, tag_lastoffset, 36 tag_playtime, tag_lastplayed, tag_commitid, tag_mtime, tag_lastelapsed,
37 tag_lastoffset,
37 /* Real tags end here, count them. */ 38 /* Real tags end here, count them. */
38 TAG_COUNT, 39 TAG_COUNT,
39 /* Virtual tags */ 40 /* Virtual tags */
@@ -51,7 +52,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
51#define IDX_BUF_DEPTH 64 52#define IDX_BUF_DEPTH 64
52 53
53/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ 54/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
54#define TAGCACHE_MAGIC 0x5443480e 55#define TAGCACHE_MAGIC 0x5443480f
55 56
56/* Dump store/restore header version 'TCSxx'. */ 57/* Dump store/restore header version 'TCSxx'. */
57#define TAGCACHE_STATEFILE_MAGIC 0x54435301 58#define TAGCACHE_STATEFILE_MAGIC 0x54435301
@@ -107,10 +108,11 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
107 (1LU << tag_tracknumber) | (1LU << tag_length) | (1LU << tag_bitrate) | \ 108 (1LU << tag_tracknumber) | (1LU << tag_length) | (1LU << tag_bitrate) | \
108 (1LU << tag_playcount) | (1LU << tag_rating) | (1LU << tag_playtime) | \ 109 (1LU << tag_playcount) | (1LU << tag_rating) | (1LU << tag_playtime) | \
109 (1LU << tag_lastplayed) | (1LU << tag_commitid) | (1LU << tag_mtime) | \ 110 (1LU << tag_lastplayed) | (1LU << tag_commitid) | (1LU << tag_mtime) | \
110 (1LU << tag_lastoffset) | (1LU << tag_virt_basename) | \ 111 (1LU << tag_lastelapsed) | (1LU << tag_lastoffset) | \
111 (1LU << tag_virt_length_min) | (1LU << tag_virt_length_sec) | \ 112 (1LU << tag_virt_basename) | (1LU << tag_virt_length_min) | \
112 (1LU << tag_virt_playtime_min) | (1LU << tag_virt_playtime_sec) | \ 113 (1LU << tag_virt_length_sec) | (1LU << tag_virt_playtime_min) | \
113 (1LU << tag_virt_entryage) | (1LU << tag_virt_autoscore)) 114 (1LU << tag_virt_playtime_sec) | (1LU << tag_virt_entryage) | \
115 (1LU << tag_virt_autoscore))
114 116
115#define TAGCACHE_IS_NUMERIC(tag) (BIT_N(tag) & TAGCACHE_NUMERIC_TAGS) 117#define TAGCACHE_IS_NUMERIC(tag) (BIT_N(tag) & TAGCACHE_NUMERIC_TAGS)
116 118
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 9eef38b5e6..ff364ec5e4 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -330,6 +330,7 @@ static int get_tag(int *tag)
330 {"playcount", tag_playcount}, 330 {"playcount", tag_playcount},
331 {"rating", tag_rating}, 331 {"rating", tag_rating},
332 {"lastplayed", tag_lastplayed}, 332 {"lastplayed", tag_lastplayed},
333 {"lastelapsed", tag_lastelapsed},
333 {"lastoffset", tag_lastoffset}, 334 {"lastoffset", tag_lastoffset},
334 {"commitid", tag_commitid}, 335 {"commitid", tag_commitid},
335 {"entryage", tag_virt_entryage}, 336 {"entryage", tag_virt_entryage},
@@ -841,8 +842,16 @@ static void tagtree_buffer_event(void *data)
841 #if CONFIG_CODEC == SWCODEC 842 #if CONFIG_CODEC == SWCODEC
842 if (autoresume) 843 if (autoresume)
843 { 844 {
844 /* Load current file resume offset if not already defined (by 845 /* Load current file resume info if not already defined (by
845 another resume mechanism) */ 846 another resume mechanism) */
847 if (id3->elapsed == 0)
848 {
849 id3->elapsed = tagcache_get_numeric(&tcs, tag_lastelapsed);
850
851 logf("tagtree_buffer_event: Set elapsed for %s to %lX\n",
852 str_or_empty(id3->title), id3->elapsed);
853 }
854
846 if (id3->offset == 0) 855 if (id3->offset == 0)
847 { 856 {
848 id3->offset = tagcache_get_numeric(&tcs, tag_lastoffset); 857 id3->offset = tagcache_get_numeric(&tcs, tag_lastoffset);
@@ -940,12 +949,13 @@ static void tagtree_track_finish_event(void *data)
940#if CONFIG_CODEC == SWCODEC 949#if CONFIG_CODEC == SWCODEC
941 if (autoresume) 950 if (autoresume)
942 { 951 {
952 unsigned long elapsed = auto_skip ? 0 : id3->elapsed;
943 unsigned long offset = auto_skip ? 0 : id3->offset; 953 unsigned long offset = auto_skip ? 0 : id3->offset;
944 954 tagcache_update_numeric(tagcache_idx, tag_lastelapsed, elapsed);
945 tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset); 955 tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset);
946 956
947 logf("tagtree_track_finish_event: Save offset for %s: %lX", 957 logf("tagtree_track_finish_event: Save resume for %s: %lX %lX",
948 str_or_empty(id3->title), offset); 958 str_or_empty(id3->title), elapsed, offset);
949 } 959 }
950#endif 960#endif
951} 961}
@@ -2034,7 +2044,7 @@ static int tagtree_play_folder(struct tree_context* c)
2034 c->selected_item = 0; 2044 c->selected_item = 0;
2035 gui_synclist_select_item(&tree_lists, c->selected_item); 2045 gui_synclist_select_item(&tree_lists, c->selected_item);
2036 2046
2037 playlist_start(c->selected_item,0); 2047 playlist_start(c->selected_item, 0, 0);
2038 playlist_get_current()->num_inserted_tracks = 0; /* make warn on playlist erase work */ 2048 playlist_get_current()->num_inserted_tracks = 0; /* make warn on playlist erase work */
2039 return 0; 2049 return 0;
2040} 2050}
diff --git a/apps/tree.c b/apps/tree.c
index cc080ac7fa..f72774fe1e 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -1052,8 +1052,8 @@ void tree_mem_init(void)
1052 tree_get_filetypes(&filetypes, &filetypes_count); 1052 tree_get_filetypes(&filetypes, &filetypes_count);
1053} 1053}
1054 1054
1055bool bookmark_play(char *resume_file, int index, int offset, int seed, 1055bool bookmark_play(char *resume_file, int index, unsigned long elapsed,
1056 char *filename) 1056 unsigned long offset, int seed, char *filename)
1057{ 1057{
1058 int i; 1058 int i;
1059 char* suffix = strrchr(resume_file, '.'); 1059 char* suffix = strrchr(resume_file, '.');
@@ -1082,7 +1082,7 @@ bool bookmark_play(char *resume_file, int index, int offset, int seed,
1082 { 1082 {
1083 if (global_settings.playlist_shuffle) 1083 if (global_settings.playlist_shuffle)
1084 playlist_shuffle(seed, -1); 1084 playlist_shuffle(seed, -1);
1085 playlist_start(index,offset); 1085 playlist_start(index, elapsed, offset);
1086 started = true; 1086 started = true;
1087 } 1087 }
1088 *slash='/'; 1088 *slash='/';
@@ -1133,7 +1133,7 @@ bool bookmark_play(char *resume_file, int index, int offset, int seed,
1133 else 1133 else
1134 return false; 1134 return false;
1135 } 1135 }
1136 playlist_start(index,offset); 1136 playlist_start(index, elapsed, offset);
1137 started = true; 1137 started = true;
1138 } 1138 }
1139 } 1139 }
diff --git a/apps/tree.h b/apps/tree.h
index 70494f8a88..1601447b58 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -139,8 +139,8 @@ struct tree_context* tree_get_context(void);
139void tree_flush(void); 139void tree_flush(void);
140void tree_restore(void); 140void tree_restore(void);
141 141
142bool bookmark_play(char* resume_file, int index, int offset, int seed, 142bool bookmark_play(char* resume_file, int index, unsigned long elapsed,
143 char *filename); 143 unsigned long offset, int seed, char *filename);
144 144
145extern struct gui_synclist tree_lists; 145extern struct gui_synclist tree_lists;
146#endif 146#endif