summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--firmware/export/audio.h2
-rw-r--r--lib/rbcodec/codecs/a52.c28
-rw-r--r--lib/rbcodec/codecs/a52_rm.c11
-rw-r--r--lib/rbcodec/codecs/aac.c15
-rw-r--r--lib/rbcodec/codecs/aiff.c17
-rw-r--r--lib/rbcodec/codecs/alac.c29
-rw-r--r--lib/rbcodec/codecs/ape.c70
-rw-r--r--lib/rbcodec/codecs/asap.c10
-rw-r--r--lib/rbcodec/codecs/atrac3_oma.c21
-rw-r--r--lib/rbcodec/codecs/atrac3_rm.c18
-rw-r--r--lib/rbcodec/codecs/au.c17
-rw-r--r--lib/rbcodec/codecs/ay.c6
-rw-r--r--lib/rbcodec/codecs/cook.c13
-rw-r--r--lib/rbcodec/codecs/flac.c16
-rw-r--r--lib/rbcodec/codecs/gbs.c5
-rw-r--r--lib/rbcodec/codecs/hes.c5
-rw-r--r--lib/rbcodec/codecs/kss.c5
-rw-r--r--lib/rbcodec/codecs/mod.c11
-rw-r--r--lib/rbcodec/codecs/mpa.c5
-rw-r--r--lib/rbcodec/codecs/mpc.c18
-rw-r--r--lib/rbcodec/codecs/nsf.c6
-rw-r--r--lib/rbcodec/codecs/opus.c47
-rw-r--r--lib/rbcodec/codecs/raac.c21
-rw-r--r--lib/rbcodec/codecs/sgc.c5
-rw-r--r--lib/rbcodec/codecs/sid.c22
-rw-r--r--lib/rbcodec/codecs/smaf.c21
-rw-r--r--lib/rbcodec/codecs/speex.c60
-rw-r--r--lib/rbcodec/codecs/tta.c16
-rw-r--r--lib/rbcodec/codecs/vgm.c5
-rw-r--r--lib/rbcodec/codecs/vorbis.c14
-rw-r--r--lib/rbcodec/codecs/vox.c20
-rw-r--r--lib/rbcodec/codecs/wav.c20
-rw-r--r--lib/rbcodec/codecs/wav64.c19
-rw-r--r--lib/rbcodec/codecs/wavpack.c14
-rwxr-xr-xlib/rbcodec/codecs/wma.c28
-rw-r--r--lib/rbcodec/codecs/wmapro.c17
-rw-r--r--lib/rbcodec/codecs/wmavoice.c24
65 files changed, 729 insertions, 302 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
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 57f7981b34..08a88d6325 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -49,7 +49,7 @@
49 49
50 50
51void audio_init(void) INIT_ATTR; 51void audio_init(void) INIT_ATTR;
52void audio_play(long offset); 52void audio_play(unsigned long elapsed, unsigned long offset);
53void audio_stop(void); 53void audio_stop(void);
54/* Stops audio from serving playback and frees resources*/ 54/* Stops audio from serving playback and frees resources*/
55void audio_hard_stop(void); 55void audio_hard_stop(void);
diff --git a/lib/rbcodec/codecs/a52.c b/lib/rbcodec/codecs/a52.c
index 77caaf87c1..da670308b8 100644
--- a/lib/rbcodec/codecs/a52.c
+++ b/lib/rbcodec/codecs/a52.c
@@ -150,19 +150,28 @@ enum codec_status codec_run(void)
150 150
151 samplesdone = 0; 151 samplesdone = 0;
152 152
153 /* The main decoding loop */
154 if (ci->id3->offset) { 153 if (ci->id3->offset) {
155 if (ci->seek_buffer(ci->id3->offset)) { 154 sample_loc = (ci->id3->offset / ci->id3->bytesperframe) *
156 samplesdone = (ci->id3->offset / ci->id3->bytesperframe) * 155 A52_SAMPLESPERFRAME;
157 A52_SAMPLESPERFRAME; 156 param = ci->id3->offset;
158 ci->set_elapsed(samplesdone/(ci->id3->frequency / 1000)); 157 }
159 } 158 else if (ci->id3->elapsed) {
159 sample_loc = ci->id3->elapsed/1000 * ci->id3->frequency;
160 param = sample_loc/A52_SAMPLESPERFRAME*ci->id3->bytesperframe;
160 } 161 }
161 else { 162 else {
162 ci->seek_buffer(ci->id3->first_frame_offset); 163 sample_loc = 0;
163 ci->set_elapsed(0); 164 param = ci->id3->first_frame_offset;
165 }
166
167 if (ci->seek_buffer(param)) {
168 samplesdone = sample_loc;
164 } 169 }
165 170
171 ci->set_elapsed(samplesdone/(ci->id3->frequency/1000));
172
173 /* The main decoding loop */
174
166 while (1) { 175 while (1) {
167 enum codec_command_action action = ci->get_command(&param); 176 enum codec_command_action action = ci->get_command(&param);
168 177
@@ -172,7 +181,8 @@ enum codec_status codec_run(void)
172 if (action == CODEC_ACTION_SEEK_TIME) { 181 if (action == CODEC_ACTION_SEEK_TIME) {
173 sample_loc = param/1000 * ci->id3->frequency; 182 sample_loc = param/1000 * ci->id3->frequency;
174 183
175 if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) { 184 if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*
185 ci->id3->bytesperframe)) {
176 samplesdone = sample_loc; 186 samplesdone = sample_loc;
177 ci->set_elapsed(samplesdone/(ci->id3->frequency/1000)); 187 ci->set_elapsed(samplesdone/(ci->id3->frequency/1000));
178 } 188 }
diff --git a/lib/rbcodec/codecs/a52_rm.c b/lib/rbcodec/codecs/a52_rm.c
index 42868437d8..3f07a43ce9 100644
--- a/lib/rbcodec/codecs/a52_rm.c
+++ b/lib/rbcodec/codecs/a52_rm.c
@@ -148,13 +148,15 @@ enum codec_status codec_run(void)
148 int consumed, packet_offset; 148 int consumed, packet_offset;
149 int playback_on = -1; 149 int playback_on = -1;
150 size_t resume_offset; 150 size_t resume_offset;
151 enum codec_command_action action;
151 intptr_t param; 152 intptr_t param;
152 enum codec_command_action action = CODEC_ACTION_NULL;
153 153
154 if (codec_init()) { 154 if (codec_init()) {
155 return CODEC_ERROR; 155 return CODEC_ERROR;
156 } 156 }
157 157
158 action = CODEC_ACTION_NULL;
159 param = ci->id3->elapsed;
158 resume_offset = ci->id3->offset; 160 resume_offset = ci->id3->offset;
159 161
160 ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); 162 ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
@@ -171,11 +173,14 @@ enum codec_status codec_run(void)
171 samplesdone = 0; 173 samplesdone = 0;
172 174
173 /* check for a mid-track resume and force a seek time accordingly */ 175 /* check for a mid-track resume and force a seek time accordingly */
174 if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { 176 if (resume_offset) {
175 resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; 177 resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
176 /* put number of subpackets to skip in resume_offset */ 178 /* put number of subpackets to skip in resume_offset */
177 resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); 179 resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
178 param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); 180 param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
181 }
182
183 if (param > 0) {
179 action = CODEC_ACTION_SEEK_TIME; 184 action = CODEC_ACTION_SEEK_TIME;
180 } 185 }
181 else { 186 else {
diff --git a/lib/rbcodec/codecs/aac.c b/lib/rbcodec/codecs/aac.c
index c9cf737b48..015e332be2 100644
--- a/lib/rbcodec/codecs/aac.c
+++ b/lib/rbcodec/codecs/aac.c
@@ -72,6 +72,7 @@ enum codec_status codec_run(void)
72 uint32_t sbr_fac = 1; 72 uint32_t sbr_fac = 1;
73 unsigned char c = 0; 73 unsigned char c = 0;
74 void *ret; 74 void *ret;
75 enum codec_command_action action;
75 intptr_t param; 76 intptr_t param;
76 bool empty_first_frame = false; 77 bool empty_first_frame = false;
77 78
@@ -82,6 +83,8 @@ enum codec_status codec_run(void)
82 return CODEC_ERROR; 83 return CODEC_ERROR;
83 } 84 }
84 85
86 action = CODEC_ACTION_NULL;
87 param = ci->id3->elapsed;
85 file_offset = ci->id3->offset; 88 file_offset = ci->id3->offset;
86 89
87 ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); 90 ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency);
@@ -138,11 +141,16 @@ enum codec_status codec_run(void)
138 sound_samples_done = 0; 141 sound_samples_done = 0;
139 } 142 }
140 NeAACDecPostSeekReset(decoder, i); 143 NeAACDecPostSeekReset(decoder, i);
144 elapsed_time = (sound_samples_done * 10) /
145 (ci->id3->frequency / 100);
146 } else if (param) {
147 elapsed_time = param;
148 action = CODEC_ACTION_SEEK_TIME;
141 } else { 149 } else {
150 elapsed_time = 0;
142 sound_samples_done = 0; 151 sound_samples_done = 0;
143 } 152 }
144 153
145 elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100);
146 ci->set_elapsed(elapsed_time); 154 ci->set_elapsed(elapsed_time);
147 155
148 if (i == 0) 156 if (i == 0)
@@ -152,7 +160,8 @@ enum codec_status codec_run(void)
152 160
153 /* The main decoding loop */ 161 /* The main decoding loop */
154 while (i < demux_res.num_sample_byte_sizes) { 162 while (i < demux_res.num_sample_byte_sizes) {
155 enum codec_command_action action = ci->get_command(&param); 163 if (action == CODEC_ACTION_NULL)
164 action = ci->get_command(&param);
156 165
157 if (action == CODEC_ACTION_HALT) 166 if (action == CODEC_ACTION_HALT)
158 break; 167 break;
@@ -180,6 +189,8 @@ enum codec_status codec_run(void)
180 ci->seek_complete(); 189 ci->seek_complete();
181 } 190 }
182 191
192 action = CODEC_ACTION_NULL;
193
183 /* There can be gaps between chunks, so skip ahead if needed. It 194 /* There can be gaps between chunks, so skip ahead if needed. It
184 * doesn't seem to happen much, but it probably means that a 195 * doesn't seem to happen much, but it probably means that a
185 * "proper" file can have chunks out of order. Why one would want 196 * "proper" file can have chunks out of order. Why one would want
diff --git a/lib/rbcodec/codecs/aiff.c b/lib/rbcodec/codecs/aiff.c
index 3bedfa5760..9fee781c03 100644
--- a/lib/rbcodec/codecs/aiff.c
+++ b/lib/rbcodec/codecs/aiff.c
@@ -99,6 +99,7 @@ enum codec_status codec_run(void)
99 codec_set_replaygain(ci->id3); 99 codec_set_replaygain(ci->id3);
100 100
101 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 101 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
102 param = ci->id3->elapsed;
102 bytesdone = ci->id3->offset; 103 bytesdone = ci->id3->offset;
103 104
104 /* assume the AIFF header is less than 1024 bytes */ 105 /* assume the AIFF header is less than 1024 bytes */
@@ -270,10 +271,20 @@ enum codec_status codec_run(void)
270 ci->advance_buffer(firstblockposn); 271 ci->advance_buffer(firstblockposn);
271 272
272 /* make sure we're at the correct offset */ 273 /* make sure we're at the correct offset */
273 if (bytesdone > (uint32_t) firstblockposn) { 274 if (bytesdone > (uint32_t) firstblockposn || param) {
275 uint32_t seek_val;
276 int seek_mode;
277
278 if (bytesdone) {
279 seek_val = bytesdone - MIN((uint32_t) firstblockposn, bytesdone);
280 seek_mode = PCM_SEEK_POS;
281 } else {
282 seek_val = param;
283 seek_mode = PCM_SEEK_TIME;
284 }
285
274 /* Round down to previous block */ 286 /* Round down to previous block */
275 struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, 287 struct pcm_pos *newpos = codec->get_seek_pos(seek_val, seek_mode, NULL);
276 PCM_SEEK_POS, NULL);
277 288
278 if (newpos->pos > format.numbytes) 289 if (newpos->pos > format.numbytes)
279 return CODEC_OK; 290 return CODEC_OK;
diff --git a/lib/rbcodec/codecs/alac.c b/lib/rbcodec/codecs/alac.c
index 5eb6e001f7..a3a5ad43b8 100644
--- a/lib/rbcodec/codecs/alac.c
+++ b/lib/rbcodec/codecs/alac.c
@@ -50,7 +50,7 @@ enum codec_status codec_run(void)
50 demux_res_t demux_res; 50 demux_res_t demux_res;
51 stream_t input_stream; 51 stream_t input_stream;
52 uint32_t samplesdone; 52 uint32_t samplesdone;
53 uint32_t elapsedtime = 0; 53 uint32_t elapsedtime;
54 int samplesdecoded; 54 int samplesdecoded;
55 unsigned int i; 55 unsigned int i;
56 unsigned char* buffer; 56 unsigned char* buffer;
@@ -71,9 +71,9 @@ enum codec_status codec_run(void)
71 71
72 stream_create(&input_stream,ci); 72 stream_create(&input_stream,ci);
73 73
74 /* Read from ci->id3->offset before calling qtmovie_read. */ 74 /* Read resume info before calling qtmovie_read. */
75 samplesdone = (uint32_t)(((uint64_t)(ci->id3->offset) * ci->id3->frequency) / 75 elapsedtime = ci->id3->elapsed;
76 (ci->id3->bitrate*128)); 76 samplesdone = ci->id3->offset;
77 77
78 /* if qtmovie_read returns successfully, the stream is up to 78 /* if qtmovie_read returns successfully, the stream is up to
79 * the movie data, which can be used directly by the decoder */ 79 * the movie data, which can be used directly by the decoder */
@@ -87,16 +87,24 @@ enum codec_status codec_run(void)
87 87
88 /* Set i for first frame, seek to desired sample position for resuming. */ 88 /* Set i for first frame, seek to desired sample position for resuming. */
89 i=0; 89 i=0;
90 if (samplesdone > 0) { 90
91 if (m4a_seek(&demux_res, &input_stream, samplesdone, 91 if (elapsedtime || samplesdone) {
92 if (samplesdone) {
93 samplesdone =
94 (uint32_t)((uint64_t)samplesdone*ci->id3->frequency /
95 (ci->id3->bitrate*128));
96 }
97 else {
98 samplesdone = (elapsedtime/10) * (ci->id3->frequency/100);
99 }
100
101 if (!m4a_seek(&demux_res, &input_stream, samplesdone,
92 &samplesdone, (int*) &i)) { 102 &samplesdone, (int*) &i)) {
93 elapsedtime = (samplesdone * 10) / (ci->id3->frequency / 100);
94 ci->set_elapsed(elapsedtime);
95 } else {
96 samplesdone = 0; 103 samplesdone = 0;
97 } 104 }
98 } 105 }
99 106
107 elapsedtime = (samplesdone*10)/(ci->id3->frequency/100);
100 ci->set_elapsed(elapsedtime); 108 ci->set_elapsed(elapsedtime);
101 109
102 /* The main decoding loop */ 110 /* The main decoding loop */
@@ -106,9 +114,6 @@ enum codec_status codec_run(void)
106 if (action == CODEC_ACTION_HALT) 114 if (action == CODEC_ACTION_HALT)
107 break; 115 break;
108 116
109 /* Request the required number of bytes from the input buffer */
110 buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE);
111
112 /* Deal with any pending seek requests */ 117 /* Deal with any pending seek requests */
113 if (action == CODEC_ACTION_SEEK_TIME) { 118 if (action == CODEC_ACTION_SEEK_TIME) {
114 if (m4a_seek(&demux_res, &input_stream, 119 if (m4a_seek(&demux_res, &input_stream,
diff --git a/lib/rbcodec/codecs/ape.c b/lib/rbcodec/codecs/ape.c
index 577e7b65e2..a6c5254d45 100644
--- a/lib/rbcodec/codecs/ape.c
+++ b/lib/rbcodec/codecs/ape.c
@@ -155,6 +155,7 @@ enum codec_status codec_run(void)
155 int res; 155 int res;
156 int firstbyte; 156 int firstbyte;
157 size_t resume_offset; 157 size_t resume_offset;
158 enum codec_command_action action;
158 intptr_t param; 159 intptr_t param;
159 160
160 if (codec_init()) { 161 if (codec_init()) {
@@ -162,8 +163,12 @@ enum codec_status codec_run(void)
162 return CODEC_ERROR; 163 return CODEC_ERROR;
163 } 164 }
164 165
166 action = CODEC_ACTION_NULL;
167 param = 0;
168
165 /* Remember the resume position - when the codec is opened, the 169 /* Remember the resume position - when the codec is opened, the
166 playback engine will reset it. */ 170 playback engine will reset it. */
171 elapsedtime = ci->id3->elapsed;
167 resume_offset = ci->id3->offset; 172 resume_offset = ci->id3->offset;
168 173
169 ci->seek_buffer(0); 174 ci->seek_buffer(0);
@@ -213,14 +218,21 @@ enum codec_status codec_run(void)
213 218
214 ape_resume(&ape_ctx, resume_offset, 219 ape_resume(&ape_ctx, resume_offset,
215 &currentframe, &samplesdone, &samplestoskip, &firstbyte); 220 &currentframe, &samplesdone, &samplestoskip, &firstbyte);
216 } else { 221 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
222 }
223 else {
217 currentframe = 0; 224 currentframe = 0;
218 samplesdone = 0; 225 samplesdone = 0;
219 samplestoskip = 0; 226 samplestoskip = 0;
220 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */ 227 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */
228
229 if (elapsedtime) {
230 /* Resume by simulated seeking */
231 param = elapsedtime;
232 action = CODEC_ACTION_SEEK_TIME;
233 }
221 } 234 }
222 235
223 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
224 ci->set_elapsed(elapsedtime); 236 ci->set_elapsed(elapsedtime);
225 237
226 /* Initialise the buffer */ 238 /* Initialise the buffer */
@@ -247,36 +259,44 @@ frame_start:
247 /* Decode the frame a chunk at a time */ 259 /* Decode the frame a chunk at a time */
248 while (nblocks > 0) 260 while (nblocks > 0)
249 { 261 {
250 enum codec_command_action action = ci->get_command(&param); 262 if (action == CODEC_ACTION_NULL)
263 action = ci->get_command(&param);
251 264
252 if (action == CODEC_ACTION_HALT) 265 if (action != CODEC_ACTION_NULL) {
253 goto done; 266 if (action == CODEC_ACTION_HALT)
267 goto done;
254 268
255 /* Deal with any pending seek requests */ 269 /* Deal with any pending seek requests */
256 if (action == CODEC_ACTION_SEEK_TIME) 270 if (action == CODEC_ACTION_SEEK_TIME)
257 {
258 if (ape_calc_seekpos(&ape_ctx,
259 (param/10) * (ci->id3->frequency/100),
260 &currentframe,
261 &newfilepos,
262 &samplestoskip))
263 { 271 {
264 samplesdone = currentframe * ape_ctx.blocksperframe; 272 if (ape_calc_seekpos(&ape_ctx,
265 273 (param/10) * (ci->id3->frequency/100),
266 /* APE's bytestream is weird... */ 274 &currentframe,
267 firstbyte = 3 - (newfilepos & 3); 275 &newfilepos,
268 newfilepos &= ~3; 276 &samplestoskip))
269 277 {
270 ci->seek_buffer(newfilepos); 278 samplesdone = currentframe * ape_ctx.blocksperframe;
271 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); 279
280 /* APE's bytestream is weird... */
281 firstbyte = 3 - (newfilepos & 3);
282 newfilepos &= ~3;
283
284 ci->seek_buffer(newfilepos);
285 inbuffer = ci->request_buffer(&bytesleft,
286 INPUT_CHUNKSIZE);
287
288 elapsedtime = (samplesdone*10)/
289 (ape_ctx.samplerate/100);
290 ci->set_elapsed(elapsedtime);
291 ci->seek_complete();
292 action = CODEC_ACTION_NULL;
293 goto frame_start; /* Sorry... */
294 }
272 295
273 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
274 ci->set_elapsed(elapsedtime);
275 ci->seek_complete(); 296 ci->seek_complete();
276 goto frame_start; /* Sorry... */
277 } 297 }
278 298
279 ci->seek_complete(); 299 action = CODEC_ACTION_NULL;
280 } 300 }
281 301
282 blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks); 302 blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks);
diff --git a/lib/rbcodec/codecs/asap.c b/lib/rbcodec/codecs/asap.c
index 19b39a44c4..2c350ba450 100644
--- a/lib/rbcodec/codecs/asap.c
+++ b/lib/rbcodec/codecs/asap.c
@@ -52,6 +52,8 @@ enum codec_status codec_run(void)
52 return CODEC_ERROR; 52 return CODEC_ERROR;
53 } 53 }
54 54
55 param = ci->id3->elapsed;
56
55 codec_set_replaygain(ci->id3); 57 codec_set_replaygain(ci->id3);
56 58
57 int bytes_done =0; 59 int bytes_done =0;
@@ -86,8 +88,6 @@ enum codec_status codec_run(void)
86 ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); 88 ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
87 bytesPerSample = 4; 89 bytesPerSample = 4;
88 } 90 }
89 /* reset eleapsed */
90 ci->set_elapsed(0);
91 91
92 song = asap.module_info->default_song; 92 song = asap.module_info->default_song;
93 duration = asap.module_info->durations[song]; 93 duration = asap.module_info->durations[song];
@@ -100,6 +100,11 @@ enum codec_status codec_run(void)
100 ASAP_PlaySong(&asap, song, duration); 100 ASAP_PlaySong(&asap, song, duration);
101 ASAP_MutePokeyChannels(&asap, 0); 101 ASAP_MutePokeyChannels(&asap, 0);
102 102
103 if (param)
104 goto resume_start;
105
106 ci->set_elapsed(0);
107
103 /* The main decoder loop */ 108 /* The main decoder loop */
104 while (1) { 109 while (1) {
105 enum codec_command_action action = ci->get_command(&param); 110 enum codec_command_action action = ci->get_command(&param);
@@ -108,6 +113,7 @@ enum codec_status codec_run(void)
108 break; 113 break;
109 114
110 if (action == CODEC_ACTION_SEEK_TIME) { 115 if (action == CODEC_ACTION_SEEK_TIME) {
116 resume_start:
111 /* New time is ready in param */ 117 /* New time is ready in param */
112 118
113 /* seek to pos */ 119 /* seek to pos */
diff --git a/lib/rbcodec/codecs/atrac3_oma.c b/lib/rbcodec/codecs/atrac3_oma.c
index 50f7c8f163..65d9ed8b38 100644
--- a/lib/rbcodec/codecs/atrac3_oma.c
+++ b/lib/rbcodec/codecs/atrac3_oma.c
@@ -50,13 +50,15 @@ enum codec_status codec_run(void)
50 int elapsed = 0; 50 int elapsed = 0;
51 size_t resume_offset; 51 size_t resume_offset;
52 intptr_t param; 52 intptr_t param;
53 enum codec_command_action action = CODEC_ACTION_NULL; 53 enum codec_command_action action;
54 54
55 if (codec_init()) { 55 if (codec_init()) {
56 DEBUGF("codec init failed\n"); 56 DEBUGF("codec init failed\n");
57 return CODEC_ERROR; 57 return CODEC_ERROR;
58 } 58 }
59 59
60 action = CODEC_ACTION_NULL;
61 param = ci->id3->elapsed;
60 resume_offset = ci->id3->offset; 62 resume_offset = ci->id3->offset;
61 63
62 codec_set_replaygain(ci->id3); 64 codec_set_replaygain(ci->id3);
@@ -79,11 +81,13 @@ enum codec_status codec_run(void)
79 frame_counter = 0; 81 frame_counter = 0;
80 82
81 /* check for a mid-track resume and force a seek time accordingly */ 83 /* check for a mid-track resume and force a seek time accordingly */
82 if(resume_offset > ci->id3->first_frame_offset) { 84 if (resume_offset) {
83 resume_offset -= ci->id3->first_frame_offset; 85 resume_offset -= MIN(resume_offset, ci->id3->first_frame_offset);
84 /* calculate resume_offset in frames */ 86 /* calculate resume_offset in frames */
85 resume_offset = (int)resume_offset / FRAMESIZE; 87 param = (resume_offset/FRAMESIZE) * ((FRAMESIZE * 8)/BITRATE);
86 param = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE); 88 }
89
90 if ((unsigned long)param) {
87 action = CODEC_ACTION_SEEK_TIME; 91 action = CODEC_ACTION_SEEK_TIME;
88 } 92 }
89 else { 93 else {
@@ -100,11 +104,9 @@ enum codec_status codec_run(void)
100 if (action == CODEC_ACTION_HALT) 104 if (action == CODEC_ACTION_HALT)
101 break; 105 break;
102 106
103 bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
104
105 if (action == CODEC_ACTION_SEEK_TIME) { 107 if (action == CODEC_ACTION_SEEK_TIME) {
106 /* Do not allow seeking beyond the file's length */ 108 /* Do not allow seeking beyond the file's length */
107 if ((unsigned) param > ci->id3->length) { 109 if ((unsigned long) param > ci->id3->length) {
108 ci->set_elapsed(ci->id3->length); 110 ci->set_elapsed(ci->id3->length);
109 ci->seek_complete(); 111 ci->seek_complete();
110 break; 112 break;
@@ -123,7 +125,6 @@ enum codec_status codec_run(void)
123 seek_frame_offset = (param * BITRATE) / (8 * FRAMESIZE); 125 seek_frame_offset = (param * BITRATE) / (8 * FRAMESIZE);
124 frame_counter = seek_frame_offset; 126 frame_counter = seek_frame_offset;
125 ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE); 127 ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE);
126 bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
127 elapsed = param; 128 elapsed = param;
128 ci->set_elapsed(elapsed); 129 ci->set_elapsed(elapsed);
129 ci->seek_complete(); 130 ci->seek_complete();
@@ -131,6 +132,8 @@ enum codec_status codec_run(void)
131 132
132 action = CODEC_ACTION_NULL; 133 action = CODEC_ACTION_NULL;
133 134
135 bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE);
136
134 res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE); 137 res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE);
135 138
136 if(res != (int)FRAMESIZE) { 139 if(res != (int)FRAMESIZE) {
diff --git a/lib/rbcodec/codecs/atrac3_rm.c b/lib/rbcodec/codecs/atrac3_rm.c
index 997507425e..4b528c0a8d 100644
--- a/lib/rbcodec/codecs/atrac3_rm.c
+++ b/lib/rbcodec/codecs/atrac3_rm.c
@@ -57,17 +57,19 @@ enum codec_status codec_run(void)
57 uint8_t *bit_buffer; 57 uint8_t *bit_buffer;
58 uint16_t fs,sps,h; 58 uint16_t fs,sps,h;
59 uint32_t packet_count; 59 uint32_t packet_count;
60 int scrambling_unit_size, num_units, elapsed = 0; 60 int scrambling_unit_size, num_units, elapsed;
61 int playback_on = -1; 61 int playback_on = -1;
62 size_t resume_offset; 62 size_t resume_offset;
63 intptr_t param; 63 intptr_t param;
64 enum codec_command_action action = CODEC_ACTION_NULL; 64 enum codec_command_action action;
65 65
66 if (codec_init()) { 66 if (codec_init()) {
67 DEBUGF("codec init failed\n"); 67 DEBUGF("codec init failed\n");
68 return CODEC_ERROR; 68 return CODEC_ERROR;
69 } 69 }
70 70
71 action = CODEC_ACTION_NULL;
72 elapsed = ci->id3->elapsed;
71 resume_offset = ci->id3->offset; 73 resume_offset = ci->id3->offset;
72 74
73 codec_set_replaygain(ci->id3); 75 codec_set_replaygain(ci->id3);
@@ -98,15 +100,20 @@ enum codec_status codec_run(void)
98 } 100 }
99 101
100 /* check for a mid-track resume and force a seek time accordingly */ 102 /* check for a mid-track resume and force a seek time accordingly */
101 if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { 103 if(resume_offset) {
102 resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; 104 resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
103 num_units = (int)resume_offset / scrambling_unit_size; 105 num_units = (int)resume_offset / scrambling_unit_size;
104 /* put number of subpackets to skip in resume_offset */ 106 /* put number of subpackets to skip in resume_offset */
105 resume_offset /= (sps + PACKET_HEADER_SIZE); 107 resume_offset /= (sps + PACKET_HEADER_SIZE);
106 param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); 108 elapsed = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
109 }
110
111 if (elapsed > 0) {
112 param = elapsed;
107 action = CODEC_ACTION_SEEK_TIME; 113 action = CODEC_ACTION_SEEK_TIME;
108 } 114 }
109 else { 115 else {
116 elapsed = 0;
110 ci->set_elapsed(0); 117 ci->set_elapsed(0);
111 } 118 }
112 119
@@ -151,6 +158,7 @@ seek_start :
151 158
152 /* Seek to the start of the track */ 159 /* Seek to the start of the track */
153 if (param == 0) { 160 if (param == 0) {
161 elapsed = 0;
154 ci->set_elapsed(0); 162 ci->set_elapsed(0);
155 ci->seek_complete(); 163 ci->seek_complete();
156 action = CODEC_ACTION_NULL; 164 action = CODEC_ACTION_NULL;
diff --git a/lib/rbcodec/codecs/au.c b/lib/rbcodec/codecs/au.c
index 7ae7fe3e94..18d4296125 100644
--- a/lib/rbcodec/codecs/au.c
+++ b/lib/rbcodec/codecs/au.c
@@ -139,6 +139,7 @@ enum codec_status codec_run(void)
139 codec_set_replaygain(ci->id3); 139 codec_set_replaygain(ci->id3);
140 140
141 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 141 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
142 param = ci->id3->elapsed;
142 bytesdone = ci->id3->offset; 143 bytesdone = ci->id3->offset;
143 144
144 ci->memset(&format, 0, sizeof(struct pcm_format)); 145 ci->memset(&format, 0, sizeof(struct pcm_format));
@@ -236,10 +237,20 @@ enum codec_status codec_run(void)
236 } 237 }
237 238
238 /* make sure we're at the correct offset */ 239 /* make sure we're at the correct offset */
239 if (bytesdone > (uint32_t) firstblockposn) { 240 if (bytesdone > (uint32_t) firstblockposn || param) {
241 uint32_t seek_val;
242 int seek_mode;
243
244 if (bytesdone) {
245 seek_val = bytesdone - MIN((uint32_t) firstblockposn, bytesdone);
246 seek_mode = PCM_SEEK_POS;
247 } else {
248 seek_val = param;
249 seek_mode = PCM_SEEK_TIME;
250 }
251
240 /* Round down to previous block */ 252 /* Round down to previous block */
241 struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, 253 struct pcm_pos *newpos = codec->get_seek_pos(seek_val, seek_mode, NULL);
242 PCM_SEEK_POS, NULL);
243 254
244 if (newpos->pos > format.numbytes) 255 if (newpos->pos > format.numbytes)
245 goto done; 256 goto done;
diff --git a/lib/rbcodec/codecs/ay.c b/lib/rbcodec/codecs/ay.c
index b11ad84294..88936df131 100644
--- a/lib/rbcodec/codecs/ay.c
+++ b/lib/rbcodec/codecs/ay.c
@@ -56,6 +56,7 @@ enum codec_status codec_run(void)
56 /* reset values */ 56 /* reset values */
57 track = is_multitrack = 0; 57 track = is_multitrack = 0;
58 elapsed_time = 0; 58 elapsed_time = 0;
59 param = ci->id3->elapsed;
59 60
60 DEBUGF("AY: next_track\n"); 61 DEBUGF("AY: next_track\n");
61 if (codec_init()) { 62 if (codec_init()) {
@@ -87,6 +88,10 @@ enum codec_status codec_run(void)
87 is_multitrack = 1; 88 is_multitrack = 1;
88 } 89 }
89 90
91 if (param) {
92 goto resume_start;
93 }
94
90next_track: 95next_track:
91 set_codec_track(track, is_multitrack); 96 set_codec_track(track, is_multitrack);
92 97
@@ -98,6 +103,7 @@ next_track:
98 break; 103 break;
99 104
100 if (action == CODEC_ACTION_SEEK_TIME) { 105 if (action == CODEC_ACTION_SEEK_TIME) {
106 resume_start:
101 if (is_multitrack) { 107 if (is_multitrack) {
102 track = param/1000; 108 track = param/1000;
103 ci->seek_complete(); 109 ci->seek_complete();
diff --git a/lib/rbcodec/codecs/cook.c b/lib/rbcodec/codecs/cook.c
index 55188aad36..402d1d3fa6 100644
--- a/lib/rbcodec/codecs/cook.c
+++ b/lib/rbcodec/codecs/cook.c
@@ -56,14 +56,16 @@ enum codec_status codec_run(void)
56 uint32_t packet_count; 56 uint32_t packet_count;
57 int scrambling_unit_size, num_units; 57 int scrambling_unit_size, num_units;
58 size_t resume_offset; 58 size_t resume_offset;
59 intptr_t param = 0; 59 intptr_t param;
60 enum codec_command_action action = CODEC_ACTION_NULL; 60 enum codec_command_action action;
61 61
62 if (codec_init()) { 62 if (codec_init()) {
63 DEBUGF("codec init failed\n"); 63 DEBUGF("codec init failed\n");
64 return CODEC_ERROR; 64 return CODEC_ERROR;
65 } 65 }
66 66
67 action = CODEC_ACTION_NULL;
68 param = ci->id3->elapsed;
67 resume_offset = ci->id3->offset; 69 resume_offset = ci->id3->offset;
68 70
69 codec_set_replaygain(ci->id3); 71 codec_set_replaygain(ci->id3);
@@ -97,12 +99,15 @@ enum codec_status codec_run(void)
97 } 99 }
98 100
99 /* check for a mid-track resume and force a seek time accordingly */ 101 /* check for a mid-track resume and force a seek time accordingly */
100 if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { 102 if(resume_offset) {
101 resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; 103 resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
102 num_units = (int)resume_offset / scrambling_unit_size; 104 num_units = (int)resume_offset / scrambling_unit_size;
103 /* put number of subpackets to skip in resume_offset */ 105 /* put number of subpackets to skip in resume_offset */
104 resume_offset /= (sps + PACKET_HEADER_SIZE); 106 resume_offset /= (sps + PACKET_HEADER_SIZE);
105 param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); 107 param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate);
108 }
109
110 if (param) {
106 action = CODEC_ACTION_SEEK_TIME; 111 action = CODEC_ACTION_SEEK_TIME;
107 } 112 }
108 else { 113 else {
diff --git a/lib/rbcodec/codecs/flac.c b/lib/rbcodec/codecs/flac.c
index 3390c24a2c..eab6e7c2bc 100644
--- a/lib/rbcodec/codecs/flac.c
+++ b/lib/rbcodec/codecs/flac.c
@@ -468,7 +468,8 @@ enum codec_status codec_run(void)
468 return CODEC_ERROR; 468 return CODEC_ERROR;
469 } 469 }
470 470
471 /* Need to save offset for later use (cleared indirectly by flac_init) */ 471 /* Need to save resume for later use (cleared indirectly by flac_init) */
472 elapsedtime = ci->id3->elapsed;
472 samplesdone = ci->id3->offset; 473 samplesdone = ci->id3->offset;
473 474
474 if (!flac_init(&fc,ci->id3->first_frame_offset)) { 475 if (!flac_init(&fc,ci->id3->first_frame_offset)) {
@@ -481,9 +482,16 @@ enum codec_status codec_run(void)
481 STEREO_MONO : STEREO_NONINTERLEAVED); 482 STEREO_MONO : STEREO_NONINTERLEAVED);
482 codec_set_replaygain(ci->id3); 483 codec_set_replaygain(ci->id3);
483 484
484 flac_seek_offset(&fc, samplesdone); 485 if (samplesdone || !elapsedtime) {
485 samplesdone=fc.samplenumber+fc.blocksize; 486 flac_seek_offset(&fc, samplesdone);
486 elapsedtime=((uint64_t)samplesdone*1000)/(ci->id3->frequency); 487 samplesdone=fc.samplenumber+fc.blocksize;
488 elapsedtime=((uint64_t)samplesdone*1000)/(ci->id3->frequency);
489 }
490 else if (!flac_seek(&fc,(uint32_t)((uint64_t)elapsedtime
491 *ci->id3->frequency/1000))) {
492 elapsedtime = 0;
493 }
494
487 ci->set_elapsed(elapsedtime); 495 ci->set_elapsed(elapsedtime);
488 496
489 /* The main decoding loop */ 497 /* The main decoding loop */
diff --git a/lib/rbcodec/codecs/gbs.c b/lib/rbcodec/codecs/gbs.c
index def05ed351..717f56c82f 100644
--- a/lib/rbcodec/codecs/gbs.c
+++ b/lib/rbcodec/codecs/gbs.c
@@ -76,6 +76,11 @@ enum codec_status codec_run(void)
76 if (gbs_emu.m3u.size > 0) 76 if (gbs_emu.m3u.size > 0)
77 gbs_emu.track_count = gbs_emu.m3u.size; 77 gbs_emu.track_count = gbs_emu.m3u.size;
78 78
79 if (ci->id3->elapsed) {
80 track = ci->id3->elapsed/1000;
81 if (track >= gbs_emu.track_count) return CODEC_OK;
82 }
83
79next_track: 84next_track:
80 set_codec_track(track); 85 set_codec_track(track);
81 86
diff --git a/lib/rbcodec/codecs/hes.c b/lib/rbcodec/codecs/hes.c
index 849fd88f12..56f49621c6 100644
--- a/lib/rbcodec/codecs/hes.c
+++ b/lib/rbcodec/codecs/hes.c
@@ -76,6 +76,11 @@ enum codec_status codec_run(void)
76 if (hes_emu.m3u.size > 0) 76 if (hes_emu.m3u.size > 0)
77 hes_emu.track_count = hes_emu.m3u.size; 77 hes_emu.track_count = hes_emu.m3u.size;
78 78
79 if (ci->id3->elapsed) {
80 track = ci->id3->elapsed/1000;
81 if (track >= hes_emu.track_count) return CODEC_OK;
82 }
83
79next_track: 84next_track:
80 set_codec_track(track); 85 set_codec_track(track);
81 86
diff --git a/lib/rbcodec/codecs/kss.c b/lib/rbcodec/codecs/kss.c
index 92efcd4e5f..e6cf866cdd 100644
--- a/lib/rbcodec/codecs/kss.c
+++ b/lib/rbcodec/codecs/kss.c
@@ -79,6 +79,11 @@ enum codec_status codec_run(void)
79 if (kss_emu.m3u.size > 0) 79 if (kss_emu.m3u.size > 0)
80 kss_emu.track_count = kss_emu.m3u.size; 80 kss_emu.track_count = kss_emu.m3u.size;
81 81
82 if (ci->id3->elapsed) {
83 track = ci->id3->elapsed/1000;
84 if (track >= kss_emu.track_count) return CODEC_OK;
85 }
86
82next_track: 87next_track:
83 set_codec_track(track); 88 set_codec_track(track);
84 89
diff --git a/lib/rbcodec/codecs/mod.c b/lib/rbcodec/codecs/mod.c
index 8bb2dc5163..4dd0cde6e5 100644
--- a/lib/rbcodec/codecs/mod.c
+++ b/lib/rbcodec/codecs/mod.c
@@ -1319,7 +1319,16 @@ enum codec_status codec_run(void)
1319 loadmod(modfile); 1319 loadmod(modfile);
1320 1320
1321 /* The main decoder loop */ 1321 /* The main decoder loop */
1322 ci->set_elapsed(0); 1322#if 0
1323 /* Needs to be a bit more elaborate or critical stuff is missed */
1324 if (ci->id3->elapsed) {
1325 modplayer.patterntableposition = ci->id3->elapsed/1000;
1326 modplayer.currentline = 0;
1327 }
1328#endif
1329
1330 ci->set_elapsed(modplayer.patterntableposition*1000);
1331
1323 bytesdone = 0; 1332 bytesdone = 0;
1324 old_patterntableposition = 0; 1333 old_patterntableposition = 0;
1325 1334
diff --git a/lib/rbcodec/codecs/mpa.c b/lib/rbcodec/codecs/mpa.c
index 07db248099..ca12087e87 100644
--- a/lib/rbcodec/codecs/mpa.c
+++ b/lib/rbcodec/codecs/mpa.c
@@ -346,6 +346,11 @@ enum codec_status codec_run(void)
346 current_frequency = ci->id3->frequency; 346 current_frequency = ci->id3->frequency;
347 codec_set_replaygain(ci->id3); 347 codec_set_replaygain(ci->id3);
348 348
349 if (!ci->id3->offset && ci->id3->elapsed) {
350 /* Have elapsed time but not offset */
351 ci->id3->offset = get_file_pos(ci->id3->elapsed);
352 }
353
349 if (ci->id3->offset) { 354 if (ci->id3->offset) {
350 ci->seek_buffer(ci->id3->offset); 355 ci->seek_buffer(ci->id3->offset);
351 set_elapsed(ci->id3); 356 set_elapsed(ci->id3);
diff --git a/lib/rbcodec/codecs/mpc.c b/lib/rbcodec/codecs/mpc.c
index 9db40242dc..79f2aa22db 100644
--- a/lib/rbcodec/codecs/mpc.c
+++ b/lib/rbcodec/codecs/mpc.c
@@ -108,6 +108,7 @@ enum codec_status codec_run(void)
108 * sample seek position from the file offset, the sampling frequency and 108 * sample seek position from the file offset, the sampling frequency and
109 * the bitrate. As the saved position is exactly calculated the reverse way 109 * the bitrate. As the saved position is exactly calculated the reverse way
110 * there is no loss of information except rounding. */ 110 * there is no loss of information except rounding. */
111 elapsed_time = ci->id3->elapsed;
111 samplesdone = 100 * (((mpc_uint64_t)ci->id3->offset * frequency) / byterate); 112 samplesdone = 100 * (((mpc_uint64_t)ci->id3->offset * frequency) / byterate);
112 113
113 /* Set up digital signal processing for correct number of channels */ 114 /* Set up digital signal processing for correct number of channels */
@@ -122,19 +123,24 @@ enum codec_status codec_run(void)
122 123
123 codec_set_replaygain(ci->id3); 124 codec_set_replaygain(ci->id3);
124 125
125 /* Resume to saved sample offset. */ 126 if (samplesdone > 0 || elapsed_time)
126 elapsed_time = 0;
127
128 if (samplesdone > 0)
129 { 127 {
130 if (mpc_demux_seek_sample(demux, samplesdone) == MPC_STATUS_OK) 128 mpc_int64_t new_offset = samplesdone;
129
130 if (new_offset <= 0)
131 new_offset = (elapsed_time/10)*frequency; /* by time */
132
133 /* Resume to sample offset. */
134 if (mpc_demux_seek_sample(demux, new_offset) == MPC_STATUS_OK)
131 { 135 {
132 elapsed_time = (samplesdone*10)/frequency; 136 samplesdone = new_offset;
133 } 137 }
134 else 138 else
135 { 139 {
136 samplesdone = 0; 140 samplesdone = 0;
137 } 141 }
142
143 elapsed_time = (samplesdone*10)/frequency;
138 } 144 }
139 145
140 ci->set_elapsed(elapsed_time); 146 ci->set_elapsed(elapsed_time);
diff --git a/lib/rbcodec/codecs/nsf.c b/lib/rbcodec/codecs/nsf.c
index 4c5b37c3fa..cbdf8e3ec5 100644
--- a/lib/rbcodec/codecs/nsf.c
+++ b/lib/rbcodec/codecs/nsf.c
@@ -57,6 +57,7 @@ enum codec_status codec_run(void)
57 57
58 track = is_multitrack = 0; 58 track = is_multitrack = 0;
59 elapsed_time = 0; 59 elapsed_time = 0;
60 param = ci->id3->elapsed;
60 61
61 DEBUGF("NSF: next_track\n"); 62 DEBUGF("NSF: next_track\n");
62 if (codec_init()) { 63 if (codec_init()) {
@@ -85,6 +86,10 @@ enum codec_status codec_run(void)
85 86
86 if (nsf_emu.track_count > 1) is_multitrack = 1; 87 if (nsf_emu.track_count > 1) is_multitrack = 1;
87 88
89 if (param) {
90 goto resume_start;
91 }
92
88next_track: 93next_track:
89 set_codec_track(track, is_multitrack); 94 set_codec_track(track, is_multitrack);
90 95
@@ -96,6 +101,7 @@ next_track:
96 break; 101 break;
97 102
98 if (action == CODEC_ACTION_SEEK_TIME) { 103 if (action == CODEC_ACTION_SEEK_TIME) {
104 resume_start:
99 if (is_multitrack) { 105 if (is_multitrack) {
100 track = param/1000; 106 track = param/1000;
101 ci->seek_complete(); 107 ci->seek_complete();
diff --git a/lib/rbcodec/codecs/opus.c b/lib/rbcodec/codecs/opus.c
index 15d96ff6fe..2c495aa8d0 100644
--- a/lib/rbcodec/codecs/opus.c
+++ b/lib/rbcodec/codecs/opus.c
@@ -314,6 +314,7 @@ enum codec_status codec_main(enum codec_entry_call_reason reason)
314enum codec_status codec_run(void) 314enum codec_status codec_run(void)
315{ 315{
316 int error = CODEC_ERROR; 316 int error = CODEC_ERROR;
317 enum codec_command_action action;
317 intptr_t param; 318 intptr_t param;
318 ogg_sync_state oy; 319 ogg_sync_state oy;
319 ogg_page og; 320 ogg_page og;
@@ -325,13 +326,17 @@ enum codec_status codec_run(void)
325 OpusDecoder *st = NULL; 326 OpusDecoder *st = NULL;
326 OpusHeader header; 327 OpusHeader header;
327 int ret; 328 int ret;
328 unsigned long strtoffset = ci->id3->offset; 329 unsigned long strtoffset;
329 int skip = 0; 330 int skip = 0;
330 int64_t seek_target; 331 int64_t seek_target;
331 uint64_t granule_pos; 332 uint64_t granule_pos;
332 333
333 ogg_malloc_init(); 334 ogg_malloc_init();
334 335
336 action = CODEC_ACTION_NULL;
337 param = ci->id3->elapsed;
338 strtoffset = ci->id3->offset;
339
335 global_stack = 0; 340 global_stack = 0;
336 341
337#if defined(CPU_COLDFIRE) 342#if defined(CPU_COLDFIRE)
@@ -351,28 +356,40 @@ enum codec_status codec_run(void)
351 ci->seek_buffer(0); 356 ci->seek_buffer(0);
352 ci->set_elapsed(0); 357 ci->set_elapsed(0);
353 358
359 if (!strtoffset && param) {
360 action = CODEC_ACTION_SEEK_TIME;
361 }
362
363 goto next_page;
364
354 while (1) { 365 while (1) {
355 enum codec_command_action action = ci->get_command(&param); 366 if (action == CODEC_ACTION_NULL)
367 action = ci->get_command(&param);
356 368
357 if (action == CODEC_ACTION_HALT) 369 if (action != CODEC_ACTION_NULL) {
358 break; 370 if (action == CODEC_ACTION_HALT)
371 break;
372
373 if (action == CODEC_ACTION_SEEK_TIME) {
374 if (st != NULL) {
375 /* calculate granule to seek to (including seek rewind) */
376 seek_target = (48LL * param) + header.preskip;
377 skip = MIN(seek_target, SEEK_REWIND);
378 seek_target -= skip;
359 379
360 if (action == CODEC_ACTION_SEEK_TIME) { 380 LOGF("Opus seek page:%lld,%lld,%ld\n",
361 if (st != NULL) { 381 seek_target, page_granule, (long)param);
362 /* calculate granule to seek to (including seek rewind) */ 382 speex_seek_page_granule(seek_target, page_granule, &oy, &os);
363 seek_target = (48LL * param) + header.preskip; 383 }
364 skip = MIN(seek_target, SEEK_REWIND);
365 seek_target -= skip;
366 384
367 LOGF("Opus seek page:%lld,%lld,%ld\n", 385 ci->set_elapsed(param);
368 seek_target, page_granule, (long)param); 386 ci->seek_complete();
369 speex_seek_page_granule(seek_target, page_granule, &oy, &os);
370 } 387 }
371 388
372 ci->set_elapsed(param); 389 action = CODEC_ACTION_NULL;
373 ci->seek_complete();
374 } 390 }
375 391
392 next_page:
376 /*Get the ogg buffer for writing*/ 393 /*Get the ogg buffer for writing*/
377 if (get_more_data(&oy) < 1) { 394 if (get_more_data(&oy) < 1) {
378 goto done; 395 goto done;
diff --git a/lib/rbcodec/codecs/raac.c b/lib/rbcodec/codecs/raac.c
index 523560b63e..d2d3531028 100644
--- a/lib/rbcodec/codecs/raac.c
+++ b/lib/rbcodec/codecs/raac.c
@@ -63,14 +63,16 @@ enum codec_status codec_run(void)
63 unsigned char c = 0; /* channels */ 63 unsigned char c = 0; /* channels */
64 int playback_on = -1; 64 int playback_on = -1;
65 size_t resume_offset; 65 size_t resume_offset;
66 enum codec_command_action action;
66 intptr_t param; 67 intptr_t param;
67 enum codec_command_action action = CODEC_ACTION_NULL;
68 68
69 if (codec_init()) { 69 if (codec_init()) {
70 DEBUGF("FAAD: Codec init error\n"); 70 DEBUGF("FAAD: Codec init error\n");
71 return CODEC_ERROR; 71 return CODEC_ERROR;
72 } 72 }
73 73
74 action = CODEC_ACTION_NULL;
75 param = ci->id3->elapsed;
74 resume_offset = ci->id3->offset; 76 resume_offset = ci->id3->offset;
75 77
76 ci->memset(&rmctx,0,sizeof(RMContext)); 78 ci->memset(&rmctx,0,sizeof(RMContext));
@@ -104,15 +106,21 @@ enum codec_status codec_run(void)
104 } 106 }
105 107
106 /* check for a mid-track resume and force a seek time accordingly */ 108 /* check for a mid-track resume and force a seek time accordingly */
107 if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { 109 if (resume_offset) {
108 resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; 110 resume_offset -= MIN(resume_offset, rmctx.data_offset + DATA_HEADER_SIZE);
109 /* put number of subpackets to skip in resume_offset */ 111 /* put number of subpackets to skip in resume_offset */
110 resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); 112 resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE);
111 param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); 113 param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate);
114 }
115
116 if (param > 0) {
112 action = CODEC_ACTION_SEEK_TIME; 117 action = CODEC_ACTION_SEEK_TIME;
113 } 118 }
114 ci->set_elapsed(0); 119 else {
115 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); 120 /* Seek to the first packet */
121 ci->set_elapsed(0);
122 ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE);
123 }
116 124
117 /* The main decoding loop */ 125 /* The main decoding loop */
118 while (1) { 126 while (1) {
@@ -124,7 +132,7 @@ enum codec_status codec_run(void)
124 132
125 if (action == CODEC_ACTION_SEEK_TIME) { 133 if (action == CODEC_ACTION_SEEK_TIME) {
126 /* Do not allow seeking beyond the file's length */ 134 /* Do not allow seeking beyond the file's length */
127 if ((unsigned) param > ci->id3->length) { 135 if ((unsigned long)param > ci->id3->length) {
128 ci->set_elapsed(ci->id3->length); 136 ci->set_elapsed(ci->id3->length);
129 ci->seek_complete(); 137 ci->seek_complete();
130 break; 138 break;
@@ -164,6 +172,7 @@ enum codec_status codec_run(void)
164 172
165 ci->advance_buffer(pkt.length); 173 ci->advance_buffer(pkt.length);
166 } 174 }
175
167 ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE); 176 ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE);
168 buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000); 177 buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000);
169 NeAACDecPostSeekReset(decoder, decoder->frame); 178 NeAACDecPostSeekReset(decoder, decoder->frame);
diff --git a/lib/rbcodec/codecs/sgc.c b/lib/rbcodec/codecs/sgc.c
index 348a54a2d3..eb260975c5 100644
--- a/lib/rbcodec/codecs/sgc.c
+++ b/lib/rbcodec/codecs/sgc.c
@@ -91,6 +91,11 @@ enum codec_status codec_run(void)
91 if (sgc_emu.m3u.size > 0) 91 if (sgc_emu.m3u.size > 0)
92 sgc_emu.track_count = sgc_emu.m3u.size; 92 sgc_emu.track_count = sgc_emu.m3u.size;
93 93
94 if (ci->id3->elapsed) {
95 track = ci->id3->elapsed/1000;
96 if (track >= sgc_emu.track_count) return CODEC_OK;
97 }
98
94next_track: 99next_track:
95 set_codec_track(track); 100 set_codec_track(track);
96 101
diff --git a/lib/rbcodec/codecs/sid.c b/lib/rbcodec/codecs/sid.c
index 1a6d04155d..6e39d3f759 100644
--- a/lib/rbcodec/codecs/sid.c
+++ b/lib/rbcodec/codecs/sid.c
@@ -1253,6 +1253,7 @@ enum codec_status codec_run(void)
1253 unsigned char subSongsMax, subSong, song_speed; 1253 unsigned char subSongsMax, subSong, song_speed;
1254 unsigned char *sidfile = NULL; 1254 unsigned char *sidfile = NULL;
1255 intptr_t param; 1255 intptr_t param;
1256 bool resume;
1256 1257
1257 if (codec_init()) { 1258 if (codec_init()) {
1258 return CODEC_ERROR; 1259 return CODEC_ERROR;
@@ -1269,15 +1270,10 @@ enum codec_status codec_run(void)
1269 return CODEC_ERROR; 1270 return CODEC_ERROR;
1270 } 1271 }
1271 1272
1272 c64Init(SAMPLE_RATE); 1273 param = ci->id3->elapsed;
1273 LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, 1274 resume = param != 0;
1274 &subSongsMax, &subSong, &song_speed, (unsigned short)filesize);
1275 sidPoke(24, 15); /* Turn on full volume */
1276 cpuJSR(init_addr, subSong); /* Start the song initialize */
1277 1275
1278 1276 goto sid_start;
1279 /* Set the elapsed time to the current subsong (in seconds) */
1280 ci->set_elapsed(subSong*1000);
1281 1277
1282 /* The main decoder loop */ 1278 /* The main decoder loop */
1283 while (1) { 1279 while (1) {
@@ -1287,20 +1283,26 @@ enum codec_status codec_run(void)
1287 break; 1283 break;
1288 1284
1289 if (action == CODEC_ACTION_SEEK_TIME) { 1285 if (action == CODEC_ACTION_SEEK_TIME) {
1286 sid_start:
1290 /* New time is ready in param */ 1287 /* New time is ready in param */
1291 1288
1292 /* Start playing from scratch */ 1289 /* Start playing from scratch */
1293 c64Init(SAMPLE_RATE); 1290 c64Init(SAMPLE_RATE);
1294 LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, 1291 LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr,
1295 &subSongsMax, &subSong, &song_speed, (unsigned short)filesize); 1292 &subSongsMax, &subSong, &song_speed,
1293 (unsigned short)filesize);
1296 sidPoke(24, 15); /* Turn on full volume */ 1294 sidPoke(24, 15); /* Turn on full volume */
1297 subSong = param / 1000; /* Now use the current seek time in seconds as subsong */ 1295 if (!resume || (resume && param))
1296 subSong = param / 1000; /* Now use the current seek time in
1297 seconds as subsong */
1298 cpuJSR(init_addr, subSong); /* Start the song initialize */ 1298 cpuJSR(init_addr, subSong); /* Start the song initialize */
1299 nSamplesToRender = 0; /* Start the rendering from scratch */ 1299 nSamplesToRender = 0; /* Start the rendering from scratch */
1300 1300
1301 /* Set the elapsed time to the current subsong (in seconds) */ 1301 /* Set the elapsed time to the current subsong (in seconds) */
1302 ci->set_elapsed(subSong*1000); 1302 ci->set_elapsed(subSong*1000);
1303 ci->seek_complete(); 1303 ci->seek_complete();
1304
1305 resume = false;
1304 } 1306 }
1305 1307
1306 nSamplesRendered = 0; 1308 nSamplesRendered = 0;
diff --git a/lib/rbcodec/codecs/smaf.c b/lib/rbcodec/codecs/smaf.c
index d56aa0a860..e4ec6342b2 100644
--- a/lib/rbcodec/codecs/smaf.c
+++ b/lib/rbcodec/codecs/smaf.c
@@ -360,7 +360,8 @@ enum codec_status codec_run(void)
360 360
361 codec_set_replaygain(ci->id3); 361 codec_set_replaygain(ci->id3);
362 362
363 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 363 /* Need to save resume for later use (cleared indirectly by advance_buffer) */
364 param = ci->id3->elapsed;
364 bytesdone = ci->id3->offset; 365 bytesdone = ci->id3->offset;
365 366
366 decodedsamples = 0; 367 decodedsamples = 0;
@@ -408,11 +409,21 @@ enum codec_status codec_run(void)
408 ci->seek_buffer(firstblockposn); 409 ci->seek_buffer(firstblockposn);
409 410
410 /* make sure we're at the correct offset */ 411 /* make sure we're at the correct offset */
411 if (bytesdone > (uint32_t) firstblockposn) 412 if (bytesdone > (uint32_t) firstblockposn || param) {
412 { 413 uint32_t seek_val;
414 int seek_mode;
415
416 if (bytesdone) {
417 seek_val = bytesdone - MIN((uint32_t) firstblockposn, bytesdone);
418 seek_mode = PCM_SEEK_POS;
419 } else {
420 seek_val = param;
421 seek_mode = PCM_SEEK_TIME;
422 }
423
413 /* Round down to previous block */ 424 /* Round down to previous block */
414 struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, 425 struct pcm_pos *newpos = codec->get_seek_pos(seek_val, seek_mode,
415 PCM_SEEK_POS, &read_buffer); 426 &read_buffer);
416 427
417 if (newpos->pos > format.numbytes) 428 if (newpos->pos > format.numbytes)
418 return CODEC_OK; 429 return CODEC_OK;
diff --git a/lib/rbcodec/codecs/speex.c b/lib/rbcodec/codecs/speex.c
index ac3bc963b1..a073151ee2 100644
--- a/lib/rbcodec/codecs/speex.c
+++ b/lib/rbcodec/codecs/speex.c
@@ -381,7 +381,6 @@ enum codec_status codec_run(void)
381 int error = CODEC_ERROR; 381 int error = CODEC_ERROR;
382 382
383 SpeexBits bits; 383 SpeexBits bits;
384 int eof = 0;
385 spx_ogg_sync_state oy; 384 spx_ogg_sync_state oy;
386 spx_ogg_page og; 385 spx_ogg_page og;
387 spx_ogg_packet op; 386 spx_ogg_packet op;
@@ -403,9 +402,10 @@ enum codec_status codec_run(void)
403 int packet_count = 0; 402 int packet_count = 0;
404 int lookahead; 403 int lookahead;
405 int headerssize = 0; 404 int headerssize = 0;
406 unsigned long strtoffset = ci->id3->offset; 405 unsigned long strtoffset;
407 void *st = NULL; 406 void *st = NULL;
408 int j = 0; 407 int j = 0;
408 enum codec_command_action action;
409 intptr_t param; 409 intptr_t param;
410 410
411 memset(&bits, 0, sizeof(bits)); 411 memset(&bits, 0, sizeof(bits));
@@ -416,6 +416,10 @@ enum codec_status codec_run(void)
416 goto exit; 416 goto exit;
417 } 417 }
418 418
419 action = CODEC_ACTION_NULL;
420 param = ci->id3->elapsed;
421 strtoffset = ci->id3->offset;
422
419 ci->seek_buffer(0); 423 ci->seek_buffer(0);
420 ci->set_elapsed(0); 424 ci->set_elapsed(0);
421 425
@@ -425,29 +429,39 @@ enum codec_status codec_run(void)
425 429
426 codec_set_replaygain(ci->id3); 430 codec_set_replaygain(ci->id3);
427 431
428 eof = 0; 432 if (!strtoffset && param) {
429 while (!eof) { 433 action = CODEC_ACTION_SEEK_TIME;
430 enum codec_command_action action = ci->get_command(&param); 434 }
431 435
432 if (action == CODEC_ACTION_HALT) 436 goto next_page;
433 break; 437
434 438 while (1) {
435 /*seek (seeks to the page before the position) */ 439 if (action == CODEC_ACTION_NULL)
436 if (action == CODEC_ACTION_SEEK_TIME) { 440 action = ci->get_command(&param);
437 if(samplerate!=0&&packet_count>1){ 441
438 LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n", 442 if (action != CODEC_ACTION_NULL) {
439 ((spx_int64_t)param/1000) * 443 if (action == CODEC_ACTION_HALT)
440 (spx_int64_t)samplerate, 444 break;
441 page_granule, (long)param, 445
442 (page_granule/samplerate)*1000, samplerate); 446 /*seek (seeks to the page before the position) */
443 447 if (action == CODEC_ACTION_SEEK_TIME) {
444 speex_seek_page_granule(((spx_int64_t)param/1000) * 448 if(samplerate!=0&&packet_count>1){
445 (spx_int64_t)samplerate, 449 LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n",
446 page_granule, &oy, headerssize); 450 ((spx_int64_t)param/1000) *
451 (spx_int64_t)samplerate,
452 page_granule, (long)param,
453 (page_granule/samplerate)*1000, samplerate);
454
455 speex_seek_page_granule(((spx_int64_t)param/1000) *
456 (spx_int64_t)samplerate,
457 page_granule, &oy, headerssize);
458 }
459
460 ci->set_elapsed(param);
461 ci->seek_complete();
447 } 462 }
448 463
449 ci->set_elapsed(param); 464 action = CODEC_ACTION_NULL;
450 ci->seek_complete();
451 } 465 }
452 466
453next_page: 467next_page:
diff --git a/lib/rbcodec/codecs/tta.c b/lib/rbcodec/codecs/tta.c
index 564496a14e..b7660a920f 100644
--- a/lib/rbcodec/codecs/tta.c
+++ b/lib/rbcodec/codecs/tta.c
@@ -82,10 +82,20 @@ enum codec_status codec_run(void)
82 decodedsamples = 0; 82 decodedsamples = 0;
83 endofstream = 0; 83 endofstream = 0;
84 84
85 if (ci->id3->offset > 0) 85 if (ci->id3->offset || ci->id3->elapsed)
86 { 86 {
87 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 87 /* Need to save offset for later use (cleared indirectly by
88 new_pos = set_position(ci->id3->offset, TTA_SEEK_POS); 88 advance_buffer) */
89 unsigned int pos = ci->id3->offset;
90 enum tta_seek_type type = TTA_SEEK_POS;
91
92 if (!pos) {
93 pos = ci->id3->elapsed / SEEK_STEP;
94 type = TTA_SEEK_TIME;
95 }
96
97 new_pos = set_position(pos, type);
98
89 if (new_pos >= 0) 99 if (new_pos >= 0)
90 decodedsamples = new_pos; 100 decodedsamples = new_pos;
91 } 101 }
diff --git a/lib/rbcodec/codecs/vgm.c b/lib/rbcodec/codecs/vgm.c
index 9f2f1b9c5e..126305944f 100644
--- a/lib/rbcodec/codecs/vgm.c
+++ b/lib/rbcodec/codecs/vgm.c
@@ -127,7 +127,10 @@ enum codec_status codec_run(void)
127 127
128 Vgm_start_track(&vgm_emu); 128 Vgm_start_track(&vgm_emu);
129 129
130 ci->set_elapsed(0); 130 if (ci->id3->elapsed != 0)
131 Track_seek(&vgm_emu, ci->id3->elapsed);
132
133 codec_update_elapsed();
131 codec_update_fade(); 134 codec_update_fade();
132 135
133 /* The main decoder loop */ 136 /* The main decoder loop */
diff --git a/lib/rbcodec/codecs/vorbis.c b/lib/rbcodec/codecs/vorbis.c
index c09d2cea6d..ca9db9b802 100644
--- a/lib/rbcodec/codecs/vorbis.c
+++ b/lib/rbcodec/codecs/vorbis.c
@@ -126,7 +126,6 @@ enum codec_status codec_run(void)
126 long n; 126 long n;
127 int current_section; 127 int current_section;
128 int previous_section; 128 int previous_section;
129 int eof;
130 ogg_int64_t vf_offsets[2]; 129 ogg_int64_t vf_offsets[2];
131 ogg_int64_t vf_dataoffsets; 130 ogg_int64_t vf_dataoffsets;
132 ogg_uint32_t vf_serialnos; 131 ogg_uint32_t vf_serialnos;
@@ -193,16 +192,17 @@ enum codec_status codec_run(void)
193 if (ci->id3->offset) { 192 if (ci->id3->offset) {
194 ci->seek_buffer(ci->id3->offset); 193 ci->seek_buffer(ci->id3->offset);
195 ov_raw_seek(&vf, ci->id3->offset); 194 ov_raw_seek(&vf, ci->id3->offset);
196 ci->set_elapsed(ov_time_tell(&vf));
197 ci->set_offset(ov_raw_tell(&vf)); 195 ci->set_offset(ov_raw_tell(&vf));
198 } 196 }
199 else { 197 else if (ci->id3->elapsed) {
200 ci->set_elapsed(0); 198 ov_time_seek(&vf, ci->id3->elapsed);
201 } 199 }
202 200
201 ci->set_elapsed(ov_time_tell(&vf));
202
203 previous_section = -1; 203 previous_section = -1;
204 eof = 0; 204
205 while (!eof) { 205 while (1) {
206 enum codec_command_action action = ci->get_command(&param); 206 enum codec_command_action action = ci->get_command(&param);
207 207
208 if (action == CODEC_ACTION_HALT) 208 if (action == CODEC_ACTION_HALT)
@@ -230,7 +230,7 @@ enum codec_status codec_run(void)
230 } 230 }
231 231
232 if (n == 0) { 232 if (n == 0) {
233 eof = 1; 233 break;
234 } else if (n < 0) { 234 } else if (n < 0) {
235 DEBUGF("Vorbis: Error decoding frame\n"); 235 DEBUGF("Vorbis: Error decoding frame\n");
236 } else { 236 } else {
diff --git a/lib/rbcodec/codecs/vox.c b/lib/rbcodec/codecs/vox.c
index 66979e2911..d84af24cfc 100644
--- a/lib/rbcodec/codecs/vox.c
+++ b/lib/rbcodec/codecs/vox.c
@@ -73,7 +73,8 @@ enum codec_status codec_run(void)
73 73
74 codec_set_replaygain(ci->id3); 74 codec_set_replaygain(ci->id3);
75 75
76 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 76 /* Need to save resume for later use (cleared indirectly by advance_buffer) */
77 param = ci->id3->elapsed;
77 bytesdone = ci->id3->offset; 78 bytesdone = ci->id3->offset;
78 ci->seek_buffer(0); 79 ci->seek_buffer(0);
79 80
@@ -123,10 +124,21 @@ enum codec_status codec_run(void)
123 ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); 124 ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
124 125
125 /* make sure we're at the correct offset */ 126 /* make sure we're at the correct offset */
126 if (bytesdone > (uint32_t) firstblockposn) { 127 if (bytesdone > (uint32_t) firstblockposn || param) {
128 uint32_t seek_val;
129 int seek_mode;
130
131 if (bytesdone) {
132 seek_val = bytesdone - MIN((uint32_t )firstblockposn, bytesdone);
133 seek_mode = PCM_SEEK_POS;
134 } else {
135 seek_val = param;
136 seek_mode = PCM_SEEK_TIME;
137 }
138
127 /* Round down to previous block */ 139 /* Round down to previous block */
128 struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, 140 struct pcm_pos *newpos = codec->get_seek_pos(seek_val, seek_mode,
129 PCM_SEEK_POS, &read_buffer); 141 &read_buffer);
130 142
131 if (newpos->pos > format.numbytes) { 143 if (newpos->pos > format.numbytes) {
132 return CODEC_OK; 144 return CODEC_OK;
diff --git a/lib/rbcodec/codecs/wav.c b/lib/rbcodec/codecs/wav.c
index cc65ab83b2..3208e2b5b8 100644
--- a/lib/rbcodec/codecs/wav.c
+++ b/lib/rbcodec/codecs/wav.c
@@ -182,7 +182,8 @@ enum codec_status codec_run(void)
182 182
183 codec_set_replaygain(ci->id3); 183 codec_set_replaygain(ci->id3);
184 184
185 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 185 /* Need to save resume for later use (cleared indirectly by advance_buffer) */
186 param = ci->id3->elapsed;
186 bytesdone = ci->id3->offset; 187 bytesdone = ci->id3->offset;
187 188
188 /* get RIFF chunk header */ 189 /* get RIFF chunk header */
@@ -361,10 +362,21 @@ enum codec_status codec_run(void)
361 } 362 }
362 363
363 /* make sure we're at the correct offset */ 364 /* make sure we're at the correct offset */
364 if (bytesdone > (uint32_t) firstblockposn) { 365 if (bytesdone > (uint32_t) firstblockposn || param) {
366 uint32_t seek_val;
367 int seek_mode;
368
369 if (bytesdone) {
370 seek_val = bytesdone - MIN((uint32_t) firstblockposn, bytesdone);
371 seek_mode = PCM_SEEK_POS;
372 } else {
373 seek_val = param;
374 seek_mode = PCM_SEEK_TIME;
375 }
376
365 /* Round down to previous block */ 377 /* Round down to previous block */
366 struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, 378 struct pcm_pos *newpos = codec->get_seek_pos(seek_val, seek_mode,
367 PCM_SEEK_POS, &read_buffer); 379 &read_buffer);
368 380
369 if (newpos->pos > format.numbytes) 381 if (newpos->pos > format.numbytes)
370 return CODEC_OK; 382 return CODEC_OK;
diff --git a/lib/rbcodec/codecs/wav64.c b/lib/rbcodec/codecs/wav64.c
index 9b3b2d38e4..96e605faad 100644
--- a/lib/rbcodec/codecs/wav64.c
+++ b/lib/rbcodec/codecs/wav64.c
@@ -191,6 +191,7 @@ enum codec_status codec_run(void)
191 codec_set_replaygain(ci->id3); 191 codec_set_replaygain(ci->id3);
192 192
193 /* Need to save offset for later use (cleared indirectly by advance_buffer) */ 193 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
194 param = ci->id3->elapsed;
194 bytesdone = ci->id3->offset; 195 bytesdone = ci->id3->offset;
195 196
196 /* get RIFF chunk header */ 197 /* get RIFF chunk header */
@@ -363,10 +364,22 @@ enum codec_status codec_run(void)
363 } 364 }
364 365
365 /* make sure we're at the correct offset */ 366 /* make sure we're at the correct offset */
366 if (bytesdone > (uint32_t) firstblockposn) { 367 if (bytesdone > (uint32_t) firstblockposn || param) {
368 uint32_t seek_val;
369 int seek_mode;
370
371 /* we prefer offset resume */
372 if (bytesdone > (uint32_t) firstblockposn) {
373 seek_val = bytesdone - firstblockposn;
374 seek_mode = PCM_SEEK_POS;
375 } else {
376 seek_val = param;
377 seek_mode = PCM_SEEK_TIME;
378 }
379
367 /* Round down to previous block */ 380 /* Round down to previous block */
368 struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, 381 struct pcm_pos *newpos = codec->get_seek_pos(seek_val, seek_mode,
369 PCM_SEEK_POS, &read_buffer); 382 &read_buffer);
370 383
371 if (newpos->pos > format.numbytes) { 384 if (newpos->pos > format.numbytes) {
372 return CODEC_OK; 385 return CODEC_OK;
diff --git a/lib/rbcodec/codecs/wavpack.c b/lib/rbcodec/codecs/wavpack.c
index c0cdafabac..d9c294397c 100644
--- a/lib/rbcodec/codecs/wavpack.c
+++ b/lib/rbcodec/codecs/wavpack.c
@@ -55,12 +55,16 @@ enum codec_status codec_run(void)
55 int bps; 55 int bps;
56 */ 56 */
57 int nchans, sr_100; 57 int nchans, sr_100;
58 unsigned long offset;
58 intptr_t param; 59 intptr_t param;
59 60
60 if (codec_init()) 61 if (codec_init())
61 return CODEC_ERROR; 62 return CODEC_ERROR;
62 63
63 ci->seek_buffer (ci->id3->offset); 64 param = ci->id3->elapsed;
65 offset = ci->id3->offset;
66
67 ci->seek_buffer (offset);
64 68
65 /* Create a decoder instance */ 69 /* Create a decoder instance */
66 wpc = WavpackOpenFileInput (read_callback, error); 70 wpc = WavpackOpenFileInput (read_callback, error);
@@ -75,7 +79,12 @@ enum codec_status codec_run(void)
75 ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO); 79 ci->configure(DSP_SET_STEREO_MODE, nchans == 2 ? STEREO_INTERLEAVED : STEREO_MONO);
76 sr_100 = ci->id3->frequency / 100; 80 sr_100 = ci->id3->frequency / 100;
77 81
78 ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10); 82 if (!offset && param) {
83 goto resume_start; /* resume by elapsed */
84 }
85 else {
86 ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10);
87 }
79 88
80 /* The main decoder loop */ 89 /* The main decoder loop */
81 90
@@ -87,6 +96,7 @@ enum codec_status codec_run(void)
87 break; 96 break;
88 97
89 if (action == CODEC_ACTION_SEEK_TIME) { 98 if (action == CODEC_ACTION_SEEK_TIME) {
99 resume_start:;
90 int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10; 100 int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10;
91 int n, d, skip; 101 int n, d, skip;
92 102
diff --git a/lib/rbcodec/codecs/wma.c b/lib/rbcodec/codecs/wma.c
index 9039f81429..9a5e0c71fa 100755
--- a/lib/rbcodec/codecs/wma.c
+++ b/lib/rbcodec/codecs/wma.c
@@ -52,13 +52,16 @@ enum codec_status codec_run(void)
52 int audiobufsize; 52 int audiobufsize;
53 int packetlength = 0; 53 int packetlength = 0;
54 int errcount = 0; 54 int errcount = 0;
55 enum codec_command_action action;
55 intptr_t param; 56 intptr_t param;
56 57
57 /* Remember the resume position - when the codec is opened, the 58 /* Remember the resume position - when the codec is opened, the
58 playback engine will reset it. */ 59 playback engine will reset it. */
60 elapsedtime = ci->id3->elapsed;
59 resume_offset = ci->id3->offset; 61 resume_offset = ci->id3->offset;
60 62
61restart_track: 63restart_track:
64 action = CODEC_ACTION_NULL;
62 65
63 /* Proper reset of the decoder context. */ 66 /* Proper reset of the decoder context. */
64 memset(&wmadec, 0, sizeof(wmadec)); 67 memset(&wmadec, 0, sizeof(wmadec));
@@ -78,13 +81,20 @@ restart_track:
78 return CODEC_ERROR; 81 return CODEC_ERROR;
79 } 82 }
80 83
81 if (resume_offset > ci->id3->first_frame_offset) 84 if (resume_offset > ci->id3->first_frame_offset || elapsedtime)
82 { 85 {
83 /* Get start of current packet */ 86 if (resume_offset) {
84 int packet_offset = (resume_offset - ci->id3->first_frame_offset) 87 /* Get start of current packet */
85 % wfx.packet_size; 88 int packet_offset = (resume_offset -
86 ci->seek_buffer(resume_offset - packet_offset); 89 MIN(resume_offset, ci->id3->first_frame_offset))
87 elapsedtime = asf_get_timestamp(&i); 90 % wfx.packet_size;
91 ci->seek_buffer(resume_offset - packet_offset);
92 elapsedtime = asf_get_timestamp(&i);
93 }
94 else {
95 param = elapsedtime;
96 action = CODEC_ACTION_SEEK_TIME;
97 }
88 } 98 }
89 else 99 else
90 { 100 {
@@ -104,7 +114,8 @@ restart_track:
104 /* The main decoding loop */ 114 /* The main decoding loop */
105 while (res >= 0) 115 while (res >= 0)
106 { 116 {
107 enum codec_command_action action = ci->get_command(&param); 117 if (action == CODEC_ACTION_NULL)
118 action = ci->get_command(&param);
108 119
109 if (action != CODEC_ACTION_NULL) { 120 if (action != CODEC_ACTION_NULL) {
110 121
@@ -126,6 +137,7 @@ restart_track:
126 if (param == 0) { 137 if (param == 0) {
127 ci->set_elapsed(0); 138 ci->set_elapsed(0);
128 ci->seek_complete(); 139 ci->seek_complete();
140 elapsedtime = 0;
129 goto restart_track; /* Pretend you never saw this... */ 141 goto restart_track; /* Pretend you never saw this... */
130 } 142 }
131 143
@@ -140,6 +152,8 @@ restart_track:
140 ci->set_elapsed(elapsedtime); 152 ci->set_elapsed(elapsedtime);
141 ci->seek_complete(); 153 ci->seek_complete();
142 } 154 }
155
156 action = CODEC_ACTION_NULL;
143 } 157 }
144 158
145 errcount = 0; 159 errcount = 0;
diff --git a/lib/rbcodec/codecs/wmapro.c b/lib/rbcodec/codecs/wmapro.c
index d99ca1aa9e..f15f36813d 100644
--- a/lib/rbcodec/codecs/wmapro.c
+++ b/lib/rbcodec/codecs/wmapro.c
@@ -55,6 +55,8 @@ enum codec_status codec_run(void)
55 int size; /* Size of the input frame to the decoder */ 55 int size; /* Size of the input frame to the decoder */
56 intptr_t param; 56 intptr_t param;
57 57
58 elapsedtime = ci->id3->elapsed;
59
58restart_track: 60restart_track:
59 if (codec_init()) { 61 if (codec_init()) {
60 LOGF("(WMA PRO) Error: Error initialising codec\n"); 62 LOGF("(WMA PRO) Error: Error initialising codec\n");
@@ -75,11 +77,17 @@ restart_track:
75 return CODEC_ERROR; 77 return CODEC_ERROR;
76 } 78 }
77 79
78 /* Now advance the file position to the first frame */ 80 if (elapsedtime) {
79 ci->seek_buffer(ci->id3->first_frame_offset); 81 elapsedtime = asf_seek(elapsedtime, &wfx);
82 if (elapsedtime < 1)
83 return CODEC_OK;
84 }
85 else {
86 /* Now advance the file position to the first frame */
87 ci->seek_buffer(ci->id3->first_frame_offset);
88 }
80 89
81 elapsedtime = 0; 90 ci->set_elapsed(elapsedtime);
82 ci->set_elapsed(0);
83 91
84 /* The main decoding loop */ 92 /* The main decoding loop */
85 93
@@ -95,6 +103,7 @@ restart_track:
95 if (param == 0) { 103 if (param == 0) {
96 ci->set_elapsed(0); 104 ci->set_elapsed(0);
97 ci->seek_complete(); 105 ci->seek_complete();
106 elapsedtime = 0;
98 goto restart_track; /* Pretend you never saw this... */ 107 goto restart_track; /* Pretend you never saw this... */
99 } 108 }
100 109
diff --git a/lib/rbcodec/codecs/wmavoice.c b/lib/rbcodec/codecs/wmavoice.c
index 214e0737e7..4c89c6dc96 100644
--- a/lib/rbcodec/codecs/wmavoice.c
+++ b/lib/rbcodec/codecs/wmavoice.c
@@ -67,7 +67,6 @@ enum codec_status codec_run(void)
67{ 67{
68 uint32_t elapsedtime; 68 uint32_t elapsedtime;
69 asf_waveformatex_t wfx; /* Holds the stream properties */ 69 asf_waveformatex_t wfx; /* Holds the stream properties */
70 size_t resume_offset;
71 int res; /* Return values from asf_read_packet() and decode_packet() */ 70 int res; /* Return values from asf_read_packet() and decode_packet() */
72 uint8_t* audiobuf; /* Pointer to the payload of one wma pro packet */ 71 uint8_t* audiobuf; /* Pointer to the payload of one wma pro packet */
73 int audiobufsize; /* Payload size */ 72 int audiobufsize; /* Payload size */
@@ -76,8 +75,8 @@ enum codec_status codec_run(void)
76 int pktcnt = 0; /* Count of the packets played */ 75 int pktcnt = 0; /* Count of the packets played */
77 intptr_t param; 76 intptr_t param;
78 77
79 /* Remember the resume position */ 78 elapsedtime = ci->id3->elapsed;
80 resume_offset = ci->id3->offset; 79
81restart_track: 80restart_track:
82 if (codec_init()) { 81 if (codec_init()) {
83 LOGF("(WMA Voice) Error: Error initialising codec\n"); 82 LOGF("(WMA Voice) Error: Error initialising codec\n");
@@ -105,13 +104,17 @@ restart_track:
105 return CODEC_ERROR; 104 return CODEC_ERROR;
106 } 105 }
107 106
108 /* Now advance the file position to the first frame */ 107 if (elapsedtime) {
109 ci->seek_buffer(ci->id3->first_frame_offset); 108 elapsedtime = asf_seek(elapsedtime, &wfx);
110 109 if (elapsedtime < 1)
111 elapsedtime = 0; 110 return CODEC_OK;
112 ci->set_elapsed(0); 111 }
112 else {
113 /* Now advance the file position to the first frame */
114 ci->seek_buffer(ci->id3->first_frame_offset);
115 }
113 116
114 resume_offset = 0; 117 ci->set_elapsed(elapsedtime);
115 118
116 /* The main decoding loop */ 119 /* The main decoding loop */
117 120
@@ -129,6 +132,7 @@ restart_track:
129 if (param == 0) { 132 if (param == 0) {
130 ci->set_elapsed(0); 133 ci->set_elapsed(0);
131 ci->seek_complete(); 134 ci->seek_complete();
135 elapsedtime = 0;
132 goto restart_track; /* Pretend you never saw this... */ 136 goto restart_track; /* Pretend you never saw this... */
133 } 137 }
134 138
@@ -136,7 +140,7 @@ restart_track:
136 if (elapsedtime < 1){ 140 if (elapsedtime < 1){
137 ci->set_elapsed(0); 141 ci->set_elapsed(0);
138 ci->seek_complete(); 142 ci->seek_complete();
139 goto next_track; 143 break;
140 } 144 }
141 145
142 ci->set_elapsed(elapsedtime); 146 ci->set_elapsed(elapsedtime);