comparison data/fileio/MP3FileReader.cpp @ 1305:9f9f55a8af92 mp3-gapless

Add gapless flag to MP3FileReader, and implement trimming the delay samples from the start (padding is not yet trimmed from end)
author Chris Cannam
date Tue, 29 Nov 2016 11:35:56 +0000
parents fa574c909c3d
children b325e91505b5
comparison
equal deleted inserted replaced
1304:7cff8367d9b1 1305:9f9f55a8af92
37 #ifdef _MSC_VER 37 #ifdef _MSC_VER
38 #include <io.h> 38 #include <io.h>
39 #define open _open 39 #define open _open
40 #endif 40 #endif
41 41
42 using std::string;
43
44 static sv_frame_t DEFAULT_DECODER_DELAY = 529;
45
42 MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode, 46 MP3FileReader::MP3FileReader(FileSource source, DecodeMode decodeMode,
43 CacheMode mode, sv_samplerate_t targetRate, 47 CacheMode mode, GaplessMode gaplessMode,
48 sv_samplerate_t targetRate,
44 bool normalised, 49 bool normalised,
45 ProgressReporter *reporter) : 50 ProgressReporter *reporter) :
46 CodedAudioFileReader(mode, targetRate, normalised), 51 CodedAudioFileReader(mode, targetRate, normalised),
47 m_source(source), 52 m_source(source),
48 m_path(source.getLocalFilename()), 53 m_path(source.getLocalFilename()),
54 m_gaplessMode(gaplessMode),
49 m_decodeErrorShown(false), 55 m_decodeErrorShown(false),
50 m_decodeThread(0) 56 m_decodeThread(0)
51 { 57 {
52 SVDEBUG << "MP3FileReader: local path: \"" << m_path 58 SVDEBUG << "MP3FileReader: local path: \"" << m_path
53 << "\", decode mode: " << decodeMode << " (" 59 << "\", decode mode: " << decodeMode << " ("
63 m_mp3FrameCount = 0; 69 m_mp3FrameCount = 0;
64 m_completion = 0; 70 m_completion = 0;
65 m_done = false; 71 m_done = false;
66 m_reporter = reporter; 72 m_reporter = reporter;
67 73
74 if (m_gaplessMode == Gapless) {
75 CodedAudioFileReader::setSamplesToTrim(DEFAULT_DECODER_DELAY, 0);
76 }
77
68 struct stat stat; 78 struct stat stat;
69 if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { 79 if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) {
70 m_error = QString("File %1 does not exist.").arg(m_path); 80 m_error = QString("File %1 does not exist.").arg(m_path);
71 return; 81 return;
72 } 82 }
363 { 373 {
364 DecoderData *data = (DecoderData *)dp; 374 DecoderData *data = (DecoderData *)dp;
365 return data->reader->filter(stream, frame); 375 return data->reader->filter(stream, frame);
366 } 376 }
367 377
378 static string toMagic(uint32_t fourcc)
379 {
380 string magic("....");
381 for (int i = 0; i < 4; ++i) {
382 magic[3-i] = char((fourcc >> (8*i)) & 0xff);
383 }
384 return magic;
385 }
386
368 enum mad_flow 387 enum mad_flow
369 MP3FileReader::filter(struct mad_stream const *stream, 388 MP3FileReader::filter(struct mad_stream const *stream,
370 struct mad_frame *) 389 struct mad_frame *)
371 { 390 {
372 if (m_mp3FrameCount > 0) { 391 if (m_mp3FrameCount > 0) {
392 // only handle info frame if it appears as first mp3 frame
373 return MAD_FLOW_CONTINUE; 393 return MAD_FLOW_CONTINUE;
394 }
395
396 if (m_gaplessMode == Gappy) {
397 // Our non-gapless mode does not even filter out the Xing/LAME
398 // frame. That's because the main reason non-gapless mode
399 // exists is for backward compatibility with MP3FileReader
400 // behaviour before the gapless support was added, so we even
401 // need to keep the spurious 1152 samples resulting from
402 // feeding Xing/LAME frame to the decoder as otherwise we'd
403 // have different output from before.
404 SVDEBUG << "MP3FileReader: Not gapless mode, not checking Xing/LAME frame"
405 << endl;
406 return MAD_FLOW_CONTINUE;
407 }
408
409 struct mad_bitptr ptr = stream->anc_ptr;
410 string magic = toMagic(mad_bit_read(&ptr, 32));
411
412 if (magic == "Xing" || magic == "Info") {
413
414 SVDEBUG << "MP3FileReader: Found Xing/LAME metadata frame (magic = \""
415 << magic << "\")" << endl;
416
417 // All we want at this point is the LAME encoder delay and
418 // padding values. We expect to see the Xing/Info magic (which
419 // we've already read), then 116 bytes of Xing data, then LAME
420 // magic, 5 byte version string, 12 bytes of LAME data that we
421 // aren't currently interested in, then the delays encoded as
422 // two 12-bit numbers into three bytes.
423 //
424 // (See gabriel.mp3-tech.org/mp3infotag.html)
425
426 for (int skip = 0; skip < 116; ++skip) {
427 (void)mad_bit_read(&ptr, 8);
428 }
429
430 magic = toMagic(mad_bit_read(&ptr, 32));
431
432 if (magic == "LAME") {
433
434 SVDEBUG << "MP3FileReader: Found LAME-specific metadata" << endl;
435
436 for (int skip = 0; skip < 5 + 12; ++skip) {
437 (void)mad_bit_read(&ptr, 8);
438 }
439
440 uint32_t delay = mad_bit_read(&ptr, 12);
441 uint32_t padding = mad_bit_read(&ptr, 12);
442
443 sv_frame_t delayToDrop = DEFAULT_DECODER_DELAY + delay;
444 sv_frame_t paddingToDrop = padding - DEFAULT_DECODER_DELAY;
445 if (paddingToDrop < 0) paddingToDrop = 0;
446
447 SVDEBUG << "MP3FileReader: LAME encoder delay = " << delay
448 << ", padding = " << padding << endl;
449
450 SVDEBUG << "MP3FileReader: Will be trimming " << delayToDrop
451 << " samples at start and " << paddingToDrop
452 << " at end" << endl;
453
454 CodedAudioFileReader::setSamplesToTrim(delayToDrop, paddingToDrop);
455
456 } else {
457 SVDEBUG << "MP3FileReader: Xing frame has no LAME metadata" << endl;
458 }
459
460 return MAD_FLOW_IGNORE;
461
374 } else { 462 } else {
375 struct mad_bitptr ptr = stream->anc_ptr; 463 return MAD_FLOW_CONTINUE;
376 unsigned long fourcc = mad_bit_read(&ptr, 32);
377 std::string magic("....");
378 for (int i = 0; i < 4; ++i) {
379 magic[3-i] = char((fourcc >> (8*i)) & 0xff);
380 }
381 if (magic == "Xing" || magic == "Info" || magic == "LAME") {
382 SVDEBUG << "MP3FileReader: Discarding metadata frame (magic = \""
383 << magic << "\")" << endl;
384 return MAD_FLOW_IGNORE;
385 } else {
386 return MAD_FLOW_CONTINUE;
387 }
388 } 464 }
389 } 465 }
390 466
391 enum mad_flow 467 enum mad_flow
392 MP3FileReader::output_callback(void *dp, 468 MP3FileReader::output_callback(void *dp,