diff options
Diffstat (limited to 'firmware')
-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 | } |