Added a garbage collection to the file pool. Apparently something in the kernel or in libsndfile is memory-mapping the files, so that the memory usage stays quite high and there are no disk read. But since ASan does not complain, it seems okay...
This commit is contained in:
parent
b6678113fc
commit
6c70269fcc
4 changed files with 52 additions and 11 deletions
|
|
@ -28,7 +28,9 @@
|
|||
#include "absl/types/span.h"
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sndfile.hh>
|
||||
#include <thread>
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template <class T>
|
||||
|
|
@ -42,7 +44,7 @@ std::unique_ptr<AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, int numFram
|
|||
sndFile.readf(tempReadBuffer->channelWriter(0), numFrames);
|
||||
::readInterleaved<float>(tempReadBuffer->getSpan(0), returnedBuffer->getSpan(0), returnedBuffer->getSpan(1));
|
||||
}
|
||||
return returnedBuffer;
|
||||
return std::move(returnedBuffer);
|
||||
}
|
||||
|
||||
std::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(std::string_view filename, uint32_t offset) noexcept
|
||||
|
|
@ -79,11 +81,20 @@ std::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(
|
|||
if (preloadedData.contains(filename)) {
|
||||
auto alreadyPreloaded = preloadedData[filename];
|
||||
if (preloadedSize > alreadyPreloaded->getNumFrames()) {
|
||||
// FIXME: Okay, ideally here you would have a double indirection so that we can update _all_ the preloaded
|
||||
// files in previous regions to account for the new offset
|
||||
//
|
||||
// Before this next command, some old regions and the file pool hold a shared pointer to the same audio buffer.
|
||||
// This audio buffer is OK for the old regions, but too small for the new one.
|
||||
// By resetting the filepool data to a new, longer audiobuffer, we are creating 2 copies of the same audio data.
|
||||
// The filepool and the new regions have the longer copy, and the older regions have the shorter copy.
|
||||
// This is not entirely optimal, but is it better to write a double shared pointer ?
|
||||
// std::shared_ptr<std::shared_ptr<AudioBuffer>>> is a bit ugly...
|
||||
alreadyPreloaded.reset(readFromFile<float>(sndFile, preloadedSize).release());
|
||||
}
|
||||
returnedValue.preloadedData = alreadyPreloaded;
|
||||
} else {
|
||||
returnedValue.preloadedData = std::shared_ptr<AudioBuffer<float>>(readFromFile<float>(sndFile, preloadedSize));
|
||||
returnedValue.preloadedData = readFromFile<float>(sndFile, preloadedSize);
|
||||
preloadedData[filename] = returnedValue.preloadedData;
|
||||
}
|
||||
|
||||
|
|
@ -99,10 +110,11 @@ void sfz::FilePool::enqueueLoading(Voice* voice, std::string_view sample, int nu
|
|||
|
||||
void sfz::FilePool::loadingThread() noexcept
|
||||
{
|
||||
FileLoadingInformation fileToLoad {};
|
||||
while (!quitThread) {
|
||||
if (!loadingQueue.wait_dequeue_timed(fileToLoad, 10ms))
|
||||
FileLoadingInformation fileToLoad {};
|
||||
if (!loadingQueue.wait_dequeue_timed(fileToLoad, 200ms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileToLoad.voice == nullptr) {
|
||||
DBG("Background thread error: voice is null.");
|
||||
|
|
@ -117,15 +129,34 @@ void sfz::FilePool::loadingThread() noexcept
|
|||
}
|
||||
|
||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||
auto fileLoaded = std::make_unique<AudioBuffer<float>>(sndFile.channels(), fileToLoad.numFrames);
|
||||
fileToLoad.voice->setFileData(readFromFile<float>(sndFile, fileToLoad.numFrames), fileToLoad.ticket);
|
||||
|
||||
std::lock_guard guard { fileHandleMutex };
|
||||
auto newHandle = fileHandles.emplace_back(readFromFile<float>(sndFile, fileToLoad.numFrames));
|
||||
fileToLoad.voice->setFileData(newHandle, fileToLoad.ticket);
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::FilePool::garbageThread() noexcept
|
||||
{
|
||||
while (!quitThread) {
|
||||
for (auto handle = fileHandles.begin(); handle < fileHandles.end();) {
|
||||
if (handle->use_count() == 1) {
|
||||
handle->reset();
|
||||
std::lock_guard guard { fileHandleMutex };
|
||||
std::iter_swap(handle, fileHandles.end() - 1);
|
||||
fileHandles.pop_back();
|
||||
} else {
|
||||
handle++;
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::FilePool::clear()
|
||||
{
|
||||
preloadedData.clear();
|
||||
while(loadingQueue.pop()){
|
||||
while (loadingQueue.pop()) {
|
||||
// Pop the queue
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
#include "filesystem.h"
|
||||
#include "readerwriterqueue.h"
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
|
@ -39,6 +40,7 @@ class FilePool {
|
|||
public:
|
||||
FilePool()
|
||||
: fileLoadingThread(std::thread(&FilePool::loadingThread, this))
|
||||
, garbageCollectionThread(std::thread(&FilePool::garbageThread, this))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -46,6 +48,7 @@ public:
|
|||
{
|
||||
quitThread = true;
|
||||
fileLoadingThread.join();
|
||||
garbageCollectionThread.join();
|
||||
}
|
||||
void setRootDirectory(const std::filesystem::path& directory) noexcept { rootDirectory = directory; }
|
||||
size_t getNumPreloadedSamples() const noexcept { return preloadedData.size(); }
|
||||
|
|
@ -71,7 +74,11 @@ private:
|
|||
|
||||
moodycamel::BlockingReaderWriterQueue<FileLoadingInformation> loadingQueue { config::numVoices };
|
||||
void loadingThread() noexcept;
|
||||
void garbageThread() noexcept;
|
||||
std::thread fileLoadingThread;
|
||||
std::thread garbageCollectionThread;
|
||||
std::vector<std::shared_ptr<AudioBuffer<float>>> fileHandles;
|
||||
std::mutex fileHandleMutex;
|
||||
bool quitThread { false };
|
||||
absl::flat_hash_map<std::string_view, std::shared_ptr<AudioBuffer<float>>> preloadedData;
|
||||
LEAK_DETECTOR(FilePool);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "SIMDHelpers.h"
|
||||
#include "SfzHelpers.h"
|
||||
#include "absl/algorithm/container.h"
|
||||
#include <memory>
|
||||
|
||||
sfz::Voice::Voice(const CCValueArray& ccState)
|
||||
: ccState(ccState)
|
||||
|
|
@ -117,7 +118,7 @@ void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept
|
|||
normalizePercents(region->amplitudeEG.getStart(ccState, velocity)));
|
||||
}
|
||||
|
||||
void sfz::Voice::setFileData(std::unique_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept
|
||||
void sfz::Voice::setFileData(std::shared_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept
|
||||
{
|
||||
if (ticket != this->ticket)
|
||||
return;
|
||||
|
|
@ -473,13 +474,14 @@ sfz::Voice::TriggerType sfz::Voice::getTriggerType() const noexcept
|
|||
void sfz::Voice::reset() noexcept
|
||||
{
|
||||
dataReady.store(false);
|
||||
fileData.reset();
|
||||
state = State::idle;
|
||||
if (region != nullptr) {
|
||||
DBG("Reset voice with sample " << region->sample);
|
||||
}
|
||||
region = nullptr;
|
||||
sourcePosition = 0;
|
||||
floatPositionOffset = 0.0f;
|
||||
region = nullptr;
|
||||
noteIsOff = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "LeakDetector.h"
|
||||
#include <absl/types/span.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
namespace sfz {
|
||||
class Voice {
|
||||
|
|
@ -49,7 +50,7 @@ public:
|
|||
void startVoice(Region* region, int delay, int channel, int number, uint8_t value, TriggerType triggerType) noexcept;
|
||||
|
||||
void expectFileData(unsigned ticket);
|
||||
void setFileData(std::unique_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept;
|
||||
void setFileData(std::shared_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept;
|
||||
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept;
|
||||
void registerCC(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept;
|
||||
void registerPitchWheel(int delay, int channel, int pitch) noexcept;
|
||||
|
|
@ -108,7 +109,7 @@ private:
|
|||
int initialDelay { 0 };
|
||||
|
||||
std::atomic<bool> dataReady { false };
|
||||
std::unique_ptr<AudioBuffer<float>> fileData { nullptr };
|
||||
std::shared_ptr<AudioBuffer<float>> fileData { nullptr };
|
||||
unsigned ticket { 0 };
|
||||
|
||||
Buffer<float> tempBuffer1;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue