summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiika Pekkarinen <miipekk@ihme.org>2006-07-15 17:36:25 +0000
committerMiika Pekkarinen <miipekk@ihme.org>2006-07-15 17:36:25 +0000
commit45dfe2a36f03d1ada7036dedb50fb98d7c5421b2 (patch)
tree866a57b677dce40ef5b772e1514a9ec6a35f60ea
parente60cb43a982e03b062bd42dbe260abca299fe14d (diff)
downloadrockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.tar.gz
rockbox-45dfe2a36f03d1ada7036dedb50fb98d7c5421b2.zip
Initial runtimedb support for tagcache. Only for developers,
statistical data will be lost in future until changelogs has been implemented. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10217 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/playback.c20
-rw-r--r--apps/settings_menu.c11
-rw-r--r--apps/tagcache.c264
-rw-r--r--apps/tagcache.h12
-rw-r--r--apps/tagnavi.config19
-rw-r--r--apps/tagtree.c236
6 files changed, 420 insertions, 142 deletions
diff --git a/apps/playback.c b/apps/playback.c
index 4fc0c33921..8ffd5889c5 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -2189,11 +2189,13 @@ void audio_thread(void)
2189 2189
2190 case Q_AUDIO_PLAY: 2190 case Q_AUDIO_PLAY:
2191 logf("starting..."); 2191 logf("starting...");
2192 audio_clear_track_entries(true, false);
2192 audio_play_start((size_t)ev.data); 2193 audio_play_start((size_t)ev.data);
2193 break ; 2194 break ;
2194 2195
2195 case Q_AUDIO_STOP: 2196 case Q_AUDIO_STOP:
2196 logf("audio_stop"); 2197 logf("audio_stop");
2198 audio_clear_track_entries(true, false);
2197 audio_stop_playback(); 2199 audio_stop_playback();
2198 break ; 2200 break ;
2199 2201
@@ -2816,22 +2818,6 @@ void mpeg_id3_options(bool _v1first)
2816} 2818}
2817 2819
2818#ifdef ROCKBOX_HAS_LOGF 2820#ifdef ROCKBOX_HAS_LOGF
2819void test_buffer_event(struct mp3entry *id3, bool last_track)
2820{
2821 (void)id3;
2822 (void)last_track;
2823
2824 logf("be:%d%s", last_track, id3->path);
2825}
2826
2827void test_unbuffer_event(struct mp3entry *id3, bool last_track)
2828{
2829 (void)id3;
2830 (void)last_track;
2831
2832 logf("ube:%d%s", last_track, id3->path);
2833}
2834
2835void test_track_changed_event(struct mp3entry *id3) 2821void test_track_changed_event(struct mp3entry *id3)
2836{ 2822{
2837 (void)id3; 2823 (void)id3;
@@ -2854,8 +2840,6 @@ static void playback_init(void)
2854#endif 2840#endif
2855 2841
2856#ifdef ROCKBOX_HAS_LOGF 2842#ifdef ROCKBOX_HAS_LOGF
2857 audio_set_track_buffer_event(test_buffer_event);
2858 audio_set_track_unbuffer_event(test_unbuffer_event);
2859 audio_set_track_changed_event(test_track_changed_event); 2843 audio_set_track_changed_event(test_track_changed_event);
2860#endif 2844#endif
2861 2845
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index f3cae84743..f00612a180 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -1545,6 +1545,16 @@ static bool tagcache_autoupdate(void)
1545 return rc; 1545 return rc;
1546} 1546}
1547 1547
1548static bool tagcache_runtimedb(void)
1549{
1550 bool rc = set_bool_options(str(LANG_RUNTIMEDB_ACTIVE),
1551 &global_settings.runtimedb,
1552 STR(LANG_ON),
1553 STR(LANG_OFF),
1554 NULL);
1555 return rc;
1556}
1557
1548static bool tagcache_settings_menu(void) 1558static bool tagcache_settings_menu(void)
1549{ 1559{
1550 int m; 1560 int m;
@@ -1557,6 +1567,7 @@ static bool tagcache_settings_menu(void)
1557 { ID2P(LANG_TAGCACHE_AUTOUPDATE), tagcache_autoupdate }, 1567 { ID2P(LANG_TAGCACHE_AUTOUPDATE), tagcache_autoupdate },
1558 { ID2P(LANG_TAGCACHE_FORCE_UPDATE), tagcache_rebuild }, 1568 { ID2P(LANG_TAGCACHE_FORCE_UPDATE), tagcache_rebuild },
1559 { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update }, 1569 { ID2P(LANG_TAGCACHE_UPDATE), tagcache_update },
1570 { ID2P(LANG_RUNTIMEDB_ACTIVE), tagcache_runtimedb },
1560 }; 1571 };
1561 1572
1562 m=menu_init( items, sizeof(items) / sizeof(*items), NULL, 1573 m=menu_init( items, sizeof(items) / sizeof(*items), NULL,
diff --git a/apps/tagcache.c b/apps/tagcache.c
index affdc46a66..a5675850bf 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -58,7 +58,8 @@ static const int sorted_tags[] = { tag_artist, tag_album, tag_genre, tag_compose
58static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_composer }; 58static const int unique_tags[] = { tag_artist, tag_album, tag_genre, tag_composer };
59 59
60/* Numeric tags (we can use these tags with conditional clauses). */ 60/* Numeric tags (we can use these tags with conditional clauses). */
61static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate }; 61static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length, tag_bitrate,
62 tag_playcount, tag_playtime, tag_lastplayed, tag_virt_autoscore };
62 63
63/* Status information of the tagcache. */ 64/* Status information of the tagcache. */
64static struct tagcache_stat stat; 65static struct tagcache_stat stat;
@@ -179,8 +180,8 @@ bool tagcache_is_sorted_tag(int type)
179} 180}
180 181
181#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 182#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
182static struct index_entry *find_entry_ram(const char *filename, 183static long find_entry_ram(const char *filename,
183 const struct dircache_entry *dc) 184 const struct dircache_entry *dc)
184{ 185{
185 static long last_pos = 0; 186 static long last_pos = 0;
186 int counter = 0; 187 int counter = 0;
@@ -188,7 +189,7 @@ static struct index_entry *find_entry_ram(const char *filename,
188 189
189 /* Check if we tagcache is loaded into ram. */ 190 /* Check if we tagcache is loaded into ram. */
190 if (!stat.ramcache) 191 if (!stat.ramcache)
191 return NULL; 192 return -1;
192 193
193 if (dc == NULL) 194 if (dc == NULL)
194 dc = dircache_get_entry_ptr(filename); 195 dc = dircache_get_entry_ptr(filename);
@@ -196,7 +197,7 @@ static struct index_entry *find_entry_ram(const char *filename,
196 if (dc == NULL) 197 if (dc == NULL)
197 { 198 {
198 logf("tagcache: file not found."); 199 logf("tagcache: file not found.");
199 return NULL; 200 return -1;
200 } 201 }
201 202
202 try_again: 203 try_again:
@@ -211,7 +212,7 @@ static struct index_entry *find_entry_ram(const char *filename,
211 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc) 212 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc)
212 { 213 {
213 last_pos = MAX(0, i - 3); 214 last_pos = MAX(0, i - 3);
214 return &hdr->indices[i]; 215 return i;
215 } 216 }
216 217
217 if (++counter == 100) 218 if (++counter == 100)
@@ -227,28 +228,28 @@ static struct index_entry *find_entry_ram(const char *filename,
227 goto try_again; 228 goto try_again;
228 } 229 }
229 230
230 return NULL; 231 return -1;
231} 232}
232#endif 233#endif
233 234
234static struct index_entry *find_entry_disk(const char *filename, bool retrieve) 235static long find_entry_disk(const char *filename)
235{ 236{
236 static struct index_entry idx;
237 static long last_pos = -1; 237 static long last_pos = -1;
238 long pos_history[POS_HISTORY_COUNT]; 238 long pos_history[POS_HISTORY_COUNT];
239 long pos_history_idx = 0; 239 long pos_history_idx = 0;
240 struct tagcache_header tch;
241 bool found = false; 240 bool found = false;
242 struct tagfile_entry tfe; 241 struct tagfile_entry tfe;
243 int masterfd, fd = filenametag_fd; 242 int fd;
244 char buf[MAX_PATH]; 243 char buf[MAX_PATH];
245 int i; 244 int i;
246 int pos = -1; 245 int pos = -1;
247 246
247 fd = filenametag_fd;
248 if (fd < 0) 248 if (fd < 0)
249 { 249 {
250 last_pos = -1; 250 last_pos = -1;
251 return NULL; 251 if ( (fd = open(TAGCACHE_FILE_MASTER, O_RDONLY)) < 0)
252 return -1;
252 } 253 }
253 254
254 check_again: 255 check_again:
@@ -276,7 +277,7 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve)
276 logf("too long tag #1"); 277 logf("too long tag #1");
277 close(fd); 278 close(fd);
278 last_pos = -1; 279 last_pos = -1;
279 return NULL; 280 return -2;
280 } 281 }
281 282
282 if (read(fd, buf, tfe.tag_length) != tfe.tag_length) 283 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
@@ -284,7 +285,7 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve)
284 logf("read error #2"); 285 logf("read error #2");
285 close(fd); 286 close(fd);
286 last_pos = -1; 287 last_pos = -1;
287 return NULL; 288 return -3;
288 } 289 }
289 290
290 if (!strcasecmp(filename, buf)) 291 if (!strcasecmp(filename, buf))
@@ -307,90 +308,106 @@ static struct index_entry *find_entry_disk(const char *filename, bool retrieve)
307 logf("seek again"); 308 logf("seek again");
308 goto check_again; 309 goto check_again;
309 } 310 }
310 //close(fd); 311
311 return NULL; 312 if (fd != filenametag_fd)
313 close(fd);
314 return -4;
312 } 315 }
313 316
314 if (!retrieve) 317 if (fd != filenametag_fd)
315 { 318 close(fd);
316 /* Just return a valid pointer without a valid entry. */ 319
317 return &idx; 320 return tfe.idx_id;
318 } 321}
322
323bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
324{
325 long idx_id = -1;
319 326
320 /* Found. Now read the index_entry (if requested). */ 327#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
321 masterfd = open(TAGCACHE_FILE_MASTER, O_RDONLY); 328 if (stat.ramcache && dircache_is_enabled())
322 if (masterfd < 0) 329 idx_id = find_entry_ram(filename, NULL);
323 { 330#endif
324 logf("open fail");
325 return NULL;
326 }
327 331
328 if (read(fd, &tch, sizeof(struct tagcache_header)) != 332 if (idx_id < 0)
329 sizeof(struct tagcache_header) || tch.magic != TAGCACHE_MAGIC) 333 idx_id = find_entry_disk(filename);
330 {
331 logf("header error");
332 return NULL;
333 }
334 334
335 for (i = 0; i < tch.entry_count; i++) 335 if (idx_id < 0)
336 { 336 return false;
337 if (read(masterfd, &idx, sizeof(struct index_entry)) !=
338 sizeof(struct index_entry))
339 {
340 logf("read error #3");
341 close(fd);
342 return NULL;
343 }
344
345 if (idx.tag_seek[tag_filename] == pos)
346 break ;
347 }
348 close(masterfd);
349 337
350 /* Not found? */ 338 if (!tagcache_search(tcs, tag_filename))
351 if (i == tch.entry_count) 339 return false;
352 {
353 logf("not found!");
354 return NULL;
355 }
356 340
357 return &idx; 341 tcs->entry_count = 0;
342 tcs->idx_id = idx_id;
343
344 return true;
358} 345}
359 346
360static long tagcache_get_seek(const struct tagcache_search *tcs, 347static bool tagcache_get_index(const struct tagcache_search *tcs,
361 int tag, int idxid) 348 int idxid, struct index_entry *idx)
362{ 349{
363 struct index_entry idx;
364
365#ifdef HAVE_TC_RAMCACHE 350#ifdef HAVE_TC_RAMCACHE
366 if (tcs->ramsearch) 351 if (tcs->ramsearch)
367 { 352 {
368 if (hdr->indices[idxid].flag & FLAG_DELETED) 353 if (hdr->indices[idxid].flag & FLAG_DELETED)
369 return false; 354 return false;
370 355
371 return hdr->indices[idxid].tag_seek[tag]; 356 memcpy(idx, &hdr->indices[idxid], sizeof(struct index_entry));
357 return true;
372 } 358 }
373#endif 359#endif
374 360
375 lseek(tcs->masterfd, idxid * sizeof(struct index_entry) 361 lseek(tcs->masterfd, idxid * sizeof(struct index_entry)
376 + sizeof(struct tagcache_header), SEEK_SET); 362 + sizeof(struct tagcache_header), SEEK_SET);
377 if (read(tcs->masterfd, &idx, sizeof(struct index_entry)) != 363 if (read(tcs->masterfd, idx, sizeof(struct index_entry)) !=
378 sizeof(struct index_entry)) 364 sizeof(struct index_entry))
379 { 365 {
380 logf("read error #3"); 366 logf("read error #3");
381 return -4; 367 return false;
368 }
369
370 return true;
371}
372
373static long check_virtual_tags(int tag, const struct index_entry *idx)
374{
375 long data = 0;
376
377 switch (tag)
378 {
379 case tag_virt_autoscore:
380 if (idx->tag_seek[tag_length] == 0
381 || idx->tag_seek[tag_playcount] == 0)
382 {
383 data = 0;
384 }
385 else
386 {
387 data = 100 * idx->tag_seek[tag_playtime]
388 / idx->tag_seek[tag_length]
389 / idx->tag_seek[tag_playcount];
390 }
391 break;
392
393 default:
394 data = idx->tag_seek[tag];
382 } 395 }
383 396
384 return idx.tag_seek[tag]; 397 return data;
385} 398}
386 399
387long tagcache_get_numeric(const struct tagcache_search *tcs, int tag) 400long tagcache_get_numeric(const struct tagcache_search *tcs, int tag)
388{ 401{
402 struct index_entry idx;
389 403
390 if (!tagcache_is_numeric_tag(tag)) 404 if (!tagcache_is_numeric_tag(tag))
391 return -1; 405 return -1;
392 406
393 return tagcache_get_seek(tcs, tag, tcs->idx_id); 407 if (!tagcache_get_index(tcs, tcs->idx_id, &idx))
408 return -2;
409
410 return check_virtual_tags(tag, &idx);
394} 411}
395 412
396static bool check_against_clause(long numeric, const char *str, 413static bool check_against_clause(long numeric, const char *str,
@@ -459,16 +476,20 @@ static bool build_lookup_list(struct tagcache_search *tcs)
459 /* Go through all conditional clauses. */ 476 /* Go through all conditional clauses. */
460 for (j = 0; j < tcs->clause_count; j++) 477 for (j = 0; j < tcs->clause_count; j++)
461 { 478 {
462 int seek = hdr->indices[i].tag_seek[tcs->clause[j]->tag]; 479 struct index_entry *idx = &hdr->indices[i];
480 int seek;
463 char *str = NULL; 481 char *str = NULL;
464 struct tagfile_entry *entry; 482 struct tagfile_entry *entry;
465 483
484 seek = check_virtual_tags(tcs->clause[j]->tag, idx);
485
466 if (!tagcache_is_numeric_tag(tcs->clause[j]->tag)) 486 if (!tagcache_is_numeric_tag(tcs->clause[j]->tag))
467 { 487 {
468 entry = (struct tagfile_entry *)&hdr->tags[tcs->clause[j]->tag][seek]; 488 entry = (struct tagfile_entry *)&hdr->tags[tcs->clause[j]->tag][seek];
469 str = entry->tag_data; 489 str = entry->tag_data;
470 } 490 }
471 491
492
472 if (!check_against_clause(seek, str, tcs->clause[j])) 493 if (!check_against_clause(seek, str, tcs->clause[j]))
473 break ; 494 break ;
474 } 495 }
@@ -529,9 +550,11 @@ static bool build_lookup_list(struct tagcache_search *tcs)
529 for (i = 0; i < tcs->clause_count; i++) 550 for (i = 0; i < tcs->clause_count; i++)
530 { 551 {
531 struct tagfile_entry tfe; 552 struct tagfile_entry tfe;
532 int seek = entry.tag_seek[tcs->clause[i]->tag]; 553 int seek;
533 char str[256]; 554 char str[256];
534 555
556 seek = check_virtual_tags(tcs->clause[i]->tag, &entry);
557
535 memset(str, 0, sizeof str); 558 memset(str, 0, sizeof str);
536 if (!tagcache_is_numeric_tag(tcs->clause[i]->tag)) 559 if (!tagcache_is_numeric_tag(tcs->clause[i]->tag))
537 { 560 {
@@ -852,9 +875,13 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
852 char *buf, long size) 875 char *buf, long size)
853{ 876{
854 struct tagfile_entry tfe; 877 struct tagfile_entry tfe;
878 struct index_entry idx;
855 long seek; 879 long seek;
856 880
857 seek = tagcache_get_seek(tcs, tcs->type, idxid); 881 if (!tagcache_get_index(tcs, idxid, &idx))
882 return false;
883
884 seek = idx.tag_seek[tcs->type];
858 if (seek < 0) 885 if (seek < 0)
859 { 886 {
860 logf("Retrieve failed"); 887 logf("Retrieve failed");
@@ -974,12 +1001,15 @@ static long get_tag_numeric(const struct index_entry *entry, int tag)
974bool tagcache_fill_tags(struct mp3entry *id3, const char *filename) 1001bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
975{ 1002{
976 struct index_entry *entry; 1003 struct index_entry *entry;
1004 int idx_id;
977 1005
978 /* Find the corresponding entry in tagcache. */ 1006 /* Find the corresponding entry in tagcache. */
979 entry = find_entry_ram(filename, NULL); 1007 idx_id = find_entry_ram(filename, NULL);
980 if (entry == NULL || !stat.ramcache) 1008 if (idx_id < 0 || !stat.ramcache)
981 return false; 1009 return false;
982 1010
1011 entry = &hdr->indices[idx_id];
1012
983 id3->title = get_tag(entry, tag_title)->tag_data; 1013 id3->title = get_tag(entry, tag_title)->tag_data;
984 id3->artist = get_tag(entry, tag_artist)->tag_data; 1014 id3->artist = get_tag(entry, tag_artist)->tag_data;
985 id3->album = get_tag(entry, tag_album)->tag_data; 1015 id3->album = get_tag(entry, tag_album)->tag_data;
@@ -1036,13 +1066,13 @@ static void add_tagcache(const char *path)
1036#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) 1066#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1037 if (stat.ramcache && dircache_is_enabled()) 1067 if (stat.ramcache && dircache_is_enabled())
1038 { 1068 {
1039 if (find_entry_ram(path, dc)) 1069 if (find_entry_ram(path, dc) >= 0)
1040 return ; 1070 return ;
1041 } 1071 }
1042 else 1072 else
1043#endif 1073#endif
1044 { 1074 {
1045 if (find_entry_disk(path, false)) 1075 if (find_entry_disk(path) >= 0)
1046 return ; 1076 return ;
1047 } 1077 }
1048 1078
@@ -2051,35 +2081,101 @@ static void free_tempbuf(void)
2051 tempbuf_size = 0; 2081 tempbuf_size = 0;
2052} 2082}
2053 2083
2054static bool delete_entry(long idx_id) 2084static int open_master_fd(struct tagcache_header *hdr)
2055{ 2085{
2056 int fd; 2086 int fd;
2057 int tag, i;
2058 struct index_entry idx, myidx;
2059 struct tagcache_header hdr;
2060 char buf[MAX_PATH];
2061 int in_use[TAG_COUNT];
2062 2087
2063 fd = open(TAGCACHE_FILE_MASTER, O_RDWR); 2088 fd = open(TAGCACHE_FILE_MASTER, O_RDWR);
2064 if (fd < 0) 2089 if (fd < 0)
2065 { 2090 {
2066 logf("master file open failed for R/W"); 2091 logf("master file open failed for R/W");
2067 return false; 2092 return fd;
2068 } 2093 }
2069 2094
2070 /* Check the header. */ 2095 /* Check the header. */
2071 read(fd, &hdr, sizeof(struct tagcache_header)); 2096 read(fd, hdr, sizeof(struct tagcache_header));
2072 if (hdr.magic != TAGCACHE_MAGIC) 2097 if (hdr->magic != TAGCACHE_MAGIC)
2073 { 2098 {
2074 logf("header error"); 2099 logf("header error");
2100 close(fd);
2101 return -2;
2102 }
2103
2104 return fd;
2105}
2106
2107bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
2108 int tag, long data)
2109{
2110 struct index_entry idx;
2111 struct tagcache_header myhdr;
2112
2113 if (!tagcache_is_numeric_tag(tag))
2075 return false; 2114 return false;
2115
2116#ifdef HAVE_TC_RAMCACHE
2117 /* Update ram entries first. */
2118 if (tcs->ramsearch)
2119 {
2120 hdr->indices[tcs->idx_id].tag_seek[tag] = data;
2121 hdr->indices[tcs->idx_id].flag |= FLAG_DIRTYNUM;
2076 } 2122 }
2123#endif
2124
2125 /* And now update the db on disk also. */
2126 if (tcs->masterfd < 0)
2127 {
2128 if ( (tcs->masterfd = open_master_fd(&myhdr)) < 0)
2129 return false;
2130 }
2131
2132 lseek(tcs->masterfd, tcs->idx_id * sizeof(struct index_entry)
2133 + sizeof(struct tagcache_header), SEEK_SET);
2134 if (read(tcs->masterfd, &idx, sizeof(struct index_entry))
2135 != sizeof(struct index_entry))
2136 {
2137 logf("read error");
2138 return false;
2139 }
2140
2141 idx.flag |= FLAG_DIRTYNUM;
2142 idx.tag_seek[tag] = data;
2143
2144 lseek(tcs->masterfd, -sizeof(struct index_entry), SEEK_CUR);
2145 if (write(tcs->masterfd, &idx, sizeof(struct index_entry))
2146 != sizeof(struct index_entry))
2147 {
2148 logf("write error");
2149 return false;
2150 }
2151
2152 return true;
2153}
2154
2155static bool delete_entry(long idx_id)
2156{
2157 int fd;
2158 int tag, i;
2159 struct index_entry idx, myidx;
2160 struct tagcache_header myhdr;
2161 char buf[MAX_PATH];
2162 int in_use[TAG_COUNT];
2163
2164#ifdef HAVE_TC_RAMCACHE
2165 /* At first mark the entry removed from ram cache. */
2166 if (hdr)
2167 hdr->indices[idx_id].flag |= FLAG_DELETED;
2168#endif
2169
2170 if ( (fd = open_master_fd(&myhdr) < 0) )
2171 return false;
2077 2172
2078 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR); 2173 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR);
2079 if (read(fd, &myidx, sizeof(struct index_entry)) 2174 if (read(fd, &myidx, sizeof(struct index_entry))
2080 != sizeof(struct index_entry)) 2175 != sizeof(struct index_entry))
2081 { 2176 {
2082 logf("read error"); 2177 logf("read error");
2178 close(fd);
2083 return false; 2179 return false;
2084 } 2180 }
2085 2181
@@ -2089,6 +2185,7 @@ static bool delete_entry(long idx_id)
2089 != sizeof(struct index_entry)) 2185 != sizeof(struct index_entry))
2090 { 2186 {
2091 logf("write error"); 2187 logf("write error");
2188 close(fd);
2092 return false; 2189 return false;
2093 } 2190 }
2094 2191
@@ -2097,7 +2194,7 @@ static bool delete_entry(long idx_id)
2097 in_use[tag] = 0; 2194 in_use[tag] = 0;
2098 2195
2099 lseek(fd, sizeof(struct tagcache_header), SEEK_SET); 2196 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
2100 for (i = 0; i < hdr.entry_count; i++) 2197 for (i = 0; i < myhdr.entry_count; i++)
2101 { 2198 {
2102 if (read(fd, &idx, sizeof(struct index_entry)) 2199 if (read(fd, &idx, sizeof(struct index_entry))
2103 != sizeof(struct index_entry)) 2200 != sizeof(struct index_entry))
@@ -2376,7 +2473,6 @@ static bool load_tagcache(void)
2376 { 2473 {
2377 logf("Entry no longer valid."); 2474 logf("Entry no longer valid.");
2378 logf("-> %s", buf); 2475 logf("-> %s", buf);
2379 idx->flag |= FLAG_DELETED;
2380 delete_entry(fe->idx_id); 2476 delete_entry(fe->idx_id);
2381 continue ; 2477 continue ;
2382 } 2478 }
@@ -2397,7 +2493,6 @@ static bool load_tagcache(void)
2397 { 2493 {
2398 logf("Entry no longer valid."); 2494 logf("Entry no longer valid.");
2399 logf("-> %s", buf); 2495 logf("-> %s", buf);
2400 idx->flag |= FLAG_DELETED;
2401 delete_entry(fe->idx_id); 2496 delete_entry(fe->idx_id);
2402 continue; 2497 continue;
2403 } 2498 }
@@ -2808,6 +2903,7 @@ void tagcache_init(void)
2808{ 2903{
2809 stat.initialized = false; 2904 stat.initialized = false;
2810 stat.commit_step = 0; 2905 stat.commit_step = 0;
2906 filenametag_fd = -1;
2811 2907
2812 queue_init(&tagcache_queue); 2908 queue_init(&tagcache_queue);
2813 create_thread(tagcache_thread, tagcache_stack, 2909 create_thread(tagcache_thread, tagcache_stack,
diff --git a/apps/tagcache.h b/apps/tagcache.h
index a82f6e1634..04f9567936 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -23,9 +23,11 @@
23 23
24enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title, 24enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
25 tag_filename, tag_composer, tag_year, tag_tracknumber, 25 tag_filename, tag_composer, tag_year, tag_tracknumber,
26 tag_bitrate, tag_length }; 26 tag_bitrate, tag_length, tag_playcount, tag_playtime, tag_lastplayed,
27 /* Virtual tags */
28 tag_virt_autoscore };
27 29
28#define TAG_COUNT 10 30#define TAG_COUNT 13
29 31
30/* Allow a little drift to the filename ordering (should not be too high/low). */ 32/* Allow a little drift to the filename ordering (should not be too high/low). */
31#define POS_HISTORY_COUNT 4 33#define POS_HISTORY_COUNT 4
@@ -34,7 +36,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
34#define IDX_BUF_DEPTH 64 36#define IDX_BUF_DEPTH 64
35 37
36/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */ 38/* Tag Cache Header version 'TCHxx'. Increment when changing internal structures. */
37#define TAGCACHE_MAGIC 0x54434804 39#define TAGCACHE_MAGIC 0x54434805
38 40
39/* How much to allocate extra space for ramcache. */ 41/* How much to allocate extra space for ramcache. */
40#define TAGCACHE_RESERVE 32768 42#define TAGCACHE_RESERVE 32768
@@ -68,6 +70,7 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
68/* Flags */ 70/* Flags */
69#define FLAG_DELETED 0x0001 /* Entry has been removed from db */ 71#define FLAG_DELETED 0x0001 /* Entry has been removed from db */
70#define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */ 72#define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */
73#define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */
71 74
72enum clause { clause_none, clause_is, clause_gt, clause_gteq, clause_lt, 75enum clause { clause_none, clause_is, clause_gt, clause_gteq, clause_lt,
73 clause_lteq, clause_contains, clause_begins_with, clause_ends_with }; 76 clause_lteq, clause_contains, clause_begins_with, clause_ends_with };
@@ -123,6 +126,7 @@ struct tagcache_search {
123bool tagcache_is_numeric_tag(int type); 126bool tagcache_is_numeric_tag(int type);
124bool tagcache_is_unique_tag(int type); 127bool tagcache_is_unique_tag(int type);
125bool tagcache_is_sorted_tag(int type); 128bool tagcache_is_sorted_tag(int type);
129bool tagcache_find_index(struct tagcache_search *tcs, const char *filename);
126bool tagcache_search(struct tagcache_search *tcs, int tag); 130bool tagcache_search(struct tagcache_search *tcs, int tag);
127bool tagcache_search_add_filter(struct tagcache_search *tcs, 131bool tagcache_search_add_filter(struct tagcache_search *tcs,
128 int tag, int seek); 132 int tag, int seek);
@@ -133,6 +137,8 @@ bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
133 char *buf, long size); 137 char *buf, long size);
134void tagcache_search_finish(struct tagcache_search *tcs); 138void tagcache_search_finish(struct tagcache_search *tcs);
135long tagcache_get_numeric(const struct tagcache_search *tcs, int tag); 139long tagcache_get_numeric(const struct tagcache_search *tcs, int tag);
140bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
141 int tag, long data);
136 142
137struct tagcache_stat* tagcache_get_stat(void); 143struct tagcache_stat* tagcache_get_stat(void);
138int tagcache_get_commit_step(void); 144int tagcache_get_commit_step(void);
diff --git a/apps/tagnavi.config b/apps/tagnavi.config
index 2b00d59e1c..285f826425 100644
--- a/apps/tagnavi.config
+++ b/apps/tagnavi.config
@@ -1,11 +1,16 @@
1"Artists" artist : album : title 1"Artists" artist : album : title = "%02d. %s" tracknum title
2"Albums" album : title 2"Albums" album : title = "%02d. %s" tracknum title
3"Genres" genre : artist : album : title 3"Genres" genre : artist : album : title = "%02d. %s" tracknum title
4"Composers" composer : album : title 4"Composers" composer : album : title = "%02d. %s" tracknum title
5"Tracks" title 5"Tracks" title
6"Search by artist" artist ? artist ~ "" : album : title 6"Search by artist" artist ? artist ~ "" : album : title = "%02d. %s" tracknum title
7"Search by album" album ? album ~ "" : title 7"Search by album" album ? album ~ "" : title = "%02d. %s" tracknum title
8"Search by title" title ? title ~ "" 8"Search by title" title ? title ~ ""
9"Search by year" artist ? year = "" : album : title 9"Search by year" artist ? year = "" : album : title = "%02d. %s" tracknum title
10"Search by score" title = "(%3d) %s" autoscore title ? autoscore > ""
11"Most played tracks" title = "(%d) %s" playcount title ? playcount > "1"
12"Never played tracks" artist ? playcount == "0" : album : title = "%02d. %s" tracknum title
13"Best tracks" title ? autoscore > "60" & playcount > "1"
10"Example 1" artist ? year >= "2000" & artist ^ "a" : album : title 14"Example 1" artist ? year >= "2000" & artist ^ "a" : album : title
11"Example 2" genre ? genre ~ "metal" : artist ? year >= "2000" : album ? year >= "2000" : title 15"Example 2" genre ? genre ~ "metal" : artist ? year >= "2000" : album ? year >= "2000" : title
16"List played tracks" title = "(%3d/%d) %s" autoscore playcount title ? playcount > "0"
diff --git a/apps/tagtree.c b/apps/tagtree.c
index ded2426d41..396248d7ce 100644
--- a/apps/tagtree.c
+++ b/apps/tagtree.c
@@ -39,6 +39,7 @@
39#include "gui/list.h" 39#include "gui/list.h"
40#include "buffer.h" 40#include "buffer.h"
41#include "atoi.h" 41#include "atoi.h"
42#include "playback.h"
42 43
43#define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config" 44#define FILE_SEARCH_INSTRUCTIONS ROCKBOX_DIR "/tagnavi.config"
44 45
@@ -47,11 +48,28 @@ static int tagtree_play_folder(struct tree_context* c);
47static char searchstring[32]; 48static char searchstring[32];
48#define MAX_TAGS 5 49#define MAX_TAGS 5
49 50
51/*
52 * "%3d. %s" autoscore title
53 *
54 * valid = true
55 * formatstr = "%-3d. %s"
56 * tags[0] = tag_autoscore
57 * tags[1] = tag_title
58 * tag_count = 2
59 */
60struct display_format {
61 bool valid;
62 char formatstr[64];
63 int tags[MAX_TAGS];
64 int tag_count;
65};
66
50struct search_instruction { 67struct search_instruction {
51 char name[64]; 68 char name[64];
52 int tagorder[MAX_TAGS]; 69 int tagorder[MAX_TAGS];
53 int tagorder_count; 70 int tagorder_count;
54 struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES]; 71 struct tagcache_search_clause clause[MAX_TAGS][TAGCACHE_MAX_CLAUSES];
72 struct display_format format[MAX_TAGS];
55 int clause_count[MAX_TAGS]; 73 int clause_count[MAX_TAGS];
56 int result_seek[MAX_TAGS]; 74 int result_seek[MAX_TAGS];
57}; 75};
@@ -121,6 +139,8 @@ static int get_tag(int *tag)
121 MATCH(tag, buf, "title", tag_title); 139 MATCH(tag, buf, "title", tag_title);
122 MATCH(tag, buf, "tracknum", tag_tracknumber); 140 MATCH(tag, buf, "tracknum", tag_tracknumber);
123 MATCH(tag, buf, "year", tag_year); 141 MATCH(tag, buf, "year", tag_year);
142 MATCH(tag, buf, "playcount", tag_playcount);
143 MATCH(tag, buf, "autoscore", tag_virt_autoscore);
124 144
125 logf("NO MATCH: %s\n", buf); 145 logf("NO MATCH: %s\n", buf);
126 if (buf[0] == '?') 146 if (buf[0] == '?')
@@ -163,7 +183,7 @@ static int get_clause(int *condition)
163 return 0; 183 return 0;
164} 184}
165 185
166static bool add_clause(struct search_instruction *inst, 186static bool add_clause(struct search_instruction *inst,
167 int tag, int type, const char *str) 187 int tag, int type, const char *str)
168{ 188{
169 int len = strlen(str); 189 int len = strlen(str);
@@ -206,14 +226,51 @@ static bool add_clause(struct search_instruction *inst,
206 return true; 226 return true;
207} 227}
208 228
229static int get_format_str(struct display_format *fmt)
230{
231 int ret;
232
233 memset(fmt, 0, sizeof(struct display_format));
234
235 if (get_token_str(fmt->formatstr, sizeof fmt->formatstr) < 0)
236 return -10;
237
238 while (fmt->tag_count < MAX_TAGS)
239 {
240 ret = get_tag(&fmt->tags[fmt->tag_count]);
241 if (ret < 0)
242 return -11;
243
244 if (ret == 0)
245 break;
246
247 fmt->tag_count++;
248 }
249
250 fmt->valid = true;
251
252 return 1;
253}
254
209static int get_condition(struct search_instruction *inst) 255static int get_condition(struct search_instruction *inst)
210{ 256{
257 struct display_format format;
258 struct display_format *fmt = NULL;
211 int tag; 259 int tag;
212 int condition; 260 int condition;
213 char buf[32]; 261 char buf[32];
214 262
215 switch (*strp) 263 switch (*strp)
216 { 264 {
265 case '=':
266 if (get_format_str(&format) < 0)
267 {
268 logf("get_format_str() parser failed!");
269 return -4;
270 }
271 fmt = &format;
272 break;
273
217 case '?': 274 case '?':
218 case ' ': 275 case ' ':
219 case '&': 276 case '&':
@@ -225,6 +282,14 @@ static int get_condition(struct search_instruction *inst)
225 return 0; 282 return 0;
226 } 283 }
227 284
285 if (fmt)
286 {
287 memcpy(&inst->format[inst->tagorder_count], fmt,
288 sizeof(struct display_format));
289 }
290 else
291 inst->format[inst->tagorder_count].valid = false;
292
228 if (get_tag(&tag) <= 0) 293 if (get_tag(&tag) <= 0)
229 return -1; 294 return -1;
230 295
@@ -294,6 +359,62 @@ static int compare(const void *p1, const void *p2)
294 return strncasecmp(e1->name, e2->name, MAX_PATH); 359 return strncasecmp(e1->name, e2->name, MAX_PATH);
295} 360}
296 361
362static void tagtree_buffer_event(struct mp3entry *id3, bool last_track)
363{
364 (void)id3;
365 (void)last_track;
366
367 logf("be:%d%s", last_track, id3->path);
368}
369
370static void tagtree_unbuffer_event(struct mp3entry *id3, bool last_track)
371{
372 (void)last_track;
373 long playcount;
374 long playtime;
375 long lastplayed;
376
377 /* Do not gather data unless proper setting has been enabled. */
378 if (!global_settings.runtimedb)
379 return;
380
381 /* Don't process unplayed tracks. */
382 if (id3->elapsed == 0)
383 return;
384
385 if (!tagcache_find_index(&tcs, id3->path))
386 {
387 logf("tc stat: not found: %s", id3->path);
388 return;
389 }
390
391 playcount = tagcache_get_numeric(&tcs, tag_playcount);
392 playtime = tagcache_get_numeric(&tcs, tag_playtime);
393 lastplayed = tagcache_get_numeric(&tcs, tag_lastplayed);
394
395 playcount++;
396
397 /* Ignore the last 15s (crossfade etc.) */
398 playtime += MIN(id3->length, id3->elapsed + 15 * 1000);
399
400 logf("ube:%s", id3->path);
401 logf("-> %d/%d/%d", last_track, playcount, playtime);
402 logf("-> %d/%d/%d", id3->elapsed, id3->length, MIN(id3->length, id3->elapsed + 15 * 1000));
403
404 /* lastplayed not yet supported. */
405
406 if (!tagcache_modify_numeric_entry(&tcs, tag_playcount, playcount)
407 || !tagcache_modify_numeric_entry(&tcs, tag_playtime, playtime)
408 || !tagcache_modify_numeric_entry(&tcs, tag_lastplayed, tag_lastplayed))
409 {
410 logf("tc stat: modify failed!");
411 tagcache_search_finish(&tcs);
412 return;
413 }
414
415 tagcache_search_finish(&tcs);
416}
417
297void tagtree_init(void) 418void tagtree_init(void)
298{ 419{
299 int fd; 420 int fd;
@@ -350,6 +471,9 @@ void tagtree_init(void)
350 close(fd); 471 close(fd);
351 472
352 audiobuf += sizeof(struct search_instruction) * si_count + 4; 473 audiobuf += sizeof(struct search_instruction) * si_count + 4;
474
475 audio_set_track_buffer_event(tagtree_buffer_event);
476 audio_set_track_unbuffer_event(tagtree_unbuffer_event);
353} 477}
354 478
355bool show_search_progress(bool init, int count) 479bool show_search_progress(bool init, int count)
@@ -445,48 +569,100 @@ int retrieve_entries(struct tree_context *c, struct tagcache_search *tcs,
445 569
446 while (tagcache_get_next(tcs)) 570 while (tagcache_get_next(tcs))
447 { 571 {
572 struct display_format *fmt = &csi->format[extra];
573
448 if (total_count++ < offset) 574 if (total_count++ < offset)
449 continue; 575 continue;
450 576
451 dptr->newtable = navibrowse; 577 dptr->newtable = navibrowse;
452 dptr->extraseek = tcs->result_seek; 578 dptr->extraseek = tcs->result_seek;
453 if (!tcs->ramsearch || tag == tag_title) 579 if (tag == tag_title)
580 dptr->newtable = playtrack;
581
582 if (!tcs->ramsearch || fmt->valid)
454 { 583 {
455 int tracknum = -1; 584 char buf[MAX_PATH];
585 int buf_pos = 0;
456 586
457 dptr->name = &c->name_buffer[namebufused]; 587 if (fmt->valid)
458 if (tag == tag_title)
459 {
460 dptr->newtable = playtrack;
461 if (c->currtable != allsubentries && c->dirlevel > 1)
462 tracknum = tagcache_get_numeric(tcs, tag_tracknumber);
463 }
464
465 if (tracknum > 0)
466 { 588 {
467 snprintf(dptr->name, c->name_buffer_size - namebufused, "%02d. %s", 589 char fmtbuf[8];
468 tracknum, tcs->result); 590 bool read_format = false;
469 namebufused += strlen(dptr->name) + 1; 591 int fmtbuf_pos = 0;
470 if (namebufused >= c->name_buffer_size) 592 int parpos = 0;
593
594 memset(buf, 0, sizeof buf);
595 for (i = 0; fmt->formatstr[i] != '\0'; i++)
471 { 596 {
472 logf("chunk mode #1: %d", current_entry_count); 597 if (fmt->formatstr[i] == '%')
473 c->dirfull = true; 598 {
474 sort = false; 599 read_format = true;
475 break ; 600 fmtbuf_pos = 0;
601 if (parpos >= fmt->tag_count)
602 {
603 logf("too many format tags");
604 return 0;
605 }
606 }
607
608 if (read_format)
609 {
610 fmtbuf[fmtbuf_pos++] = fmt->formatstr[i];
611 if (fmtbuf_pos >= (long)sizeof(fmtbuf))
612 {
613 logf("format parse error");
614 return 0;
615 }
616
617 if (fmt->formatstr[i] == 's')
618 {
619 fmtbuf[fmtbuf_pos] = '\0';
620 read_format = false;
621 snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf, tcs->result);
622 buf_pos += strlen(&buf[buf_pos]);
623 parpos++;
624 }
625 else if (fmt->formatstr[i] == 'd')
626 {
627 fmtbuf[fmtbuf_pos] = '\0';
628 read_format = false;
629 snprintf(&buf[buf_pos], MAX_PATH - buf_pos, fmtbuf,
630 tagcache_get_numeric(tcs, fmt->tags[parpos]));
631 buf_pos += strlen(&buf[buf_pos]);
632 parpos++;
633 }
634 continue;
635 }
636
637 buf[buf_pos++] = fmt->formatstr[i];
638
639 if (buf_pos - 1 >= (long)sizeof(buf))
640 {
641 logf("buffer overflow");
642 return 0;
643 }
476 } 644 }
645
646 buf[buf_pos++] = '\0';
477 } 647 }
648
649 dptr->name = &c->name_buffer[namebufused];
650 if (fmt->valid)
651 namebufused += buf_pos;
478 else 652 else
479 {
480 namebufused += tcs->result_len; 653 namebufused += tcs->result_len;
481 if (namebufused >= c->name_buffer_size) 654
482 { 655 if (namebufused >= c->name_buffer_size)
483 logf("chunk mode #2: %d", current_entry_count); 656 {
484 c->dirfull = true; 657 logf("chunk mode #2: %d", current_entry_count);
485 sort = false; 658 c->dirfull = true;
486 break ; 659 sort = false;
487 } 660 break ;
488 strcpy(dptr->name, tcs->result);
489 } 661 }
662 if (fmt->valid)
663 strcpy(dptr->name, buf);
664 else
665 strcpy(dptr->name, tcs->result);
490 } 666 }
491 else 667 else
492 dptr->name = tcs->result; 668 dptr->name = tcs->result;