From 9d756e2760a0926aa416b22e276c4a5b2685e84e Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Sat, 21 Jul 2007 17:35:19 +0000 Subject: Queue song statistical data to the tagcache system and update entirely in background. Fixes ratings disappearing or not saving in the DB at all. Fixes also UI delay when stopping playback and new statistics are committed to DB. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13955 a1c6a512-1295-4272-9138-f99709370657 --- apps/onplay.c | 7 +- apps/tagcache.c | 183 ++++++++++++++++++++++++++++++++++++++++++-------- apps/tagcache.h | 6 ++ apps/tagtree.c | 49 +++++--------- firmware/export/id3.h | 2 + 5 files changed, 187 insertions(+), 60 deletions(-) diff --git a/apps/onplay.c b/apps/onplay.c index cba03733fd..d7c2504650 100644 --- a/apps/onplay.c +++ b/apps/onplay.c @@ -953,11 +953,14 @@ char *rating_name(int selected_item, void * data, char *buffer) static bool set_rating_inline(void) { struct mp3entry* id3 = audio_current_track(); - if(id3) { - if(id3->rating<10) + if (id3 && id3->tagcache_idx) + { + if (id3->rating<10) id3->rating++; else id3->rating=0; + + tagcache_update_numeric(id3->tagcache_idx, tag_rating, id3->rating); } return false; } diff --git a/apps/tagcache.c b/apps/tagcache.c index f832f1e543..6d738bc5fb 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c @@ -136,8 +136,26 @@ enum tagcache_queue { Q_IMPORT_CHANGELOG, Q_UPDATE, Q_REBUILD, + + /* Internal tagcache command queue. */ + CMD_UPDATE_MASTER_HEADER, + CMD_UPDATE_NUMERIC, +}; + +struct tagcache_command_entry { + long command; + long idx_id; + long tag; + long data; }; +static struct tagcache_command_entry command_queue[TAGCACHE_COMMAND_QUEUE_LENGTH]; +static volatile int command_queue_widx = 0; +static volatile int command_queue_ridx = 0; +static struct mutex command_queue_mutex; +/* Timestamp of the last added event, so we can wait a bit before committing the + * whole queue at once. */ +static long command_queue_timestamp = 0; /* Tag database structures. */ @@ -2737,33 +2755,6 @@ static void free_tempbuf(void) tempbuf_size = 0; } -long tagcache_increase_serial(void) -{ - long old; - - if (!tc_stat.ready) - return -2; - - while (read_lock) - sleep(1); - - old = current_tcmh.serial++; - if (!update_master_header()) - return -1; - - return old; -} - -long tagcache_get_serial(void) -{ - return current_tcmh.serial; -} - -long tagcache_get_commitid(void) -{ - return current_tcmh.commitid; -} - static bool modify_numeric_entry(int masterfd, int idx_id, int tag, long data) { struct index_entry idx; @@ -2783,6 +2774,7 @@ static bool modify_numeric_entry(int masterfd, int idx_id, int tag, long data) return write_index(masterfd, idx_id, &idx); } +#if 0 bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, int tag, long data) { @@ -2796,6 +2788,137 @@ bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, return modify_numeric_entry(tcs->masterfd, tcs->idx_id, tag, data); } +#endif + +#define COMMAND_QUEUE_IS_EMPTY (command_queue_ridx == command_queue_widx) + +static bool command_queue_is_full(void) +{ + int next; + + next = command_queue_widx + 1; + if (next >= TAGCACHE_COMMAND_QUEUE_LENGTH) + next = 0; + + return (next == command_queue_ridx); +} + +void run_command_queue(bool force) +{ + struct master_header myhdr; + int masterfd; + + if (COMMAND_QUEUE_IS_EMPTY) + return; + + if (!force && !command_queue_is_full() + && current_tick - TAGCACHE_COMMAND_QUEUE_COMMIT_DELAY + < command_queue_timestamp) + { + return; + } + + mutex_lock(&command_queue_mutex); + + if ( (masterfd = open_master_fd(&myhdr, true)) < 0) + return; + + while (command_queue_ridx != command_queue_widx) + { + struct tagcache_command_entry *ce = &command_queue[command_queue_ridx]; + + switch (ce->command) + { + case CMD_UPDATE_MASTER_HEADER: + { + close(masterfd); + update_master_header(); + + /* Re-open the masterfd. */ + if ( (masterfd = open_master_fd(&myhdr, true)) < 0) + return; + + break; + } + case CMD_UPDATE_NUMERIC: + { + modify_numeric_entry(masterfd, ce->idx_id, ce->tag, ce->data); + break; + } + } + + if (++command_queue_ridx >= TAGCACHE_COMMAND_QUEUE_LENGTH) + command_queue_ridx = 0; + } + + close(masterfd); + + mutex_unlock(&command_queue_mutex); +} + +static void queue_command(int cmd, long idx_id, int tag, long data) +{ + while (1) + { + int next; + + mutex_lock(&command_queue_mutex); + next = command_queue_widx + 1; + if (next >= TAGCACHE_COMMAND_QUEUE_LENGTH) + next = 0; + + /* Make sure queue is not full. */ + if (next != command_queue_ridx) + { + struct tagcache_command_entry *ce = &command_queue[command_queue_widx]; + + ce->command = cmd; + ce->idx_id = idx_id; + ce->tag = tag; + ce->data = data; + + command_queue_widx = next; + command_queue_timestamp = current_tick; + mutex_unlock(&command_queue_mutex); + break; + } + + /* Queue is full, try again later... */ + mutex_unlock(&command_queue_mutex); + sleep(1); + } +} + +long tagcache_increase_serial(void) +{ + long old; + + if (!tc_stat.ready) + return -2; + + while (read_lock) + sleep(1); + + old = current_tcmh.serial++; + queue_command(CMD_UPDATE_MASTER_HEADER, 0, 0, 0); + + return old; +} + +void tagcache_update_numeric(int idx_id, int tag, long data) +{ + queue_command(CMD_UPDATE_NUMERIC, idx_id, tag, data); +} + +long tagcache_get_serial(void) +{ + return current_tcmh.serial; +} + +long tagcache_get_commitid(void) +{ + return current_tcmh.commitid; +} static bool write_tag(int fd, const char *tagstr, const char *datastr) { @@ -3860,6 +3983,8 @@ static void tagcache_thread(void) while (1) { + run_command_queue(false); + queue_wait_w_tmo(&tagcache_queue, &ev, HZ); switch (ev.id) @@ -3942,6 +4067,9 @@ bool tagcache_prepare_shutdown(void) void tagcache_shutdown(void) { + /* Flush the command queue. */ + run_command_queue(true); + #ifdef HAVE_EEPROM_SETTINGS if (tc_stat.ramcache) tagcache_dumpsave(); @@ -4023,6 +4151,7 @@ void tagcache_init(void) write_lock = read_lock = 0; #ifndef __PCTOOL__ + mutex_init(&command_queue_mutex); queue_init(&tagcache_queue, true); create_thread(tagcache_thread, tagcache_stack, sizeof(tagcache_stack), tagcache_thread_name diff --git a/apps/tagcache.h b/apps/tagcache.h index 70677c602b..3d80c6975f 100644 --- a/apps/tagcache.h +++ b/apps/tagcache.h @@ -63,6 +63,11 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, /* Always strict align entries for best performance and binary compatability. */ #define TAGCACHE_STRICT_ALIGN 1 +/* Max events in the internal tagcache command queue. */ +#define TAGCACHE_COMMAND_QUEUE_LENGTH 32 +/* Idle time before committing events in the command queue. */ +#define TAGCACHE_COMMAND_QUEUE_COMMIT_DELAY HZ*2 + #define TAGCACHE_MAX_FILTERS 4 #define TAGCACHE_MAX_CLAUSES 32 @@ -170,6 +175,7 @@ long tagcache_increase_serial(void); long tagcache_get_serial(void); bool tagcache_import_changelog(void); bool tagcache_create_changelog(struct tagcache_search *tcs); +void tagcache_update_numeric(int idx_id, int tag, long data); bool tagcache_modify_numeric_entry(struct tagcache_search *tcs, int tag, long data); diff --git a/apps/tagtree.c b/apps/tagtree.c index 03673fc653..271f30ffa8 100644 --- a/apps/tagtree.c +++ b/apps/tagtree.c @@ -624,9 +624,14 @@ static void tagtree_buffer_event(struct mp3entry *id3, bool last_track) } id3->playcount = tagcache_get_numeric(&tcs, tag_playcount); - if(!id3->rating) id3->rating = tagcache_get_numeric(&tcs, tag_rating); + if (!id3->rating) + id3->rating = tagcache_get_numeric(&tcs, tag_rating); id3->lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); id3->score = tagcache_get_numeric(&tcs, tag_virt_autoscore) / 10; + id3->playtime = tagcache_get_numeric(&tcs, tag_playtime); + + /* Store our tagcache index pointer. */ + id3->tagcache_idx = tcs.idx_id; tagcache_search_finish(&tcs); } @@ -635,7 +640,6 @@ static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track) { (void)last_track; long playcount; - long rating; long playtime; long lastplayed; @@ -646,55 +650,38 @@ static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track) return; } - /* Don't process unplayed tracks. */ - if (id3->elapsed == 0) + if (!id3->tagcache_idx) { - logf("not logging unplayed track"); + logf("No tagcache index pointer found"); return; } - if (!tagcache_find_index(&tcs, id3->path)) + /* Don't process unplayed tracks. */ + if (id3->elapsed == 0) { - logf("tc stat: not found: %s", id3->path); + logf("not logging unplayed track"); return; } - playcount = tagcache_get_numeric(&tcs, tag_playcount); - playtime = tagcache_get_numeric(&tcs, tag_playtime); - lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed); - - playcount++; - - rating = (long) id3->rating; - + playcount = id3->playcount + 1; lastplayed = tagcache_increase_serial(); if (lastplayed < 0) { logf("incorrect tc serial:%ld", lastplayed); - tagcache_search_finish(&tcs); return; } /* Ignore the last 15s (crossfade etc.) */ - playtime += MIN(id3->length, id3->elapsed + 15 * 1000); + playtime = id3->playtime + MIN(id3->length, id3->elapsed + 15 * 1000); logf("ube:%s", id3->path); - logf("-> %d/%ld/%ld/%ld", last_track, playcount, rating, playtime); + logf("-> %d/%ld/%ld", last_track, playcount, playtime); logf("-> %ld/%ld/%ld", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000)); - /* lastplayed not yet supported. */ - - if (!tagcache_modify_numeric_entry(&tcs, tag_playcount, playcount) - || !tagcache_modify_numeric_entry(&tcs, tag_rating, rating) - || !tagcache_modify_numeric_entry(&tcs, tag_playtime, playtime) - || !tagcache_modify_numeric_entry(&tcs, tag_lastplayed, lastplayed)) - { - logf("tc stat: modify failed!"); - tagcache_search_finish(&tcs); - return; - } - - tagcache_search_finish(&tcs); + /* Queue the updates to the tagcache system. */ + tagcache_update_numeric(id3->tagcache_idx, tag_playcount, playcount); + tagcache_update_numeric(id3->tagcache_idx, tag_playtime, playtime); + tagcache_update_numeric(id3->tagcache_idx, tag_lastplayed, lastplayed); } bool tagtree_export(void) diff --git a/firmware/export/id3.h b/firmware/export/id3.h index 4ce2e9978f..3b69d50075 100644 --- a/firmware/export/id3.h +++ b/firmware/export/id3.h @@ -202,10 +202,12 @@ struct mp3entry { long rundbentryoffset; /* runtime database fields */ + long tagcache_idx; short rating; short score; long playcount; long lastplayed; + long playtime; /* replaygain support */ -- cgit v1.2.3