summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Holmgren <magnushol@gmail.com>2007-08-25 10:25:13 +0000
committerMagnus Holmgren <magnushol@gmail.com>2007-08-25 10:25:13 +0000
commit4e687327691e55282941e4493fca6d606281cdd0 (patch)
treea2c1be847f5a09ea99bfb91177f07e8ac0a4abad
parent163d72ff1f680f81555c9cdd664fef8bde87df24 (diff)
downloadrockbox-4e687327691e55282941e4493fca6d606281cdd0.tar.gz
rockbox-4e687327691e55282941e4493fca6d606281cdd0.zip
Small ReplayGain cleanup. Move RVA2 specific code to id3.c and don't do the parsing via strings. Generalize parts of the code for later use by MPC. Make some local functions static. Add and update some comments for clarity.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14453 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/export/replaygain.h4
-rw-r--r--firmware/id3.c76
-rw-r--r--firmware/replaygain.c149
3 files changed, 131 insertions, 98 deletions
diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h
index ca9b654ce9..ec5bbd1a8c 100644
--- a/firmware/export/replaygain.h
+++ b/firmware/export/replaygain.h
@@ -23,11 +23,9 @@
23#include "id3.h" 23#include "id3.h"
24 24
25long get_replaygain_int(long int_gain); 25long get_replaygain_int(long int_gain);
26long get_replaygain(const char* str);
27long get_replaypeak(const char* str);
28long parse_replaygain(const char* key, const char* value, 26long parse_replaygain(const char* key, const char* value,
29 struct mp3entry* entry, char* buffer, int length); 27 struct mp3entry* entry, char* buffer, int length);
30long parse_replaygain_rva(const char* key, const char* value, 28long parse_replaygain_int(bool album, long gain, long peak,
31 struct mp3entry* entry, char* buffer, int length); 29 struct mp3entry* entry, char* buffer, int length);
32 30
33#endif 31#endif
diff --git a/firmware/id3.c b/firmware/id3.c
index 184bdb5725..a0dda92eb9 100644
--- a/firmware/id3.c
+++ b/firmware/id3.c
@@ -400,8 +400,6 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
400 int desc_len = strlen(tag); 400 int desc_len = strlen(tag);
401 int value_len = 0; 401 int value_len = 0;
402 402
403 /* Note: for ID3v2.4, parse_replaygain will not overwrite replaygain
404 values already parsed from RVA2 tags */
405 if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { 403 if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) {
406 /* At least part of the value was read, so we can safely try to 404 /* At least part of the value was read, so we can safely try to
407 * parse it 405 * parse it
@@ -411,37 +409,71 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
411 bufferpos - (tag - entry->id3v2buf)); 409 bufferpos - (tag - entry->id3v2buf));
412 } 410 }
413 411
414 if (value_len) { 412 return tag - entry->id3v2buf + value_len;
415 bufferpos = tag - entry->id3v2buf + value_len;
416 } else {
417 bufferpos = tag - entry->id3v2buf;
418 }
419
420 return bufferpos;
421} 413}
422 414
423/* parse RVA2 binary data and convert to replaygain information. */ 415/* parse RVA2 binary data and convert to replaygain information. */
424static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) 416static int parserva2( struct mp3entry* entry, char* tag, int bufferpos )
425{ 417{
426 char* value = NULL;
427 int desc_len = strlen(tag); 418 int desc_len = strlen(tag);
419 int end_pos = tag - entry->id3v2buf + desc_len + 5;
428 int value_len = 0; 420 int value_len = 0;
421 unsigned char* value = tag + desc_len + 1;
429 422
430 /* Only parse RVA2 replaygain tags if tag version == 2.4 */ 423 /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel
431 if (entry->id3version == ID3_VER_2_4 && 424 * type is master volume.
432 (tag - entry->id3v2buf + desc_len + 2) < bufferpos) { 425 */
433 value = tag + desc_len + 1; 426 if (entry->id3version == ID3_VER_2_4 && end_pos < bufferpos
434 value_len = parse_replaygain_rva(tag, value, entry, tag, 427 && *value++ == 1) {
435 bufferpos - (tag - entry->id3v2buf)); 428 long gain = 0;
436 } 429 long peak = 0;
430 long peakbits;
431 long peakbytes;
432 bool album = false;
433
434 /* The RVA2 specification is unclear on some things (id string and
435 * peak volume), but this matches how Quod Libet use them.
436 */
437
438 gain = (int16_t) ((value[0] << 8) | value[1]);
439 value += 2;
440 peakbits = *value++;
441 peakbytes = (peakbits + 7) / 8;
442
443 /* Only use the topmost 24 bits for peak volume */
444 if (peakbytes > 3) {
445 peakbytes = 3;
446 }
437 447
438 if (value_len) { 448 /* Make sure the peak bits were read */
439 bufferpos = tag - entry->id3v2buf + value_len; 449 if (end_pos + peakbytes < bufferpos) {
440 } else { 450 long shift = ((8 - (peakbits & 7)) & 7) + (3 - peakbytes) * 8;
441 bufferpos = tag - entry->id3v2buf; 451
452 for ( ; peakbytes; peakbytes--) {
453 peak <<= 8;
454 peak += *value++;
455 }
456
457 peak <<= shift;
458
459 if (peakbits > 24) {
460 peak += *value >> (8 - shift);
461 }
462 }
463
464 if (strcasecmp(tag, "album") == 0) {
465 album = true;
466 } else if (strcasecmp(tag, "track") != 0) {
467 gain = 0;
468 }
469
470 if (gain) {
471 value_len = parse_replaygain_int(album, gain, peak * 2, entry,
472 tag, sizeof(entry->id3v2buf) - (tag - entry->id3v2buf));
473 }
442 } 474 }
443 475
444 return bufferpos; 476 return tag - entry->id3v2buf + value_len;
445} 477}
446#endif 478#endif
447 479
diff --git a/firmware/replaygain.c b/firmware/replaygain.c
index c934586f4b..07726f19da 100644
--- a/firmware/replaygain.c
+++ b/firmware/replaygain.c
@@ -28,10 +28,6 @@
28#include "id3.h" 28#include "id3.h"
29#include "debug.h" 29#include "debug.h"
30 30
31/* Type of channel for RVA2 frame. There are more than this defined in the spec
32 but we don't use them. */
33#define MASTER_CHANNEL 1
34
35/* The fixed point math routines (with the exception of fp_atof) are based 31/* The fixed point math routines (with the exception of fp_atof) are based
36 * on oMathFP by Dan Carter (http://orbisstudios.com). 32 * on oMathFP by Dan Carter (http://orbisstudios.com).
37 */ 33 */
@@ -306,12 +302,12 @@ static long convert_gain(long gain)
306 return gain; 302 return gain;
307} 303}
308 304
309long get_replaygain_int(long int_gain) 305/* Get the sample scale factor in Q7.24 format from a gain value. Returns 0
310{ 306 * for no gain.
311 return convert_gain(int_gain * FP_ONE / 100); 307 *
312} 308 * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
313 309 */
314long get_replaygain(const char* str) 310static long get_replaygain(const char* str)
315{ 311{
316 long gain = 0; 312 long gain = 0;
317 313
@@ -324,7 +320,11 @@ long get_replaygain(const char* str)
324 return gain; 320 return gain;
325} 321}
326 322
327long get_replaypeak(const char* str) 323/* Get the peak volume in Q7.24 format.
324 *
325 * str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
326 */
327static long get_replaypeak(const char* str)
328{ 328{
329 long peak = 0; 329 long peak = 0;
330 330
@@ -336,12 +336,24 @@ long get_replaypeak(const char* str)
336 return peak; 336 return peak;
337} 337}
338 338
339/* Check for a ReplayGain tag conforming to the "VorbisGain standard". If 339/* Get a sample scale factor in Q7.24 format from a gain value.
340 * found, set the mp3entry accordingly. buffer is where to store the text 340 *
341 * contents of the gain tags; up to length bytes (including end nil) can be 341 * int_gain Gain in dB, multiplied by 100.
342 * written. Returns number of bytes written to the tag text buffer, or zero 342 */
343 * if no ReplayGain tag was found (or nothing was copied to the buffer for 343long get_replaygain_int(long int_gain)
344 * other reasons). 344{
345 return convert_gain(int_gain * FP_ONE / 100);
346}
347
348/* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
349 * valid tag is found, update mp3entry struct accordingly. Existing values
350 * are not overwritten. Returns number of bytes written to buffer.
351 *
352 * key Name of the tag.
353 * value Value of the tag.
354 * entry mp3entry struct to update.
355 * buffer Where to store the text for gain values (for later display).
356 * length Bytes left in buffer.
345 */ 357 */
346long parse_replaygain(const char* key, const char* value, 358long parse_replaygain(const char* key, const char* value,
347 struct mp3entry* entry, char* buffer, int length) 359 struct mp3entry* entry, char* buffer, int length)
@@ -390,74 +402,65 @@ long parse_replaygain(const char* key, const char* value,
390 return 0; 402 return 0;
391} 403}
392 404
393static long get_rva_values(const char *frame, long *gain, long *peak, 405/* Set ReplayGain values from integers. Existing values are not overwritten.
394 char **string, char *buffer, int length) 406 * Returns number of bytes written to buffer.
407 *
408 * album If true, set album values, otherwise set track values.
409 * gain Gain value in dB, multiplied by 512. 0 for no gain.
410 * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
411 * peak volume.
412 * buffer Where to store the text for gain values (for later display).
413 * length Bytes left in buffer.
414 */
415long parse_replaygain_int(bool album, long gain, long peak,
416 struct mp3entry* entry, char* buffer, int length)
395{ 417{
396 long value, len; 418 long len = 0;
397 int negative = 0;
398 char tmpbuf[10];
399 int peakbits, peakbytes, shift;
400 unsigned long peakvalue = 0;
401 419
402 value = 256 * ((unsigned char)*frame) + ((unsigned char)*(frame + 1)); 420 if (buffer != NULL)
403 if (value & 0x8000)
404 { 421 {
405 value = -(value | ~0xFFFF); 422 len = snprintf(buffer, length, "%d.%02d dB", gain / 512,
406 negative = 1; 423 ((abs(gain) & 0x01ff) * 100 + 256) / 512);
424 len++;
407 } 425 }
408 426
409 len = snprintf(tmpbuf, sizeof(tmpbuf), "%s%d.%02d dB", negative ? "-" : "", 427 if (gain != 0)
410 value / 512, (value & 0x1FF) * 195 / 1000);
411
412 *gain = get_replaygain(tmpbuf);
413
414 len = MIN(len, length - 1);
415 if (len > 1)
416 { 428 {
417 strncpy(buffer, tmpbuf, len); 429 gain = convert_gain(gain * FP_ONE / 512);
418 buffer[len] = 0;
419 *string = buffer;
420 } 430 }
421 431
422 frame += 2; 432 DEBUGF(" Album: %d\n", album);
423 peakbits = *(unsigned char *)frame++; 433 DEBUGF(" Gain: %ld.%06ld\n", gain >> 24,
424 peakbytes = MIN(4, (peakbits + 7) >> 3); 434 (long) (((long long) (abs(gain) & 0x00ffffff) * 1000000) / 0x01000000));
425 shift = ((8 - (peakbits & 7)) & 7) + (4 - peakbytes) * 8; 435 DEBUGF(" Peak: %ld.%06ld\n", peak >> 24,
436 (long) (((long long) (abs(peak) & 0x00ffffff) * 1000000) / 0x01000000));
426 437
427 for (; peakbytes; peakbytes--) 438 if (album)
428 { 439 {
429 peakvalue <<= 8; 440 if (!entry->album_gain)
430 peakvalue += (unsigned long)*frame++; 441 {
431 } 442 entry->album_gain = gain;
432 443 entry->album_gain_string = buffer;
433 peakvalue <<= shift; 444 }
434
435 if (peakbits > 32)
436 peakvalue += (unsigned long)*frame >> (8 - shift);
437
438 snprintf(tmpbuf, sizeof(tmpbuf), "%d.%06d", peakvalue >> 31,
439 (peakvalue & ~(1 << 31)) / 2147);
440
441 *peak = get_replaypeak(tmpbuf);
442
443 return len + 1;
444}
445 445
446long parse_replaygain_rva(const char* key, const char* value, 446 if (!entry->album_peak)
447 struct mp3entry* entry, char* buffer, int length) 447 {
448{ 448 entry->album_peak = peak;
449 /* Values will be overwritten if they already exist. This gives priority to 449 }
450 replaygain in RVA2 fields over TXXX fields for ID3v2.4. */
451 if ((strcasecmp(key, "track") == 0) && *value == MASTER_CHANNEL)
452 {
453 return get_rva_values(value + 1, &(entry->track_gain), &(entry->track_peak),
454 &(entry->track_gain_string), buffer, length);
455 } 450 }
456 else if ((strcasecmp(key, "album") == 0) && *value == MASTER_CHANNEL) 451 else
457 { 452 {
458 return get_rva_values(value + 1, &(entry->album_gain), &(entry->album_peak), 453 if (!entry->track_gain)
459 &(entry->album_gain_string), buffer, length); 454 {
460 } 455 entry->track_gain = gain;
456 entry->track_gain_string = buffer;
457 }
461 458
462 return 0; 459 if (!entry->track_peak)
460 {
461 entry->track_peak = peak;
462 }
463 }
464
465 return len;
463} 466}