|
|
@ -16,7 +16,12 @@ namespace audio { |
|
|
|
[[maybe_unused]] static constexpr char kTag[] = "playlist"; |
|
|
|
[[maybe_unused]] static constexpr char kTag[] = "playlist"; |
|
|
|
|
|
|
|
|
|
|
|
Playlist::Playlist(std::string playlistFilepath) |
|
|
|
Playlist::Playlist(std::string playlistFilepath) |
|
|
|
: filepath_(playlistFilepath), mutex_(), total_size_(0), pos_(0) {} |
|
|
|
: filepath_(playlistFilepath), |
|
|
|
|
|
|
|
mutex_(), |
|
|
|
|
|
|
|
total_size_(0), |
|
|
|
|
|
|
|
pos_(-1), |
|
|
|
|
|
|
|
offset_cache_(&memory::kSpiRamResource), |
|
|
|
|
|
|
|
sample_size_(50) {} |
|
|
|
|
|
|
|
|
|
|
|
auto Playlist::open() -> bool { |
|
|
|
auto Playlist::open() -> bool { |
|
|
|
FRESULT res = |
|
|
|
FRESULT res = |
|
|
@ -47,8 +52,10 @@ auto Playlist::size() const -> size_t { |
|
|
|
auto Playlist::append(Item i) -> void { |
|
|
|
auto Playlist::append(Item i) -> void { |
|
|
|
std::unique_lock<std::mutex> lock(mutex_); |
|
|
|
std::unique_lock<std::mutex> lock(mutex_); |
|
|
|
auto offset = f_tell(&file_); |
|
|
|
auto offset = f_tell(&file_); |
|
|
|
|
|
|
|
bool first_entry = current_value_.empty(); |
|
|
|
// Seek to end and append
|
|
|
|
// Seek to end and append
|
|
|
|
auto res = f_lseek(&file_, f_size(&file_)); |
|
|
|
auto end = f_size(&file_); |
|
|
|
|
|
|
|
auto res = f_lseek(&file_, end); |
|
|
|
if (res != FR_OK) { |
|
|
|
if (res != FR_OK) { |
|
|
|
ESP_LOGE(kTag, "Seek to end of file failed? Error %d", res); |
|
|
|
ESP_LOGE(kTag, "Seek to end of file failed? Error %d", res); |
|
|
|
return; |
|
|
|
return; |
|
|
@ -58,10 +65,13 @@ auto Playlist::append(Item i) -> void { |
|
|
|
if (std::holds_alternative<std::string>(i)) { |
|
|
|
if (std::holds_alternative<std::string>(i)) { |
|
|
|
path = std::get<std::string>(i); |
|
|
|
path = std::get<std::string>(i); |
|
|
|
f_printf(&file_, "%s\n", path.c_str()); |
|
|
|
f_printf(&file_, "%s\n", path.c_str()); |
|
|
|
total_size_++; |
|
|
|
if (total_size_ % sample_size_ == 0) { |
|
|
|
if (current_value_.empty()) { |
|
|
|
offset_cache_.push_back(end); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (first_entry) { |
|
|
|
current_value_ = path; |
|
|
|
current_value_ = path; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
total_size_++; |
|
|
|
} |
|
|
|
} |
|
|
|
// Restore position
|
|
|
|
// Restore position
|
|
|
|
res = f_lseek(&file_, offset); |
|
|
|
res = f_lseek(&file_, offset); |
|
|
@ -77,8 +87,26 @@ auto Playlist::append(Item i) -> void { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto Playlist::skipTo(size_t position) -> void { |
|
|
|
auto Playlist::skipTo(size_t position) -> void { |
|
|
|
|
|
|
|
std::unique_lock<std::mutex> lock(mutex_); |
|
|
|
|
|
|
|
// Check our cache and go to nearest entry
|
|
|
|
pos_ = position; |
|
|
|
pos_ = position; |
|
|
|
consumeAndCount(position); |
|
|
|
auto remainder = position % sample_size_; |
|
|
|
|
|
|
|
auto quotient = (position - remainder) / sample_size_; |
|
|
|
|
|
|
|
if (offset_cache_.size() <= quotient) { |
|
|
|
|
|
|
|
// Fall back case
|
|
|
|
|
|
|
|
ESP_LOGW(kTag, "File offset cache failed, falling back..."); |
|
|
|
|
|
|
|
f_rewind(&file_); |
|
|
|
|
|
|
|
advanceBy(pos_); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
auto entry = offset_cache_.at(quotient); |
|
|
|
|
|
|
|
// Go to byte offset
|
|
|
|
|
|
|
|
auto res = f_lseek(&file_, entry); |
|
|
|
|
|
|
|
if (res != FR_OK) { |
|
|
|
|
|
|
|
ESP_LOGW(kTag, "Error going to byte offset %llu for playlist entry index %d", entry, pos_); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Count ahead entries
|
|
|
|
|
|
|
|
advanceBy(remainder+1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto Playlist::next() -> void { |
|
|
|
auto Playlist::next() -> void { |
|
|
@ -99,6 +127,7 @@ auto Playlist::value() const -> std::string { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
auto Playlist::clear() -> bool { |
|
|
|
auto Playlist::clear() -> bool { |
|
|
|
|
|
|
|
std::unique_lock<std::mutex> lock(mutex_); |
|
|
|
auto res = f_close(&file_); |
|
|
|
auto res = f_close(&file_); |
|
|
|
if (res != FR_OK) { |
|
|
|
if (res != FR_OK) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
@ -110,6 +139,7 @@ auto Playlist::clear() -> bool { |
|
|
|
} |
|
|
|
} |
|
|
|
total_size_ = 0; |
|
|
|
total_size_ = 0; |
|
|
|
current_value_.clear(); |
|
|
|
current_value_.clear(); |
|
|
|
|
|
|
|
offset_cache_.clear(); |
|
|
|
pos_ = 0; |
|
|
|
pos_ = 0; |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
@ -128,6 +158,7 @@ auto Playlist::consumeAndCount(ssize_t upto) -> bool { |
|
|
|
size_t count = 0; |
|
|
|
size_t count = 0; |
|
|
|
f_rewind(&file_); |
|
|
|
f_rewind(&file_); |
|
|
|
while (!f_eof(&file_)) { |
|
|
|
while (!f_eof(&file_)) { |
|
|
|
|
|
|
|
auto offset = f_tell(&file_); |
|
|
|
// TODO: Correctly handle lines longer than this
|
|
|
|
// TODO: Correctly handle lines longer than this
|
|
|
|
// TODO: Also correctly handle the case where the last entry doesn't end in
|
|
|
|
// TODO: Also correctly handle the case where the last entry doesn't end in
|
|
|
|
// \n
|
|
|
|
// \n
|
|
|
@ -136,6 +167,9 @@ auto Playlist::consumeAndCount(ssize_t upto) -> bool { |
|
|
|
ESP_LOGW(kTag, "Error consuming playlist file at line %d", count); |
|
|
|
ESP_LOGW(kTag, "Error consuming playlist file at line %d", count); |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (count % sample_size_ == 0) { |
|
|
|
|
|
|
|
offset_cache_.push_back(offset); |
|
|
|
|
|
|
|
} |
|
|
|
count++; |
|
|
|
count++; |
|
|
|
|
|
|
|
|
|
|
|
if (upto >= 0 && count > upto) { |
|
|
|
if (upto >= 0 && count > upto) { |
|
|
@ -151,4 +185,23 @@ auto Playlist::consumeAndCount(ssize_t upto) -> bool { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto Playlist::advanceBy(ssize_t amt) -> bool { |
|
|
|
|
|
|
|
TCHAR buff[512]; |
|
|
|
|
|
|
|
size_t count = 0; |
|
|
|
|
|
|
|
while (!f_eof(&file_)) { |
|
|
|
|
|
|
|
auto res = f_gets(buff, 512, &file_); |
|
|
|
|
|
|
|
if (res == NULL) { |
|
|
|
|
|
|
|
ESP_LOGW(kTag, "Error consuming playlist file at line %d", count); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
count++; |
|
|
|
|
|
|
|
if (count >= amt) { |
|
|
|
|
|
|
|
size_t len = strlen(buff); |
|
|
|
|
|
|
|
current_value_.assign(buff, len - 1); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // namespace audio
|
|
|
|
} // namespace audio
|