diff options
Diffstat (limited to 'firmware/replaygain.c')
-rw-r--r-- | firmware/replaygain.c | 149 |
1 files changed, 76 insertions, 73 deletions
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 | } |