Flesh out opus decoder. it doesn't work! i hate opus.

custom
jacqueline 2 years ago
parent e1181fbe59
commit 6c3501dbcb
  1. 2
      src/audio/audio_task.cpp
  2. 4
      src/audio/fatfs_audio_input.cpp
  3. 3
      src/audio/i2s_audio_output.cpp
  4. 6
      src/codecs/CMakeLists.txt
  5. 3
      src/codecs/codec.cpp
  6. 13
      src/codecs/include/opus.hpp
  7. 2
      src/codecs/include/types.hpp
  8. 78
      src/codecs/opus.cpp
  9. 1
      src/database/include/track.hpp
  10. 3
      src/database/tag_parser.cpp

@ -125,7 +125,7 @@ AudioTask::AudioTask(IAudioSource* source, IAudioSink* sink)
has_begun_decoding_(false), has_begun_decoding_(false),
current_input_format_(), current_input_format_(),
current_output_format_(), current_output_format_(),
codec_buffer_(new RawStream(kSampleBufferSize)) {} codec_buffer_(new RawStream(kSampleBufferSize, MALLOC_CAP_8BIT)) {}
void AudioTask::Main() { void AudioTask::Main() {
for (;;) { for (;;) {

@ -313,8 +313,8 @@ auto FatfsAudioInput::ContainerToStreamType(database::Encoding enc)
return codecs::StreamType::kPcm; return codecs::StreamType::kPcm;
case database::Encoding::kFlac: case database::Encoding::kFlac:
return codecs::StreamType::kFlac; return codecs::StreamType::kFlac;
case database::Encoding::kOgg: // Misnamed; this is Ogg Vorbis. case database::Encoding::kOpus:
return codecs::StreamType::kVorbis; return codecs::StreamType::kOpus;
case database::Encoding::kUnsupported: case database::Encoding::kUnsupported:
default: default:
return {}; return {};

@ -120,8 +120,7 @@ auto I2SAudioOutput::PrepareFormat(const StreamInfo::Pcm& orig)
return StreamInfo::Pcm{ return StreamInfo::Pcm{
.channels = std::min<uint8_t>(orig.channels, 2), .channels = std::min<uint8_t>(orig.channels, 2),
.bits_per_sample = std::clamp<uint8_t>(orig.bits_per_sample, 16, 32), .bits_per_sample = std::clamp<uint8_t>(orig.bits_per_sample, 16, 32),
.sample_rate = 44100, .sample_rate = std::clamp<uint32_t>(orig.sample_rate, 8000, 96000),
//.sample_rate = std::clamp<uint32_t>(orig.sample_rate, 8000, 96000),
}; };
} }

@ -3,16 +3,18 @@
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
idf_component_register( idf_component_register(
SRCS "codec.cpp" "mad.cpp" "foxenflac.cpp" SRCS "codec.cpp" "mad.cpp" "foxenflac.cpp" "opus.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES "result" "span" "libmad" "libfoxenflac") REQUIRES "result" "span" "libmad" "libfoxenflac")
target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS}) target_compile_options("${COMPONENT_LIB}" PRIVATE ${EXTRA_WARNINGS})
set(OPUS_FIXED_POINT ON) set(OPUS_FIXED_POINT ON)
set(OPUS_ENABLE_FLOAT_API ON) set(OPUS_ENABLE_FLOAT_API OFF)
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF) set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF) set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
set(OPUS_BUILD_TESTING OFF)
set(OPUS_BUILD_SHARED_LIBS OFF)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)

@ -10,6 +10,7 @@
#include <optional> #include <optional>
#include "foxenflac.hpp" #include "foxenflac.hpp"
#include "opus.hpp"
#include "mad.hpp" #include "mad.hpp"
#include "types.hpp" #include "types.hpp"
@ -21,6 +22,8 @@ auto CreateCodecForType(StreamType type) -> std::optional<ICodec*> {
return new MadMp3Decoder(); return new MadMp3Decoder();
case StreamType::kFlac: case StreamType::kFlac:
return new FoxenFlacDecoder(); return new FoxenFlacDecoder();
case StreamType::kOpus:
return new XiphOpusDecoder();
default: default:
return {}; return {};
} }

@ -14,6 +14,7 @@
#include <utility> #include <utility>
#include "opus.h" #include "opus.h"
#include "sample.hpp"
#include "span.hpp" #include "span.hpp"
#include "codec.hpp" #include "codec.hpp"
@ -36,14 +37,18 @@ class XiphOpusDecoder : public ICodec {
* Writes samples for the current frame. * Writes samples for the current frame.
*/ */
auto ContinueStream(cpp::span<const std::byte> input, auto ContinueStream(cpp::span<const std::byte> input,
cpp::span<std::byte> output) cpp::span<sample::Sample> output)
-> Result<OutputInfo> override; -> Result<OutputInfo> override;
auto SeekStream(cpp::span<const std::byte> input, std::size_t target_sample) auto SeekStream(cpp::span<const std::byte> input, std::size_t target_sample)
-> Result<void> override; -> Result<void> override;
private: private:
OpusDecoder *opus_; OpusDecoder* opus_;
float *sample_buffer_; cpp::span<int16_t> sample_buffer_;
std::size_t sample_buffer_len_; int32_t pos_in_buffer_;
int32_t samples_in_buffer_;
};
} // namespace codecs } // namespace codecs

@ -13,8 +13,8 @@ namespace codecs {
enum class StreamType { enum class StreamType {
kMp3, kMp3,
kPcm, kPcm,
kVorbis,
kFlac, kFlac,
kOpus,
}; };
} // namespace codecs } // namespace codecs

