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:
parent
ffa8626b0c
commit
023f6b6efd
14 changed files with 236 additions and 242 deletions
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -420,6 +420,7 @@ static void init(void)
|
|||
global_settings.superbass);
|
||||
#endif /* CONFIG_CODEC != SWCODEC */
|
||||
|
||||
if (global_settings.audioscrobbler)
|
||||
scrobbler_init();
|
||||
|
||||
audio_init();
|
||||
|
@ -700,7 +701,10 @@ static void init(void)
|
|||
playlist_init();
|
||||
tree_mem_init();
|
||||
filetype_init();
|
||||
|
||||
if (global_settings.audioscrobbler)
|
||||
scrobbler_init();
|
||||
|
||||
shortcuts_init();
|
||||
|
||||
#if CONFIG_CODEC != SWCODEC
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
54
apps/mpeg.c
54
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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
187
apps/scrobbler.c
187
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),
|
||||
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%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,
|
||||
"%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,
|
||||
(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:"");
|
||||
}
|
||||
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());
|
||||
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();
|
||||
|
||||
pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue