Get rid of some superfluous single-purpose functions in playback.

* Remove explicit tracking of elapsed time of previous track.
* Remove function to obtain auto skip flag.
* Most playback events now carry the extra information instead and
  pass 'struct track_event *' for data.
* Tweak scrobbler to use PLAYBACK_EVENT_TRACK_FINISH, which makes
  it cleaner and removes the struct mp3entry.

Change-Id: I500d2abb4056a32646496efc3617406e36811ec5
This commit is contained in:
Michael Sevakis 2013-07-12 12:06:38 -04:00
parent ffa8626b0c
commit 023f6b6efd
14 changed files with 236 additions and 242 deletions

View file

@ -31,21 +31,29 @@
/** Playback events **/ /** Playback events **/
enum { 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), 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, 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, PLAYBACK_EVENT_CUR_TRACK_READY,
/* Current user track finished */ /* Current user track finished
data = &(struct track_event){} */
PLAYBACK_EVENT_TRACK_FINISH, 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, 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, PLAYBACK_EVENT_TRACK_SKIP,
/* Next track medadata was just loaded */ /* Next track medadata was just loaded
data = &(struct track_event){} */
PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
/* Voice is playing: data = &(bool){true|false} */ /* Voice is playing
data = &(bool){true|false} */
PLAYBACK_EVENT_VOICE_PLAYING, PLAYBACK_EVENT_VOICE_PLAYING,
}; };

View file