@ -5,6 +5,7 @@
*/ */
#include "opus.hpp" #include "opus.hpp"
#include <stdint.h> #include <stdint.h>
#include <sys/_stdint.h> #include <sys/_stdint.h>
@ -12,36 +13,101 @@
#include <cstring> #include <cstring>
#include <optional> #include <optional>
#include "esp_heap_caps.h"
#include "mad.h" #include "mad.h"
#include "codec.hpp" #include "codec.hpp"
#include "esp_log.h" #include "esp_log.h"
#include "opus.h" #include "opus.h"
#include "opus_types.h"
#include "result.hpp" #include "result.hpp"
#include "sample.hpp"
#include "types.hpp" #include "types.hpp"
namespace codecs { namespace codecs {
static constexpr std::size_t kSampleBufferSize = 5760 * sizeof(float); static constexpr char kTag[] = "opus";
// "If this is less than the maximum packet duration (120ms; 5760 for 48kHz),
// this function will not be capable of decoding some packets"
static constexpr size_t kSampleBufferSize = 5760;
XiphOpusDecoder::XiphOpusDecoder() { XiphOpusDecoder::XiphOpusDecoder() {
int err; int err;
opus_ = opus_decoder_create(48000, 2, &err); opus_ = opus_decoder_create(48000, 2, &err);
assert(err == OPUS_OK); assert(err == OPUS_OK);
pos_in_buffer_ = 0;
sample_buffer_ = {reinterpret_cast<opus_int16*>(
heap_caps_calloc(kSampleBufferSize, sizeof(opus_int16),
MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM)),
kSampleBufferSize};
} }
XiphOpusDecoder::~XiphOpusDecoder() { XiphOpusDecoder::~XiphOpusDecoder() {
opus_decoder_destroy(opus_); opus_decoder_destroy(opus_);
heap_caps_free(sample_buffer_.data());
} }
auto XiphOpusDecoder::BeginStream(const cpp::span<const std::byte> input) auto XiphOpusDecoder::BeginStream(const cpp::span<const std::byte> input)
-> Result<OutputFormat> {} -> Result<OutputFormat> {
return {0, OutputFormat{
.num_channels = 2,
.sample_rate_hz = 48000,
}};
}
auto read_uint32(cpp::span<const std::byte> src) -> uint32_t {
return static_cast<uint32_t>(src[0] << 24) |
static_cast<uint32_t>(src[1] << 16) |
static_cast<uint32_t>(src[2] << 8) |
static_cast<uint32_t>(src[3] << 0);
}
auto XiphOpusDecoder::ContinueStream(cpp::span<const std::byte> input, auto XiphOpusDecoder::ContinueStream(cpp::span<const std::byte> input,
cpp::span<std::byte> output) cpp::span<sample::Sample> output)
-> Result<OutputInfo> { -> Result<OutputInfo> {
int samples_decoded = opus_decode_float( size_t bytes_used = 0;
opus_, reinterpret_cast<const unsigned char*>(input.data()), if (pos_in_buffer_ >= samples_in_buffer_) {
input.size_bytes(), sample_buffer_, sample_buffer_len_, 0); ESP_LOGI(kTag, "sample buffer is empty. parsing more.");
if (input.size() < 4) {
return {0, cpp::fail(Error::kOutOfInput)};
}
uint32_t payload_length = read_uint32(input);
ESP_LOGI(kTag, "payload length is %lu", payload_length);
if (input.size() - 4 < payload_length) {
ESP_LOGI(kTag, "input too small for payload");
return {0, cpp::fail(Error::kOutOfInput)};
}
// Next 4 bytes are the 'final range'.
// uint32_t enc_final_range = read_uint32(input.subspan(4));
bytes_used = payload_length + 8;
pos_in_buffer_ = 0;
samples_in_buffer_ = opus_decode(
opus_, reinterpret_cast<const unsigned char*>(input.data() + 8),
payload_length, sample_buffer_.data(), sample_buffer_.size(), 0);
if (samples_in_buffer_ < 0) {
ESP_LOGE(kTag, "error decoding stream");
return {bytes_used, cpp::fail(Error::kMalformedData)};
}
}
size_t samples_written = 0;
while (pos_in_buffer_ < samples_in_buffer_ &&
samples_written < output.size()) {
output[samples_written++] =
sample::FromSigned(sample_buffer_[pos_in_buffer_++], 16);
}
return {bytes_used,
OutputInfo{
.samples_written = samples_written,
.is_finished_writing = pos_in_buffer_ >= samples_in_buffer_,
}};
} }
auto XiphOpusDecoder::SeekStream(cpp::span<const std::byte> input, auto XiphOpusDecoder::SeekStream(cpp::span<const std::byte> input,

@ -43,6 +43,7 @@ enum class Encoding {
kWav = 2, kWav = 2,
kOgg = 3, kOgg = 3,
kFlac = 4, kFlac = 4,
kOpus = 5,
}; };
enum class Tag { enum class Tag {

@ -153,6 +153,9 @@ auto TagParserImpl::ReadAndParseTags(const std::string& path, TrackTags* out)
case Fwav: case Fwav:
out->encoding(Encoding::kWav); out->encoding(Encoding::kWav);
break; break;
case Fopus:
out->encoding(Encoding::kOpus);
break;
default: default:
out->encoding(Encoding::kUnsupported); out->encoding(Encoding::kUnsupported);
} }

Loading…
Cancel
Save