/* * Copyright 2023 jacqueline * * SPDX-License-Identifier: GPL-3.0-only */ #include "opus.hpp" #include #include #include #include #include #include #include "esp_heap_caps.h" #include "mad.h" #include "codec.hpp" #include "esp_log.h" #include "opusfile.h" #include "result.hpp" #include "sample.hpp" #include "types.hpp" namespace codecs { static constexpr char kTag[] = "opus"; static int read_cb(void* src, unsigned char* ptr, int nbytes) { IStream* source = reinterpret_cast(src); return source->Read( {reinterpret_cast(ptr), static_cast(nbytes)}); } static int seek_cb(void* src, int64_t offset, int whence) { IStream* source = reinterpret_cast(src); if (!source->CanSeek()) { return -1; } IStream::SeekFrom from; switch (whence) { case SEEK_CUR: from = IStream::SeekFrom::kCurrentPosition; break; case SEEK_END: from = IStream::SeekFrom::kEndOfStream; break; case SEEK_SET: from = IStream::SeekFrom::kStartOfStream; break; default: return -1; } source->SeekTo(offset, from); return 0; } static int64_t tell_cb(void* src) { IStream* source = reinterpret_cast(src); return source->CurrentPosition(); } static const OpusFileCallbacks kCallbacks{ .read = read_cb, .seek = seek_cb, .tell = tell_cb, .close = NULL, }; XiphOpusDecoder::XiphOpusDecoder() : input_(nullptr), opus_(nullptr), num_channels_() {} XiphOpusDecoder::~XiphOpusDecoder() { if (opus_ != nullptr) { op_free(opus_); } } auto XiphOpusDecoder::OpenStream(std::shared_ptr input) -> cpp::result { input_ = input; int res; opus_ = op_open_callbacks(input.get(), &kCallbacks, nullptr, 0, &res); if (res < 0) { std::string err; switch (res) { case OP_EREAD: err = "OP_EREAD"; break; case OP_EFAULT: err = "OP_EFAULT"; break; case OP_EIMPL: err = "OP_EIMPL"; break; case OP_EINVAL: err = "OP_EINVAL"; break; case OP_ENOTFORMAT: err = "OP_ENOTFORMAT"; break; case OP_EBADHEADER: err = "OP_EBADHEADER"; break; case OP_EVERSION: err = "OP_EVERSION"; break; case OP_EBADLINK: err = "OP_EBADLINK"; break; case OP_EBADTIMESTAMP: err = "OP_BADTIMESTAMP"; break; default: err = "unknown"; } ESP_LOGE(kTag, "error beginning stream: %s", err.c_str()); return cpp::fail(Error::kMalformedData); } return OutputFormat{ .num_channels = 2, .sample_rate_hz = 48000, }; } auto XiphOpusDecoder::DecodeTo(cpp::span output) -> cpp::result { int samples_written = op_read_stereo(opus_, output.data(), output.size()); if (samples_written < 0) { ESP_LOGE(kTag, "read failed %i", samples_written); return cpp::fail(Error::kMalformedData); } samples_written *= 2; // Fixed to stereo return OutputInfo{ .samples_written = static_cast(samples_written), .is_stream_finished = samples_written == 0, }; } auto XiphOpusDecoder::SeekTo(size_t target) -> cpp::result { return {}; } } // namespace codecs