Merge pull request 'Add bitrate info' (#172) from tjk/tangara-fw:bitrate into main

Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/172
Reviewed-by: cooljqln <cooljqln@noreply.codeberg.org>
custom
cooljqln 3 months ago
commit faf52c162f
  1. 2
      lua/track_info.lua
  2. 7
      src/codecs/dr_flac.cpp
  3. 1
      src/codecs/include/codec.hpp
  4. 2
      src/codecs/include/mad.hpp
  5. 23
      src/codecs/mad.cpp
  6. 2
      src/codecs/native.cpp
  7. 7
      src/codecs/opus.cpp
  8. 7
      src/codecs/vorbis.cpp
  9. 3
      src/codecs/wav.cpp
  10. 2
      src/tangara/audio/audio_decoder.cpp

@ -50,6 +50,7 @@ return screen:new {
local disc = label("") local disc = label("")
local tracknum = label("") local tracknum = label("")
local encoding = label("") local encoding = label("")
local bitrate_kbps = label("")
local sample_rate = label("") local sample_rate = label("")
local num_channels = label("") local num_channels = label("")
local bits_per_sample = label("") local bits_per_sample = label("")
@ -75,6 +76,7 @@ return screen:new {
disc:set { text = "Disc: " .. (track.disc or "") } disc:set { text = "Disc: " .. (track.disc or "") }
tracknum:set { text = "Track: " .. (track.track or "") } tracknum:set { text = "Track: " .. (track.track or "") }
encoding:set { text = "Encoding: " .. (track.encoding or "") } encoding:set { text = "Encoding: " .. (track.encoding or "") }
bitrate_kbps:set { text = "Bitrate (kbps): " .. (track.bitrate_kbps or "") }
sample_rate:set { text = "Sample rate: " .. (track.sample_rate or "") } sample_rate:set { text = "Sample rate: " .. (track.sample_rate or "") }
num_channels:set { text = "Channels: " .. (track.num_channels or "") } num_channels:set { text = "Channels: " .. (track.num_channels or "") }
bits_per_sample:set { text = "Bits per sample: " .. (track.bits_per_sample or "") } bits_per_sample:set { text = "Bits per sample: " .. (track.bits_per_sample or "") }

@ -97,6 +97,13 @@ auto DrFlacDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
.sample_rate_hz = static_cast<uint32_t>(flac_->sampleRate), .sample_rate_hz = static_cast<uint32_t>(flac_->sampleRate),
.total_samples = flac_->totalPCMFrameCount * flac_->channels, .total_samples = flac_->totalPCMFrameCount * flac_->channels,
}; };
if (input->Size() && format.total_samples) {
double sample_size = *(input->Size()) * 8.0 / *(format.total_samples);
format.bitrate_kbps = static_cast<uint32_t>(
flac_->sampleRate * flac_->channels * sample_size / 1024);
}
return format; return format;
} }

@ -106,6 +106,7 @@ class ICodec {
uint8_t num_channels; uint8_t num_channels;
uint32_t sample_rate_hz; uint32_t sample_rate_hz;
std::optional<uint32_t> total_samples; std::optional<uint32_t> total_samples;
std::optional<uint32_t> bitrate_kbps;
bool operator==(const OutputFormat&) const = default; bool operator==(const OutputFormat&) const = default;
}; };

