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 @@ /** Playback events **/ enum { - /* Playback is starting from a stopped state */ + /* Playback is starting from a stopped state + data = NULL */ PLAYBACK_EVENT_START_PLAYBACK = (EVENT_CLASS_PLAYBACK|1), - /* Audio has begun buffering for decoding track (or is already completed) */ + /* Audio has begun buffering for decoding track (or is already completed) + data = &(struct track_event){} */ PLAYBACK_EVENT_TRACK_BUFFER, - /* Handles for current user track are ready (other than audio or codec) */ + /* Handles for current user track are ready (other than audio or codec) + data = &(struct track_event){} */ PLAYBACK_EVENT_CUR_TRACK_READY, - /* Current user track finished */ + /* Current user track finished + data = &(struct track_event){} */ PLAYBACK_EVENT_TRACK_FINISH, - /* A new current user track has begun */ + /* A new current user track has begun + data = &(struct track_event){} */ PLAYBACK_EVENT_TRACK_CHANGE, - /* A manual skip is about to be processed */ + /* A manual skip is about to be processed + data = NULL */ PLAYBACK_EVENT_TRACK_SKIP, - /* Next track medadata was just loaded */ + /* Next track medadata was just loaded + data = &(struct track_event){} */ PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, - /* Voice is playing: data = &(bool){true|false} */ + /* Voice is playing + data = &(bool){true|false} */ PLAYBACK_EVENT_VOICE_PLAYING, }; 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) static void track_changed_callback(void *param) { struct wps_state *state = skin_get_global_state(); - state->id3 = (struct mp3entry*)param; + state->id3 = ((struct track_event *)param)->id3; state->nid3 = audio_next_track(); if (state->id3->cuesheet) { 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 }; * notify about track change, and show track info */ static void track_changed_callback(void *param) { - struct mp3entry* id3 = (struct mp3entry*)param; + struct mp3entry* id3 = ((struct track_event *)param)->id3; JNIEnv e = *env_ptr; if (id3) { @@ -108,7 +108,9 @@ static void track_changed_callback(void *param) * notify about track finishing */ static void track_finished_callback(void *param) { - (void)param; + if (((struct track_event *)param)->flags & TEF_REWIND) + return; /* Not a true track end */ + JNIEnv e = *env_ptr; e->CallVoidMethod(env_ptr, NotificationManager_instance, 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) global_settings.superbass); #endif /* CONFIG_CODEC != SWCODEC */ - scrobbler_init(); + if (global_settings.audioscrobbler) + scrobbler_init(); audio_init(); @@ -700,7 +701,10 @@ static void init(void) playlist_init(); tree_mem_init(); filetype_init(); - scrobbler_init(); + + if (global_settings.audioscrobbler) + scrobbler_init(); + shortcuts_init(); #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 scrobbler_init(); if(scrobbler_is_enabled() && !global_settings.audioscrobbler) - scrobbler_shutdown(); + scrobbler_shutdown(false); break; } 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) { long msg_id = -1; - scrobbler_poweroff(); - #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) if(!charger_inserted()) #endif @@ -349,6 +347,7 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter) #if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC audio_close_recording(); #endif + scrobbler_shutdown(true); if(global_settings.talk_menu) { 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; #ifndef SIMULATOR static void stop_playing(void); -/* Play time of the previous track */ -static unsigned long prev_track_elapsed; static int track_read_idx = 0; static int track_write_idx = 0; @@ -362,7 +360,15 @@ static bool audio_dispatch_event(unsigned short event, unsigned long data) } return false; } -#endif + +static void send_track_event(unsigned int id, struct mp3entry *id3) +{ + struct mp3entry *cur_id3 = + &trackdata[track_read_idx & MAX_TRACK_ENTRIES_MASK].id3; + unsigned int flags = id3 == cur_id3 ? TEF_CURRENT : 0; + send_event(id, &(struct track_event){ .flags = flags, .id3 = id3 }); +} +#endif /* SIMULATOR */ /***********************************************************************/ @@ -609,7 +615,7 @@ static void generate_unbuffer_events(void) for (i = 0; i < numentries; i++) { /* Send an event to notify that track has finished. */ - send_event(PLAYBACK_EVENT_TRACK_FINISH, &trackdata[cur_idx].id3); + send_track_event(PLAYBACK_EVENT_TRACK_FINISH, &trackdata[cur_idx].id3); cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK; } } @@ -623,7 +629,7 @@ static void generate_postbuffer_events(void) for (i = 0; i < numentries; i++) { - send_event(PLAYBACK_EVENT_TRACK_BUFFER, &trackdata[cur_idx].id3); + send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, &trackdata[cur_idx].id3); cur_idx = (cur_idx + 1) & MAX_TRACK_ENTRIES_MASK; } } @@ -1006,7 +1012,7 @@ static struct trackdata *add_track_to_tag_list(const char *filename) send_nid3_event = (track_write_idx == track_read_idx + 1); track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK; if (send_nid3_event) - send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, NULL); + send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, &track->id3); debug_tags(); return track; } @@ -1093,17 +1099,11 @@ static int new_file(int steps) static void stop_playing(void) { - struct trackdata *track; - /* Stop the current stream */ mp3_play_stop(); playing = false; filling = false; - track = get_trackdata(0); - if (track != NULL) - prev_track_elapsed = track->id3.elapsed; - if(mpeg_file >= 0) close(mpeg_file); mpeg_file = -1; @@ -1112,17 +1112,12 @@ static void stop_playing(void) reset_mp3_buffer(); } -static void end_current_track(void) { - struct trackdata *track; - +static void end_current_track(void) +{ play_pending = false; playing = false; mp3_play_pause(false); - track = get_trackdata(0); - if (track != NULL) - prev_track_elapsed = track->id3.elapsed; - reset_mp3_buffer(); remove_all_tags(); generate_unbuffer_events(); @@ -1164,9 +1159,6 @@ static void track_change(void) { DEBUGF("Track change\n"); - struct trackdata *track = get_trackdata(0); - prev_track_elapsed = track->id3.elapsed; - #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) /* Reset the AVC */ sound_set_avc(-1); @@ -1177,17 +1169,15 @@ static void track_change(void) remove_current_tag(); update_playlist(); if (is_playing) - send_event(PLAYBACK_EVENT_TRACK_CHANGE, audio_current_track()); + { + send_track_event(PLAYBACK_EVENT_TRACK_CHANGE, + audio_current_track()); + } } current_track_counter++; } -unsigned long audio_prev_elapsed(void) -{ - return prev_track_elapsed; -} - #ifdef DEBUG void hexdump(const unsigned char *buf, int len) { @@ -1229,7 +1219,8 @@ static void start_playback_if_ready(void) if (play_pending_track_change) { play_pending_track_change = false; - send_event(PLAYBACK_EVENT_TRACK_CHANGE, audio_current_track()); + send_track_event(PLAYBACK_EVENT_TRACK_CHANGE, + audio_current_track()); } play_pending = false; } @@ -2828,11 +2819,6 @@ void audio_play(long offset) void audio_stop(void) { #ifndef SIMULATOR - if (playing) - { - struct trackdata *track = get_trackdata(0); - prev_track_elapsed = track->id3.elapsed; - } mpeg_stop_done = false; queue_post(&mpeg_queue, MPEG_STOP, 0); 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) */ /* Peeking functions can yield and mess us up */ static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/ - -/** For Scrobbler support **/ - -/* Previous track elapsed time */ -static unsigned long prev_track_elapsed = 0; /* (A,O-) */ - - /** For album art support **/ #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT #ifdef HAVE_ALBUMART @@ -296,9 +289,8 @@ enum track_skip_type would work as expected */ /* Used to indicate status for the events. Must be separate to satisfy all - clients so the correct metadata is read when sending the change events - and also so that it is read correctly outside the events. */ -static bool automatic_skip = false; /* (A, O-) */ + clients so the correct metadata is read when sending the change events. */ +static unsigned int track_event_flags = TEF_NONE; /* (A, O-) */ /* Pending manual track skip offset */ static int skip_offset = 0; /* (A, O) */ @@ -1056,6 +1048,16 @@ static void audio_handle_track_load_status(int trackstat) } } +/* Send track events that use a struct track_event for data */ +static void send_track_event(unsigned int id, unsigned int flags, + struct mp3entry *id3) +{ + if (id3 == id3_get(PLAYING_ID3)) + flags |= TEF_CURRENT; + + send_event(id, &(struct track_event){ .flags = flags, .id3 = id3 }); +} + /* Announce the end of playing the current track */ static void audio_playlist_track_finish(void) { @@ -1066,12 +1068,8 @@ static void audio_playlist_track_finish(void) if (id3) { - send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); - prev_track_elapsed = id3->elapsed; - } - else - { - prev_track_elapsed = 0; + send_track_event(PLAYBACK_EVENT_TRACK_FINISH, + track_event_flags, id3); } } @@ -1081,7 +1079,10 @@ static void audio_playlist_track_change(void) struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); if (id3) - send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3); + { + send_track_event(PLAYBACK_EVENT_TRACK_CHANGE, + track_event_flags, id3); + } position_key = pcmbuf_get_position_key(); @@ -1092,8 +1093,8 @@ static void audio_playlist_track_change(void) static void audio_update_and_announce_next_track(const struct mp3entry *id3_next) { id3_write_locked(NEXTTRACK_ID3, id3_next); - send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, - id3_get(NEXTTRACK_ID3)); + send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, + 0, id3_get(NEXTTRACK_ID3)); } /* 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) bool resume = !auto_skip; /* Send the "buffer" event to obtain the resume position for the codec */ - send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); + send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, 0, cur_id3); if (!resume) { @@ -1497,7 +1498,7 @@ static bool audio_start_codec(bool auto_skip) #endif { /* Send the "buffer" event now */ - send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); + send_track_event(PLAYBACK_EVENT_TRACK_BUFFER, 0, cur_id3); } buf_pin_handle(info->id3_hid, false); @@ -1893,7 +1894,8 @@ static int audio_finish_load_track(struct track_info *info) /* Send only when the track handles could not all be opened ahead of time for the user's current track - otherwise everything is ready by the time PLAYBACK_EVENT_TRACK_CHANGE is sent */ - send_event(PLAYBACK_EVENT_CUR_TRACK_READY, id3_get(PLAYING_ID3)); + send_track_event(PLAYBACK_EVENT_CUR_TRACK_READY, 0, + id3_get(PLAYING_ID3)); } #ifdef HAVE_CODEC_BUFFERING @@ -2157,7 +2159,7 @@ static void audio_on_finish_load_track(int id3_hid) buf_read_cuesheet(info->cuesheet_hid); } - if (audio_start_codec(automatic_skip)) + if (audio_start_codec(track_event_flags & TEF_AUTO_SKIP)) { if (is_user_current) { @@ -2356,7 +2358,7 @@ static void audio_on_codec_complete(int status) int trackstat = LOAD_TRACK_OK; - automatic_skip = true; + track_event_flags = TEF_AUTO_SKIP; skip_pending = TRACK_SKIP_AUTO; /* Does this track have an entry allocated? */ @@ -2471,7 +2473,7 @@ static void audio_start_playback(size_t offset, unsigned int flags) halt_decoding_track(true); - automatic_skip = false; + track_event_flags = TEF_NONE; ff_rw_mode = false; if (flags & AUDIO_START_RESTART) @@ -2595,7 +2597,7 @@ static void audio_stop_playback(void) audio_playlist_track_finish(); skip_pending = TRACK_SKIP_NONE; - automatic_skip = false; + track_event_flags = TEF_NONE; /* Close all tracks and mark them NULL */ remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback); @@ -2667,7 +2669,7 @@ static void audio_on_skip(void) ff_rw_mode = false; /* Manual skip */ - automatic_skip = false; + track_event_flags = TEF_NONE; /* If there was an auto skip in progress, there will be residual advancement of the playlist and/or track list so compensation will be @@ -2755,7 +2757,7 @@ static void audio_on_dir_skip(int direction) ff_rw_mode = false; /* Manual skip */ - automatic_skip = false; + track_event_flags = TEF_NONE; audio_playlist_track_finish(); @@ -2820,14 +2822,14 @@ static void audio_on_ff_rewind(long time) struct mp3entry *id3 = id3_get(PLAYING_ID3); struct mp3entry *ci_id3 = id3_get(CODEC_ID3); - automatic_skip = false; + track_event_flags = TEF_NONE; - /* Send event before clobbering the time */ - /* FIXME: Nasty, but the tagtree expects this so that rewinding and - then skipping back to this track resumes properly. Something else - should be sent. We're not _really_ finishing the track are we? */ + /* Send event before clobbering the time if rewinding. */ if (time == 0) - send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); + { + send_track_event(PLAYBACK_EVENT_TRACK_FINISH, + track_event_flags | TEF_REWIND, id3); + } id3->elapsed = time; queue_reply(&audio_queue, 1); @@ -3662,14 +3664,6 @@ void playback_release_aa_slot(int slot) } #endif /* HAVE_ALBUMART */ -/* Is an automatic skip in progress? If called outside transition callbacks, - indicates the last skip type at the time it was processed and isn't very - meaningful. */ -bool audio_automatic_skip(void) -{ - return automatic_skip; -} - /* Would normally calculate byte offset from an elapsed time but is not used on SWCODEC */ int audio_get_file_pos(void) @@ -3677,12 +3671,6 @@ int audio_get_file_pos(void) return 0; } -/* Return the elapsed time of the track previous to the current */ -unsigned long audio_prev_elapsed(void) -{ - return prev_track_elapsed; -} - /* Return total file buffer length after accounting for the talk buf */ size_t audio_get_filebuflen(void) { 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 bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ size_t audio_get_filebuflen(void); -/* Automatic transition? Only valid to call during the track change events, - otherwise the result is undefined. */ -bool audio_automatic_skip(void); - unsigned int playback_status(void); #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 static char current_track_path[MAX_PATH]; static void rootmenu_track_changed_callback(void* param) { - struct mp3entry *id3 = (struct mp3entry *)param; + struct mp3entry *id3 = ((struct track_event *)param)->id3; strlcpy(current_track_path, id3->path, MAX_PATH); } static 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 /* longest entry I've had is 323, add a safety margin */ #define SCROBBLER_CACHE_LEN 512 -static int scrobbler_cache; - -static int cache_pos; -static struct mp3entry scrobbler_entry; -static bool pending = false; static bool scrobbler_initialised = false; +static int scrobbler_cache = 0; +static int cache_pos = 0; +static bool pending = false; #if CONFIG_RTC static time_t timestamp; -#else -static unsigned long timestamp; -#endif - -/* Crude work-around for Archos Sims - return a set amount */ -#if (CONFIG_CODEC != SWCODEC) && (CONFIG_PLATFORM & PLATFORM_HOSTED) -unsigned long audio_prev_elapsed(void) -{ - return 120000; -} -#endif +#define BASE_FILENAME ".scrobbler.log" +#define HDR_STR_TIMELESS +#define get_timestamp() ((long)timestamp) +#define record_timestamp() ((void)(timestamp = mktime(get_time()))) +#else /* !CONFIG_RTC */ +#define HDR_STR_TIMELESS " Timeless" +#define BASE_FILENAME ".scrobbler-timeless.log" +#define get_timestamp() (0l) +#define record_timestamp() ({}) +#endif /* CONFIG_RTC */ static void get_scrobbler_filename(char *path, size_t size) { int used; - -#if CONFIG_RTC - const char *base_filename = ".scrobbler.log"; -#else - const char *base_filename = ".scrobbler-timeless.log"; -#endif - /* Get location of USB mass storage area */ #ifdef APPLICATION #if (CONFIG_PLATFORM & PLATFORM_MAEMO) - used = snprintf(path, size, "/home/user/MyDocs/%s", base_filename); + used = snprintf(path, size, "/home/user/MyDocs/%s", BASE_FILENAME); #elif (CONFIG_PLATFORM & PLATFORM_ANDROID) - used = snprintf(path, size, "/sdcard/%s", base_filename); + used = snprintf(path, size, "/sdcard/%s", BASE_FILENAME); #elif defined (SAMSUNG_YPR0) - used = snprintf(path, size, "%s/%s", HOME_DIR, base_filename); + used = snprintf(path, size, "%s/%s", HOME_DIR, BASE_FILENAME); #else /* SDL/unknown RaaA build */ - used = snprintf(path, size, "%s/%s", ROCKBOX_DIR, base_filename); + used = snprintf(path, size, "%s/%s", ROCKBOX_DIR, BASE_FILENAME); #endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */ #else - used = snprintf(path, size, "/%s", base_filename); + used = snprintf(path, size, "/%s", BASE_FILENAME); #endif if (used >= (int)size) @@ -121,12 +111,9 @@ static void write_cache(void) if(fd >= 0) { fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n" - "#TZ/UNKNOWN\n" -#if CONFIG_RTC - "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n"); -#else - "#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n"); -#endif + "#TZ/UNKNOWN\n" "#CLIENT/Rockbox " + TARGET_NAME SCROBBLER_REVISION + HDR_STR_TIMELESS "\n"); close(fd); } @@ -170,51 +157,43 @@ static void scrobbler_flush_callback(void *data) write_cache(); } -static void add_to_cache(unsigned long play_length) +static void add_to_cache(const struct mp3entry *id) { if ( cache_pos >= SCROBBLER_MAX_CACHE ) write_cache(); - int ret; char rating = 'S'; /* Skipped */ char* scrobbler_buf = core_get_data(scrobbler_cache); logf("SCROBBLER: add_to_cache[%d]", cache_pos); - if ( play_length > (scrobbler_entry.length/2) ) + if (id->elapsed > id->length / 2) rating = 'L'; /* Listened */ - if (scrobbler_entry.tracknum > 0) - { - ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), - SCROBBLER_CACHE_LEN, - "%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n", - scrobbler_entry.artist, - scrobbler_entry.album?scrobbler_entry.album:"", - scrobbler_entry.title, - scrobbler_entry.tracknum, - (int)scrobbler_entry.length/1000, - rating, - (long)timestamp, - scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); - } else { - ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), - SCROBBLER_CACHE_LEN, - "%s\t%s\t%s\t\t%d\t%c\t%ld\t%s\n", - scrobbler_entry.artist, - scrobbler_entry.album?scrobbler_entry.album:"", - scrobbler_entry.title, - (int)scrobbler_entry.length/1000, - rating, - (long)timestamp, - scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); - } + char tracknum[11] = { "" }; + + if (id->tracknum > 0) + snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum); + + int ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), + SCROBBLER_CACHE_LEN, + "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n", + id->artist, + id->album ?: "", + id->title, + tracknum, + (int)(id->length / 1000), + rating, + get_timestamp(), + id->mb_track_id ?: ""); if ( ret >= SCROBBLER_CACHE_LEN ) { logf("SCROBBLER: entry too long:"); - logf("SCROBBLER: %s", scrobbler_entry.path); - } else { + logf("SCROBBLER: %s", id->path); + } + else + { cache_pos++; register_storage_idle_func(scrobbler_flush_callback); } @@ -223,15 +202,11 @@ static void add_to_cache(unsigned long play_length) static void scrobbler_change_event(void *data) { - struct mp3entry *id = (struct mp3entry*)data; - /* add entry using the previous scrobbler_entry and timestamp */ - if (pending) - add_to_cache(audio_prev_elapsed()); + struct mp3entry *id = ((struct track_event *)data)->id3; /* check if track was resumed > %50 played check for blank artist or track name */ - if ((id->elapsed > (id->length/2)) || - (!id->artist ) || (!id->title ) ) + if (id->elapsed > id->length / 2 || !id->artist || !id->title) { pending = false; logf("SCROBBLER: skipping file %s", id->path); @@ -239,81 +214,85 @@ static void scrobbler_change_event(void *data) else { logf("SCROBBLER: add pending"); - copy_mp3entry(&scrobbler_entry, id); -#if CONFIG_RTC - timestamp = mktime(get_time()); -#else - timestamp = 0; -#endif + record_timestamp(); pending = true; } } +static void scrobbler_finish_event(void *data) +{ + struct track_event *te = (struct track_event *)data; + + /* add entry using the currently ending track */ + if (pending && (te->flags & TEF_CURRENT) +#if CONFIG_CODEC == SWCODEC + && !(te->flags & TEF_REWIND) +#endif + ) + { + pending = false; + add_to_cache(te->id3); + } +} + int scrobbler_init(void) { - logf("SCROBBLER: init %d", global_settings.audioscrobbler); + if (scrobbler_initialised) + return 1; - if(!global_settings.audioscrobbler) - return -1; + scrobbler_cache = core_alloc("scrobbler", + SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); - scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN); if (scrobbler_cache <= 0) { logf("SCROOBLER: OOM"); return -1; } - add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); cache_pos = 0; pending = false; + scrobbler_initialised = true; + add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event); + add_event(PLAYBACK_EVENT_TRACK_FINISH, false, scrobbler_finish_event); + return 1; } static void scrobbler_flush_cache(void) { - if (scrobbler_initialised) - { /* Add any pending entries to the cache */ - if(pending) - add_to_cache(audio_prev_elapsed()); - - /* Write the cache to disk if needed */ - if (cache_pos) - write_cache(); - + if (pending) + { pending = false; + if (audio_status()) + add_to_cache(audio_current_track()); } + + /* Write the cache to disk if needed */ + if (cache_pos) + write_cache(); } -void scrobbler_shutdown(void) +void scrobbler_shutdown(bool poweroff) { + if (!scrobbler_initialised) + return; + + remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); + remove_event(PLAYBACK_EVENT_TRACK_FINISH, scrobbler_finish_event); + scrobbler_flush_cache(); - if (scrobbler_initialised) + if (!poweroff) { - remove_event(PLAYBACK_EVENT_TRACK_CHANGE, scrobbler_change_event); - scrobbler_initialised = false; /* get rid of the buffer */ core_free(scrobbler_cache); scrobbler_cache = 0; } -} -void scrobbler_poweroff(void) -{ - if (scrobbler_initialised && pending) - { - if ( audio_status() ) - add_to_cache(audio_current_track()->elapsed); - else - add_to_cache(audio_prev_elapsed()); - - /* scrobbler_shutdown is called later, the cache will be written - * make sure the final track isn't added twice when that happens */ - pending = false; - } + scrobbler_initialised = false; } bool 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 @@ #define __SCROBBLER_H__ int scrobbler_init(void); -void scrobbler_shutdown(void); -void scrobbler_poweroff(void); +void scrobbler_shutdown(bool poweroff); bool scrobbler_is_enabled(void); #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) static void tagtree_buffer_event(void *data) { struct tagcache_search tcs; - struct mp3entry *id3 = (struct mp3entry*)data; + struct mp3entry *id3 = ((struct track_event *)data)->id3; + + bool runtimedb = global_settings.runtimedb; + bool autoresume = global_settings.autoresume_enable; /* Do not gather data unless proper setting has been enabled. */ - if (!global_settings.runtimedb && !global_settings.autoresume_enable) + if (!runtimedb && !autoresume) return; logf("be:%s", id3->path); @@ -811,7 +814,7 @@ static void tagtree_buffer_event(void *data) return; } - if (global_settings.runtimedb) + if (runtimedb) { id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); if (!id3->rating) @@ -824,7 +827,7 @@ static void tagtree_buffer_event(void *data) } #if CONFIG_CODEC == SWCODEC - if (global_settings.autoresume_enable) + if (autoresume) { /* Load current file resume offset if not already defined (by another resume mechanism) */ @@ -846,18 +849,10 @@ static void tagtree_buffer_event(void *data) static void tagtree_track_finish_event(void *data) { - long lastplayed; - long tagcache_idx; - struct mp3entry *id3 = (struct mp3entry*)data; + struct track_event *te = (struct track_event *)data; + struct mp3entry *id3 = te->id3; - /* Do not gather data unless proper setting has been enabled. */ - if (!global_settings.runtimedb && !global_settings.autoresume_enable) - { - logf("runtimedb gathering and autoresume not enabled"); - return; - } - - tagcache_idx=id3->tagcache_idx; + long tagcache_idx = id3->tagcache_idx; if (!tagcache_idx) { logf("No tagcache index pointer found"); @@ -865,26 +860,51 @@ static void tagtree_track_finish_event(void *data) } tagcache_idx--; - /* Don't process unplayed tracks, or tracks interrupted within the - first 15 seconds. */ - if (id3->elapsed == 0 #if CONFIG_CODEC == SWCODEC /* HWCODEC doesn't have automatic_skip */ - || (id3->elapsed < 15 * 1000 && !audio_automatic_skip()) + bool auto_skip = te->flags & TEF_AUTO_SKIP; #endif - ) + bool runtimedb = global_settings.runtimedb; + bool autoresume = global_settings.autoresume_enable; + + /* Don't process unplayed tracks, or tracks interrupted within the + first 15 seconds but always process autoresume point */ + if (runtimedb && (id3->elapsed == 0 +#if CONFIG_CODEC == SWCODEC + || (id3->elapsed < 15 * 1000 && !auto_skip) +#endif + )) { - logf("not logging unplayed or skipped track"); + logf("not db logging unplayed or skipped track"); + runtimedb = false; + } + +#if CONFIG_CODEC == SWCODEC + /* 3s because that is the threshold the WPS uses to rewind instead + of skip backwards */ + if (autoresume && (id3->elapsed == 0 + || (id3->elapsed < 3 * 1000 && !auto_skip))) + { + logf("not logging autoresume"); + autoresume = false; + } +#endif + + /* Do not gather data unless proper setting has been enabled and at least + one is still slated to be recorded */ + if (!(runtimedb || autoresume)) + { + logf("runtimedb gathering and autoresume not enabled/ignored"); return; } - lastplayed = tagcache_increase_serial(); + long lastplayed = tagcache_increase_serial(); if (lastplayed < 0) { logf("incorrect tc serial:%ld", lastplayed); return; } - if (global_settings.runtimedb) + if (runtimedb) { long playcount; long playtime; @@ -906,10 +926,9 @@ static void tagtree_track_finish_event(void *data) } #if CONFIG_CODEC == SWCODEC - if (global_settings.autoresume_enable) + if (autoresume) { - unsigned long offset - = audio_automatic_skip() ? 0 : id3->offset; + unsigned long offset = auto_skip ? 0 : id3->offset; tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset); 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); void audio_spdif_set_monitor(int monitor_spdif); #endif /* HAVE_SPDIF_IN */ -unsigned long audio_prev_elapsed(void); - -#if CONFIG_CODEC != SWCODEC /***********************************************************************/ /* audio event handling */ +enum track_event_flags +{ + TEF_NONE = 0x0, /* no flags are set */ + TEF_CURRENT = 0x1, /* event is for the current track */ +#if CONFIG_CODEC == SWCODEC + TEF_AUTO_SKIP = 0x2, /* event is sent in context of auto skip */ + TEF_REWIND = 0x4, /* interpret as rewind, id3->elapsed is the + position before the seek back to 0 */ +#endif /* CONFIG_CODEC == SWCODEC */ +}; +struct track_event +{ + unsigned int flags; /* combo of enum track_event_flags values */ + struct mp3entry *id3; /* pointer to mp3entry describing track */ +}; + +#if CONFIG_CODEC != SWCODEC /* subscribe to one or more audio event(s) by OR'ing together the desired */ /* event IDs (defined below); a handler is called with a solitary event ID */ /* (so switch() is okay) and possibly some useful data (depending on the */