From 7379ba37c02c38e731e7639b11e7f6621c0eb6f5 Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Mon, 23 Dec 2019 13:59:43 +0100 Subject: [PATCH] The file promise logic actually allocated Changed to add a pool of "empty" promises, but I wonder if all of this could be made a bit simpler? --- src/sfizz/Config.h | 1 + src/sfizz/FilePool.cpp | 65 ++++++++++++++++++++++++++++++------------ src/sfizz/FilePool.h | 12 ++++++++ 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 22be0a27..5f949be8 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -40,6 +40,7 @@ namespace config { constexpr int numBackgroundThreads { 4 }; constexpr int numVoices { 64 }; constexpr int maxVoices { 256 }; + constexpr int maxFilePromises { maxVoices * 2 }; constexpr int sustainCC { 64 }; constexpr int allSoundOffCC { 120 }; constexpr int resetCC { 121 }; diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 7e61dbf3..945be27e 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -89,7 +89,11 @@ sfz::FilePool::FilePool() { for (int i = 0; i < config::numBackgroundThreads; ++i) threadPool.emplace_back( &FilePool::loadingThread, this ); + threadPool.emplace_back( &FilePool::clearingThread, this ); + + for (int i = 0; i < config::maxFilePromises; ++i) + emptyPromises.push_back(std::make_shared()); } sfz::FilePool::~FilePool() @@ -161,15 +165,21 @@ bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept { - auto promise = std::make_shared(); + if (emptyPromises.empty()) + return {}; + const auto preloaded = preloadedFiles.find(filename); - if (preloaded != preloadedFiles.end()) { - promise->filename = preloaded->first; - promise->preloadedData = preloaded->second.preloadedData; - promise->sampleRate = preloaded->second.sampleRate; - promise->oversamplingFactor = oversamplingFactor; - promiseQueue.try_enqueue(promise); - } + if (preloaded == preloadedFiles.end()) + return {}; + + auto& promise = emptyPromises.back(); + promise->filename = preloaded->first; + promise->preloadedData = preloaded->second.preloadedData; + promise->sampleRate = preloaded->second.sampleRate; + promise->oversamplingFactor = oversamplingFactor; + promiseQueue.try_enqueue(promise); + emptyPromises.pop_back(); + return promise; } @@ -193,7 +203,10 @@ void sfz::FilePool::tryToClearPromises() while (addingPromisesToClear) std::this_thread::sleep_for(1ms); - promisesToClear.clear(); + for (auto& promise: promisesToClear) { + if (promise->dataReady) + promise->reset(); + } } void sfz::FilePool::clearingThread() @@ -261,21 +274,37 @@ void sfz::FilePool::cleanupPromises() noexcept if (!canAddPromisesToClear) return; + // The garbage collection cleared the data from these so we can move them + // back to the empty queue + auto clearedIterator = promisesToClear.begin(); + auto clearedSentinel = promisesToClear.end() - 1; + while (clearedIterator != promisesToClear.end()) { + if (clearedIterator->get()->dataReady == false) { + emptyPromises.push_back(*clearedIterator); + std::iter_swap(clearedIterator, clearedSentinel); + clearedSentinel--; + promisesToClear.pop_back(); + } else { + clearedIterator++; + } + } + FilePromisePtr promise; - // Remove stuff from the filled queue and put them in a linear storage + // Remove the promises from the filled queue and put them in a linear + // storage while (filledPromiseQueue.try_dequeue(promise)) temporaryFilePromises.push_back(promise); - auto promiseIterator = temporaryFilePromises.begin(); - auto sentinel = temporaryFilePromises.end() - 1; - while (promiseIterator != temporaryFilePromises.end()) { - if (promiseIterator->use_count() == 1) { - promisesToClear.push_back(*promiseIterator); - std::iter_swap(promiseIterator, sentinel); - sentinel--; + auto filledIterator = temporaryFilePromises.begin(); + auto filledSentinel = temporaryFilePromises.end() - 1; + while (filledIterator != temporaryFilePromises.end()) { + if (filledIterator->use_count() == 1) { + promisesToClear.push_back(*filledIterator); + std::iter_swap(filledIterator, filledSentinel); + filledSentinel--; temporaryFilePromises.pop_back(); } else { - promiseIterator++; + filledIterator++; } } } diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 4007d33a..b01763b4 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -58,6 +58,17 @@ struct FilePromise return AudioSpan(*preloadedData); } + void reset() + { + fileData.reset(); + preloadedData.reset(); + filename = ""; + availableFrames = 0; + dataReady = false; + oversamplingFactor = config::defaultOversamplingFactor; + sampleRate = config::defaultSampleRate; + } + absl::string_view filename {}; AudioBufferPtr preloadedData {}; AudioBuffer fileData {}; @@ -214,6 +225,7 @@ private: std::atomic threadsLoading { 0 }; // File promises data structures along with their guards. + std::vector emptyPromises; std::vector temporaryFilePromises; std::vector promisesToClear; std::atomic addingPromisesToClear { false };