summaryrefslogtreecommitdiff
path: root/src/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'src/codecs')
-rw-r--r--src/codecs/foxenflac.cpp1
-rw-r--r--src/codecs/include/codec.hpp4
-rw-r--r--src/codecs/include/mad.hpp2
-rw-r--r--src/codecs/mad.cpp82
4 files changed, 83 insertions, 6 deletions
diff --git a/src/codecs/foxenflac.cpp b/src/codecs/foxenflac.cpp
index a2d6f000..ee21da65 100644
--- a/src/codecs/foxenflac.cpp
+++ b/src/codecs/foxenflac.cpp
@@ -43,6 +43,7 @@ auto FoxenFlacDecoder::BeginStream(const cpp::span<const std::byte> input)
.num_channels = static_cast<uint8_t>(channels),
.bits_per_sample = 32, // libfoxenflac output is fixed-size.
.sample_rate_hz = static_cast<uint32_t>(fs),
+ .duration_seconds = {},
}};
}
diff --git a/src/codecs/include/codec.hpp b/src/codecs/include/codec.hpp
index 4b5ab47f..299b16e4 100644
--- a/src/codecs/include/codec.hpp
+++ b/src/codecs/include/codec.hpp
@@ -7,6 +7,7 @@
#pragma once
#include <stdint.h>
+#include <sys/_stdint.h>
#include <cstddef>
#include <cstdint>
@@ -50,6 +51,9 @@ class ICodec {
uint8_t num_channels;
uint8_t bits_per_sample;
uint32_t sample_rate_hz;
+
+ std::optional<uint32_t> duration_seconds;
+ std::optional<uint32_t> bits_per_second;
};
/*
diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp
index d2643230..fbae560c 100644
--- a/src/codecs/include/mad.hpp
+++ b/src/codecs/include/mad.hpp
@@ -42,6 +42,8 @@ class MadMp3Decoder : public ICodec {
-> Result<void> override;
private:
+ auto GetVbrLength(const mad_header& header) -> std::optional<uint32_t>;
+
mad_stream stream_;
mad_frame frame_;
mad_synth synth_;
diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp
index 23b4ccf6..ca9a0f6e 100644
--- a/src/codecs/mad.cpp
+++ b/src/codecs/mad.cpp
@@ -6,8 +6,10 @@
#include "mad.hpp"
#include <stdint.h>
+#include <sys/_stdint.h>
#include <cstdint>
+#include <cstring>
#include <optional>
#include "mad.h"
@@ -79,12 +81,22 @@ auto MadMp3Decoder::BeginStream(const cpp::span<const std::byte> input)
}
uint8_t channels = MAD_NCHANNELS(&header);
- return {GetBytesUsed(input.size_bytes()),
- OutputFormat{
- .num_channels = channels,
- .bits_per_sample = 24, // We always scale to 24 bits
- .sample_rate_hz = header.samplerate,
- }};
+ OutputFormat output{
+ .num_channels = channels,
+ .bits_per_sample = 24, // We always scale to 24 bits
+ .sample_rate_hz = header.samplerate,
+ .duration_seconds = {},
+ .bits_per_second = {},
+ };
+
+ auto vbr_length = GetVbrLength(header);
+ if (vbr_length) {
+ output.duration_seconds = vbr_length;
+ } else {
+ output.bits_per_second = header.bitrate;
+ }
+
+ return {GetBytesUsed(input.size_bytes()), output};
}
auto MadMp3Decoder::ContinueStream(cpp::span<const std::byte> input,
@@ -219,4 +231,62 @@ auto MadMp3Decoder::SeekStream(cpp::span<const std::byte> input,
}
}
+/*
+ * Implementation taken from SDL_mixer and modified. Original is zlib-licensed,
+ * copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
+ */
+auto MadMp3Decoder::GetVbrLength(const mad_header& header)
+ -> std::optional<uint32_t> {
+ if (!stream_.this_frame || !stream_.next_frame ||
+ stream_.next_frame <= stream_.this_frame ||
+ (stream_.next_frame - stream_.this_frame) < 48) {
+ return {};
+ }
+
+ int mpeg_version = (stream_.this_frame[1] >> 3) & 0x03;
+
+ int xing_offset = 0;
+ switch (mpeg_version) {
+ case 0x03: /* MPEG1 */
+ if (header.mode == MAD_MODE_SINGLE_CHANNEL) {
+ xing_offset = 4 + 17;
+ } else {
+ xing_offset = 4 + 32;
+ }
+ break;
+ default: /* MPEG2 and MPEG2.5 */
+ if (header.mode == MAD_MODE_SINGLE_CHANNEL) {
+ xing_offset = 4 + 17;
+ } else {
+ xing_offset = 4 + 9;
+ }
+ break;
+ }
+
+ uint32_t samples_per_frame = 32 * MAD_NSBSAMPLES(&header);
+
+ unsigned char const* frames_count_raw;
+ uint32_t frames_count = 0;
+ if (std::memcmp(stream_.this_frame + xing_offset, "Xing", 4) == 0 ||
+ std::memcmp(stream_.this_frame + xing_offset, "Info", 4) == 0) {
+ /* Xing header to get the count of frames for VBR */
+ frames_count_raw = stream_.this_frame + xing_offset + 8;
+ frames_count = ((uint32_t)frames_count_raw[0] << 24) +
+ ((uint32_t)frames_count_raw[1] << 16) +
+ ((uint32_t)frames_count_raw[2] << 8) +
+ ((uint32_t)frames_count_raw[3]);
+ } else if (std::memcmp(stream_.this_frame + xing_offset, "VBRI", 4) == 0) {
+ /* VBRI header to get the count of frames for VBR */
+ frames_count_raw = stream_.this_frame + xing_offset + 14;
+ frames_count = ((uint32_t)frames_count_raw[0] << 24) +
+ ((uint32_t)frames_count_raw[1] << 16) +
+ ((uint32_t)frames_count_raw[2] << 8) +
+ ((uint32_t)frames_count_raw[3]);
+ } else {
+ return {};
+ }
+
+ return (double)(frames_count * samples_per_frame) / header.samplerate;
+}
+
} // namespace codecs