Index: rockbox/apps/onplay.c =================================================================== RCS file: /cvsroot/rockbox/apps/onplay.c,v retrieving revision 1.71 diff -u -r1.71 onplay.c --- rockbox/apps/onplay.c 9 Feb 2006 09:09:32 -0000 1.71 +++ rockbox/apps/onplay.c 26 Feb 2006 20:56:00 -0000 @@ -146,8 +146,8 @@ static bool shuffle_playlist(void) { playlist_sort(NULL, true); - playlist_randomise(NULL, current_tick, true); - + playlist_randomise(NULL, current_tick, true); + return false; } @@ -176,18 +176,18 @@ { /* Ask if user wants to recurse directory */ bool exit = false; - + lcd_clear_display(); lcd_puts_scroll(0, 0, str(LANG_RECURSE_DIRECTORY_QUESTION)); lcd_puts_scroll(0, 1, (unsigned char *)selected_file); - + #ifdef HAVE_LCD_BITMAP lcd_puts(0, 3, str(LANG_CONFIRM_WITH_PLAY_RECORDER)); - lcd_puts(0, 4, str(LANG_CANCEL_WITH_ANY_RECORDER)); + lcd_puts(0, 4, str(LANG_CANCEL_WITH_ANY_RECORDER)); #endif - + lcd_update(); - + while (!exit) { int btn = button_get(true); switch (btn) { @@ -243,7 +243,7 @@ /* Sub-menu for playlist options */ static bool playlist_options(void) { - struct menu_item items[12]; + struct menu_item items[12]; struct playlist_args args[12]; /* increase these 2 if you add entries! */ int m, i=0, pstart=0, result; bool ret = false; @@ -264,18 +264,18 @@ items[i].function = playlist_viewer; i++; pstart++; - + items[i].desc = ID2P(LANG_SAVE_DYNAMIC_PLAYLIST); items[i].function = save_playlist; i++; pstart++; - + items[i].desc = ID2P(LANG_SHUFFLE_PLAYLIST); items[i].function = shuffle_playlist; i++; - pstart++; + pstart++; } - + if (context == CONTEXT_TREE || context == CONTEXT_ID3DB) { if (audio_status() & AUDIO_STATUS_PLAY) @@ -299,7 +299,7 @@ args[i].position = PLAYLIST_INSERT_SHUFFLED; args[i].queue = false; i++; - + items[i].desc = ID2P(LANG_QUEUE); args[i].position = PLAYLIST_INSERT; args[i].queue = true; @@ -348,7 +348,7 @@ int result = 0; DIR* dir; int dirlen = strlen(dirname); - + dir = opendir(dirname); if (!dir) return -1; /* open error */ @@ -364,17 +364,17 @@ /* append name to current directory */ snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); - if (entry->attribute & ATTR_DIRECTORY) + if (entry->attribute & ATTR_DIRECTORY) { /* remove a subdirectory */ if (!strcmp((char *)entry->d_name, ".") || !strcmp((char *)entry->d_name, "..")) continue; /* skip these */ result = remove_dir(dirname, len); /* recursion */ - if (result) + if (result) break; /* or better continue, delete what we can? */ } - else + else { /* remove a file */ result = remove(dirname); } @@ -384,7 +384,7 @@ if (!result) { /* remove the now empty directory */ dirname[dirlen] = '\0'; /* terminate to original length */ - + result = rmdir(dirname); } @@ -446,7 +446,7 @@ ret = read_bmp_file(selected_file, &bm, sizeof(main_backdrop), FORMAT_NATIVE); - if ((ret > 0) && (bm.width == LCD_WIDTH) + if ((ret > 0) && (bm.width == LCD_WIDTH) && (bm.height == LCD_HEIGHT)) { lcd_set_backdrop(&main_backdrop[0][0]); gui_syncsplash(HZ, true, str(LANG_BACKDROP_LOADED)); @@ -491,7 +491,7 @@ cwd = getcwd(NULL, 0); memset(dirname, 0, sizeof dirname); - + snprintf(dirname, sizeof dirname, "%s/", cwd[1] ? cwd : ""); @@ -552,7 +552,7 @@ items[i].function = sound_menu; i++; } - + if (context == CONTEXT_WPS || context == CONTEXT_TREE || ((context == CONTEXT_ID3DB) && @@ -569,7 +569,7 @@ items[i].function = bookmark_menu; i++; } - + if (file) { if (context == CONTEXT_WPS) @@ -577,13 +577,17 @@ items[i].desc = ID2P(LANG_MENU_SHOW_ID3_INFO); items[i].function = browse_id3; i++; + /*DCB*/ + items[i].desc = ID2P(LANG_MENU_SHOW_LYRICS); + items[i].function = show_id3_lyrics; + i++; if(rundb_initialized) { items[i].desc = ID2P(LANG_MENU_SET_RATING); items[i].function = set_rating; i++; } } - + #ifdef HAVE_MULTIVOLUME if (!(attr & ATTR_VOLUME)) /* no rename+delete for volumes */ #endif @@ -659,7 +663,7 @@ if (result >= 0) items[result].function(); menu_exit(m); - + if (exit_to_main) result = main_menu(); Index: rockbox/apps/screens.c =================================================================== RCS file: /cvsroot/rockbox/apps/screens.c,v retrieving revision 1.138 diff -u -r1.138 screens.c --- rockbox/apps/screens.c 21 Jan 2006 23:43:56 -0000 1.138 +++ rockbox/apps/screens.c 26 Feb 2006 20:56:02 -0000 @@ -5,7 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id: screens.c,v 1.138 2006-01-21 23:43:56 lostlogic Exp $ + * $Id: screens.c,v 1.138 2006/01/21 23:43:56 lostlogic Exp $ * * Copyright (C) 2002 Björn Stenberg * @@ -1074,7 +1074,7 @@ playlist_amount()); line = draw_id3_item(line, top, LANG_ID3_PLAYLIST, buf); - snprintf(buf, sizeof(buf), "%d kbps%s", id3->bitrate, + snprintf(buf, sizeof(buf), "%d kbps%s", id3->bitrate, id3->vbr ? str(LANG_ID3_VBR) : (const unsigned char*) ""); line = draw_id3_item(line, top, LANG_ID3_BITRATE, buf); @@ -1168,6 +1168,23 @@ return false; } +/*DCB Open the lyrics text file with the viewer*/ +bool show_id3_lyrics(void) +{ + bool finish; + int file_test; + file_test=open("/.rockbox/lyrics.txt", O_RDONLY); + + if (file_test >= 0) + { + close(file_test); + finish = plugin_load(ROCKBOX_DIR "/viewers/viewer.rock" ,ROCKBOX_DIR "/lyrics.txt"); + } + else + gui_syncsplash(HZ*2, true, "No compatable lyrics available"); + return false; +} + bool set_rating(void) { struct mp3entry* id3 = audio_current_track(); Index: rockbox/apps/screens.h =================================================================== RCS file: /cvsroot/rockbox/apps/screens.h,v retrieving revision 1.25 diff -u -r1.25 screens.h --- rockbox/apps/screens.h 5 Dec 2005 19:04:50 -0000 1.25 +++ rockbox/apps/screens.h 26 Feb 2006 20:56:02 -0000 @@ -5,7 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id: screens.h,v 1.25 2005-12-05 19:04:50 preglow Exp $ + * $Id: screens.h,v 1.25 2005/12/05 19:04:50 preglow Exp $ * * Copyright (C) 2002 Björn Stenberg * @@ -37,7 +37,7 @@ || CONFIG_KEYPAD == IRIVER_H300_PAD int pitch_screen(void); #endif -#if CONFIG_KEYPAD == RECORDER_PAD +#if CONFIG_KEYPAD == RECORDER_PAD extern bool quick_screen_f3(int button_enter); #endif extern bool quick_screen_quick(int button_enter); @@ -48,6 +48,7 @@ bool shutdown_screen(void); bool browse_id3(void); +bool show_id3_lyrics(void); /*DCB*/ bool set_rating(void); #endif Index: rockbox/apps/lang/english.lang =================================================================== RCS file: /cvsroot/rockbox/apps/lang/english.lang,v retrieving revision 1.223 diff -u -r1.223 english.lang --- rockbox/apps/lang/english.lang 26 Feb 2006 02:48:05 -0000 1.223 +++ rockbox/apps/lang/english.lang 26 Feb 2006 20:56:06 -0000 @@ -24,7 +24,7 @@ eng: "General Settings" voice: "General Settings" new: - +seeme id: LANG_INFO desc: in the main menu eng: "Info" @@ -3778,3 +3778,9 @@ eng: "Reset Colours" voice: new: + +id: LANG_MENU_SHOW_LYRICS +desc: in onplay menu. Show lyrics embedded in id3 DCB +eng: "Show Lyrics" +voice: "Show Lyrics" +new: \ No newline at end of file Index: rockbox/firmware/id3.c =================================================================== RCS file: /cvsroot/rockbox/firmware/id3.c,v retrieving revision 1.125 diff -u -r1.125 id3.c --- rockbox/firmware/id3.c 1 Feb 2006 16:42:02 -0000 1.125 +++ rockbox/firmware/id3.c 26 Feb 2006 20:56:09 -0000 @@ -5,7 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id: id3.c,v 1.125 2006-02-01 16:42:02 dave Exp $ + * $Id: id3.c,v 1.125 2006/02/01 16:42:02 dave Exp $ * * Copyright (C) 2002 by Daniel Stenberg * @@ -22,7 +22,7 @@ * all sorts of friendly Rockbox people. * */ - + /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach */ @@ -135,8 +135,8 @@ numerical values, for cases where a number won't do, e.g., YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" - Text is more flexible, and as the main use of id3 data is to - display it, converting it to an int just means reconverting to + Text is more flexible, and as the main use of id3 data is to + display it, converting it to an int just means reconverting to display it, at a runtime cost.) 2. If any special processing beyond copying the tag value from the Id3 @@ -160,22 +160,22 @@ char* ID3 Tag symbolic name -- see the ID3 specification for these, sizeof() that name minus 1, offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), - pointer to your special processing function or NULL + pointer to your special processing function or NULL if you need no special processing - Many ID3 symbolic names come in more than one form. You can add both - forms, each referencing the same variable in struct mp3entry. + Many ID3 symbolic names come in more than one form. You can add both + forms, each referencing the same variable in struct mp3entry. If both forms are present, the last found will be used. Note that the offset can be zero, in which case no entry will be set in the mp3entry struct; the frame is still read into the buffer and the special processing function is called (several times, if there are several frames with the same name). - + 4. Alternately, use the TAG_LIST_ENTRY macro with - ID3 tag symbolic name, - variable in struct mp3entry, + ID3 tag symbolic name, + variable in struct mp3entry, special processing function address - - 5. Add code to wps-display.c function get_tag to assign a printf-like + + 5. Add code to wps-display.c function get_tag to assign a printf-like format specifier for the tag */ /* Structure for ID3 Tag extraction information */ @@ -195,7 +195,7 @@ unsigned char *rp, *wp; wp = rp = (unsigned char *)tag; - + rp = (unsigned char *)tag; for(i = 0;i < len;i++) { /* Read the next byte and write it back, but don't increment the @@ -232,7 +232,7 @@ char *rp; wp = buf; - + while(remaining) { rp = wp; rc = read(fd, rp, remaining); @@ -318,16 +318,16 @@ char* value = NULL; int desc_len = strlen(tag); int value_len = 0; - + if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { - /* At least part of the value was read, so we can safely try to - * parse it + /* At least part of the value was read, so we can safely try to + * parse it */ value = tag + desc_len + 1; - value_len = parse_replaygain(tag, value, entry, tag, + value_len = parse_replaygain(tag, value, entry, tag, bufferpos - (tag - entry->id3v2buf)); } - + if (value_len) { bufferpos = tag - entry->id3v2buf + value_len; } else { @@ -352,6 +352,11 @@ { "TCOM", 4, offsetof(struct mp3entry, composer), NULL }, { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre }, { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre }, + { "USLT", 4, offsetof(struct mp3entry, lyric_string), NULL }, + /* DCB USLT is the unsynchronised lyrics tag, + 4 letters to USLT, directly after this is the length of the Lyrics in HEX, + After this is the Language, + NULL as we are extracting a string without converting it to a numeral*/ #if CONFIG_CODEC == SWCODEC { "TXXX", 4, 0, &parseuser }, #endif @@ -447,7 +452,7 @@ * * Returns: true if a title was found and created, else false */ -static bool setid3v1title(int fd, struct mp3entry *entry) +static bool setid3v1title(int fd, struct mp3entry *entry) { unsigned char buffer[128]; static const char offsets[] = {3, 33, 63, 93, 125, 127}; @@ -523,11 +528,11 @@ * * Returns: true if a title was found and created, else false */ -static void setid3v2title(int fd, struct mp3entry *entry) +static void setid3v2title(int fd, struct mp3entry *entry) { int minframesize; int size; - long bufferpos = 0, totframelen, framelen; + long bufferpos = 0, totframelen, framelen, uslt_lyriclen=0; /*DCB uslt_lyriclen*/ char header[10]; char tmp[4]; unsigned char version; @@ -544,7 +549,9 @@ int rc; global_ff_found = false; - + + remove("/.rockbox/lyrics.txt"); /*DCB*/ + /* Bail out if the tag is shorter than 10 bytes */ if(entry->id3v2len < 10) return; @@ -584,7 +591,7 @@ entry->title = entry->artist = entry->album = NULL; global_flags = header[5]; - + /* Skip the extended header if it is present */ if(global_flags & 0x40) { if(version == ID3_VER_2_3) { @@ -601,14 +608,14 @@ lseek(fd, framelen - 10, SEEK_CUR); } - + if(version >= ID3_VER_2_4) { if(4 != read(fd, header, 4)) return; /* The 2.4 extended header size does include the entire header, so here we can just skip it. This header is unsynched. */ - framelen = UNSYNC(header[0], header[1], + framelen = UNSYNC(header[0], header[1], header[2], header[3]); lseek(fd, framelen - 4, SEEK_CUR); @@ -619,14 +626,14 @@ if(global_flags & 0x80) { global_unsynch = true; } - - /* - * We must have at least minframesize bytes left for the - * remaining frames to be interesting + + /* + * We must have at least minframesize bytes left for the + * remaining frames to be interesting */ while (size >= minframesize && bufferpos < buffersize - 1) { flags = 0; - + /* Read frame header and check length */ if(version >= ID3_VER_2_3) { if(global_unsynch && version <= ID3_VER_2_3) @@ -639,14 +646,14 @@ size -= 10; flags = BYTES2INT(0, 0, header[8], header[9]); - + if (version >= ID3_VER_2_4) { - framelen = UNSYNC(header[4], header[5], + framelen = UNSYNC(header[4], header[5], header[6], header[7]); } else { /* version .3 files don't use synchsafe ints for * size */ - framelen = BYTES2INT(header[4], header[5], + framelen = BYTES2INT(header[4], header[5], header[6], header[7]); } } else { @@ -654,17 +661,17 @@ return; /* Adjust for the 6 bytes we read */ size -= 6; - + framelen = BYTES2INT(0, header[3], header[4], header[5]); } /* Keep track of the total size */ totframelen = framelen; - + DEBUGF("framelen = %d\n", framelen); if(framelen == 0){ if (header[0] == 0 && header[1] == 0 && header[2] == 0) - return; + return; else continue; } @@ -675,7 +682,7 @@ if(flags) { skip = 0; - + if (version >= ID3_VER_2_4) { if(flags & 0x0040) { /* Grouping identity */ lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ @@ -687,7 +694,7 @@ framelen--; } } - + if(flags & 0x000c) /* Compression or encryption */ { /* Skip it using the total size in case @@ -710,13 +717,16 @@ } } } - + /* If the frame is larger than the remaining buffer space we try to read as much as would fit in the buffer */ if(framelen >= buffersize - bufferpos) framelen = buffersize - bufferpos - 1; DEBUGF("id3v2 frame: %.4s\n", header); + /*DCB Find unsynchronised lyric length*/ + if (header[0] == 'U'&& header[1] =='S'&& header[2] =='L'&& header[3] =='T') + uslt_lyriclen = totframelen-5; /* Check for certain frame headers @@ -724,7 +734,7 @@ the amount of bytes we read. If we fail to read as many bytes as we expect, we assume that we can't read from this file, and bail out. - + For each frame. we will iterate over the list of supported tags, and read the tag into entry's buffer. All tags will be kept as strings, for cases where a number won't do, e.g., YEAR: "circa @@ -733,7 +743,7 @@ flexible, and as the main use of id3 data is to display it, converting it to an int just means reconverting to display it, at a runtime cost. - + For tags that the current code does convert to ints, a post processing function will be called via a pointer to function. */ @@ -742,7 +752,7 @@ char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) : NULL; char* tag; - + /* Only ID3_VER_2_2 uses frames with three-character names. */ if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { @@ -755,24 +765,56 @@ * particular) be updated to handle the case of being called * multiple times, or should the "*ptag" check be removed? */ - if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { - + if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { + /* found a tag matching one in tagList, and not yet filled */ tag = buffer + bufferpos; if(global_unsynch && version <= ID3_VER_2_3) bytesread = read_unsynched(fd, tag, framelen); else - bytesread = read(fd, tag, framelen); - + { + /*DCB Unsynchronised Lyrics found, copy the lyrics to a text file */ + if (header[0] == 'U'&& header[1] =='S'&& header[2] =='L'&& header[3] =='T') + { + long countreadwrite=0; + char textbuffer[9]; + int file_sett; + if(uslt_lyriclen>0) + { + file_sett=open("/.rockbox/lyrics.txt", O_RDWR | O_CREAT); + + if (file_sett >= 0) + { + DEBUGF("Current pointer position%d\n", lseek(fd, 0, SEEK_CUR)); + lseek(fd, 5, SEEK_CUR); + while(countreadwrite++ < uslt_lyriclen/10) + { + bytesread = read(fd, textbuffer, 10); + //DEBUGF("%.10s", textbuffer); + write(file_sett, textbuffer, 10); + } + bytesread = read(fd, textbuffer, uslt_lyriclen%10); + write(file_sett, textbuffer, uslt_lyriclen%10); + DEBUGF("Current pointer position%d\n", lseek(fd, 0, SEEK_CUR)); + close(file_sett); + } + } + } + + bytesread = read(fd, tag, framelen); + } + + + if( bytesread != framelen ) return; - + size -= bytesread; - + if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) bytesread = unsynchronize_frame(tag, bytesread); - + /* UTF-8 could potentially be 3 times larger */ /* so we need to create a new buffer */ @@ -814,7 +856,7 @@ } else { if(data_length_ind) totframelen = data_length_ind; - + size -= totframelen; if( lseek(fd, totframelen, SEEK_CUR) == -1 ) return; @@ -830,7 +872,7 @@ * * Returns: the size of the tag or 0 if none was found */ -int getid3v2len(int fd) +int getid3v2len(int fd) { char buf[6]; int offset; @@ -860,7 +902,7 @@ * Arguments: file - the file to calculate the length upon * entry - the entry to update with the length * - * Returns: the song length in milliseconds, + * Returns: the song length in milliseconds, * 0 means that it couldn't be calculated */ static int getsonglength(int fd, struct mp3entry *entry) @@ -869,7 +911,7 @@ struct mp3info info; long bytecount; - /* Start searching after ID3v2 header */ + /* Start searching after ID3v2 header */ if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) return 0; @@ -880,7 +922,7 @@ if(bytecount < 0) return -1; - + bytecount += entry->id3v2len; /* Validate byte count, in case the file has been edited without @@ -888,11 +930,11 @@ */ if (info.byte_count) { - const unsigned long expected = entry->filesize - entry->id3v1len + const unsigned long expected = entry->filesize - entry->id3v1len - entry->id3v2len; const unsigned long diff = MAX(10240, info.byte_count / 20); - - if ((info.byte_count > expected + diff) + + if ((info.byte_count > expected + diff) || (info.byte_count < expected - diff)) { DEBUGF("Note: info.byte_count differs from expected value by " @@ -931,7 +973,7 @@ rate MP3, so just use the default formula */ filetime = info.file_time; - + if(filetime == 0) { filetime = (entry->filesize - bytecount) / (info.bitrate / 8); @@ -947,11 +989,11 @@ entry->lead_trim = info.enc_delay; entry->tail_trim = info.enc_padding; - + memcpy(entry->toc, info.toc, sizeof(info.toc)); entry->vbr_header_pos = info.vbr_header_pos; - + /* Update the seek point for the first playable frame */ entry->first_frame_offset = bytecount; DEBUGF("First frame is at %x\n", entry->first_frame_offset); @@ -968,7 +1010,7 @@ { int fd; int v1found = false; - + fd = open(filename, O_RDONLY); if(-1 == fd) return true; @@ -978,7 +1020,7 @@ #endif strncpy(entry->path, filename, sizeof(entry->path)); - + entry->title = NULL; entry->filesize = filesize(fd); entry->id3v2len = getid3v2len(fd); @@ -987,7 +1029,7 @@ if(v1first) v1found = setid3v1title(fd, entry); - + if (!v1found && entry->id3v2len) setid3v2title(fd, entry); entry->length = getsonglength(fd, entry); @@ -995,7 +1037,7 @@ /* Subtract the meta information from the file size to get the true size of the MP3 stream */ entry->filesize -= entry->first_frame_offset; - + /* only seek to end of file if no id3v2 tags were found, and we already haven't looked for a v1 tag */ if (!v1first && !entry->id3v2len) { @@ -1038,10 +1080,10 @@ " Title: %s\n" " Artist: %s\n" " Album: %s\n" - " Genre: %s (%d) \n" - " Composer: %s\n" + " Genre: %s (%d) \n" + " Composer: %s\n" " Year: %s (%d)\n" - " Track: %s (%d)\n" + " Track: %s (%d)\n" " Length: %s / %d s\n" " Bitrate: %d\n" " Frequency: %d\n", @@ -1061,7 +1103,7 @@ mp3.bitrate, mp3.frequency); } - + return 0; } Index: rockbox/firmware/export/id3.h =================================================================== RCS file: /cvsroot/rockbox/firmware/export/id3.h,v retrieving revision 1.26 diff -u -r1.26 id3.h --- rockbox/firmware/export/id3.h 1 Feb 2006 16:42:02 -0000 1.26 +++ rockbox/firmware/export/id3.h 26 Feb 2006 20:56:10 -0000 @@ -5,7 +5,7 @@ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ - * $Id: id3.h,v 1.26 2006-02-01 16:42:02 dave Exp $ + * $Id: id3.h,v 1.26 2006/02/01 16:42:02 dave Exp $ * * Copyright (C) 2002 by Daniel Stenberg * @@ -57,6 +57,7 @@ char* track_string; char* year_string; char* composer; + char* lyric_string ; /*dcb*/ int tracknum; int version; int layer; @@ -113,9 +114,9 @@ short voladjust; long playcount; long lastplayed; - + /* replaygain support */ - + #if CONFIG_CODEC == SWCODEC char* track_gain_string; char* album_gain_string;