parent
344a46d066
commit
b242ba9986
@ -1,163 +0,0 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "audio/fatfs_audio_input.hpp" |
||||
|
||||
#include <algorithm> |
||||
#include <climits> |
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <functional> |
||||
#include <future> |
||||
#include <memory> |
||||
#include <mutex> |
||||
#include <span> |
||||
#include <string> |
||||
#include <variant> |
||||
|
||||
#include "audio/readahead_source.hpp" |
||||
#include "esp_heap_caps.h" |
||||
#include "esp_log.h" |
||||
#include "ff.h" |
||||
#include "freertos/portmacro.h" |
||||
#include "freertos/projdefs.h" |
||||
|
||||
#include "audio/audio_events.hpp" |
||||
#include "audio/audio_fsm.hpp" |
||||
#include "audio/audio_source.hpp" |
||||
#include "audio/fatfs_source.hpp" |
||||
#include "codec.hpp" |
||||
#include "database/future_fetcher.hpp" |
||||
#include "database/tag_parser.hpp" |
||||
#include "database/track.hpp" |
||||
#include "drivers/spi.hpp" |
||||
#include "events/event_queue.hpp" |
||||
#include "tasks.hpp" |
||||
#include "types.hpp" |
||||
|
||||
[[maybe_unused]] static const char* kTag = "SRC"; |
||||
|
||||
namespace audio { |
||||
|
||||
FatfsAudioInput::FatfsAudioInput(database::ITagParser& tag_parser, |
||||
tasks::WorkerPool& bg_worker) |
||||
: IAudioSource(), |
||||
tag_parser_(tag_parser), |
||||
bg_worker_(bg_worker), |
||||
new_stream_mutex_(), |
||||
new_stream_(), |
||||
has_new_stream_(false) {} |
||||
|
||||
FatfsAudioInput::~FatfsAudioInput() {} |
||||
|
||||
auto FatfsAudioInput::SetPath(std::optional<std::string> path) -> void { |
||||
if (path) { |
||||
SetPath(*path); |
||||
} else { |
||||
SetPath(); |
||||
} |
||||
} |
||||
|
||||
auto FatfsAudioInput::SetPath(const std::string& path, |
||||
uint32_t offset) -> void { |
||||
std::lock_guard<std::mutex> guard{new_stream_mutex_}; |
||||
if (OpenFile(path, offset)) { |
||||
has_new_stream_ = true; |
||||
has_new_stream_.notify_one(); |
||||
} |
||||
} |
||||
|
||||
auto FatfsAudioInput::SetPath() -> void { |
||||
std::lock_guard<std::mutex> guard{new_stream_mutex_}; |
||||
new_stream_.reset(); |
||||
has_new_stream_ = true; |
||||
has_new_stream_.notify_one(); |
||||
} |
||||
|
||||
auto FatfsAudioInput::HasNewStream() -> bool { |
||||
return has_new_stream_; |
||||
} |
||||
|
||||
auto FatfsAudioInput::NextStream() -> std::shared_ptr<TaggedStream> { |
||||
while (true) { |
||||
has_new_stream_.wait(false); |
||||
|
||||
{ |
||||
std::lock_guard<std::mutex> guard{new_stream_mutex_}; |
||||
if (!has_new_stream_.exchange(false)) { |
||||
// If the new stream went away, then we need to go back to waiting.
|
||||
continue; |
||||
} |
||||
|
||||
if (new_stream_ == nullptr) { |
||||
continue; |
||||
} |
||||
|
||||
auto stream = new_stream_; |
||||
new_stream_ = nullptr; |
||||
return stream; |
||||
} |
||||
} |
||||
} |
||||
|
||||
auto FatfsAudioInput::OpenFile(const std::string& path, |
||||
uint32_t offset) -> bool { |
||||
ESP_LOGI(kTag, "opening file %s", path.c_str()); |
||||
|
||||
auto tags = tag_parser_.ReadAndParseTags(path); |
||||
if (!tags) { |
||||
ESP_LOGE(kTag, "failed to read tags"); |
||||
return false; |
||||
} |
||||
if (!tags->title()) { |
||||
tags->title(path); |
||||
} |
||||
|
||||
auto stream_type = ContainerToStreamType(tags->encoding()); |
||||
if (!stream_type.has_value()) { |
||||
ESP_LOGE(kTag, "couldn't match container to stream"); |
||||
return false; |
||||
} |
||||
|
||||
std::unique_ptr<FIL> file = std::make_unique<FIL>(); |
||||
FRESULT res; |
||||
|
||||
{ |
||||
auto lock = drivers::acquire_spi(); |
||||
res = f_open(file.get(), path.c_str(), FA_READ); |
||||
} |
||||
|
||||
if (res != FR_OK) { |
||||
ESP_LOGE(kTag, "failed to open file! res: %i", res); |
||||
return false; |
||||
} |
||||
|
||||
auto source = |
||||
std::make_unique<FatfsSource>(stream_type.value(), std::move(file)); |
||||
new_stream_.reset(new TaggedStream(tags, std::move(source), path, offset)); |
||||
return true; |
||||
} |
||||
|
||||
auto FatfsAudioInput::ContainerToStreamType(database::Container enc) |
||||
-> std::optional<codecs::StreamType> { |
||||
switch (enc) { |
||||
case database::Container::kMp3: |
||||
return codecs::StreamType::kMp3; |
||||
case database::Container::kWav: |
||||
return codecs::StreamType::kWav; |
||||
case database::Container::kOgg: |
||||
return codecs::StreamType::kVorbis; |
||||
case database::Container::kFlac: |
||||
return codecs::StreamType::kFlac; |
||||
case database::Container::kOpus: |
||||
return codecs::StreamType::kOpus; |
||||
case database::Container::kUnsupported: |
||||
default: |
||||
return {}; |
||||
} |
||||
} |
||||
|
||||
} // namespace audio
|
@ -1,66 +0,0 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <future> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "ff.h" |
||||
#include "freertos/portmacro.h" |
||||
|
||||
#include "audio/audio_source.hpp" |
||||
#include "codec.hpp" |
||||
#include "database/future_fetcher.hpp" |
||||
#include "database/tag_parser.hpp" |
||||
#include "tasks.hpp" |
||||
#include "types.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
/*
|
||||
* Audio source that fetches data from a FatFs (or exfat i guess) filesystem. |
||||
* |
||||
* All public methods are safe to call from any task. |
||||
*/ |
||||
class FatfsAudioInput : public IAudioSource { |
||||
public: |
||||
explicit FatfsAudioInput(database::ITagParser&, tasks::WorkerPool&); |
||||
~FatfsAudioInput(); |
||||
|
||||
/*
|
||||
* Immediately cease reading any current source, and begin reading from the |
||||
* given file path. |
||||
*/ |
||||
auto SetPath(std::optional<std::string>) -> void; |
||||
auto SetPath(const std::string&, uint32_t offset = 0) -> void; |
||||
auto SetPath() -> void; |
||||
|
||||
auto HasNewStream() -> bool override; |
||||
auto NextStream() -> std::shared_ptr<TaggedStream> override; |
||||
|
||||
FatfsAudioInput(const FatfsAudioInput&) = delete; |
||||
FatfsAudioInput& operator=(const FatfsAudioInput&) = delete; |
||||
|
||||
private: |
||||
auto OpenFile(const std::string& path, uint32_t offset) -> bool; |
||||
|
||||
auto ContainerToStreamType(database::Container) |
||||
-> std::optional<codecs::StreamType>; |
||||
|
||||
database::ITagParser& tag_parser_; |
||||
tasks::WorkerPool& bg_worker_; |
||||
|
||||
std::mutex new_stream_mutex_; |
||||
std::shared_ptr<TaggedStream> new_stream_; |
||||
|
||||
std::atomic<bool> has_new_stream_; |
||||
}; |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,104 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#include "audio/fatfs_stream_factory.hpp" |
||||
|
||||
#include <cstdint> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "database/database.hpp" |
||||
#include "esp_log.h" |
||||
#include "ff.h" |
||||
#include "freertos/portmacro.h" |
||||
#include "freertos/projdefs.h" |
||||
|
||||
#include "audio/audio_source.hpp" |
||||
#include "audio/fatfs_source.hpp" |
||||
#include "codec.hpp" |
||||
#include "database/tag_parser.hpp" |
||||
#include "database/track.hpp" |
||||
#include "drivers/spi.hpp" |
||||
#include "system_fsm/service_locator.hpp" |
||||
#include "tasks.hpp" |
||||
#include "types.hpp" |
||||
|
||||
[[maybe_unused]] static const char* kTag = "SRC"; |
||||
|
||||
namespace audio { |
||||
|
||||
FatfsStreamFactory::FatfsStreamFactory(system_fsm::ServiceLocator& services) |
||||
: services_(services) {} |
||||
|
||||
auto FatfsStreamFactory::create(database::TrackId id, uint32_t offset) |
||||
-> std::shared_ptr<TaggedStream> { |
||||
auto db = services_.database().lock(); |
||||
if (!db) { |
||||
return {}; |
||||
} |
||||
auto path = db->getTrackPath(id); |
||||
if (!path) { |
||||
return {}; |
||||
} |
||||
return create(*path, offset); |
||||
} |
||||
|
||||
auto FatfsStreamFactory::create(std::string path, uint32_t offset) |
||||
-> std::shared_ptr<TaggedStream> { |
||||
auto tags = services_.tag_parser().ReadAndParseTags(path); |
||||
if (!tags) { |
||||
ESP_LOGE(kTag, "failed to read tags"); |
||||
return {}; |
||||
} |
||||
|
||||
if (!tags->title()) { |
||||
tags->title(path); |
||||
} |
||||
|
||||
auto stream_type = ContainerToStreamType(tags->encoding()); |
||||
if (!stream_type.has_value()) { |
||||
ESP_LOGE(kTag, "couldn't match container to stream"); |
||||
return {}; |
||||
} |
||||
|
||||
std::unique_ptr<FIL> file = std::make_unique<FIL>(); |
||||
FRESULT res; |
||||
|
||||
{ |
||||
auto lock = drivers::acquire_spi(); |
||||
res = f_open(file.get(), path.c_str(), FA_READ); |
||||
} |
||||
|
||||
if (res != FR_OK) { |
||||
ESP_LOGE(kTag, "failed to open file! res: %i", res); |
||||
return {}; |
||||
} |
||||
|
||||
return std::make_shared<TaggedStream>( |
||||
tags, std::make_unique<FatfsSource>(stream_type.value(), std::move(file)), |
||||
path, offset); |
||||
} |
||||
|
||||
auto FatfsStreamFactory::ContainerToStreamType(database::Container enc) |
||||
-> std::optional<codecs::StreamType> { |
||||
switch (enc) { |
||||
case database::Container::kMp3: |
||||
return codecs::StreamType::kMp3; |
||||
case database::Container::kWav: |
||||
return codecs::StreamType::kWav; |
||||
case database::Container::kOgg: |
||||
return codecs::StreamType::kVorbis; |
||||
case database::Container::kFlac: |
||||
return codecs::StreamType::kFlac; |
||||
case database::Container::kOpus: |
||||
return codecs::StreamType::kOpus; |
||||
case database::Container::kUnsupported: |
||||
default: |
||||
return {}; |
||||
} |
||||
} |
||||
|
||||
} // namespace audio
|
@ -0,0 +1,53 @@ |
||||
/*
|
||||
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||
* |
||||
* SPDX-License-Identifier: GPL-3.0-only |
||||
*/ |
||||
|
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <future> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "database/database.hpp" |
||||
#include "database/track.hpp" |
||||
#include "ff.h" |
||||
#include "freertos/portmacro.h" |
||||
|
||||
#include "audio/audio_source.hpp" |
||||
#include "codec.hpp" |
||||
#include "database/future_fetcher.hpp" |
||||
#include "database/tag_parser.hpp" |
||||
#include "system_fsm/service_locator.hpp" |
||||
#include "tasks.hpp" |
||||
#include "types.hpp" |
||||
|
||||
namespace audio { |
||||
|
||||
/*
|
||||
* Utility to create streams that read from files on the sd card. |
||||
*/ |
||||
class FatfsStreamFactory { |
||||
public: |
||||
explicit FatfsStreamFactory(system_fsm::ServiceLocator&); |
||||
|
||||
auto create(database::TrackId, uint32_t offset = 0) |
||||
-> std::shared_ptr<TaggedStream>; |
||||
auto create(std::string, uint32_t offset = 0) |
||||
-> std::shared_ptr<TaggedStream>; |
||||
|
||||
FatfsStreamFactory(const FatfsStreamFactory&) = delete; |
||||
FatfsStreamFactory& operator=(const FatfsStreamFactory&) = delete; |
||||
|
||||
private: |
||||
auto ContainerToStreamType(database::Container) |
||||
-> std::optional<codecs::StreamType>; |
||||
|
||||
system_fsm::ServiceLocator& services_; |
||||
}; |
||||
|
||||
} // namespace audio
|
Loading…
Reference in new issue