summaryrefslogtreecommitdiff
path: root/firmware/replaygain.c
diff options
context:
space:
mode:
authorDan Everton <dan@iocaine.org>2006-07-20 10:35:20 +0000
committerDan Everton <dan@iocaine.org>2006-07-20 10:35:20 +0000
commit7b5af8c045d1db6e805981e1c6d1b32de200e3f3 (patch)
tree669e1b044c9681203ef5fb72ebac466c701447a7 /firmware/replaygain.c
parent928c33c255e47c39c5f549b3e1aa40fbb3aaa09a (diff)
downloadrockbox-7b5af8c045d1db6e805981e1c6d1b32de200e3f3.tar.gz
rockbox-7b5af8c045d1db6e805981e1c6d1b32de200e3f3.zip
Add support for parsing binary data ID3 tags. Add support for parsing RVA2 ID3 tags for replaygain values instead of TXXX tags when playing files tagged with ID3v2.4. Patch by psyche78 (task #2937)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10259 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/replaygain.c')
-rw-r--r--firmware/replaygain.c110
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
308long get_replaygain_int(long int_gain) 312long 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
320long get_replaygain(const char* str) 324long 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
333long get_replaypeak(const char* str) 337long 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 */
352long parse_replaygain(const char* key, const char* value, 356long 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
402static 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
455long 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}