diff options
author | Magnus Holmgren <magnushol@gmail.com> | 2007-08-25 10:25:13 +0000 |
---|---|---|
committer | Magnus Holmgren <magnushol@gmail.com> | 2007-08-25 10:25:13 +0000 |
commit | 4e687327691e55282941e4493fca6d606281cdd0 (patch) | |
tree | a2c1be847f5a09ea99bfb91177f07e8ac0a4abad | |
parent | 163d72ff1f680f81555c9cdd664fef8bde87df24 (diff) | |
download | rockbox-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.h | 4 | ||||
-rw-r--r-- | firmware/id3.c | 76 | ||||
-rw-r--r-- | firmware/replaygain.c | 149 |
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 | ||
25 | long get_replaygain_int(long int_gain); | 25 | long get_replaygain_int(long int_gain); |
26 | long get_replaygain(const char* str); | ||
27 | long get_replaypeak(const char* str); | ||
28 | long parse_replaygain(const char* key, const char* value, | 26 | long parse_replaygain(const char* key, const char* value, |
29 | struct mp3entry* entry, char* buffer, int length); | 27 | struct mp3entry* entry, char* buffer, int length); |
30 | long parse_replaygain_rva(const char* key, const char* value, | 28 | long 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. */ |
424 | static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) | 416 | static 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 | ||
309 | long 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 | */ | |
314 | long get_replaygain(const char* str) | 310 | static 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 | ||
327 | long 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 | */ | ||
327 | static 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 | 343 | long 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 | */ |
346 | long parse_replaygain(const char* key, const char* value, | 358 | long 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 | ||
393 | static 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 | */ | ||
415 | long 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 | ||
446 | long 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 | } |