@ -36,7 +36,7 @@ class MadMp3Decoder : public ICodec {
MadMp3Decoder& operator=(const MadMp3Decoder&) = delete; MadMp3Decoder& operator=(const MadMp3Decoder&) = delete;
private: private:
auto SkipID3Tags(IStream& stream) -> void; auto SkipID3Tags(IStream& stream) -> std::optional<uint32_t>;
struct VbrInfo { struct VbrInfo {
uint32_t length; uint32_t length;

@ -63,7 +63,7 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
-> cpp::result<OutputFormat, ICodec::Error> { -> cpp::result<OutputFormat, ICodec::Error> {
input_ = input; input_ = input;
SkipID3Tags(*input); auto id3size = SkipID3Tags(*input);
// To get the output format for MP3 streams, we simply need to decode the // To get the output format for MP3 streams, we simply need to decode the
// first frame header. // first frame header.
@ -116,6 +116,14 @@ auto MadMp3Decoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
output.total_samples = cbr_length * output.sample_rate_hz * channels; output.total_samples = cbr_length * output.sample_rate_hz * channels;
} }
// 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.
if (id3size && input->Size()) {
auto data_size = input->Size().value() - id3size.value();
double sample_size = data_size * 8.0 / output.total_samples.value();
output.bitrate_kbps = static_cast<uint32_t>(output.sample_rate_hz * channels * sample_size / 1024);
}
if (offset > 1 && cbr_length > 0) { if (offset > 1 && cbr_length > 0) {
// Constant bitrate seeking // Constant bitrate seeking
uint64_t skip_bytes = header.bitrate * (offset - 1) / 8; uint64_t skip_bytes = header.bitrate * (offset - 1) / 8;
@ -263,15 +271,15 @@ auto MadMp3Decoder::DecodeTo(std::span<sample::Sample> output)
.is_stream_finished = is_eos_}; .is_stream_finished = is_eos_};
} }
auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void { auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> std::optional<uint32_t> {
// First check that the file actually does start with ID3 tags. // First check that the file actually does start with ID3 tags.
std::array<std::byte, 3> magic_buf{}; std::array<std::byte, 3> magic_buf{};
if (stream.Read(magic_buf) != 3) { if (stream.Read(magic_buf) != 3) {
return; return {};
} }
if (std::memcmp(magic_buf.data(), "ID3", 3) != 0) { if (std::memcmp(magic_buf.data(), "ID3", 3) != 0) {
stream.SeekTo(0, IStream::SeekFrom::kStartOfStream); stream.SeekTo(0, IStream::SeekFrom::kStartOfStream);
return; return {};
} }
// The size of the tags (*not* including the 10-byte header) is located 6 // The size of the tags (*not* including the 10-byte header) is located 6
@ -279,14 +287,17 @@ auto MadMp3Decoder::SkipID3Tags(IStream& stream) -> void {
std::array<std::byte, 4> size_buf{}; std::array<std::byte, 4> size_buf{};
stream.SeekTo(6, IStream::SeekFrom::kStartOfStream); stream.SeekTo(6, IStream::SeekFrom::kStartOfStream);
if (stream.Read(size_buf) != 4) { if (stream.Read(size_buf) != 4) {
return; return {};
} }
// Size is encoded with 7-bit ints for some reason. // Size is encoded with 7-bit ints for some reason.
uint32_t tags_size = (static_cast<uint32_t>(size_buf[0]) << (7 * 3)) | uint32_t tags_size = (static_cast<uint32_t>(size_buf[0]) << (7 * 3)) |
(static_cast<uint32_t>(size_buf[1]) << (7 * 2)) | (static_cast<uint32_t>(size_buf[1]) << (7 * 2)) |
(static_cast<uint32_t>(size_buf[2]) << 7) | (static_cast<uint32_t>(size_buf[2]) << 7) |
static_cast<uint32_t>(size_buf[3]); static_cast<uint32_t>(size_buf[3]);
stream.SeekTo(10 + tags_size, IStream::SeekFrom::kStartOfStream);
auto skip_bytes = 10 + tags_size;
stream.SeekTo(skip_bytes, IStream::SeekFrom::kStartOfStream);
return skip_bytes;
} }
/* /*

@ -30,6 +30,8 @@ auto NativeDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
.num_channels = 1, .num_channels = 1,
.sample_rate_hz = 48000, .sample_rate_hz = 48000,
.total_samples = {}, .total_samples = {},
// sample rate * channels * bits per sample / bits per kb
.bitrate_kbps = 48000 * 1 * 16 / 1024,
}; };
} }

@ -129,6 +129,12 @@ auto XiphOpusDecoder::OpenStream(std::shared_ptr<IStream> input,
length = l * 2; length = l * 2;
} }
auto b = op_bitrate(opus_, -1);
std::optional<uint32_t> bitrate_kbps;
if (b > 0) {
bitrate_kbps = b / 1024;
}
if (offset && op_pcm_seek(opus_, offset * 48000) != 0) { if (offset && op_pcm_seek(opus_, offset * 48000) != 0) {
return cpp::fail(Error::kInternalError); return cpp::fail(Error::kInternalError);
} }
@ -137,6 +143,7 @@ auto XiphOpusDecoder::OpenStream(std::shared_ptr<IStream> input,
.num_channels = 2, .num_channels = 2,
.sample_rate_hz = 48000, .sample_rate_hz = 48000,
.total_samples = length, .total_samples = length,
.bitrate_kbps = bitrate_kbps,
}; };
} }

@ -118,6 +118,12 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr<IStream> input,
length = l * info->channels; length = l * info->channels;
} }
auto b = ov_bitrate(vorbis_.get(), -1);
std::optional<uint32_t> bitrate_kbps;
if (b > 0) {
bitrate_kbps = b / 1024;
}
if (offset && ov_time_seek(vorbis_.get(), offset * 1000) != 0) { if (offset && ov_time_seek(vorbis_.get(), offset * 1000) != 0) {
return cpp::fail(Error::kInternalError); return cpp::fail(Error::kInternalError);
} }
@ -126,6 +132,7 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr<IStream> input,
.num_channels = static_cast<uint8_t>(info->channels), .num_channels = static_cast<uint8_t>(info->channels),
.sample_rate_hz = static_cast<uint32_t>(info->rate), .sample_rate_hz = static_cast<uint32_t>(info->rate),
.total_samples = length, .total_samples = length,
.bitrate_kbps = bitrate_kbps,
}; };
} }

@ -216,7 +216,8 @@ auto WavDecoder::OpenStream(std::shared_ptr<IStream> input, uint32_t offset)
output_format_ = {.num_channels = (uint8_t)num_channels_, output_format_ = {.num_channels = (uint8_t)num_channels_,
.sample_rate_hz = samples_per_second, .sample_rate_hz = samples_per_second,
.total_samples = number_of_samples}; .total_samples = number_of_samples,
.bitrate_kbps = samples_per_second * num_channels_ * bytes_per_sample_ * 8 / 1024};
return output_format_; return output_format_;
} }

@ -160,7 +160,7 @@ auto Decoder::prepareDecode(std::shared_ptr<TaggedStream> stream) -> void {
.uri = stream->Filepath(), .uri = stream->Filepath(),
.duration = {}, .duration = {},
.start_offset = stream->Offset(), .start_offset = stream->Offset(),
.bitrate_kbps = {}, .bitrate_kbps = open_res->bitrate_kbps,
.encoding = stream->type(), .encoding = stream->type(),
.format = .format =
{ {

Loading…
Cancel
Save