Mercurial > hg > svcore
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, |