summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/include/track_queue.hpp41
-rw-r--r--src/audio/track_queue.cpp150
-rw-r--r--src/codecs/include/vorbis.hpp4
-rw-r--r--src/codecs/miniflac.cpp11
-rw-r--r--src/codecs/vorbis.cpp46
-rw-r--r--src/dev_console/include/console.hpp2
-rw-r--r--src/tasks/tasks.cpp12
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];