diff options
Diffstat (limited to 'firmware/replaygain.c')
-rw-r--r-- | firmware/replaygain.c | 110 |
1 files changed, 93 insertions, 17 deletions
diff --git a/firmware/replaygain.c b/firmware/replaygain.c index a8331ae483..ffabb4b2ac 100644 --- a/firmware/replaygain.c +++ b/firmware/replaygain.c | |||
@@ -28,6 +28,10 @@ | |||
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 | |||
31 | /* The fixed point math routines (with the exception of fp_atof) are based | 35 | /* The fixed point math routines (with the exception of fp_atof) are based |
32 | * on oMathFP by Dan Carter (http://orbisstudios.com). | 36 | * on oMathFP by Dan Carter (http://orbisstudios.com). |
33 | */ | 37 | */ |
@@ -278,7 +282,7 @@ static long fp_atof(const char* s, int precision) | |||
278 | frac_max_int *= 10; | 282 | frac_max_int *= 10; |
279 | } | 283 | } |
280 | 284 | ||
281 | return sign * ((int_part * int_one) | 285 | return sign * ((int_part * int_one) |
282 | + (((int64_t) frac_part * int_one) / frac_max_int)); | 286 | + (((int64_t) frac_part * int_one) / frac_max_int)); |
283 | } | 287 | } |
284 | 288 | ||
@@ -286,14 +290,14 @@ static long convert_gain(long gain) | |||
286 | { | 290 | { |
287 | if (gain != 0) | 291 | if (gain != 0) |
288 | { | 292 | { |
289 | /* Don't allow unreasonably low or high gain changes. | 293 | /* Don't allow unreasonably low or high gain changes. |
290 | * Our math code can't handle it properly anyway. :) | 294 | * Our math code can't handle it properly anyway. :) |
291 | */ | 295 | */ |
292 | if (gain < (-48 * FP_ONE)) | 296 | if (gain < (-48 * FP_ONE)) |
293 | { | 297 | { |
294 | gain = -48 * FP_ONE; | 298 | gain = -48 * FP_ONE; |
295 | } | 299 | } |
296 | 300 | ||
297 | if (gain > (17 * FP_ONE)) | 301 | if (gain > (17 * FP_ONE)) |
298 | { | 302 | { |
299 | gain = 17 * FP_ONE; | 303 | gain = 17 * FP_ONE; |
@@ -301,55 +305,55 @@ static long convert_gain(long gain) | |||
301 | 305 | ||
302 | gain = fp_exp10(gain / 20) << (24 - FP_BITS); | 306 | gain = fp_exp10(gain / 20) << (24 - FP_BITS); |
303 | } | 307 | } |
304 | 308 | ||
305 | return gain; | 309 | return gain; |
306 | } | 310 | } |
307 | 311 | ||
308 | long get_replaygain_int(long int_gain) | 312 | long get_replaygain_int(long int_gain) |
309 | { | 313 | { |
310 | long gain = 0; | 314 | long gain = 0; |
311 | 315 | ||
312 | if (int_gain) | 316 | if (int_gain) |
313 | { | 317 | { |
314 | gain = convert_gain(int_gain * FP_ONE / 100); | 318 | gain = convert_gain(int_gain * FP_ONE / 100); |
315 | } | 319 | } |
316 | 320 | ||
317 | return gain; | 321 | return gain; |
318 | } | 322 | } |
319 | 323 | ||
320 | long get_replaygain(const char* str) | 324 | long get_replaygain(const char* str) |
321 | { | 325 | { |
322 | long gain = 0; | 326 | long gain = 0; |
323 | 327 | ||
324 | if (str) | 328 | if (str) |
325 | { | 329 | { |
326 | gain = fp_atof(str, FP_BITS); | 330 | gain = fp_atof(str, FP_BITS); |
327 | gain = convert_gain(gain); | 331 | gain = convert_gain(gain); |
328 | } | 332 | } |
329 | 333 | ||
330 | return gain; | 334 | return gain; |
331 | } | 335 | } |
332 | 336 | ||
333 | long get_replaypeak(const char* str) | 337 | long get_replaypeak(const char* str) |
334 | { | 338 | { |
335 | long peak = 0; | 339 | long peak = 0; |
336 | 340 | ||
337 | if (str) | 341 | if (str) |
338 | { | 342 | { |
339 | peak = fp_atof(str, 24); | 343 | peak = fp_atof(str, 24); |
340 | } | 344 | } |
341 | 345 | ||
342 | return peak; | 346 | return peak; |
343 | } | 347 | } |
344 | 348 | ||
345 | /* Check for a ReplayGain tag conforming to the "VorbisGain standard". If | 349 | /* Check for a ReplayGain tag conforming to the "VorbisGain standard". If |
346 | * found, set the mp3entry accordingly. buffer is where to store the text | 350 | * found, set the mp3entry accordingly. buffer is where to store the text |
347 | * contents of the gain tags; up to length bytes (including end nil) can be | 351 | * contents of the gain tags; up to length bytes (including end nil) can be |
348 | * written. Returns number of bytes written to the tag text buffer, or zero | 352 | * written. Returns number of bytes written to the tag text buffer, or zero |
349 | * if no ReplayGain tag was found (or nothing was copied to the buffer for | 353 | * if no ReplayGain tag was found (or nothing was copied to the buffer for |
350 | * other reasons). | 354 | * other reasons). |
351 | */ | 355 | */ |
352 | long parse_replaygain(const char* key, const char* value, | 356 | long parse_replaygain(const char* key, const char* value, |
353 | struct mp3entry* entry, char* buffer, int length) | 357 | struct mp3entry* entry, char* buffer, int length) |
354 | { | 358 | { |
355 | char **p = NULL; | 359 | char **p = NULL; |
@@ -359,7 +363,7 @@ long parse_replaygain(const char* key, const char* value, | |||
359 | { | 363 | { |
360 | entry->track_gain = get_replaygain(value); | 364 | entry->track_gain = get_replaygain(value); |
361 | p = &(entry->track_gain_string); | 365 | p = &(entry->track_gain_string); |
362 | } | 366 | } |
363 | else if ((strcasecmp(key, "replaygain_album_gain") == 0) | 367 | else if ((strcasecmp(key, "replaygain_album_gain") == 0) |
364 | || ((strcasecmp(key, "rg_audiophile") == 0) && !entry->album_gain)) | 368 | || ((strcasecmp(key, "rg_audiophile") == 0) && !entry->album_gain)) |
365 | { | 369 | { |
@@ -370,7 +374,7 @@ long parse_replaygain(const char* key, const char* value, | |||
370 | || ((strcasecmp(key, "rg_peak") == 0) && !entry->track_peak)) | 374 | || ((strcasecmp(key, "rg_peak") == 0) && !entry->track_peak)) |
371 | { | 375 | { |
372 | entry->track_peak = get_replaypeak(value); | 376 | entry->track_peak = get_replaypeak(value); |
373 | } | 377 | } |
374 | else if (strcasecmp(key, "replaygain_album_peak") == 0) | 378 | else if (strcasecmp(key, "replaygain_album_peak") == 0) |
375 | { | 379 | { |
376 | entry->album_peak = get_replaypeak(value); | 380 | entry->album_peak = get_replaypeak(value); |
@@ -379,7 +383,7 @@ long parse_replaygain(const char* key, const char* value, | |||
379 | if (p) | 383 | if (p) |
380 | { | 384 | { |
381 | int len = strlen(value); | 385 | int len = strlen(value); |
382 | 386 | ||
383 | len = MIN(len, length - 1); | 387 | len = MIN(len, length - 1); |
384 | 388 | ||
385 | /* A few characters just isn't interesting... */ | 389 | /* A few characters just isn't interesting... */ |
@@ -394,3 +398,75 @@ long parse_replaygain(const char* key, const char* value, | |||
394 | 398 | ||
395 | return 0; | 399 | return 0; |
396 | } | 400 | } |
401 | |||
402 | static long get_rva_values(const char *frame, long *gain, long *peak, | ||
403 | char **string, char *buffer, int length) | ||
404 | { | ||
405 | long value, len; | ||
406 | int negative = 0; | ||
407 | char tmpbuf[10]; | ||
408 | int peakbits, peakbytes, shift; | ||
409 | unsigned long peakvalue = 0; | ||
410 | |||
411 | value = 256 * ((unsigned char)*frame) + ((unsigned char)*(frame + 1)); | ||
412 | if (value & 0x8000) | ||
413 | { | ||
414 | value = -(value | ~0xFFFF); | ||
415 | negative = 1; | ||
416 | } | ||
417 | |||
418 | len = snprintf(tmpbuf, sizeof(tmpbuf), "%s%d.%02d dB", negative ? "-" : "", | ||
419 | value / 512, (value & 0x1FF) * 195 / 1000); | ||
420 | |||
421 | *gain = get_replaygain(tmpbuf); | ||
422 | |||
423 | len = MIN(len, length - 1); | ||
424 | if (len > 1) | ||
425 | { | ||
426 | strncpy(buffer, tmpbuf, len); | ||
427 | buffer[len] = 0; | ||
428 | *string = buffer; | ||
429 | } | ||
430 | |||
431 | frame += 2; | ||
432 | peakbits = *(unsigned char *)frame++; | ||
433 | peakbytes = MIN(4, (peakbits + 7) >> 3); | ||
434 | shift = ((8 - (peakbits & 7)) & 7) + (4 - peakbytes) * 8; | ||
435 | |||
436 | for (; peakbytes; peakbytes--) | ||
437 | { | ||
438 | peakvalue <<= 8; | ||
439 | peakvalue += (unsigned long)*frame++; | ||
440 | } | ||
441 | |||
442 | peakvalue <<= shift; | ||
443 | |||
444 | if (peakbits > 32) | ||
445 | peakvalue += (unsigned long)*frame >> (8 - shift); | ||
446 | |||
447 | snprintf(tmpbuf, sizeof(tmpbuf), "%d.%06d", peakvalue >> 31, | ||
448 | (peakvalue & ~(1 << 31)) / 2147); | ||
449 | |||
450 | *peak = get_replaypeak(tmpbuf); | ||
451 | |||
452 | return len + 1; | ||
453 | } | ||
454 | |||
455 | long parse_replaygain_rva(const char* key, const char* value, | ||
456 | struct mp3entry* entry, char* buffer, int length) | ||
457 | { | ||
458 | if ((strcasecmp(key, "track") == 0) && *value == MASTER_CHANNEL | ||
459 | && !entry->track_gain && !entry->track_peak) | ||
460 | { | ||
461 | return get_rva_values(value + 1, &(entry->track_gain), &(entry->track_peak), | ||
462 | &(entry->track_gain_string), buffer, length); | ||
463 | } | ||
464 | else if ((strcasecmp(key, "album") == 0) && *value == MASTER_CHANNEL | ||
465 | && !entry->album_gain && !entry->album_peak) | ||
466 | { | ||
467 | return get_rva_values(value + 1, &(entry->album_gain), &(entry->album_peak), | ||
468 | &(entry->album_gain_string), buffer, length); | ||
469 | } | ||
470 | |||
471 | return 0; | ||
472 | } | ||