From 3993835a35b1f7137c9d8665fb047a42e9ae6f94 Mon Sep 17 00:00:00 2001 From: Tom Kirchner Date: Sat, 11 Jan 2025 22:08:47 -0800 Subject: [PATCH] Extract gapless info from MP3 LAME header --- src/codecs/include/mad.hpp | 1 + src/codecs/mad.cpp | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp index 274862f7..fba3ef11 100644 --- a/src/codecs/include/mad.hpp +++ b/src/codecs/include/mad.hpp @@ -39,6 +39,7 @@ class MadMp3Decoder : public ICodec { auto SkipID3Tags(IStream& stream) -> std::optional; struct Mp3Info { + uint16_t starting_sample; uint32_t length; std::optional bytes; std::optional> toc; diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp index e6e641f5..af74b244 100644 --- a/src/codecs/mad.cpp +++ b/src/codecs/mad.cpp @@ -360,26 +360,34 @@ auto MadMp3Decoder::GetMp3Info(const mad_header& header) } // Check TOC and bytes in the bitstream (used for VBR seeking) + // Also get gapless playback info: encoder delay and padding std::optional> toc; std::optional bytes; + auto lame_offset = xing_offset; + uint16_t starting_sample = 0; + uint16_t encoder_padding = 0; if (xing_vbr || xing_cbr) { unsigned char const* flags_raw = stream_->this_frame + xing_offset + 4; uint32_t flags = ((uint32_t)flags_raw[0] << 24) + ((uint32_t)flags_raw[1] << 16) + ((uint32_t)flags_raw[2] << 8) + ((uint32_t)flags_raw[3]); + lame_offset += 8; auto toc_offset = 8; auto bytes_offset = 8; if (flags & 1) { // Frames field is present + lame_offset += 4; toc_offset += 4; bytes_offset += 4; } if (flags & 2) { // Bytes field is present + lame_offset += 4; toc_offset += 4; } if (flags & 4) { // TOC flag is set + lame_offset += 100; if (flags & 2) { // Bytes field unsigned char const* bytes_raw = stream_->this_frame + xing_offset + bytes_offset; @@ -391,10 +399,24 @@ auto MadMp3Decoder::GetMp3Info(const mad_header& header) // Read the table of contents in toc.emplace((stream_->this_frame + xing_offset + toc_offset), 100); } + if (flags & 8) { + lame_offset += 4; + } + + if (std::memcmp(stream_->this_frame + lame_offset, "LAME", 4) == 0) { + unsigned char const* delay_addr = stream_->this_frame + lame_offset + 21; + uint32_t delay_raw = + ((uint32_t)delay_addr[0] << 16) + + ((uint32_t)delay_addr[1] << 8) + + ((uint32_t)delay_addr[2]); + starting_sample = (delay_raw >> 12) & 0xFFF; + encoder_padding = delay_raw & 0xFFF; + } } return Mp3Info{ - .length = (frames_count * samples_per_frame), + .starting_sample = starting_sample, + .length = (frames_count * samples_per_frame - starting_sample - encoder_padding), .bytes = bytes, .toc = toc, };