diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio/include/track_queue.hpp | 41 | ||||
| -rw-r--r-- | src/audio/track_queue.cpp | 150 | ||||
| -rw-r--r-- | src/codecs/include/vorbis.hpp | 4 | ||||
| -rw-r--r-- | src/codecs/miniflac.cpp | 11 | ||||
| -rw-r--r-- | src/codecs/vorbis.cpp | 46 | ||||
| -rw-r--r-- | src/dev_console/include/console.hpp | 2 | ||||
| -rw-r--r-- | src/tasks/tasks.cpp | 12 |
7 files changed, 182 insertions, 84 deletions
diff --git a/src/audio/include/track_queue.hpp b/src/audio/include/track_queue.hpp index fd6061a7..e4fd7881 100644 --- a/src/audio/include/track_queue.hpp +++ b/src/audio/include/track_queue.hpp @@ -12,6 +12,7 @@ #include <shared_mutex> #include <vector> +#include "cppbor_parse.h" #include "database.hpp" #include "tasks.hpp" #include "track.hpp" @@ -24,6 +25,7 @@ namespace audio { */ class RandomIterator { public: + RandomIterator(); RandomIterator(size_t size); auto current() const -> size_t; @@ -35,6 +37,10 @@ class RandomIterator { auto resize(size_t) -> void; auto replay(bool) -> void; + auto seed() -> size_t& { return seed_; } + auto pos() -> size_t& { return pos_; } + auto size() -> size_t& { return size_; } + private: size_t seed_; size_t pos_; @@ -85,12 +91,11 @@ class TrackQueue { auto next() -> void; auto previous() -> void; - /* + /* * Called when the current track finishes */ auto finish() -> void; - auto skipTo(database::TrackId) -> void; /* @@ -125,6 +130,38 @@ class TrackQueue { std::optional<RandomIterator> shuffle_; bool repeat_; bool replay_; + + class QueueParseClient : public cppbor::ParseClient { + public: + QueueParseClient(TrackQueue& queue); + + ParseClient* item(std::unique_ptr<cppbor::Item>& item, + const uint8_t* hdrBegin, + const uint8_t* valueBegin, + const uint8_t* end) override; + + ParseClient* itemEnd(std::unique_ptr<cppbor::Item>& item, + const uint8_t* hdrBegin, + const uint8_t* valueBegin, + const uint8_t* end) override; + + void error(const uint8_t* position, + const std::string& errorMessage) override {} + + private: + TrackQueue& queue_; + + enum class State { + kInit, + kRoot, + kMetadata, + kShuffle, + kTracks, + kFinished, + }; + State state_; + size_t i_; + }; }; } // namespace audio diff --git a/src/audio/track_queue.cpp b/src/audio/track_queue.cpp index c4c101f6..534da10c 100644 --- a/src/audio/track_queue.cpp +++ b/src/audio/track_queue.cpp @@ -33,6 +33,9 @@ namespace audio { [[maybe_unused]] static constexpr char kTag[] = "tracks"; +RandomIterator::RandomIterator() + : seed_(0), pos_(0), size_(0), replay_(false) {} + RandomIterator::RandomIterator(size_t size) : seed_(), pos_(0), size_(size), replay_(false) { esp_fill_random(&seed_, sizeof(seed_)); @@ -338,66 +341,127 @@ auto TrackQueue::serialise() -> std::string { for (database::TrackId track : tracks_) { tracks.add(cppbor::Uint(track)); } - // FIXME: this should include the RandomIterator's seed as well. - cppbor::Array encoded{ - cppbor::Uint{pos_}, - std::move(tracks), - }; + cppbor::Map encoded; + encoded.add(cppbor::Uint{0}, cppbor::Array{ + cppbor::Uint{pos_}, + cppbor::Bool{repeat_}, + cppbor::Bool{replay_}, + }); + if (shuffle_) { + encoded.add(cppbor::Uint{1}, cppbor::Array{ + cppbor::Uint{shuffle_->size()}, + cppbor::Uint{shuffle_->seed()}, + cppbor::Uint{shuffle_->pos()}, + }); + } + encoded.add(cppbor::Uint{2}, std::move(tracks)); return encoded.toString(); } -class QueueParseClient : public cppbor::ParseClient { - public: - QueueParseClient(size_t& pos, std::pmr::vector<database::TrackId>& tracks) - : pos_(pos), - tracks_(tracks), - in_root_array_(false), - in_track_list_(false) {} - - ParseClient* item(std::unique_ptr<cppbor::Item>& item, - const uint8_t* hdrBegin, - const uint8_t* valueBegin, - const uint8_t* end) override { +TrackQueue::QueueParseClient::QueueParseClient(TrackQueue& queue) + : queue_(queue), state_(State::kInit), i_(0) {} + +cppbor::ParseClient* TrackQueue::QueueParseClient::item( + std::unique_ptr<cppbor::Item>& item, + const uint8_t* hdrBegin, + const uint8_t* valueBegin, + const uint8_t* end) { + if (state_ == State::kInit) { + if (item->type() == cppbor::MAP) { + state_ = State::kRoot; + } + } else if (state_ == State::kRoot) { + if (item->type() == cppbor::UINT) { + switch (item->asUint()->unsignedValue()) { + case 0: + state_ = State::kMetadata; + break; + case 1: + state_ = State::kShuffle; + break; + case 2: + state_ = State::kTracks; + break; + default: + state_ = State::kFinished; + } + } + } else if (state_ == State::kMetadata) { if (item->type() == cppbor::ARRAY) { - if (!in_root_array_) { - in_root_array_ = true; - } else { - in_track_list_ = true; + i_ = 0; + } else if (item->type() == cppbor::UINT) { + queue_.pos_ = item->asUint()->unsignedValue(); + } else if (item->type() == cppbor::SIMPLE) { + bool val = item->asBool()->value(); + if (i_ == 0) { + queue_.repeat_ = val; + } else if (i_ == 1) { + queue_.replay_ = val; } + i_++; + } + } else if (state_ == State::kShuffle) { + if (item->type() == cppbor::ARRAY) { + i_ = 0; + queue_.shuffle_.emplace(); + queue_.shuffle_->replay(queue_.replay_); } else if (item->type() == cppbor::UINT) { auto val = item->asUint()->unsignedValue(); - if (in_track_list_) { - tracks_.push_back(val); - } else { - pos_ = static_cast<size_t>(val); + switch (i_) { + case 0: + queue_.shuffle_->size() = val; + break; + case 1: + queue_.shuffle_->seed() = val; + break; + case 2: + queue_.shuffle_->pos() = val; + break; + default: + break; } + i_++; } - return this; + } else if (state_ == State::kTracks) { + if (item->type() == cppbor::UINT) { + queue_.tracks_.push_back(item->asUint()->unsignedValue()); + } + } else if (state_ == State::kFinished) { } + return this; +} - ParseClient* itemEnd(std::unique_ptr<cppbor::Item>& item, - const uint8_t* hdrBegin, - const uint8_t* valueBegin, - const uint8_t* end) override { - return this; +cppbor::ParseClient* TrackQueue::QueueParseClient::itemEnd( + std::unique_ptr<cppbor::Item>& item, + const uint8_t* hdrBegin, + const uint8_t* valueBegin, + const uint8_t* end) { + if (state_ == State::kInit) { + state_ = State::kFinished; + } else if (state_ == State::kRoot) { + state_ = State::kFinished; + } else if (state_ == State::kMetadata) { + if (item->type() == cppbor::ARRAY) { + state_ = State::kRoot; + } + } else if (state_ == State::kShuffle) { + if (item->type() == cppbor::ARRAY) { + state_ = State::kRoot; + } + } else if (state_ == State::kTracks) { + if (item->type() == cppbor::ARRAY) { + state_ = State::kRoot; + } + } else if (state_ == State::kFinished) { } - - void error(const uint8_t* position, - const std::string& errorMessage) override {} - - private: - size_t& pos_; - std::pmr::vector<database::TrackId>& tracks_; - - bool in_root_array_; - bool in_track_list_; -}; + return this; +} auto TrackQueue::deserialise(const std::string& s) -> void { if (s.empty()) { return; } - QueueParseClient client{pos_, tracks_}; + QueueParseClient client{*this}; const uint8_t* data = reinterpret_cast<const uint8_t*>(s.data()); cppbor::parse(data, data + s.size(), &client); notifyChanged(true); diff --git a/src/codecs/include/vorbis.hpp b/src/codecs/include/vorbis.hpp index b32ef8d5..94868c1a 100644 --- a/src/codecs/include/vorbis.hpp +++ b/src/codecs/include/vorbis.hpp @@ -14,8 +14,6 @@ #include <utility> #include "ivorbisfile.h" -#include "ogg/ogg.h" -#include "opus.h" #include "sample.hpp" #include "span.hpp" @@ -41,7 +39,7 @@ class TremorVorbisDecoder : public ICodec { private: std::shared_ptr<IStream> input_; - OggVorbis_File vorbis_; + std::unique_ptr<TremorOggVorbis_File> vorbis_; }; } // namespace codecs diff --git a/src/codecs/miniflac.cpp b/src/codecs/miniflac.cpp index d15410fe..d0b40f96 100644 --- a/src/codecs/miniflac.cpp +++ b/src/codecs/miniflac.cpp @@ -30,9 +30,16 @@ MiniFlacDecoder::MiniFlacDecoder() current_sample_() { miniflac_init(flac_.get(), MINIFLAC_CONTAINER_UNKNOWN); for (int i = 0; i < samples_by_channel_.size(); i++) { - // Full decoded frames too big to fit in internal ram :( + uint32_t caps; + if (i == 0) { + caps = MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL; + } else { + // FIXME: We can *almost* fit two channels into internal ram, but we're a + // few KiB shy of being able to do it safely. + caps = MALLOC_CAP_SPIRAM; + } samples_by_channel_[i] = reinterpret_cast<int32_t*>( - heap_caps_malloc(kMaxFrameSize * sizeof(int32_t), MALLOC_CAP_SPIRAM)); + heap_caps_malloc(kMaxFrameSize * sizeof(int32_t), caps)); } } diff --git a/src/codecs/vorbis.cpp b/src/codecs/vorbis.cpp index 65783dac..e51c6bca 100644 --- a/src/codecs/vorbis.cpp +++ b/src/codecs/vorbis.cpp @@ -4,31 +4,20 @@ * SPDX-License-Identifier: GPL-3.0-only */ -#include "ivorbiscodec.h" -#include "ivorbisfile.h" -#include "ogg/config_types.h" -#include "opus.hpp" - -#include <stdint.h> -#include <sys/_stdint.h> +#include "vorbis.hpp" #include <cstdint> #include <cstring> #include <optional> #include "esp_heap_caps.h" -#include "mad.h" +#include "esp_log.h" +#include "ivorbiscodec.h" +#include "ivorbisfile.h" #include "codec.hpp" -#include "esp_log.h" -#include "ogg/ogg.h" -#include "opus.h" -#include "opus_defines.h" -#include "opus_types.h" -#include "result.hpp" #include "sample.hpp" #include "types.hpp" -#include "vorbis.hpp" namespace codecs { @@ -39,7 +28,7 @@ static size_t read_cb(void* ptr, size_t size, size_t nmemb, void* instance) { return source->Read({reinterpret_cast<std::byte*>(ptr), size * nmemb}); } -static int seek_cb(void* instance, ogg_int64_t offset, int whence) { +static int seek_cb(void* instance, tremor_ogg_int64_t offset, int whence) { IStream* source = reinterpret_cast<IStream*>(instance); if (!source->CanSeek()) { return -1; @@ -78,17 +67,21 @@ static const ov_callbacks kCallbacks{ .tell_func = tell_cb, // Not seekable }; -TremorVorbisDecoder::TremorVorbisDecoder() : input_(), vorbis_() {} +TremorVorbisDecoder::TremorVorbisDecoder() + : input_(), + vorbis_(reinterpret_cast<TremorOggVorbis_File*>( + heap_caps_malloc(sizeof(TremorOggVorbis_File), + MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT))) {} TremorVorbisDecoder::~TremorVorbisDecoder() { - ov_clear(&vorbis_); + ov_clear(vorbis_.get()); } auto TremorVorbisDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t offset) -> cpp::result<OutputFormat, Error> { - int res = ov_open_callbacks(input.get(), &vorbis_, NULL, 0, kCallbacks); + int res = ov_open_callbacks(input.get(), vorbis_.get(), NULL, 0, kCallbacks); if (res < 0) { - std::pmr::string err; + std::string err; switch (res) { case OV_EREAD: err = "OV_EREAD"; @@ -112,13 +105,13 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t off return cpp::fail(Error::kMalformedData); } - vorbis_info* info = ov_info(&vorbis_, -1); + 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_, -1); + auto l = ov_pcm_total(vorbis_.get(), -1); std::optional<uint32_t> length; if (l > 0) { length = l * info->channels; @@ -137,9 +130,10 @@ auto TremorVorbisDecoder::OpenStream(std::shared_ptr<IStream> input,uint32_t off auto TremorVorbisDecoder::DecodeTo(cpp::span<sample::Sample> output) -> cpp::result<OutputInfo, Error> { - int bitstream = 0; - long bytes_written = ov_read(&vorbis_, reinterpret_cast<char*>(output.data()), - output.size_bytes(), &bitstream); + int unused = 0; + long bytes_written = + ov_read(vorbis_.get(), reinterpret_cast<char*>(output.data()), + ((output.size() - 1) * sizeof(sample::Sample)), &unused); if (bytes_written == OV_HOLE) { ESP_LOGE(kTag, "got OV_HOLE"); return cpp::fail(Error::kMalformedData); @@ -156,7 +150,7 @@ auto TremorVorbisDecoder::DecodeTo(cpp::span<sample::Sample> output) } auto TremorVorbisDecoder::SeekTo(size_t target) -> cpp::result<void, Error> { - if (ov_pcm_seek(&vorbis_, target) != 0) { + if (ov_pcm_seek(vorbis_.get(), target) != 0) { return cpp::fail(Error::kInternalError); } return {}; diff --git a/src/dev_console/include/console.hpp b/src/dev_console/include/console.hpp index fd4050c2..fedf3632 100644 --- a/src/dev_console/include/console.hpp +++ b/src/dev_console/include/console.hpp @@ -18,7 +18,7 @@ class Console { auto Launch() -> void; protected: - virtual auto GetStackSizeKiB() -> uint16_t { return 16; } + virtual auto GetStackSizeKiB() -> uint16_t { return 8; } virtual auto RegisterExtraComponents() -> void {} private: diff --git a/src/tasks/tasks.cpp b/src/tasks/tasks.cpp index d53eface..aa382655 100644 --- a/src/tasks/tasks.cpp +++ b/src/tasks/tasks.cpp @@ -39,22 +39,20 @@ auto AllocateStack() -> cpp::span<StackType_t>; // usually written with embedded use cases in mind. template <> auto AllocateStack<Type::kAudioDecoder>() -> cpp::span<StackType_t> { - constexpr std::size_t size = 24 * 1024; + constexpr std::size_t size = 20 * 1024; static StackType_t sStack[size]; return {sStack, size}; } -// LVGL requires only a relatively small stack. However, it can be allocated in -// PSRAM so we give it a bit of headroom for safety. +// LVGL requires only a relatively small stack. Lua's stack is allocated +// separately. template <> auto AllocateStack<Type::kUi>() -> cpp::span<StackType_t> { - constexpr std::size_t size = 16 * 1024; + constexpr std::size_t size = 14 * 1024; static StackType_t sStack[size]; return {sStack, size}; } template <> -// PCM conversion and resampling uses a very small amount of stack. It works -// entirely with PSRAM-allocated buffers, so no real speed gain from allocating -// it internally. +// PCM conversion and resampling uses a very small amount of stack. auto AllocateStack<Type::kAudioConverter>() -> cpp::span<StackType_t> { constexpr std::size_t size = 4 * 1024; static StackType_t sStack[size]; |