@ -1190,7 +1190,7 @@ long gui_wps_show(void)
static void track_changed_callback(void *param) static void track_changed_callback(void *param)
{ {
struct wps_state *state = skin_get_global_state(); 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(); state->nid3 = audio_next_track();
if (state->id3->cuesheet) if (state->id3->cuesheet)
{ {

View file

@ -46,7 +46,7 @@ static const struct dim dim = { .width = 200, .height = 200 };
* notify about track change, and show track info */ * notify about track change, and show track info */
static void track_changed_callback(void *param) 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; JNIEnv e = *env_ptr;
if (id3) if (id3)
{ {
@ -108,7 +108,9 @@ static void track_changed_callback(void *param)
* notify about track finishing */ * notify about track finishing */
static void track_finished_callback(void *param) 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; JNIEnv e = *env_ptr;
e->CallVoidMethod(env_ptr, NotificationManager_instance, e->CallVoidMethod(env_ptr, NotificationManager_instance,
finishNotification); finishNotification);

View file

@ -420,7 +420,8 @@ static void init(void)
global_settings.superbass); global_settings.superbass);
#endif /* CONFIG_CODEC != SWCODEC */ #endif /* CONFIG_CODEC != SWCODEC */
scrobbler_init(); if (global_settings.audioscrobbler)
scrobbler_init();
audio_init(); audio_init();
@ -700,7 +701,10 @@ static void init(void)
playlist_init(); playlist_init();
tree_mem_init(); tree_mem_init();
filetype_init(); filetype_init();
scrobbler_init();
if (global_settings.audioscrobbler)
scrobbler_init();
shortcuts_init(); shortcuts_init();
#if CONFIG_CODEC != SWCODEC #if CONFIG_CODEC != SWCODEC

View file

@ -157,7 +157,7 @@ static int audioscrobbler_callback(int action,const struct menu_item_ex *this_it
scrobbler_init(); scrobbler_init();
if(scrobbler_is_enabled() && !global_settings.audioscrobbler) if(scrobbler_is_enabled() && !global_settings.audioscrobbler)
scrobbler_shutdown(); scrobbler_shutdown(false);
break; break;
} }
return action; return action;

View file

@ -269,8 +269,6 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
{ {
long msg_id = -1; long msg_id = -1;
scrobbler_poweroff();
#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
if(!charger_inserted()) if(!charger_inserted())
#endif #endif
@ -349,6 +347,7 @@ static bool clean_shutdown(void (*callback)(void *), void *parameter)
#if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC #if defined(HAVE_RECORDING) && CONFIG_CODEC == SWCODEC
audio_close_recording(); audio_close_recording();
#endif #endif
scrobbler_shutdown(true);
if(global_settings.talk_menu) if(global_settings.talk_menu)
{ {

View file

@ -129,8 +129,6 @@ static unsigned int current_track_counter = 0;
#ifndef SIMULATOR #ifndef SIMULATOR
static void stop_playing(void); 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_read_idx = 0;
static int track_write_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; 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++) for (i = 0; i < numentries; i++)
{ {
/* Send an event to notify that track has finished. */ /* 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; 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++) 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; 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); send_nid3_event = (track_write_idx == track_read_idx + 1);
track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK; track_write_idx = (track_write_idx+1) & MAX_TRACK_ENTRIES_MASK;
if (send_nid3_event) if (send_nid3_event)
send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, NULL); send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, &track->id3);
debug_tags(); debug_tags();
return track; return track;
} }
@ -1093,17 +1099,11 @@ static int new_file(int steps)
static void stop_playing(void) static void stop_playing(void)
{ {
struct trackdata *track;
/* Stop the current stream */ /* Stop the current stream */
mp3_play_stop(); mp3_play_stop();
playing = false; playing = false;
filling = false; filling = false;
track = get_trackdata(0);
if (track != NULL)
prev_track_elapsed = track->id3.elapsed;
if(mpeg_file >= 0) if(mpeg_file >= 0)
close(mpeg_file); close(mpeg_file);
mpeg_file = -1; mpeg_file = -1;
@ -1112,17 +1112,12 @@ static void stop_playing(void)
reset_mp3_buffer(); reset_mp3_buffer();
} }
static void end_current_track(void) { static void end_current_track(void)
struct trackdata *track; {
play_pending = false; play_pending = false;
playing = false; playing = false;
mp3_play_pause(false); mp3_play_pause(false);
track = get_trackdata(0);
if (track != NULL)
prev_track_elapsed = track->id3.elapsed;
reset_mp3_buffer(); reset_mp3_buffer();
remove_all_tags(); remove_all_tags();
generate_unbuffer_events(); generate_unbuffer_events();
@ -1164,9 +1159,6 @@ static void track_change(void)
{ {
DEBUGF("Track change\n"); DEBUGF("Track change\n");
struct trackdata *track = get_trackdata(0);
prev_track_elapsed = track->id3.elapsed;
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
/* Reset the AVC */ /* Reset the AVC */
sound_set_avc(-1); sound_set_avc(-1);
@ -1177,17 +1169,15 @@ static void track_change(void)
remove_current_tag(); remove_current_tag();
update_playlist(); update_playlist();
if (is_playing) 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++; current_track_counter++;
} }
unsigned long audio_prev_elapsed(void)
{
return prev_track_elapsed;
}
#ifdef DEBUG #ifdef DEBUG
void hexdump(const unsigned char *buf, int len) void hexdump(const unsigned char *buf, int len)
{ {
@ -1229,7 +1219,8 @@ static void start_playback_if_ready(void)
if (play_pending_track_change) if (play_pending_track_change)
{ {
play_pending_track_change = false; 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; play_pending = false;
} }
@ -2828,11 +2819,6 @@ void audio_play(long offset)
void audio_stop(void) void audio_stop(void)
{ {
#ifndef SIMULATOR #ifndef SIMULATOR
if (playing)
{
struct trackdata *track = get_trackdata(0);
prev_track_elapsed = track->id3.elapsed;
}
mpeg_stop_done = false; mpeg_stop_done = false;
queue_post(&mpeg_queue, MPEG_STOP, 0); queue_post(&mpeg_queue, MPEG_STOP, 0);
while(!mpeg_stop_done) while(!mpeg_stop_done)

View file

@ -155,13 +155,6 @@ static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */
/* Peeking functions can yield and mess us up */ /* Peeking functions can yield and mess us up */
static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/ 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 **/ /** For album art support **/
#define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT
#ifdef HAVE_ALBUMART #ifdef HAVE_ALBUMART
@ -296,9 +289,8 @@ enum track_skip_type
would work as expected */ would work as expected */
/* Used to indicate status for the events. Must be separate to satisfy all /* 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 clients so the correct metadata is read when sending the change events. */
and also so that it is read correctly outside the events. */ static unsigned int track_event_flags = TEF_NONE; /* (A, O-) */
static bool automatic_skip = false; /* (A, O-) */
/* Pending manual track skip offset */ /* Pending manual track skip offset */
static int skip_offset = 0; /* (A, O) */ 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 */ /* Announce the end of playing the current track */
static void audio_playlist_track_finish(void) static void audio_playlist_track_finish(void)
{ {
@ -1066,12 +1068,8 @@ static void audio_playlist_track_finish(void)
if (id3) if (id3)
{ {
send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); send_track_event(PLAYBACK_EVENT_TRACK_FINISH,
prev_track_elapsed = id3->elapsed; track_event_flags, id3);
}
else
{
prev_track_elapsed = 0;
} }
} }
@ -1081,7 +1079,10 @@ static void audio_playlist_track_change(void)
struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3));
if (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(); 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) static void audio_update_and_announce_next_track(const struct mp3entry *id3_next)
{ {
id3_write_locked(NEXTTRACK_ID3, id3_next); id3_write_locked(NEXTTRACK_ID3, id3_next);
send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, send_track_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE,
id3_get(NEXTTRACK_ID3)); 0, id3_get(NEXTTRACK_ID3));
} }
/* Bring the user current mp3entry up to date and set a new offset for the /* 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; bool resume = !auto_skip;
/* Send the "buffer" event to obtain the resume position for the codec */ /* 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) if (!resume)
{ {
@ -1497,7 +1498,7 @@ static bool audio_start_codec(bool auto_skip)
#endif #endif
{ {
/* Send the "buffer" event now */ /* 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); 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 /* Send only when the track handles could not all be opened ahead of
time for the user's current track - otherwise everything is ready time for the user's current track - otherwise everything is ready
by the time PLAYBACK_EVENT_TRACK_CHANGE is sent */ 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 #ifdef HAVE_CODEC_BUFFERING
@ -2157,7 +2159,7 @@ static void audio_on_finish_load_track(int id3_hid)
buf_read_cuesheet(info->cuesheet_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) if (is_user_current)
{ {
@ -2356,7 +2358,7 @@ static void audio_on_codec_complete(int status)
int trackstat = LOAD_TRACK_OK; int trackstat = LOAD_TRACK_OK;
automatic_skip = true; track_event_flags = TEF_AUTO_SKIP;
skip_pending = TRACK_SKIP_AUTO; skip_pending = TRACK_SKIP_AUTO;
/* Does this track have an entry allocated? */ /* 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); halt_decoding_track(true);
automatic_skip = false; track_event_flags = TEF_NONE;
ff_rw_mode = false; ff_rw_mode = false;
if (flags & AUDIO_START_RESTART) if (flags & AUDIO_START_RESTART)
@ -2595,7 +2597,7 @@ static void audio_stop_playback(void)
audio_playlist_track_finish(); audio_playlist_track_finish();
skip_pending = TRACK_SKIP_NONE; skip_pending = TRACK_SKIP_NONE;
automatic_skip = false; track_event_flags = TEF_NONE;
/* Close all tracks and mark them NULL */ /* Close all tracks and mark them NULL */
remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback); remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback);
@ -2667,7 +2669,7 @@ static void audio_on_skip(void)
ff_rw_mode = false; ff_rw_mode = false;
/* Manual skip */ /* Manual skip */
automatic_skip = false; track_event_flags = TEF_NONE;
/* If there was an auto skip in progress, there will be residual /* If there was an auto skip in progress, there will be residual
advancement of the playlist and/or track list so compensation will be 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; ff_rw_mode = false;
/* Manual skip */ /* Manual skip */
automatic_skip = false; track_event_flags = TEF_NONE;
audio_playlist_track_finish(); 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 *id3 = id3_get(PLAYING_ID3);
struct mp3entry *ci_id3 = id3_get(CODEC_ID3); struct mp3entry *ci_id3 = id3_get(CODEC_ID3);
automatic_skip = false; track_event_flags = TEF_NONE;
/* Send event before clobbering the time */ /* Send event before clobbering the time if rewinding. */
/* 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? */
if (time == 0) 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; id3->elapsed = time;
queue_reply(&audio_queue, 1); queue_reply(&audio_queue, 1);
@ -3662,14 +3664,6 @@ void playback_release_aa_slot(int slot)
} }
#endif /* HAVE_ALBUMART */ #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 /* Would normally calculate byte offset from an elapsed time but is not
used on SWCODEC */ used on SWCODEC */
int audio_get_file_pos(void) int audio_get_file_pos(void)
@ -3677,12 +3671,6 @@ int audio_get_file_pos(void)
return 0; 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 */ /* Return total file buffer length after accounting for the talk buf */
size_t audio_get_filebuflen(void) size_t audio_get_filebuflen(void)
{ {

View file

@ -88,10 +88,6 @@ enum
bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */
size_t audio_get_filebuflen(void); 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); unsigned int playback_status(void);
#endif /* _PLAYBACK_H */ #endif /* _PLAYBACK_H */

View file

@ -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 char current_track_path[MAX_PATH];
static void rootmenu_track_changed_callback(void* param) 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); strlcpy(current_track_path, id3->path, MAX_PATH);
} }
static int browser(void* param) static int browser(void* param)

View file

@ -52,50 +52,40 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
/* longest entry I've had is 323, add a safety margin */ /* longest entry I've had is 323, add a safety margin */
#define SCROBBLER_CACHE_LEN 512 #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 bool scrobbler_initialised = false;
static int scrobbler_cache = 0;
static int cache_pos = 0;
static bool pending = false;
#if CONFIG_RTC #if CONFIG_RTC
static time_t timestamp; static time_t timestamp;
#else #define BASE_FILENAME ".scrobbler.log"
static unsigned long timestamp; #define HDR_STR_TIMELESS
#endif #define get_timestamp() ((long)timestamp)
#define record_timestamp() ((void)(timestamp = mktime(get_time())))
/* Crude work-around for Archos Sims - return a set amount */ #else /* !CONFIG_RTC */
#if (CONFIG_CODEC != SWCODEC) && (CONFIG_PLATFORM & PLATFORM_HOSTED) #define HDR_STR_TIMELESS " Timeless"
unsigned long audio_prev_elapsed(void) #define BASE_FILENAME ".scrobbler-timeless.log"
{ #define get_timestamp() (0l)
return 120000; #define record_timestamp() ({})
} #endif /* CONFIG_RTC */
#endif
static void get_scrobbler_filename(char *path, size_t size) static void get_scrobbler_filename(char *path, size_t size)
{ {
int used; 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 */ /* Get location of USB mass storage area */
#ifdef APPLICATION #ifdef APPLICATION
#if (CONFIG_PLATFORM & PLATFORM_MAEMO) #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) #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) #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 */ #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) */ #endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */
#else #else
used = snprintf(path, size, "/%s", base_filename); used = snprintf(path, size, "/%s", BASE_FILENAME);
#endif #endif
if (used >= (int)size) if (used >= (int)size)
@ -121,12 +111,9 @@ static void write_cache(void)
if(fd >= 0) if(fd >= 0)
{ {
fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n" fdprintf(fd, "#AUDIOSCROBBLER/" SCROBBLER_VERSION "\n"
"#TZ/UNKNOWN\n" "#TZ/UNKNOWN\n" "#CLIENT/Rockbox "
#if CONFIG_RTC TARGET_NAME SCROBBLER_REVISION
"#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION "\n"); HDR_STR_TIMELESS "\n");
#else
"#CLIENT/Rockbox " TARGET_NAME SCROBBLER_REVISION " Timeless\n");
#endif
close(fd); close(fd);
} }
@ -170,51 +157,43 @@ static void scrobbler_flush_callback(void *data)
write_cache(); 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 ) if ( cache_pos >= SCROBBLER_MAX_CACHE )
write_cache(); write_cache();
int ret;
char rating = 'S'; /* Skipped */ char rating = 'S'; /* Skipped */
char* scrobbler_buf = core_get_data(scrobbler_cache); char* scrobbler_buf = core_get_data(scrobbler_cache);
logf("SCROBBLER: add_to_cache[%d]", cache_pos); logf("SCROBBLER: add_to_cache[%d]", cache_pos);
if ( play_length > (scrobbler_entry.length/2) ) if (id->elapsed > id->length / 2)
rating = 'L'; /* Listened */ rating = 'L'; /* Listened */
if (scrobbler_entry.tracknum > 0) char tracknum[11] = { "" };
{
ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), if (id->tracknum > 0)
SCROBBLER_CACHE_LEN, snprintf(tracknum, sizeof (tracknum), "%d", id->tracknum);
"%s\t%s\t%s\t%d\t%d\t%c\t%ld\t%s\n",
scrobbler_entry.artist, int ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos),
scrobbler_entry.album?scrobbler_entry.album:"", SCROBBLER_CACHE_LEN,
scrobbler_entry.title, "%s\t%s\t%s\t%s\t%d\t%c\t%ld\t%s\n",
scrobbler_entry.tracknum, id->artist,
(int)scrobbler_entry.length/1000, id->album ?: "",
rating, id->title,
(long)timestamp, tracknum,
scrobbler_entry.mb_track_id?scrobbler_entry.mb_track_id:""); (int)(id->length / 1000),
} else { rating,
ret = snprintf(scrobbler_buf+(SCROBBLER_CACHE_LEN*cache_pos), get_timestamp(),
SCROBBLER_CACHE_LEN, id->mb_track_id ?: "");
"%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:"");
}
if ( ret >= SCROBBLER_CACHE_LEN ) if ( ret >= SCROBBLER_CACHE_LEN )
{ {
logf("SCROBBLER: entry too long:"); logf("SCROBBLER: entry too long:");
logf("SCROBBLER: %s", scrobbler_entry.path); logf("SCROBBLER: %s", id->path);
} else { }
else
{
cache_pos++; cache_pos++;
register_storage_idle_func(scrobbler_flush_callback); 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) static void scrobbler_change_event(void *data)
{ {
struct mp3entry *id = (struct mp3entry*)data; struct mp3entry *id = ((struct track_event *)data)->id3;
/* add entry using the previous scrobbler_entry and timestamp */
if (pending)
add_to_cache(audio_prev_elapsed());
/* check if track was resumed > %50 played /* check if track was resumed > %50 played
check for blank artist or track name */ check for blank artist or track name */
if ((id->elapsed > (id->length/2)) || if (id->elapsed > id->length / 2 || !id->artist || !id->title)
(!id->artist ) || (!id->title ) )
{ {
pending = false; pending = false;
logf("SCROBBLER: skipping file %s", id->path); logf("SCROBBLER: skipping file %s", id->path);
@ -239,81 +214,85 @@ static void scrobbler_change_event(void *data)
else else
{ {
logf("SCROBBLER: add pending"); logf("SCROBBLER: add pending");
copy_mp3entry(&scrobbler_entry, id); record_timestamp();
#if CONFIG_RTC
timestamp = mktime(get_time());
#else
timestamp = 0;
#endif
pending = true; 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) int scrobbler_init(void)
{ {
logf("SCROBBLER: init %d", global_settings.audioscrobbler); if (scrobbler_initialised)
return 1;
if(!global_settings.audioscrobbler) scrobbler_cache = core_alloc("scrobbler",
return -1; SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
scrobbler_cache = core_alloc("scrobbler", SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
if (scrobbler_cache <= 0) if (scrobbler_cache <= 0)
{ {
logf("SCROOBLER: OOM"); logf("SCROOBLER: OOM");
return -1; return -1;
} }
add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, scrobbler_change_event);
cache_pos = 0; cache_pos = 0;
pending = false; pending = false;
scrobbler_initialised = true; 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; return 1;
} }
static void scrobbler_flush_cache(void) static void scrobbler_flush_cache(void)
{ {
if (scrobbler_initialised)
{
/* Add any pending entries to the cache */ /* Add any pending entries to the cache */
if(pending) if (pending)
add_to_cache(audio_prev_elapsed()); {
/* Write the cache to disk if needed */
if (cache_pos)
write_cache();
pending = false; 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(); 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 */ /* get rid of the buffer */
core_free(scrobbler_cache); core_free(scrobbler_cache);
scrobbler_cache = 0; scrobbler_cache = 0;
} }
}
void scrobbler_poweroff(void) scrobbler_initialised = false;
{
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;
}
} }
bool scrobbler_is_enabled(void) bool scrobbler_is_enabled(void)

View file

@ -23,8 +23,7 @@
#define __SCROBBLER_H__ #define __SCROBBLER_H__
int scrobbler_init(void); int scrobbler_init(void);
void scrobbler_shutdown(void); void scrobbler_shutdown(bool poweroff);
void scrobbler_poweroff(void);
bool scrobbler_is_enabled(void); bool scrobbler_is_enabled(void);
#endif /* __SCROBBLER_H__ */ #endif /* __SCROBBLER_H__ */

View file

@ -794,10 +794,13 @@ static int compare(const void *p1, const void *p2)
static void tagtree_buffer_event(void *data) static void tagtree_buffer_event(void *data)
{ {
struct tagcache_search tcs; 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. */ /* Do not gather data unless proper setting has been enabled. */
if (!global_settings.runtimedb && !global_settings.autoresume_enable) if (!runtimedb && !autoresume)
return; return;
logf("be:%s", id3->path); logf("be:%s", id3->path);
@ -811,7 +814,7 @@ static void tagtree_buffer_event(void *data)
return; return;
} }
if (global_settings.runtimedb) if (runtimedb)
{ {
id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); id3->playcount = tagcache_get_numeric(&tcs, tag_playcount);
if (!id3->rating) if (!id3->rating)
@ -824,7 +827,7 @@ static void tagtree_buffer_event(void *data)
} }
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
if (global_settings.autoresume_enable) if (autoresume)
{ {
/* Load current file resume offset if not already defined (by /* Load current file resume offset if not already defined (by
another resume mechanism) */ another resume mechanism) */
@ -846,18 +849,10 @@ static void tagtree_buffer_event(void *data)
static void tagtree_track_finish_event(void *data) static void tagtree_track_finish_event(void *data)
{ {
long lastplayed; struct track_event *te = (struct track_event *)data;
long tagcache_idx; struct mp3entry *id3 = te->id3;
struct mp3entry *id3 = (struct mp3entry*)data;
/* Do not gather data unless proper setting has been enabled. */ long tagcache_idx = id3->tagcache_idx;
if (!global_settings.runtimedb && !global_settings.autoresume_enable)
{
logf("runtimedb gathering and autoresume not enabled");
return;
}
tagcache_idx=id3->tagcache_idx;
if (!tagcache_idx) if (!tagcache_idx)
{ {
logf("No tagcache index pointer found"); logf("No tagcache index pointer found");
@ -865,26 +860,51 @@ static void tagtree_track_finish_event(void *data)
} }
tagcache_idx--; 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 */ #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 #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; return;
} }
lastplayed = tagcache_increase_serial(); long lastplayed = tagcache_increase_serial();
if (lastplayed < 0) if (lastplayed < 0)
{ {
logf("incorrect tc serial:%ld", lastplayed); logf("incorrect tc serial:%ld", lastplayed);
return; return;
} }
if (global_settings.runtimedb) if (runtimedb)
{ {
long playcount; long playcount;
long playtime; long playtime;
@ -906,10 +926,9 @@ static void tagtree_track_finish_event(void *data)
} }
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
if (global_settings.autoresume_enable) if (autoresume)
{ {
unsigned long offset unsigned long offset = auto_skip ? 0 : id3->offset;
= audio_automatic_skip() ? 0 : id3->offset;
tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset); tagcache_update_numeric(tagcache_idx, tag_lastoffset, offset);

View file

@ -236,12 +236,26 @@ int audio_get_spdif_sample_rate(void);
void audio_spdif_set_monitor(int monitor_spdif); void audio_spdif_set_monitor(int monitor_spdif);
#endif /* HAVE_SPDIF_IN */ #endif /* HAVE_SPDIF_IN */
unsigned long audio_prev_elapsed(void);
#if CONFIG_CODEC != SWCODEC
/***********************************************************************/ /***********************************************************************/
/* audio event handling */ /* 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 */ /* 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 */ /* 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 */ /* (so switch() is okay) and possibly some useful data (depending on the */