Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added status full loaded into FileData #1246

Merged
merged 10 commits into from
Mar 20, 2024
120 changes: 80 additions & 40 deletions src/sfizz/FilePool.cpp
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@
#include <memory>
#include <thread>
#include <system_error>
#include <atomic_queue/defs.h>
#if defined(_WIN32)
#include <windows.h>
#else
@@ -332,20 +333,23 @@ bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexce

const auto existingFile = preloadedFiles.find(fileId);
if (existingFile != preloadedFiles.end()) {
if (framesToLoad > existingFile->second.preloadedData.getNumFrames()) {
preloadedFiles[fileId].information.maxOffset = maxOffset;
preloadedFiles[fileId].preloadedData = readFromFile(*reader, framesToLoad);
auto& fileData = existingFile->second;
if (framesToLoad > fileData.preloadedData.getNumFrames()) {
fileData.information.maxOffset = maxOffset;
fileData.preloadedData = readFromFile(*reader, framesToLoad);
fileData.fullyLoaded = frames == framesToLoad;
}
existingFile->second.preloadCallCount++;
fileData.preloadCallCount++;
} else {
fileInformation->sampleRate = static_cast<double>(reader->sampleRate());
auto insertedPair = preloadedFiles.insert_or_assign(fileId, {
readFromFile(*reader, framesToLoad),
*fileInformation
});

insertedPair.first->second.status = FileData::Status::Preloaded;
insertedPair.first->second.preloadCallCount++;
insertedPair.first->second.status = FileData::Status::Preloaded;
insertedPair.first->second.fullyLoaded = framesToLoad == frames;
}

return true;
@@ -399,8 +403,9 @@ sfz::FileDataHolder sfz::FilePool::loadFile(const FileId& fileId) noexcept
readFromFile(*reader, frames),
*fileInformation
});
insertedPair.first->second.status = FileData::Status::Preloaded;
insertedPair.first->second.preloadCallCount++;
insertedPair.first->second.status = FileData::Status::Preloaded;
insertedPair.first->second.fullyLoaded = true;
return { &insertedPair.first->second };
}

@@ -417,8 +422,9 @@ sfz::FileDataHolder sfz::FilePool::loadFromRam(const FileId& fileId, const std::
readFromFile(*reader, frames),
*fileInformation
});
insertedPair.first->second.status = FileData::Status::Preloaded;
insertedPair.first->second.preloadCallCount++;
insertedPair.first->second.status = FileData::Status::Preloaded;
insertedPair.first->second.fullyLoaded = true;
DBG("Added a file " << fileId.filename());
return { &insertedPair.first->second };
}
@@ -435,8 +441,9 @@ sfz::FileDataHolder sfz::FilePool::getFilePromise(const std::shared_ptr<FileId>&
return {};
}

if (!loadInRam) {
QueuedFileData queuedData { fileId, &preloaded->second };
auto& fileData = preloaded->second;
if (!fileData.fullyLoaded) {
QueuedFileData queuedData { fileId, &fileData };
if (!filesToLoad->try_push(queuedData)) {
DBG("[sfizz] Could not enqueue the file to load for " << fileId << " (queue capacity " << filesToLoad->capacity() << ")");
return {};
@@ -458,10 +465,15 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept

// Update all the preloaded sizes
for (auto& preloadedFile : preloadedFiles) {
const auto maxOffset = preloadedFile.second.information.maxOffset;
fs::path file { rootDirectory / preloadedFile.first.filename() };
AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse());
preloadedFile.second.preloadedData = readFromFile(*reader, preloadSize + maxOffset);
auto& fileId = preloadedFile.first;
auto& fileData = preloadedFile.second;
const auto maxOffset = fileData.information.maxOffset;
fs::path file { rootDirectory / fileId.filename() };
AudioReaderPtr reader = createAudioReader(file, fileId.isReverse());
const auto frames = reader->frames();
const auto framesToLoad = min(frames, maxOffset + preloadSize);
fileData.preloadedData = readFromFile(*reader, static_cast<uint32_t>(framesToLoad));
fileData.fullyLoaded = frames == framesToLoad;
}
}

@@ -484,28 +496,39 @@ void sfz::FilePool::loadingJob(const QueuedFileData& data) noexcept
return;
}

FileData::Status currentStatus = data.data->status.load();
FileData::Status currentStatus;

