A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

First runtime database support, self repairing, only playcount works for now, which is still rather crude; playcount gets increased even if the song started playback but was skipped... track rating should be trivial to add, autorating also works since its based on playcount.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6969 a1c6a512-1295-4272-9138-f99709370657

+241 -44
+193 -24
apps/database.c
··· 43 43 #include "keyboard.h" 44 44 #include "database.h" 45 45 #include "autoconf.h" 46 + #include "playback.h" 47 + #include "logf.h" 46 48 47 - #undef NEW_DB_CODE 48 - 49 - #ifdef NEW_DB_CODE 49 + /* internal functions */ 50 + void writetagdbheader(void); 51 + void writefentry(void); 52 + void getfentrybyoffset(int offset); 53 + void update_fentryoffsets(int start, int end); 54 + void writerundbheader(void); 55 + void getrundbentrybyoffset(int offset); 56 + void writerundbentry(void); 57 + int getfentrybyfilename(char *fname); 58 + int getfentrybyhash(int hash); 59 + int deletefentry(char *fname); 60 + int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes); 61 + int tagdb_shiftup(int targetoffset, int startingoffset, int bytes); 62 + 50 63 static char sbuf[1024]; 51 64 static struct file_entry fe; 52 65 static int currentfeoffset, currentferecord; 53 - #endif 54 66 55 67 int tagdb_fd = -1; 56 68 int tagdb_initialized = 0; ··· 63 75 int i, *p; 64 76 #endif 65 77 66 - tagdb_fd = open(ROCKBOX_DIR "/rockbox.id3db", O_RDONLY); 78 + tagdb_fd = open(ROCKBOX_DIR "/rockbox.tagdb", O_RDWR); 67 79 if (tagdb_fd < 0) { 68 80 DEBUGF("Failed opening database\n"); 69 81 return -1; ··· 111 123 112 124 /* NOTE: All these functions below are yet untested. */ 113 125 114 - #ifdef NEW_DB_CODE 115 - 116 126 /*** TagDatabase code ***/ 117 127 118 - void writetagdbheader() { 128 + void writetagdbheader(void) { 119 129 lseek(tagdb_fd,0,SEEK_SET); 120 130 write(tagdb_fd, &tagdbheader, 68); 131 + fsync(tagdb_fd); 132 + } 133 + 134 + void writefentry(void) { 135 + lseek(tagdb_fd,currentfeoffset,SEEK_SET); 136 + write(tagdb_fd,sbuf,tagdbheader.filelen); 137 + write(tagdb_fd,&fe.hash,12); 138 + fsync(tagdb_fd); 121 139 } 122 140 123 141 void getfentrybyoffset(int offset) { 142 + memset(&fe,0,sizeof(struct file_entry)); 124 143 lseek(tagdb_fd,offset,SEEK_SET); 125 - fread(tagdb_fd,sbuf,tagdbheader.filelen); 126 - fread(tagdb_fd,&fe+sizeof(char *),12); 144 + read(tagdb_fd,sbuf,tagdbheader.filelen); 145 + read(tagdb_fd,&fe.hash,12); 127 146 fe.name=sbuf; 128 147 currentfeoffset=offset; 129 148 currentferecord=(offset-tagdbheader.filestart)/FILEENTRY_SIZE; ··· 138 157 int mid=(min+max)/2; 139 158 int compare; 140 159 getfentrybyrecord(mid); 141 - compare=strcasecmp(fname,fe.name)); 160 + compare=strcasecmp(fname,fe.name); 142 161 if(compare==0) 143 162 return 1; 144 163 else if(compare<0) ··· 164 183 return 0; 165 184 int restrecord = currentferecord+1; 166 185 if(currentferecord!=tagdbheader.filecount) /* file is not last entry */ 167 - shiftdown(FILERECORD2OFFSET(currentferecord),FILERECORD2OFFSET(restrecord),(tagdbheader.filecount-restrecord)*FILEENTRY_SIZE); 186 + tagdb_shiftdown(FILERECORD2OFFSET(currentferecord),FILERECORD2OFFSET(restrecord),(tagdbheader.filecount-restrecord)*FILEENTRY_SIZE); 168 187 ftruncate(tagdb_fd,lseek(tagdb_fd,0,SEEK_END)-FILEENTRY_SIZE); 169 188 tagdbheader.filecount--; 170 189 update_fentryoffsets(restrecord,tagdbheader.filecount); ··· 172 191 return 1; 173 192 } 174 193 175 - int update_fentryoffsets(int start, int end) { 194 + void update_fentryoffsets(int start, int end) { 176 195 int i; 177 - for(int i=start;i<end;i++) { 196 + for(i=start;i<end;i++) { 178 197 getfentrybyrecord(i); 179 198 if(fe.songentry!=-1) { 180 199 int p; ··· 187 206 } 188 207 } 189 208 if(fe.rundbentry!=-1) { 190 - splash(HZ*2, "o.o.. found a rundbentry? o.o; didn't update it, update the code o.o;"); 209 + splash(HZ*2,true, "o.o.. found a rundbentry? o.o; didn't update it, update the code o.o;"); 191 210 } 192 211 } 193 212 } ··· 195 214 int tagdb_shiftdown(int targetoffset, int startingoffset, int bytes) { 196 215 int amount; 197 216 if(targetoffset>=startingoffset) { 198 - splash(HZ*2,"Woah. no beeping way. (tagdb_shiftdown)"); 217 + splash(HZ*2,true,"Woah. no beeping way. (tagdb_shiftdown)"); 199 218 return 0; 200 219 } 201 220 lseek(tagdb_fd,startingoffset,SEEK_SET); 202 - while(amount=read(tagdb_fd,sbuf,bytes > 1024 ? 1024 : bytes)) { 221 + while((amount=read(tagdb_fd,sbuf,(bytes > 1024) ? 1024 : bytes))) { 203 222 int written; 204 223 startingoffset+=amount; 205 224 lseek(tagdb_fd,targetoffset,SEEK_SET); 206 225 written=write(tagdb_fd,sbuf,amount); 207 226 targetoffset+=written; 208 227 if(amount!=written) { 209 - splash(HZ*2,"Something went very wrong. expect database corruption. (tagdb_shiftdown)"); 228 + splash(HZ*2,true,"Something went very wrong. expect database corruption. (tagdb_shiftdown)"); 210 229 return 0; 211 230 } 212 231 lseek(tagdb_fd,startingoffset,SEEK_SET); ··· 218 237 int tagdb_shiftup(int targetoffset, int startingoffset, int bytes) { 219 238 int amount,amount2; 220 239 int readpos,writepos,filelen; 221 - int ok; 222 240 if(targetoffset<=startingoffset) { 223 - splash(HZ*2,"Um. no. (tagdb_shiftup)"); 241 + splash(HZ*2,true,"Um. no. (tagdb_shiftup)"); 224 242 return 0; 225 243 } 226 244 filelen=lseek(tagdb_fd,0,SEEK_END); ··· 232 250 lseek(tagdb_fd,readpos,SEEK_SET); 233 251 amount2=read(tagdb_fd,sbuf,amount); 234 252 if(amount2!=amount) { 235 - splash(HZ*2,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); 253 + splash(HZ*2,true,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); 236 254 return 0; 237 255 } 238 256 lseek(tagdb_fd,writepos,SEEK_SET); 239 257 amount=write(tagdb_fd,sbuf,amount2); 240 258 if(amount2!=amount) { 241 - splash(HZ*2,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); 259 + splash(HZ*2,true,"Something went very wrong. expect database corruption. (tagdb_shiftup)"); 242 260 return 0; 243 261 } 244 262 bytes-=amount; ··· 246 264 if(bytes==0) 247 265 return 1; 248 266 else { 249 - splash(HZ*2,"Something went wrong, >.>;; (tagdb_shiftup)"); 267 + splash(HZ*2,true,"Something went wrong, >.>;; (tagdb_shiftup)"); 250 268 return 0; 251 269 } 252 270 } 253 271 254 272 /*** end TagDatabase code ***/ 255 273 274 + int rundb_fd = -1; 275 + int rundb_initialized = 0; 276 + struct rundb_header rundbheader; 277 + 278 + static int valid_file, currentreoffset,rundbsize; 279 + static struct rundb_entry rundbentry; 280 + 256 281 /*** RuntimeDatabase code ***/ 257 282 283 + void rundb_track_changed(struct track_info *ti) { 284 + increaseplaycount(); 285 + logf("rundb new track: %s", ti->id3.path); 286 + loadruntimeinfo(ti->id3.path); 287 + } 258 288 289 + int rundb_init(void) 290 + { 291 + unsigned char* ptr = (char*)&rundbheader.version; 292 + #ifdef ROCKBOX_LITTLE_ENDIAN 293 + int i, *p; 294 + #endif 259 295 260 - /*** end RuntimeDatabase code ***/ 296 + if(!tagdb_initialized) /* forget it.*/ 297 + return -1; 298 + 299 + rundb_fd = open(ROCKBOX_DIR "/rockbox.rundb", O_CREAT|O_RDWR); 300 + if (rundb_fd < 0) { 301 + DEBUGF("Failed opening database\n"); 302 + return -1; 303 + } 304 + if(read(rundb_fd, &rundbheader, 8)!=8) { 305 + ptr[0]=ptr[1]='R'; 306 + ptr[2]='D'; 307 + ptr[3]=0x1; 308 + rundbheader.entrycount=0; 309 + writerundbheader(); 310 + } 311 + 312 + if (ptr[0] != 'R' || 313 + ptr[1] != 'R' || 314 + ptr[2] != 'D') 315 + { 316 + splash(HZ,true,"Not a rockbox runtime database!"); 317 + return -1; 318 + } 319 + #ifdef ROCKBOX_LITTLE_ENDIAN 320 + p=(int *)&rundbheader; 321 + for(i=0;i<2;i++) { 322 + *p=BE32(*p); 323 + p++; 324 + } 325 + #endif 326 + if ( (rundbheader.version&0xFF) != RUNDB_VERSION) 327 + { 328 + splash(HZ,true,"Unsupported runtime database version %d!", rundbheader.version&0xFF); 329 + return -1; 330 + } 331 + 332 + rundb_initialized = 1; 333 + audio_set_track_changed_event(&rundb_track_changed); 334 + memset(&rundbentry,0,sizeof(struct rundb_entry)); 335 + rundbsize=lseek(rundb_fd,0,SEEK_END); 336 + return 0; 337 + } 338 + 339 + void writerundbheader(void) { 340 + lseek(rundb_fd,0,SEEK_SET); 341 + write(rundb_fd, &rundbheader, 8); 342 + fsync(rundb_fd); 343 + } 344 + 345 + #define getrundbentrybyrecord(_x_) getrundbentrybyoffset(8+_x_*20) 261 346 347 + void getrundbentrybyoffset(int offset) { 348 + lseek(rundb_fd,offset,SEEK_SET); 349 + read(rundb_fd,&rundbentry,20); 350 + currentreoffset=offset; 351 + #ifdef ROCKBOX_LITTLE_ENDIAN 352 + rundbentry.fileentry=BE32(rundbentry.fileentry); 353 + rundbentry.hash=BE32(rundbentry.hash); 354 + rundbentry.rating=BE16(rundbentry.rating); 355 + rundbentry.voladjust=BE16(rundbentry.voladjust); 356 + rundbentry.playcount=BE32(rundbentry.playcount); 357 + rundbentry.lastplayed=BE32(rundbentry.lastplayed); 262 358 #endif 359 + } 360 + 361 + int getrundbentrybyhash(int hash) { 362 + int min=0; 363 + for(min=0;min<rundbheader.entrycount;min++) { 364 + getrundbentrybyrecord(min); 365 + if(hash==rundbentry.hash) 366 + return 1; 367 + } 368 + memset(&rundbentry,0,sizeof(struct rundb_entry)); 369 + return 0; 370 + } 371 + 372 + void writerundbentry(void) { 373 + if(rundbentry.hash==0) // 0 = invalid rundb info. 374 + return; 375 + lseek(rundb_fd,currentreoffset,SEEK_SET); 376 + write(rundb_fd,&rundbentry,20); 377 + fsync(rundb_fd); 378 + } 379 + 380 + void loadruntimeinfo(char *filename) { 381 + memset(&rundbentry,0,sizeof(struct rundb_entry)); 382 + valid_file=0; 383 + if(!getfentrybyfilename(filename)) 384 + return; /* file is not in tagdatabase, could not load. */ 385 + valid_file=1; 386 + if(fe.rundbentry!=-1&&fe.rundbentry<rundbsize) { 387 + logf("load rundbentry: 0x%x",fe.rundbentry); 388 + getrundbentrybyoffset(fe.rundbentry); 389 + if(fe.hash!=rundbentry.hash) { 390 + logf("Rundb: Hash mismatch. trying to repair entry.",fe.hash,rundbentry.hash); 391 + addrundbentry(); 392 + } 393 + } 394 + else // add new rundb entry. 395 + addrundbentry(); 396 + } 397 + 398 + void addrundbentry() { 399 + // first search for an entry with an equal hash. 400 + if(getrundbentrybyhash(fe.hash)) { 401 + logf("Found existing rundb entry: 0x%x",currentreoffset); 402 + fe.rundbentry=currentreoffset; 403 + writefentry(); 404 + return; 405 + } 406 + rundbheader.entrycount++; 407 + writerundbheader(); 408 + fe.rundbentry=currentreoffset=lseek(rundb_fd,0,SEEK_END); 409 + logf("Add rundb entry: 0x%x hash: 0x%x",fe.rundbentry,fe.hash); 410 + rundbentry.hash=fe.hash; 411 + rundbentry.fileentry=currentfeoffset; 412 + writefentry(); 413 + writerundbentry(); 414 + rundbsize=lseek(rundb_fd,0,SEEK_END); 415 + } 416 + 417 + void increaseplaycount(void) { 418 + if(rundbentry.hash==0) // 0 = invalid rundb info. 419 + return; 420 + rundbentry.playcount++; 421 + writerundbentry(); 422 + } 423 + 424 + void setrating(int rating) { 425 + if(rundbentry.hash==0) // 0 = invalid rundb info. 426 + return; 427 + rundbentry.rating=rating; 428 + writerundbentry(); 429 + } 430 + 431 + /*** end RuntimeDatabase code ***/
+8
apps/database.h
··· 74 74 75 75 #define TAGDB_VERSION 3 76 76 77 + extern int rundb_fd, rundb_initialized; 78 + extern struct rundb_header rundbheader; 79 + 77 80 struct rundb_header { 78 81 int version; 79 82 int entrycount; ··· 92 95 93 96 #define RUNDB_VERSION 1 94 97 98 + void tagdb_shutdown(void); 99 + void addrundbentry(void); 100 + void loadruntimeinfo(char *filename); 101 + void increaseplaycount(void); 102 + void setrating(int rating); 95 103 #endif
+1
apps/main.c
··· 288 288 pcm_init_recording(); 289 289 #endif 290 290 talk_init(); 291 + rundb_init(); 291 292 292 293 #ifdef AUTOROCK 293 294 {
+3
apps/plugin.c
··· 329 329 #ifdef ROCKBOX_HAS_LOGF 330 330 logf, 331 331 #endif 332 + &rundbheader, 333 + &rundb_fd, 334 + &rundb_initialized, 332 335 strncmp, 333 336 }; 334 337
+4 -1
apps/plugin.h
··· 87 87 #endif 88 88 89 89 /* increase this every time the api struct changes */ 90 - #define PLUGIN_API_VERSION 42 90 + #define PLUGIN_API_VERSION 44 91 91 92 92 /* update this to latest version if a change to the api struct breaks 93 93 backwards compatibility (and please take the opportunity to sort in any ··· 399 399 #ifdef ROCKBOX_HAS_LOGF 400 400 void (*logf)(const char *fmt, ...); 401 401 #endif 402 + struct rundb_header *rundbheader; 403 + int *rundb_fd; 404 + int *rundb_initialized; 402 405 int (*strncmp)(const char *, const char *, size_t); 403 406 }; 404 407
+1 -1
apps/plugins/databox/edittoken.h
··· 88 88 89 89 struct token { 90 90 char kind; 91 - char spelling[SPELLING_LENGTH + 1]; 91 + char spelling[SPELLING_LENGTH + 3]; 92 92 long intvalue; 93 93 }; 94 94 char *tokentypetostring(int tokentype);
+13 -2
apps/plugins/searchengine/dbinterface.c
··· 29 29 #define FILEENTRY_SIZE (rb->tagdbheader->filelen+12) 30 30 #define ALBUMENTRY_SIZE (rb->tagdbheader->albumlen+4+rb->tagdbheader->songarraylen*4) 31 31 #define ARTISTENTRY_SIZE (rb->tagdbheader->artistlen+rb->tagdbheader->albumarraylen*4) 32 + #define RUNDBENTRY_SIZE 20 32 33 33 34 #define FILERECORD2OFFSET(_x_) (rb->tagdbheader->filestart + _x_ * FILEENTRY_SIZE) 34 35 ··· 45 46 // zero all entries. 46 47 for(i=0;i<sizeof(struct entry)*rb->tagdbheader->filecount;i++) 47 48 *(p++)=0; 48 - if(*rb->tagdb_initialized!=1) { 49 + if(!*rb->tagdb_initialized) { 49 50 if(!rb->tagdb_init()) { 50 51 // failed loading db 51 52 return -1; ··· 110 111 } 111 112 112 113 void loadrundbdata() { 113 - // we don't do this yet. 114 114 currententry->loadedrundbdata=1; 115 + if(!*rb->rundb_initialized) 116 + return; 117 + if(currententry->rundbentry==-1) 118 + return; 119 + rb->lseek(*rb->rundb_fd,currententry->rundbentry,SEEK_SET); 120 + currententry->rundbfe=readlong(*rb->rundb_fd); 121 + currententry->rundbhash=readlong(*rb->rundb_fd); 122 + currententry->rating=readshort(*rb->rundb_fd); 123 + currententry->voladj=readshort(*rb->rundb_fd); 124 + currententry->playcount=readlong(*rb->rundb_fd); 125 + currententry->lastplayed=readlong(*rb->rundb_fd); 115 126 } 116 127 117 128 void loadartistname() {
+5 -3
apps/plugins/searchengine/dbinterface.h
··· 30 30 loadedalbumname, 31 31 loadedartistname; 32 32 char *filename; 33 - long hash; 34 - long songentry; 33 + long hash,rundbhash; 34 + long songentry,rundbfe; 35 35 long rundbentry; 36 36 short year; 37 37 short bitrate; 38 - long rating; 38 + short rating; 39 39 long playcount; 40 + long lastplayed; 41 + short voladj; 40 42 char *title; 41 43 char *genre; 42 44 long artistoffset;
+4 -5
apps/plugins/searchengine/token.h
··· 54 54 #define INTVALUE_GENRE 17 55 55 #define INTVALUE_FILENAME 18 56 56 57 - /* static char *spelling[] = { "not", "and", "or",">",">=","<", "<=","==","!=", 58 - "contains","(",")" }; */ 57 + #define SPELLING_LENGTH 100 59 58 60 59 struct token { 61 - unsigned char kind; 62 - char spelling[255]; // 255 should make it aligned again.. 63 - long intvalue; 60 + char kind; 61 + char spelling[SPELLING_LENGTH + 3]; 62 + long intvalue; 64 63 }; 65 64 66 65 char *getstring(struct token *token);
+2 -2
tools/songdb.pl
··· 9 9 10 10 use vorbiscomm; 11 11 12 - my $db = "rockbox.id3db"; 12 + my $db = "rockbox.tagdb"; 13 13 my $dir; 14 14 my $strip; 15 15 my $add; ··· 626 626 my $str = $f."\x00" x ($maxfilelen- length($f)); 627 627 my $id3 = $entries{$f}; 628 628 print DB $str; 629 - dumpint(0); 629 + dumpint(0); # TODO: add hashing; 0 for now. 630 630 dumpint($id3->{'songoffset'}); 631 631 dumpint(-1); 632 632 }
+7 -6
tools/testdbv2.c
··· 128 128 void showrundb(int offset) { 129 129 fseek(fp2,offset,SEEK_SET); 130 130 fread(&RundbEntry,sizeof(struct RundbEntry),1,fp2); 131 - RundbEntry.hash=BE32(RundbEntry.hash); 132 - RundbEntry.playcount=BE32(RundbEntry.playcount); 133 - RundbEntry.lastplayed=BE32(RundbEntry.lastplayed); 134 - RundbEntry.rating=BE16(RundbEntry.rating); 135 - RundbEntry.voladj=BE16(RundbEntry.voladj); 136 - printf("Offset: 0x%x\nHash: 0x%x\nRating: %d\nVoladj: 0x%x\n",offset,RundbEntry.hash,RundbEntry.rating,RundbEntry.voladj); 131 + RundbEntry.file=BE32(RundbEntry.file); 132 + RundbEntry.hash=BE32(RundbEntry.hash); 133 + RundbEntry.playcount=BE32(RundbEntry.playcount); 134 + RundbEntry.lastplayed=BE32(RundbEntry.lastplayed); 135 + RundbEntry.rating=BE16(RundbEntry.rating); 136 + RundbEntry.voladj=BE16(RundbEntry.voladj); 137 + printf("Offset: 0x%x\nFileEntry: 0x%x\nHash: 0x%x\nRating: %d\nVoladj: 0x%x\n",offset,RundbEntry.file,RundbEntry.hash,RundbEntry.rating,RundbEntry.voladj); 137 138 printf("Playcount: 0x%x\nLastplayed: %d\n",RundbEntry.playcount,RundbEntry.lastplayed); 138 139 } 139 140