/* * Copyright 2023 jacqueline * * SPDX-License-Identifier: GPL-3.0-only */ #include "vorbis.hpp" #include #include #include #include "esp_heap_caps.h" #include "esp_log.h" #include "ivorbiscodec.h" #include "ivorbisfile.h" #include "codec.hpp" #include "sample.hpp" #include "types.hpp" namespace codecs { [[maybe_unused]] static constexpr char kTag[] = "vorbis"; static size_t read_cb(void* ptr, size_t size, size_t nmemb, void* instance) { IStream* source = reinterpret_cast(instance); return source->Read({reinterpret_cast(ptr), size * nmemb}); } static int seek_cb(void* instance, tremor_ogg_int64_t offset, int whence) { IStream* source = reinterpret_cast(instance); 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 int close_cb(void* src) { return 0; } static long tell_cb(void* src) { IStream* source = reinterpret_cast(src); return source->CurrentPosition(); } static const ov_callbacks kCallbacks{ .read_func = read_cb, .seek_func = seek_cb, .close_func = close_cb, .tell_func = tell_cb, // Not seekable }; TremorVorbisDecoder::TremorVorbisDecoder() : input_(), vorbis_(reinterpret_cast( heap_caps_malloc(sizeof(TremorOggVorbis_File), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))) {} TremorVorbisDecoder::~TremorVorbisDecoder() { ov_clear(vorbis_.get()); } auto TremorVorbisDecoder::OpenStream(std::shared_ptr input, uint32_t offset) -> cpp::result { int res = ov_open_callbacks(input.get(), vorbis_.get(), NULL, 0, kCallbacks); if (res < 0) { std::string err; switch (res) { case OV_EREAD: err = "OV_EREAD"; break; case OV_ENOTVORBIS: err = "OV_ENOTVORBIS"; break; case OV_EVERSION: err = "OV_EVERSION"; break; case OV_EBADHEADER: err = "OV_EBADHEADER"; break; case OV_EFAULT: err = "OV_EFAULT"; break; default: err = "unknown"; } ESP_LOGE(kTag, "error beginning stream: %s", err.c_str()); return cpp::fail(Error::kMalformedData); } vorbis_info* info = ov_info(vorbis_.get(), -1); if (info == NULL) { ESP_LOGE(kTag, "failed to get stream info"); return cpp::fail(Error::kMalformedData); } auto l = ov_pcm_total(vorbis_.get(), -1); std::optional length; if (l > 0) { length = l * info->channels; } if (offset && ov_time_seek(vorbis_.get(), offset * 1000) != 0) { return cpp::fail(Error::kInternalError); } return OutputFormat{ .num_channels = static_cast(info->channels), .sample_rate_hz = static_cast(info->rate), .total_samples = length, }; } auto TremorVorbisDecoder::DecodeTo(std::span output) -> cpp::result { int unused = 0; long bytes_written = ov_read(vorbis_.get(), reinterpret_cast(output.data()), ((output.size() - 1) * sizeof(sample::Sample)), &unused); if (bytes_written == OV_HOLE) { ESP_LOGE(kTag, "got OV_HOLE"); return OutputInfo{ .samples_written = 0, .is_stream_finished = false, }; } else if (bytes_written == OV_EBADLINK) { ESP_LOGE(kTag, "got OV_EBADLINK"); return cpp::fail(Error::kMalformedData); } else if (bytes_written == OV_EINVAL) { return cpp::fail(Error::kMalformedData); } return OutputInfo{ .samples_written = static_cast(bytes_written / sizeof(sample::Sample)), .is_stream_finished = bytes_written == 0, }; } } // namespace codecs