summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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}