summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Kirchner <git@halffull.org>2025-01-11 22:08:47 -0800
committerTom Kirchner <git@halffull.org>2025-01-11 22:10:48 -0800
commit3993835a35b1f7137c9d8665fb047a42e9ae6f94 (patch)
tree532721d850d2cc73f656298196f4c5fd0fc8800f /src
parentf8199bbd6d7b8bc64d8f58dc5638af8bc36cc73f (diff)
downloadtangara-fw-3993835a35b1f7137c9d8665fb047a42e9ae6f94.tar.gz
Extract gapless info from MP3 LAME header
Diffstat (limited to 'src')
-rw-r--r--src/codecs/include/mad.hpp1
-rw-r--r--src/codecs/mad.cpp24
2 files changed, 24 insertions, 1 deletions
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<uint32_t>;
struct Mp3Info {
+ uint16_t starting_sample;
uint32_t length;
std::optional<uint32_t> bytes;
std::optional<std::span<const unsigned char, 100>> 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<std::span<const unsigned char, 100>> toc;
std::optional<uint32_t> 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,
};