vbr-compatible mp3 duration :)

custom
jacqueline 2 years ago
parent 739f495fe2
commit 8f8bc1f088
  1. 4
      src/audio/audio_fsm.cpp
  2. 2
      src/codecs/include/mad.hpp
  3. 68
      src/codecs/mad.cpp

@ -144,8 +144,8 @@ void Playback::react(const QueueUpdate& ev) {
}
void Playback::react(const PlaybackUpdate& ev) {
// ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed,
// ev.seconds_total);
ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed,
ev.seconds_total);
}
void Playback::react(const internal::InputFileOpened& ev) {}

@ -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_;

@ -9,6 +9,7 @@
#include <sys/_stdint.h>
#include <cstdint>
#include <cstring>
#include <optional>
#include "mad.h"
@ -88,9 +89,12 @@ auto MadMp3Decoder::BeginStream(const cpp::span<const std::byte> input)
.bits_per_second = {},
};
// TODO(jacqueline): Support VBR. Although maybe libtags is the better place
// to handle this?
output.bits_per_second = header.bitrate;
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};
}
@ -227,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

Loading…
Cancel
Save