summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/codecs/include/mad.hpp9
-rw-r--r--src/codecs/mad.cpp38
2 files changed, 38 insertions, 9 deletions
diff --git a/src/codecs/include/mad.hpp b/src/codecs/include/mad.hpp
index fba3ef11..60a0b81c 100644
--- a/src/codecs/include/mad.hpp
+++ b/src/codecs/include/mad.hpp
@@ -56,7 +56,14 @@ class MadMp3Decoder : public ICodec {
std::unique_ptr<mad_frame> frame_;
std::unique_ptr<mad_synth> synth_;
- int current_sample_;
+ // Count of samples processed in the current frame (channels combined)
+ int current_frame_sample_;
+ // Count of samples processed in the current stream (channels separate, i.e. usually x2)
+ int current_stream_sample_;
+ // How many samples in the current stream (channels separate) with encoder delay/padding removed
+ int total_samples_;
+ // Encoder delay, i.e. how many samples to skip at the start of the stream
+ int skip_samples_;
bool is_eof_;
bool is_eos_;
};
diff --git a/src/codecs/mad.cpp b/src/codecs/mad.cpp
index af74b244..538f0715 100644
--- a/src/codecs/mad.cpp
+++ b/src/codecs/mad.cpp
@@ -37,7 +37,10 @@ MadMp3Decoder::MadMp3Decoder()
synth_(reinterpret_cast<mad_synth*>(
heap_caps_malloc(sizeof(mad_synth),
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))),
- current_sample_(-1),
+ current_frame_sample_(-1),
+ current_stream_sample_(0),
+ total_samples_(0),
+ skip_samples_(0),
is_eof_(false),
is_eos_(false) {
mad_stream_init(stream_.get());
@@ -63,6 +66,8 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, ICodec::Error> {
input_ = input;
+ current_stream_sample_ = 0;
+
auto id3size = SkipID3Tags(*input);
// To get the output format for MP3 streams, we simply need to decode the
@@ -115,6 +120,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
cbr_length = (input->Size().value() * 8) / header.bitrate;
output.total_samples = cbr_length * output.sample_rate_hz * channels;
}
+ total_samples_ = output.total_samples.value();
// header.bitrate is only for CBR, but we've calculated total samples for VBR
// and CBR, so we can use that to calculate sample size and therefore bitrate.
@@ -124,6 +130,11 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
output.bitrate_kbps = static_cast<uint32_t>(output.sample_rate_hz * channels * sample_size / 1024);
}
+ // For gapless MP3s, save samples to skip
+ if (mp3_info) {
+ skip_samples_ = mp3_info->starting_sample;
+ }
+
if (offset > 1 && cbr_length > 0) {
// Constant bitrate seeking
uint64_t skip_bytes = header.bitrate * (offset - 1) / 8;
@@ -199,7 +210,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
auto MadMp3Decoder::DecodeTo(std::span<sample::Sample> output)
-> cpp::result<OutputInfo, Error> {
- if (current_sample_ < 0 && !is_eos_) {
+ if (current_frame_sample_ < 0 && !is_eos_) {
if (!is_eof_) {
is_eof_ = buffer_.Refill(input_.get());
if (is_eof_) {
@@ -243,14 +254,21 @@ auto MadMp3Decoder::DecodeTo(std::span<sample::Sample> output)
// We've successfully decoded a frame! Now synthesize samples to write
// out.
mad_synth_frame(synth_.get(), frame_.get());
- current_sample_ = 0;
+ current_frame_sample_ = 0;
return GetBytesUsed();
});
}
size_t output_sample = 0;
- if (current_sample_ >= 0) {
- while (current_sample_ < synth_->pcm.length) {
+ if (current_frame_sample_ >= 0) {
+ // Skip any gap samples indicated by the headers
+ while (skip_samples_ > 0) {
+ skip_samples_--;
+ current_frame_sample_++;
+ }
+
+ // Process samples until we hit the end of the frame or stream
+ while (current_frame_sample_ < synth_->pcm.length && current_stream_sample_ <= total_samples_) {
if (output_sample + synth_->pcm.channels >= output.size()) {
// We can't fit the next full frame into the buffer.
return OutputInfo{.samples_written = output_sample,
@@ -259,14 +277,18 @@ auto MadMp3Decoder::DecodeTo(std::span<sample::Sample> output)
for (int channel = 0; channel < synth_->pcm.channels; channel++) {
output[output_sample++] =
- sample::FromMad(synth_->pcm.samples[channel][current_sample_]);
+ sample::FromMad(synth_->pcm.samples[channel][current_frame_sample_]);
}
- current_sample_++;
+ current_frame_sample_++;
+ current_stream_sample_ += synth_->pcm.channels;
+ }
+ if (current_stream_sample_ > total_samples_) {
+ is_eos_ = true;
}
}
// We wrote everything! Reset, ready for the next frame.
- current_sample_ = -1;
+ current_frame_sample_ = -1;
return OutputInfo{.samples_written = output_sample,
.is_stream_finished = is_eos_};
}