unsigned spinCounter { 0 };
while (currentStatus == FileData::Status::Invalid) {
// Spin until the state changes
if (spinCounter > 1024) {
DBG("[sfizz] " << *id << " is stuck on Invalid? Leaving the load");
return;
}

std::this_thread::sleep_for(std::chrono::microseconds(100));
while (1) {
currentStatus = data.data->status.load();
spinCounter += 1;
}
while (currentStatus == FileData::Status::Invalid) {
// Spin until the state changes
if (spinCounter > 1024) {
DBG("[sfizz] " << *id << " is stuck on Invalid? Leaving the load");
return;
}

// Already loading or loaded
if (currentStatus != FileData::Status::Preloaded)
return;
std::this_thread::sleep_for(std::chrono::microseconds(100));
currentStatus = data.data->status.load();
spinCounter += 1;
}
// wait for garbage collection
if (currentStatus == FileData::Status::GarbageCollecting) {
atomic_queue::spin_loop_pause();
atomic_queue::spin_loop_pause();
atomic_queue::spin_loop_pause();
atomic_queue::spin_loop_pause();
continue;
}
// Already loading or loaded
if (currentStatus != FileData::Status::Preloaded)
return;

// Someone else got the token
if (!data.data->status.compare_exchange_strong(currentStatus, FileData::Status::Streaming))
return;
// go outside loop if this gets token
if (data.data->status.compare_exchange_strong(currentStatus, FileData::Status::Streaming))
break;
}

streamFromFile(*reader, data.data->fileData, &data.data->availableFrames);

@@ -619,10 +642,12 @@ void sfz::FilePool::setRamLoading(bool loadInRam) noexcept
for (auto& preloadedFile : preloadedFiles) {
fs::path file { rootDirectory / preloadedFile.first.filename() };
AudioReaderPtr reader = createAudioReader(file, preloadedFile.first.isReverse());
preloadedFile.second.preloadedData = readFromFile(
auto& fileData = preloadedFile.second;
fileData.preloadedData = readFromFile(
*reader,
preloadedFile.second.information.end
fileData.information.end
);
fileData.fullyLoaded = true;
}
} else {
setPreloadSize(preloadSize);
@@ -648,11 +673,6 @@ void sfz::FilePool::triggerGarbageCollection() noexcept
}

sfz::FileData& data = it->second;
if (data.status == FileData::Status::Preloaded)
return true;

if (data.status != FileData::Status::Done)
return false;

if (data.readerCount != 0)
return false;
@@ -661,10 +681,30 @@ void sfz::FilePool::triggerGarbageCollection() noexcept
if (secondsIdle < config::fileClearingPeriod)
return false;

data.availableFrames = 0;
data.status = FileData::Status::Preloaded;
garbageToCollect.push_back(std::move(data.fileData));
return true;
auto status = data.status.load();
if (status == FileData::Status::Preloaded) {
// do the garbage collection when availableFrames != 0
if (data.availableFrames == 0) {
return true;
}
}
else if (status != FileData::Status::Done) {
return false;
}

// do garbage collection when changing the status is success
if (data.status.compare_exchange_strong(status, FileData::Status::GarbageCollecting)) {
// recheck readerCount
auto readerCount = data.readerCount.load();
if (readerCount == 0) {
data.availableFrames = 0;
garbageToCollect.push_back(std::move(data.fileData));
data.status = FileData::Status::Preloaded;
return true;
}
data.status = status;
}
return false;
});

std::error_code ec;
6 changes: 4 additions & 2 deletions src/sfizz/FilePool.h
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@ struct FileInformation {
// Strict C++11 disallows member initialization if aggregate initialization is to be used...
struct FileData
{
enum class Status { Invalid, Preloaded, Streaming, Done };
enum class Status { Invalid, Preloaded, Streaming, Done, GarbageCollecting };
FileData() = default;
FileData(FileAudioBuffer preloaded, FileInformation info)
: preloadedData(std::move(preloaded)), information(std::move(info))
@@ -76,7 +76,8 @@ struct FileData
}
AudioSpan<const float> getData()
{
if (availableFrames > preloadedData.getNumFrames())
ASSERT(readerCount > 0);
if (status != Status::GarbageCollecting && availableFrames > preloadedData.getNumFrames())
return AudioSpan<const float>(fileData).first(availableFrames);
else
return AudioSpan<const float>(preloadedData);
@@ -113,6 +114,7 @@ struct FileData
FileAudioBuffer fileData {};
int preloadCallCount { 0 };
std::atomic<Status> status { Status::Invalid };
bool fullyLoaded { false };
std::atomic<size_t> availableFrames { 0 };
std::atomic<int> readerCount { 0 };
std::chrono::time_point<std::chrono::high_resolution_clock> lastViewerLeftAt;