summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/appevents.h24
-rw-r--r--apps/gui/wps.c2
-rw-r--r--apps/hosted/android/notification.c6
-rw-r--r--apps/main.c8
-rw-r--r--apps/menus/playback_menu.c2
-rw-r--r--apps/misc.c3
-rw-r--r--apps/mpeg.c54
-rw-r--r--apps/playback.c84
-rw-r--r--apps/playback.h4
-rw-r--r--apps/root_menu.c2
-rw-r--r--apps/scrobbler.c195
-rw-r--r--apps/scrobbler.h3
-rw-r--r--apps/tagtree.c71
-rw-r--r--firmware/export/audio.h20
14 files changed, 236 insertions, 242 deletions
diff --git a/apps/appevents.h b/apps/appevents.h
index 506f00329b..8677dbd522 100644
--- a/apps/appevents.h
+++ b/apps/appevents.h
@@ -31,21 +31,29 @@
31 31
32/** Playback events **/ 32/** Playback events **/
33enum { 33enum {
34 /* Playback is starting from a stopped state */ 34 /* Playback is starting from a stopped state
35 data = NULL */
35 PLAYBACK_EVENT_START_PLAYBACK = (EVENT_CLASS_PLAYBACK|1), 36 PLAYBACK_EVENT_START_PLAYBACK = (EVENT_CLASS_PLAYBACK|1),
36 /* Audio has begun buffering for decoding track (or is already completed) */ 37 /* Audio has begun buffering for decoding track (or is already completed)
38 data = &(struct track_event){} */
37 PLAYBACK_EVENT_TRACK_BUFFER, 39 PLAYBACK_EVENT_TRACK_BUFFER,
38 /* Handles for current user track are ready (other than audio or codec) */ 40 /* Handles for current user track are ready (other than audio or codec)
41 data = &(struct track_event){} */
39 PLAYBACK_EVENT_CUR_TRACK_READY, 42 PLAYBACK_EVENT_CUR_TRACK_READY,
40 /* Current user track finished */ 43 /* Current user track finished
44 data = &(struct track_event){} */
41 PLAYBACK_EVENT_TRACK_FINISH, 45 PLAYBACK_EVENT_TRACK_FINISH,
42 /* A new current user track has begun */ 46 /* A new current user track has begun
47 data = &(struct track_event){} */
43 PLAYBACK_EVENT_TRACK_CHANGE, 48 PLAYBACK_EVENT_TRACK_CHANGE,
44 /* A manual skip is about to be processed */ 49 /* A manual skip is about to be processed
50 data = NULL */
45 PLAYBACK_EVENT_TRACK_SKIP, 51 PLAYBACK_EVENT_TRACK_SKIP,
46 /* Next track medadata was just loaded */ 52 /* Next track medadata was just loaded
53 data = &(struct track_event){} */
47 PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, 54 PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
48 /* Voice is playing: data = &(bool){true|false} */ 55 /* Voice is playing
56 data = &(bool){true|false} */
49 PLAYBACK_EVENT_VOICE_PLAYING, 57 PLAYBACK_EVENT_VOICE_PLAYING,
50}; 58};
51 59
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index a3d7a1bcf1..726df6add4 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -1190,7 +1190,7 @@ long gui_wps_show(void)
1190static void track_changed_callback(void *param) 1190static void track_changed_callback(void *param)
1191{ 1191{
1192 struct wps_state *state = skin_get_global_state(); 1192 struct wps_state *state = skin_get_global_state();
1193 state->id3 = (struct mp3entry*)param; 1193 state->id3 = ((struct track_event *)param)->id3;
1194 state->nid3 = audio_next_track(); 1194 state->nid3 = audio_next_track();
1195 if (state->id3->cuesheet) 1195 if (state->id3->cuesheet)
1196 { 1196 {
diff --git a/apps/hosted/android/notification.c b/apps/hosted/android/notification.c
index 4bb8d0a528..874cd3bcef 100644
--- a/apps/hosted/android/notification.c
+++ b/apps/hosted/android/notification.c
@@ -46,7 +46,7 @@ static const struct dim dim = { .width = 200, .height = 200 };
46 * notify about track change, and show track info */ 46 * notify about track change, and show track info */
47static void track_changed_callback(void *param) 47static void track_changed_callback(void *param)
48{ 48{
49 struct mp3entry* id3 = (struct mp3entry*)param; 49 struct mp3entry* id3 = ((struct track_event *)param)->id3;
50 JNIEnv e = *env_ptr; 50 JNIEnv e = *env_ptr;
51 if (id3) 51 if (id3)
52 { 52 {
@@ -108,7 +108,9 @@ static void track_changed_callback(void *param)
108 * notify about track finishing */ 108 * notify about track finishing */
109static void track_finished_callback(void *param) 109static void track_finished_callback(void *param)
110{ 110{
111 (void)param; 111 if (((struct track_event *)param)->flags & TEF_REWIND)
112 return; /* Not a true track end */
113
112 JNIEnv e = *env_ptr; 114 JNIEnv e = *env_ptr;
113 e->CallVoidMethod(env_ptr, NotificationManager_instance, 115 e->CallVoidMethod(env_ptr, NotificationManager_instance,
114 finishNotification); 116 finishNotification);
diff --git a/apps/main.c b/apps/main.c
index 7f44d89a6a..7333f7dc8d 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -420,7 +420,8 @@ static void init(void)
420 global_settings.superbass); 420 global_settings.superbass);
421#endif /* CONFIG_CODEC != SWCODEC */ 421#endif /* CONFIG_CODEC != SWCODEC */
422 422
423 scrobbler_init(); 423 if (global_settings.audioscrobbler)
424 scrobbler_init();
424 425
425 audio_init(); 426 audio_init();
426 427
@@ -700,7 +701,10 @@ static void init(void)
700 playlist_init(); 701 playlist_init();
701 tree_mem_init(); 702 tree_mem_init();
702 filetype_init(); 703 filetype_init();
703 scrobbler_init(); 704
705 if (global_settings.audioscrobbler)
706 scrobbler_init();
707
704 shortcuts_init(); 708 shortcuts_init();
705 709
706#if CONFIG_CODEC != SWCODEC 710#if CONFIG_CODEC != SWCODEC
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index dbfb44f15d..a82c88e04a 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -157,7 +157,7 @@ static int audioscrobbler_callback(int action,const struct menu_item_ex *this_it
157 scrobbler_init(); 157 scrobbler_init();
158 158
159 if(scrobbler_is_enabled() && !global_settings.audioscrobbler) 159 if(scrobbler_is_enabled() && !global_settings.audioscrobbler)
160 scrobbler_shutdown(); 160 scrobbler_shutdown(false);
161 break; 161 break;
162 } 162 }
163 return action; 163 return action;
diff --git a/apps/misc.c b/apps/misc.c
index 91244f2c39..8dff227bc1 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -269,8 +269,6 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
269{ 269{
270 long msg_id = -1; 270 long msg_id = -1;
271 271
272 scrobbler_poweroff();
273
274#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) 272#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
275 if(!charger_inserted()) 273 if(!charger_inserted())
276#endif 274#endif
@@ -349,6 +347,7 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
349#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC 347#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC
350 audio_close_recording(); 348 audio_close_recording();
351#endif 349#endif
350 scrobbler_shutdown(true);
352 351
353 if(global_settings.talk_menu) 352 if(global_settings.talk_menu)
354 { 353 {
diff --git a/apps/mpeg.c b/apps/mpeg.c
index 5c206c79f1..2783a24085 100644
--- a/apps/mpeg.c
+++ b/apps/mpeg.c
@@ -129,8 +129,6 @@ static unsigned int current_track_counter = 0;
129 129
130#ifndef SIMULATOR 130#ifndef SIMULATOR
131static void stop_playing(void); 131static void stop_playing(void);
132/* Play time of the previous track */
133static unsigned long prev_track_elapsed;
134 132
135static int track_read_idx = 0; 133static int track_read_idx = 0;
136static int track_write_idx = 0; 134static int track_write_idx = 0;
@@ -362,7 +360,15 @@ static bool audio_dispatch_event(unsigned short event, unsigned long data)
362 } 360 }
363 return false; 361 return false;
364} 362}
365#endif 363
364static void send_track_event(unsigned int id, struct mp3entry *id3)
365{
366 struct mp3entry *cur_id3 =
367 &trackdata[track_read_idx & MAX_TRACK_ENTRIES_MASK].id3;
368 unsigned int flags = id3 == cur_id3 ? TEF_CURRENT : 0;
369 send_event(id, &(struct track_event){ .flags = flags, .id3 = id3 });
370}
371#endif /* SIMULATOR */
366 372
367/***********************************************************************/ 373/***********************************************************************/
368 374
@@ -609,7 +615,7 @@ static void generate_unbuffer_events(void)
609 for (i = 0; i < numentries; i++) 615 for (i = 0; i < numentries; i++)
610 { 616 {
611 /* Send an event to notify that track has finished. */ 617 /* Send an event to notify that track has finished. */
612 send_event(PLAYBACK_EVENT_TRACK_FINISH, &trackdata[cur_idx].id3); 618 send_track_event(PLAYBACK_EVENT_TRACK_FINISH, &trackdata[cur_idx].id3);
613 cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK; 619 cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK;
614 } 620 }
615} 621}
@@ -623,7 +629,7 @@ static void generate_postbuffer_events(void)
623 629
624 for (i = 0; i < numentries; i++) 630 for (i = 0; i < numentries; i++)
625 { 631 {
626 send_event(PLAYBACK_EVENT_TRACK_BUFFER, &trackdata[cur_idx].id3); 632 send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, &trackdata[cur_idx].id3);
627 cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK; 633 cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK;
628 } 634 }
629} 635}
@@ -1006,7 +1012,7 @@ static struct trackdata *add_track_to_tag_list(const char *filename)
1006 send_nid3_event = (track_write_idx == track_read_idx + 1); 1012 send_nid3_event = (track_write_idx == track_read_idx + 1);
1007 track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK; 1013 track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK;
1008 if (send_nid3_event) 1014 if (send_nid3_event)
1009 send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, NULL); 1015 send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, &track->id3);
1010 debug_tags(); 1016 debug_tags();
1011 return track; 1017 return track;
1012} 1018}
@@ -1093,17 +1099,11 @@ static int new_file(int steps)
1093 1099
1094static void stop_playing(void) 1100static void stop_playing(void)
1095{ 1101{
1096 struct trackdata *track;
1097
1098 /* Stop the current stream */ 1102 /* Stop the current stream */
1099 mp3_play_stop(); 1103 mp3_play_stop();
1100 playing = false; 1104 playing = false;
1101 filling = false; 1105 filling = false;
1102 1106
1103 track = get_trackdata(0);
1104 if (track != NULL)
1105 prev_track_elapsed = track->id3.elapsed;
1106
1107 if(mpeg_file >= 0) 1107 if(mpeg_file >= 0)
1108 close(mpeg_file); 1108 close(mpeg_file);
1109 mpeg_file = -1; 1109 mpeg_file = -1;
@@ -1112,17 +1112,12 @@ static void stop_playing(void)
1112 reset_mp3_buffer(); 1112 reset_mp3_buffer();
1113} 1113}
1114 1114
1115static void end_current_track(void) { 1115static void end_current_track(void)
1116 struct trackdata *track; 1116{
1117
1118 play_pending = false; 1117 play_pending = false;
1119 playing = false; 1118 playing = false;
1120 mp3_play_pause(false); 1119 mp3_play_pause(false);
1121 1120
1122 track = get_trackdata(0);
1123 if (track != NULL)
1124 prev_track_elapsed = track->id3.elapsed;
1125
1126 reset_mp3_buffer(); 1121 reset_mp3_buffer();
1127 remove_all_tags(); 1122 remove_all_tags();
1128 generate_unbuffer_events(); 1123 generate_unbuffer_events();
@@ -1164,9 +1159,6 @@ static void track_change(void)
1164{ 1159{
1165 DEBUGF("Track change\n"); 1160 DEBUGF("Track change\n");
1166 1161
1167 struct trackdata *track = get_trackdata(0);
1168 prev_track_elapsed = track->id3.elapsed;
1169
1170#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 1162#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1171 /* Reset the AVC */ 1163 /* Reset the AVC */
1172 sound_set_avc(-1); 1164 sound_set_avc(-1);
@@ -1177,17 +1169,15 @@ static void track_change(void)
1177 remove_current_tag(); 1169 remove_current_tag();
1178 update_playlist(); 1170 update_playlist();
1179 if (is_playing) 1171 if (is_playing)
1180 send_event(PLAYBACK_EVENT_TRACK_CHANGE, audio_current_track()); 1172 {
1173 send_track_event(PLAYBACK_EVENT_TRACK_CHANGE,
1174 audio_current_track());
1175 }
1181 } 1176 }
1182 1177
1183 current_track_counter++; 1178 current_track_counter++;
1184} 1179}
1185 1180
1186unsigned long audio_prev_elapsed(void)
1187{
1188 return prev_track_elapsed;
1189}
1190
1191#ifdef DEBUG 1181#ifdef DEBUG
1192void hexdump(const unsigned char *buf, int len) 1182void hexdump(const unsigned char *buf, int len)
1193{ 1183{
@@ -1229,7 +1219,8 @@ static void start_playback_if_ready(void)
1229 if (play_pending_track_change) 1219 if (play_pending_track_change)
1230 { 1220 {
1231 play_pending_track_change = false; 1221 play_pending_track_change = false;
1232 send_event(PLAYBACK_EVENT_TRACK_CHANGE, audio_current_track()); 1222 send_track_event(PLAYBACK_EVENT_TRACK_CHANGE,
1223 audio_current_track());
1233 } 1224 }
1234 play_pending = false; 1225 play_pending = false;
1235 } 1226 }
@@ -2828,11 +2819,6 @@ void audio_play(long offset)
2828void audio_stop(void) 2819void audio_stop(void)
2829{ 2820{
2830#ifndef SIMULATOR 2821#ifndef SIMULATOR
2831 if (playing)
2832 {
2833 struct trackdata *track = get_trackdata(0);
2834 prev_track_elapsed = track->id3.elapsed;
2835 }
2836 mpeg_stop_done = false; 2822 mpeg_stop_done = false;
2837 queue_post(&mpeg_queue, MPEG_STOP, 0); 2823 queue_post(&mpeg_queue, MPEG_STOP, 0);
2838 while(!mpeg_stop_done) 2824 while(!mpeg_stop_done)
diff --git a/apps/playback.c b/apps/playback.c
index 8b498f265e..a1db82eafd 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -155,13 +155,6 @@ static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */
155/* Peeking functions can yield and mess us up */ 155/* Peeking functions can yield and mess us up */
156static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/ 156static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/
157 157
158
159/** For Scrobbler support **/
160
161/* Previous track elapsed time */
162static unsigned long prev_track_elapsed = 0; /* (A,O-) */
163
164
165/** For album art support **/ 158/** For album art support **/
166#define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT 159#define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT
167#ifdef HAVE_ALBUMART 160#ifdef HAVE_ALBUMART
@@ -296,9 +289,8 @@ enum track_skip_type
296 would work as expected */ 289 would work as expected */
297 290
298/* Used to indicate status for the events. Must be separate to satisfy all 291/* Used to indicate status for the events. Must be separate to satisfy all
299 clients so the correct metadata is read when sending the change events 292 clients so the correct metadata is read when sending the change events. */
300 and also so that it is read correctly outside the events. */ 293static unsigned int track_event_flags = TEF_NONE; /* (A, O-) */
301static bool automatic_skip = false; /* (A, O-) */
302 294
303/* Pending manual track skip offset */ 295/* Pending manual track skip offset */
304static int skip_offset = 0; /* (A, O) */ 296static int skip_offset = 0; /* (A, O) */
@@ -1056,6 +1048,16 @@ static void audio_handle_track_load_status(int trackstat)
1056 } 1048 }
1057} 1049}
1058 1050
1051/* Send track events that use a struct track_event for data */
1052static void send_track_event(unsigned int id, unsigned int flags,
1053 struct mp3entry *id3)
1054{
1055 if (id3 == id3_get(PLAYING_ID3))
1056 flags |= TEF_CURRENT;
1057
1058 send_event(id, &(struct track_event){ .flags = flags, .id3 = id3 });
1059}
1060
1059/* Announce the end of playing the current track */ 1061/* Announce the end of playing the current track */
1060static void audio_playlist_track_finish(void) 1062static void audio_playlist_track_finish(void)
1061{ 1063{
@@ -1066,12 +1068,8 @@ static void audio_playlist_track_finish(void)
1066 1068
1067 if (id3) 1069 if (id3)
1068 { 1070 {
1069 send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); 1071 send_track_event(PLAYBACK_EVENT_TRACK_FINISH,
1070 prev_track_elapsed = id3->elapsed; 1072 track_event_flags, id3);
1071 }
1072 else
1073 {
1074 prev_track_elapsed = 0;
1075 } 1073 }
1076} 1074}
1077 1075
@@ -1081,7 +1079,10 @@ static void audio_playlist_track_change(void)
1081 struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); 1079 struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3));
1082 1080
1083 if (id3) 1081 if (id3)
1084 send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3); 1082 {
1083 send_track_event(PLAYBACK_EVENT_TRACK_CHANGE,
1084 track_event_flags, id3);
1085 }
1085 1086
1086 position_key = pcmbuf_get_position_key(); 1087 position_key = pcmbuf_get_position_key();
1087 1088
@@ -1092,8 +1093,8 @@ static void audio_playlist_track_change(void)
1092static void audio_update_and_announce_next_track(const struct mp3entry *id3_next) 1093static void audio_update_and_announce_next_track(const struct mp3entry *id3_next)
1093{ 1094{
1094 id3_write_locked(NEXTTRACK_ID3, id3_next); 1095 id3_write_locked(NEXTTRACK_ID3, id3_next);
1095 send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, 1096 send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
1096 id3_get(NEXTTRACK_ID3)); 1097 0, id3_get(NEXTTRACK_ID3));
1097} 1098}
1098 1099
1099/* Bring the user current mp3entry up to date and set a new offset for the 1100/* Bring the user current mp3entry up to date and set a new offset for the
@@ -1441,7 +1442,7 @@ static bool audio_start_codec(bool auto_skip)
1441 bool resume = !auto_skip; 1442 bool resume = !auto_skip;
1442 1443
1443 /* Send the "buffer" event to obtain the resume position for the codec */ 1444 /* Send the "buffer" event to obtain the resume position for the codec */
1444 send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); 1445 send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, 0, cur_id3);
1445 1446
1446 if (!resume) 1447 if (!resume)
1447 { 1448 {
@@ -1497,7 +1498,7 @@ static bool audio_start_codec(bool auto_skip)
1497#endif 1498#endif
1498 { 1499 {
1499 /* Send the "buffer" event now */ 1500 /* Send the "buffer" event now */
1500 send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); 1501 send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, 0, cur_id3);
1501 } 1502 }
1502 1503
1503 buf_pin_handle(info->id3_hid, false); 1504 buf_pin_handle(info->id3_hid, false);
@@ -1893,7 +1894,8 @@ static int audio_finish_load_track(struct track_info *info)
1893 /* Send only when the track handles could not all be opened ahead of 1894 /* Send only when the track handles could not all be opened ahead of
1894 time for the user's current track - otherwise everything is ready 1895 time for the user's current track - otherwise everything is ready
1895 by the time PLAYBACK_EVENT_TRACK_CHANGE is sent */ 1896 by the time PLAYBACK_EVENT_TRACK_CHANGE is sent */
1896 send_event(PLAYBACK_EVENT_CUR_TRACK_READY, id3_get(PLAYING_ID3)); 1897 send_track_event(PLAYBACK_EVENT_CUR_TRACK_READY, 0,
1898 id3_get(PLAYING_ID3));
1897 } 1899 }
1898 1900
1899#ifdef HAVE_CODEC_BUFFERING 1901#ifdef HAVE_CODEC_BUFFERING
@@ -2157,7 +2159,7 @@ static void audio_on_finish_load_track(int id3_hid)
2157 buf_read_cuesheet(info->cuesheet_hid); 2159 buf_read_cuesheet(info->cuesheet_hid);
2158 } 2160 }
2159 2161
2160 if (audio_start_codec(automatic_skip)) 2162 if (audio_start_codec(track_event_flags & TEF_AUTO_SKIP))
2161 { 2163 {
2162 if (is_user_current) 2164 if (is_user_current)
2163 { 2165 {
@@ -2356,7 +2358,7 @@ static void audio_on_codec_complete(int status)
2356 2358
2357 int trackstat = LOAD_TRACK_OK; 2359 int trackstat = LOAD_TRACK_OK;
2358 2360
2359 automatic_skip = true; 2361 track_event_flags = TEF_AUTO_SKIP;
2360 skip_pending = TRACK_SKIP_AUTO; 2362 skip_pending = TRACK_SKIP_AUTO;
2361 2363
2362 /* Does this track have an entry allocated? */ 2364 /* Does this track have an entry allocated? */
@@ -2471,7 +2473,7 @@ static void audio_start_playback(size_t offset, unsigned int flags)
2471 2473
2472 halt_decoding_track(true); 2474 halt_decoding_track(true);
2473 2475
2474 automatic_skip = false; 2476 track_event_flags = TEF_NONE;
2475 ff_rw_mode = false; 2477 ff_rw_mode = false;
2476 2478
2477 if (flags & AUDIO_START_RESTART) 2479 if (flags & AUDIO_START_RESTART)
@@ -2595,7 +2597,7 @@ static void audio_stop_playback(void)
2595 audio_playlist_track_finish(); 2597 audio_playlist_track_finish();
2596 2598
2597 skip_pending = TRACK_SKIP_NONE; 2599 skip_pending = TRACK_SKIP_NONE;
2598 automatic_skip = false; 2600 track_event_flags = TEF_NONE;
2599 2601
2600 /* Close all tracks and mark them NULL */ 2602 /* Close all tracks and mark them NULL */
2601 remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback); 2603 remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback);
@@ -2667,7 +2669,7 @@ static void audio_on_skip(void)
2667 ff_rw_mode = false; 2669 ff_rw_mode = false;
2668 2670
2669 /* Manual skip */ 2671 /* Manual skip */
2670 automatic_skip = false; 2672 track_event_flags = TEF_NONE;
2671 2673
2672 /* If there was an auto skip in progress, there will be residual 2674 /* If there was an auto skip in progress, there will be residual
2673 advancement of the playlist and/or track list so compensation will be 2675 advancement of the playlist and/or track list so compensation will be
@@ -2755,7 +2757,7 @@ static void audio_on_dir_skip(int direction)
2755 ff_rw_mode = false; 2757 ff_rw_mode = false;
2756 2758
2757 /* Manual skip */ 2759 /* Manual skip */
2758 automatic_skip = false; 2760 track_event_flags = TEF_NONE;
2759 2761
2760 audio_playlist_track_finish(); 2762 audio_playlist_track_finish();
2761 2763
@@ -2820,14 +2822,14 @@ static void audio_on_ff_rewind(long time)
2820 struct mp3entry *id3 = id3_get(PLAYING_ID3); 2822 struct mp3entry *id3 = id3_get(PLAYING_ID3);
2821 struct mp3entry *ci_id3 = id3_get(CODEC_ID3); 2823 struct mp3entry *ci_id3 = id3_get(CODEC_ID3);
2822 2824
2823 automatic_skip = false; 2825 track_event_flags = TEF_NONE;
2824 2826
2825 /* Send event before clobbering the time */ 2827 /* Send event before clobbering the time if rewinding. */
2826 /* FIXME: Nasty, but the tagtree expects this so that rewinding and
2827 then skipping back to this track resumes properly. Something else
2828 should be sent. We're not _really_ finishing the track are we? */
2829 if (time == 0) 2828 if (time == 0)
2830 send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); 2829 {
2830 send_track_event(PLAYBACK_EVENT_TRACK_FINISH,
2831 track_event_flags | TEF_REWIND, id3);
2832 }
2831 2833
2832 id3->elapsed = time; 2834 id3->elapsed = time;
2833 queue_reply(&audio_queue, 1); 2835 queue_reply(&audio_queue, 1);
@@ -3662,14 +3664,6 @@ void playback_release_aa_slot(int slot)
3662} 3664}
3663#endif /* HAVE_ALBUMART */ 3665#endif /* HAVE_ALBUMART */
3664 3666
3665/* Is an automatic skip in progress? If called outside transition callbacks,
3666 indicates the last skip type at the time it was processed and isn't very
3667 meaningful. */
3668bool audio_automatic_skip(void)
3669{
3670 return automatic_skip;
3671}
3672
3673/* Would normally calculate byte offset from an elapsed time but is not 3667/* Would normally calculate byte offset from an elapsed time but is not
3674 used on SWCODEC */ 3668 used on SWCODEC */
3675int audio_get_file_pos(void) 3669int audio_get_file_pos(void)
@@ -3677,12 +3671,6 @@ int audio_get_file_pos(void)
3677 return 0; 3671 return 0;
3678} 3672}
3679 3673
3680/* Return the elapsed time of the track previous to the current */
3681unsigned long audio_prev_elapsed(void)
3682{
3683 return prev_track_elapsed;
3684}
3685
3686/* Return total file buffer length after accounting for the talk buf */ 3674/* Return total file buffer length after accounting for the talk buf */
3687size_t audio_get_filebuflen(void) 3675size_t audio_get_filebuflen(void)
3688{ 3676{
diff --git a/apps/playback.h b/apps/playback.h
index 0a9d22cde2..f56bbfdff0 100644
--- a/apps/playback.h
+++ b/apps/playback.h
@@ -88,10 +88,6 @@ enum
88bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ 88bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
89size_t audio_get_filebuflen(void); 89size_t audio_get_filebuflen(void);
90 90
91/* Automatic transition? Only valid to call during the track change events,
92 otherwise the result is undefined. */
93bool audio_automatic_skip(void);
94
95unsigned int playback_status(void); 91unsigned int playback_status(void);
96 92
97#endif /* _PLAYBACK_H */ 93#endif /* _PLAYBACK_H */
diff --git a/apps/root_menu.c b/apps/root_menu.c
index d03fee35f7..1ffde91eb7 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -89,7 +89,7 @@ static int last_screen = GO_TO_ROOT; /* unfortunatly needed so we can resume
89static char current_track_path[MAX_PATH]; 89static char current_track_path[MAX_PATH];
90static void rootmenu_track_changed_callback(void* param) 90static void rootmenu_track_changed_callback(void* param)
91{ 91{
92 struct mp3entry *id3 = (struct mp3entry *)param; 92 struct mp3entry *id3 = ((struct track_event *)param)->id3;
93 strlcpy(current_track_path, id3->path, MAX_PATH); 93 strlcpy(current_track_path, id3->path, MAX_PATH);
94} 94}
95static int browser(void* param) 95static int browser(void* param)
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
index be60cc15af..efd028327c 100644
--- a/apps/scrobbler.c
+++ b/apps/scrobbler.c
@@ -52,50 +52,40 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
52/* longest entry I've had is 323, add a safety margin */ 52/* longest entry I've had is 323, add a safety margin */
53#define SCROBBLER_CACHE_LEN 512 53#define SCROBBLER_CACHE_LEN 512
54 54
55static int scrobbler_cache;
56
57static int cache_pos;
58static struct mp3entry scrobbler_entry;
59static bool pending = false;
60static bool scrobbler_initialised = false; 55static bool scrobbler_initialised = false;
56static int scrobbler_cache = 0;
57static int cache_pos = 0;
58static bool pending = false;
61#if CONFIG_RTC 59#if CONFIG_RTC
62static time_t timestamp; 60static time_t timestamp;
63#else 61#define BASE_FILENAME ".scrobbler.log"
64static unsigned long timestamp; 62#define HDR_STR_TIMELESS
65#endif 63#define get_timestamp() ((long)timestamp)
66 64#define record_timestamp() ((void)(timestamp = mktime(get_time())))
67/* Crude work-around for Archos Sims - return a set amount */ 65#else /* !CONFIG_RTC */
68#if (CONFIG_CODEC != SWCODEC) && (CONFIG_PLATFORM & PLATFORM_HOSTED) 66#define HDR_STR_TIMELESS " Timeless"
69unsigned long audio_prev_elapsed(void) 67#define BASE_FILENAME ".scrobbler-timeless.log"
70{ 68#define get_timestamp() (0l)
71 return 120000; 69#define record_timestamp() ({})
72} 70#endif /* CONFIG_RTC */
73#endif
74 71
75static void get_scrobbler_filename(char *path, size_t size) 72static void get_scrobbler_filename(char *path, size_t size)
76{ 73{
77 int used; 74 int used;
78
79#if CONFIG_RTC
80 const char *base_filename = ".scrobbler.log";
81#else
82 const char *base_filename = ".scrobbler-timeless.log";
83#endif
84
85/* Get location of USB mass storage area */ 75/* Get location of USB mass storage area */
86#ifdef APPLICATION 76#ifdef APPLICATION
87#if (CONFIG_PLATFORM & PLATFORM_MAEMO) 77#if (CONFIG_PLATFORM & PLATFORM_MAEMO)
88 used = snprintf(path, size, "/home/user/MyDocs/%s", base_filename); 78 used = snprintf(path, size, "/home/user/MyDocs/%s", BASE_FILENAME);
89#elif (CONFIG_PLATFORM & PLATFORM_ANDROID) 79#elif (CONFIG_PLATFORM & PLATFORM_ANDROID)
90 used = snprintf(path, size, "/sdcard/%s", base_filename); 80 used = snprintf(path, size, "/sdcard/%s", BASE_FILENAME);
91#elif defined (SAMSUNG_YPR0) 81#elif defined (SAMSUNG_YPR0)
92 used = snprintf(path, size, "%s/%s", HOME_DIR, base_filename); 82 used = snprintf(path, size, "%s/%s", HOME_DIR, BASE_FILENAME);
93#else /* SDL/unknown RaaA build */ 83#else /* SDL/unknown RaaA build */
94 used = snprintf(path, size, "%s/%s", ROCKBOX_DIR, base_filename); 84 used = snprintf(path, size, "%s/%s", ROCKBOX_DIR, BASE_FILENAME);
95#endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */ 85#endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */
96 86
97#else 87#else
98 used = snprintf(path, size, "/%s", base_filename); 88 used = snprintf(path, size, "/%s", BASE_FILENAME);
99#endif 89#endif
100 90
101 if (used >= (int)size) 91 if (used >= (int)size)
@@ -121,12 +111,9 @@ static void write_cache(void)
121 if(fd >= 0) 111 if(fd >= 0)
122 { 112 {
123 fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n" 113 fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
124 "#TZ/UNKNOWN\n" 114 "#TZ/UNKNOWN\n" "#CLIENT/Rockbox "
125#if CONFIG_RTC 115 TARGET_NAME SCROBBLER_REVISION
126 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n"); 116 HDR_STR_TIMELESS "\n");
127#else
128 "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n");
129#endif
130 117
131 close(fd); 118 close(fd);
132 } 119 }
@@ -170,51 +157,43 @@ static void scrobbler_flush_callback(void *data)
170 write_cache(); 157 write_cache();
171} 158}
172 159
173static void add_to_cache(unsigned long play_length) 160static void add_to_cache(const struct mp3entry *id)
174{ 161{
175 if ( cache_pos >= SCROBBLER_MAX_CACHE ) 162 if ( cache_pos >= SCROBBLER_MAX_CACHE )
176 write_cache(); 163 write_cache();
177 164
178 int ret;
179 char rating = 'S'; /* Skipped */ 165 char rating = 'S'; /* Skipped */
180 char* scrobbler_buf = core_get_data(scrobbler_cache); 166 char* scrobbler_buf = core_get_data(scrobbler_cache);
181 167
182 logf("SCROBBLER: add_to_cache[%d]", cache_pos); 168 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
183 169
184 if ( play_length > (scrobbler_entry.length/2) ) 170 if (id->elapsed > id->length / 2)
185 rating = 'L'; /* Listened */ 171 rating = 'L'; /* Listened */
186 172
187 if (scrobbler_entry.tracknum > 0) 173 char tracknum[11] = { "" };
188 { 174
189 ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), 175 if (id->tracknum > 0)
190 SCROBBLER_CACHE_LEN, 176 snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum);
191 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n", 177
192 scrobbler_entry.artist, 178 int ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos),
193 scrobbler_entry.album?scrobbler_entry.album:"", 179 SCROBBLER_CACHE_LEN,
194 scrobbler_entry.title, 180 "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n",
195 scrobbler_entry.tracknum, 181 id->artist,
196 (int)scrobbler_entry.length/1000, 182 id->album ?: "",
197 rating, 183 id->title,
198 (long)timestamp, 184 tracknum,
199 scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); 185 (int)(id->length / 1000),
200 } else { 186 rating,
201 ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), 187 get_timestamp(),
202 SCROBBLER_CACHE_LEN, 188 id->mb_track_id ?: "");
203 "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n",
204 scrobbler_entry.artist,
205 scrobbler_entry.album?scrobbler_entry.album:"",
206 scrobbler_entry.title,
207 (int)scrobbler_entry.length/1000,
208 rating,
209 (long)timestamp,
210 scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:"");
211 }
212 189
213 if ( ret >= SCROBBLER_CACHE_LEN ) 190 if ( ret >= SCROBBLER_CACHE_LEN )
214 { 191 {
215 logf("SCROBBLER: entry too long:"); 192 logf("SCROBBLER: entry too long:");
216 logf("SCROBBLER: %s", scrobbler_entry.path); 193 logf("SCROBBLER: %s", id->path);
217 } else { 194 }
195 else
196 {
218 cache_pos++; 197 cache_pos++;
219 register_storage_idle_func(scrobbler_flush_callback); 198 register_storage_idle_func(scrobbler_flush_callback);
220 } 199 }
@@ -223,15 +202,11 @@ static void add_to_cache(unsigned long play_length)
223 202
224static void scrobbler_change_event(void *data) 203static void scrobbler_change_event(void *data)
225{ 204{
226 struct mp3entry *id = (struct mp3entry*)data; 205 struct mp3entry *id = ((struct track_event *)data)->id3;
227 /* add entry using the previous scrobbler_entry and timestamp */
228 if (pending)
229 add_to_cache(audio_prev_elapsed());
230 206
231 /* check if track was resumed > %50 played 207 /* check if track was resumed > %50 played
232 check for blank artist or track name */ 208 check for blank artist or track name */
233 if ((id->elapsed > (id->length/2)) || 209 if (id->elapsed > id->length / 2 || !id->artist || !id->title)
234 (!id->artist ) || (!id->title ) )
235 { 210 {
236 pending = false; 211 pending = false;
237 logf("SCROBBLER: skipping file %s", id->path); 212 logf("SCROBBLER: skipping file %s", id->path);
@@ -239,81 +214,85 @@ static void scrobbler_change_event(void *data)
239 else 214 else
240 { 215 {
241 logf("SCROBBLER: add pending"); 216 logf("SCROBBLER: add pending");
242 copy_mp3entry(&scrobbler_entry, id); 217 record_timestamp();
243#if CONFIG_RTC
244 timestamp = mktime(get_time());
245#else
246 timestamp = 0;
247#endif
248 pending = true; 218 pending = true;
249 } 219 }
250} 220}
251 221
222static void scrobbler_finish_event(void *data)
223{
224 struct track_event *te = (struct track_event *)data;
225
226 /* add entry using the currently ending track */
227 if (pending && (te->flags & TEF_CURRENT)
228#if CONFIG_CODEC == SWCODEC
229 && !(te->flags & TEF_REWIND)
230#endif
231 )
232 {
233 pending = false;
234 add_to_cache(te->id3);
235 }
236}
237
252int scrobbler_init(void) 238int scrobbler_init(void)
253{ 239{
254 logf("SCROBBLER: init %d", global_settings.audioscrobbler); 240 if (scrobbler_initialised)
241 return 1;
255 242
256 if(!global_settings.audioscrobbler) 243 scrobbler_cache = core_alloc("scrobbler",
257 return -1; 244 SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
258 245
259 scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
260 if (scrobbler_cache <= 0) 246 if (scrobbler_cache <= 0)
261 { 247 {
262 logf("SCROOBLER: OOM"); 248 logf("SCROOBLER: OOM");
263 return -1; 249 return -1;
264 } 250 }
265 251
266 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
267 cache_pos = 0; 252 cache_pos = 0;
268 pending = false; 253 pending = false;
254
269 scrobbler_initialised = true; 255 scrobbler_initialised = true;
270 256
257 add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
258 add_event(PLAYBACK_EVENT_TRACK_FINISH, false, scrobbler_finish_event);
259
271 return 1; 260 return 1;
272} 261}
273 262
274static void scrobbler_flush_cache(void) 263static void scrobbler_flush_cache(void)
275{ 264{
276 if (scrobbler_initialised)
277 {
278 /* Add any pending entries to the cache */ 265 /* Add any pending entries to the cache */
279 if(pending) 266 if (pending)
280 add_to_cache(audio_prev_elapsed()); 267 {
281
282 /* Write the cache to disk if needed */
283 if (cache_pos)
284 write_cache();
285
286 pending = false; 268 pending = false;
269 if (audio_status())
270 add_to_cache(audio_current_track());
287 } 271 }
272
273 /* Write the cache to disk if needed */
274 if (cache_pos)
275 write_cache();
288} 276}
289 277
290void scrobbler_shutdown(void) 278void scrobbler_shutdown(bool poweroff)
291{ 279{
280 if (!scrobbler_initialised)
281 return;
282
283 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
284 remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event);
285
292 scrobbler_flush_cache(); 286 scrobbler_flush_cache();
293 287
294 if (scrobbler_initialised) 288 if (!poweroff)
295 { 289 {
296 remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event);
297 scrobbler_initialised = false;
298 /* get rid of the buffer */ 290 /* get rid of the buffer */
299 core_free(scrobbler_cache); 291 core_free(scrobbler_cache);
300 scrobbler_cache = 0; 292 scrobbler_cache = 0;
301 } 293 }
302}
303 294
304void scrobbler_poweroff(void) 295 scrobbler_initialised = false;
305{
306 if (scrobbler_initialised && pending)
307 {
308 if ( audio_status() )
309 add_to_cache(audio_current_track()->elapsed);
310 else
311 add_to_cache(audio_prev_elapsed());
312
313 /* scrobbler_shutdown is called later, the cache will be written
314 * make sure the final track isn't added twice when that happens */
315 pending = false;
316 }
317} 296}
318 297
319bool scrobbler_is_enabled(void) 298bool scrobbler_is_enabled(void)
diff --git a/apps/scrobbler.h b/apps/scrobbler.h
index d96b230cb7..a3d1b361df 100644
--- a/apps/scrobbler.h
+++ b/apps/scrobbler.h
@@ -23,8 +23,7 @@
23#define __SCROBBLER_H__ 23#define __SCROBBLER_H__
24 24
25int scrobbler_init(void); 25int scrobbler_init(void);
26void scrobbler_shutdown(void); 26void scrobbler_shutdown(bool poweroff);
27void scrobbler_poweroff(void);
28bool scrobbler_is_enabled(void); 27bool scrobbler_is_enabled(void);
29 28
30#endif /* __SCROBBLER_H__ */ 29#endif /* __SCROBBLER_H__ */
diff --git a/apps/tagtree.c b/apps/tagtree.c
index 6b0c6aa3dd..417b6f28f0 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -794,10 +794,13 @@ static int compare(const void *p1, const void *p2)
794static void tagtree_buffer_event(void *data) 794static void tagtree_buffer_event(void *data)
795{ 795{
796 struct tagcache_search tcs; 796 struct tagcache_search tcs;
797 struct mp3entry *id3 = (struct mp3entry*)data; 797 struct mp3entry *id3 = ((struct track_event *)data)->id3;
798
799 bool runtimedb = global_settings.runtimedb;
800 bool autoresume = global_settings.autoresume_enable;
798 801
799 /* Do not gather data unless proper setting has been enabled. */ 802 /* Do not gather data unless proper setting has been enabled. */
800 if (!global_settings.runtimedb && !global_settings.autoresume_enable) 803 if (!runtimedb && !autoresume)
801 return; 804 return;
802 805
803 logf("be:%s", id3->path); 806 logf("be:%s", id3->path);
@@ -811,7 +814,7 @@ static void tagtree_buffer_event(void *data)
811 return; 814 return;
812 } 815 }
813 816
814 if (global_settings.runtimedb) 817 if (runtimedb)
815 { 818 {
816 id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); 819 id3->playcount = tagcache_get_numeric(&tcs, tag_playcount);
817 if (!id3->rating) 820 if (!id3->rating)
@@ -824,7 +827,7 @@ static void tagtree_buffer_event(void *data)
824 } 827 }
825 828
826 #if CONFIG_CODEC == SWCODEC 829 #if CONFIG_CODEC == SWCODEC
827 if (global_settings.autoresume_enable) 830 if (autoresume)
828 { 831 {
829 /* Load current file resume offset if not already defined (by 832 /* Load current file resume offset if not already defined (by
830 another resume mechanism) */ 833 another resume mechanism) */
@@ -846,18 +849,10 @@ static void tagtree_buffer_event(void *data)
846 849
847static void tagtree_track_finish_event(void *data) 850static void tagtree_track_finish_event(void *data)
848{ 851{
849 long lastplayed; 852 struct track_event *te = (struct track_event *)data;
850 long tagcache_idx; 853 struct mp3entry *id3 = te->id3;
851 struct mp3entry *id3 = (struct mp3entry*)data;
852
853 /* Do not gather data unless proper setting has been enabled. */
854 if (!global_settings.runtimedb && !global_settings.autoresume_enable)
855 {
856 logf("runtimedb gathering and autoresume not enabled");
857 return;
858 }
859 854
860 tagcache_idx=id3->tagcache_idx; 855 long tagcache_idx = id3->tagcache_idx;
861 if (!tagcache_idx) 856 if (!tagcache_idx)
862 { 857 {
863 logf("No tagcache index pointer found"); 858 logf("No tagcache index pointer found");
@@ -865,26 +860,51 @@ static void tagtree_track_finish_event(void *data)
865 } 860 }
866 tagcache_idx--; 861 tagcache_idx--;
867 862
868 /* Don't process unplayed tracks, or tracks interrupted within the
869 first 15 seconds. */
870 if (id3->elapsed == 0
871#if CONFIG_CODEC == SWCODEC /* HWCODEC doesn't have automatic_skip */ 863#if CONFIG_CODEC == SWCODEC /* HWCODEC doesn't have automatic_skip */
872 || (id3->elapsed < 15 * 1000 && !audio_automatic_skip()) 864 bool auto_skip = te->flags & TEF_AUTO_SKIP;
873#endif 865#endif
874 ) 866 bool runtimedb = global_settings.runtimedb;
867 bool autoresume = global_settings.autoresume_enable;
868
869 /* Don't process unplayed tracks, or tracks interrupted within the
870 first 15 seconds but always process autoresume point */
871 if (runtimedb && (id3->elapsed == 0
872#if CONFIG_CODEC == SWCODEC
873 || (id3->elapsed < 15 * 1000 && !auto_skip)
874#endif
875 ))
876 {
877 logf("not db logging unplayed or skipped track");
878 runtimedb = false;
879 }
880
881#if CONFIG_CODEC == SWCODEC
882 /* 3s because that is the threshold the WPS uses to rewind instead
883 of skip backwards */
884 if (autoresume && (id3->elapsed == 0
885 || (id3->elapsed < 3 * 1000 && !auto_skip)))
886 {
887 logf("not logging autoresume");
888 autoresume = false;
889 }
890#endif
891
892 /* Do not gather data unless proper setting has been enabled and at least
893 one is still slated to be recorded */
894 if (!(runtimedb || autoresume))
875 { 895 {
876 logf("not logging unplayed or skipped track"); 896 logf("runtimedb gathering and autoresume not enabled/ignored");
877 return; 897 return;
878 } 898 }
879 899
880 lastplayed = tagcache_increase_serial(); 900 long lastplayed = tagcache_increase_serial();
881 if (lastplayed < 0) 901 if (lastplayed < 0)
882 { 902 {
883 logf("incorrect tc serial:%ld", lastplayed); 903 logf("incorrect tc serial:%ld", lastplayed);
884 return; 904 return;
885 } 905 }
886 906
887 if (global_settings.runtimedb) 907 if (runtimedb)
888 { 908 {
889 long playcount; 909 long playcount;
890 long playtime; 910 long playtime;
@@ -906,10 +926,9 @@ static void tagtree_track_finish_event(void *data)
906 } 926 }
907 927
908#if CONFIG_CODEC == SWCODEC 928#if CONFIG_CODEC == SWCODEC
909 if (global_settings.autoresume_enable) 929 if (autoresume)
910 { 930 {
911 unsigned long offset 931 unsigned long offset = auto_skip ? 0 : id3->offset;
912 = audio_automatic_skip() ? 0 : id3->offset;
913 932
914 tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset); 933 tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset);
915 934
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 24e8e9a0e7..8108f50939 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -236,12 +236,26 @@ int audio_get_spdif_sample_rate(void);
236void audio_spdif_set_monitor(int monitor_spdif); 236void audio_spdif_set_monitor(int monitor_spdif);
237#endif /* HAVE_SPDIF_IN */ 237#endif /* HAVE_SPDIF_IN */
238 238
239unsigned long audio_prev_elapsed(void);
240
241#if CONFIG_CODEC != SWCODEC
242/***********************************************************************/ 239/***********************************************************************/
243/* audio event handling */ 240/* audio event handling */
241enum track_event_flags
242{
243 TEF_NONE = 0x0, /* no flags are set */
244 TEF_CURRENT = 0x1, /* event is for the current track */
245#if CONFIG_CODEC == SWCODEC
246 TEF_AUTO_SKIP = 0x2, /* event is sent in context of auto skip */
247 TEF_REWIND = 0x4, /* interpret as rewind, id3->elapsed is the
248 position before the seek back to 0 */
249#endif /* CONFIG_CODEC == SWCODEC */
250};
244 251
252struct track_event
253{
254 unsigned int flags; /* combo of enum track_event_flags values */
255 struct mp3entry *id3; /* pointer to mp3entry describing track */
256};
257
258#if CONFIG_CODEC != SWCODEC
245/* subscribe to one or more audio event(s) by OR'ing together the desired */ 259/* subscribe to one or more audio event(s) by OR'ing together the desired */
246/* event IDs (defined below); a handler is called with a solitary event ID */ 260/* event IDs (defined below); a handler is called with a solitary event ID */
247/* (so switch() is okay) and possibly some useful data (depending on the */ 261/* (so switch() is okay) and possibly some useful data (depending on the */