Change the FilePool to load files and not only preload

This commit is contained in:
Paul Ferrand 2020-03-28 19:45:34 +01:00
parent 2add056a51
commit 62b68b2af3
2 changed files with 65 additions and 25 deletions

View file

@ -170,13 +170,16 @@ bool sfz::FilePool::checkSample(std::string& filename) const noexcept
#endif
}
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
absl::optional<sfz::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename) noexcept
{
fs::path file { rootDirectory / filename };
if (!fs::exists(file))
return {};
SndfileHandle sndFile(file.string().c_str());
if (sndFile.channels() != 1 && sndFile.channels() != 2) {
DBG("Missing logic for " << sndFile.channels() << " channels, discarding sample " << filename);
DBG("[sfizz] Missing logic for " << sndFile.channels() << " channels, discarding sample " << filename);
return {};
}
@ -198,13 +201,11 @@ absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation
bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept
{
fs::path file { rootDirectory / filename };
if (!fs::exists(file))
auto fileInformation = getFileInformation(filename);
if (!fileInformation)
return false;
SndfileHandle sndFile(file.string().c_str());
if (sndFile.channels() != 1 && sndFile.channels() != 2)
return false;
// FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same
const auto frames = static_cast<uint32_t>(sndFile.frames());
@ -215,19 +216,47 @@ bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset)
return min(frames, maxOffset + preloadSize);
}();
if (preloadedFiles.contains(filename)) {
if (framesToLoad > preloadedFiles[filename].preloadedData->getNumFrames()) {
const auto existingFile = preloadedFiles.find(filename);
if (existingFile != preloadedFiles.end()) {
if (framesToLoad > existingFile->second.preloadedData->getNumFrames()) {
preloadedFiles[filename].preloadedData = readFromFile<float>(sndFile, framesToLoad, oversamplingFactor);
}
} else {
const float sourceSampleRate { static_cast<float>(oversamplingFactor) * static_cast<float>(sndFile.samplerate()) };
PreloadedFileHandle handle { readFromFile<float>(sndFile, framesToLoad, oversamplingFactor), sourceSampleRate };
fileInformation->sampleRate = static_cast<float>(oversamplingFactor) * static_cast<float>(sndFile.samplerate());
FileDataHandle handle {
readFromFile<float>(sndFile, framesToLoad, oversamplingFactor),
*fileInformation
};
preloadedFiles.insert_or_assign(filename, handle);
}
return true;
}
absl::optional<sfz::FileDataHandle> sfz::FilePool::loadFile(const std::string& filename) noexcept
{
fs::path file { rootDirectory / filename };
auto fileInformation = getFileInformation(filename);
if (!fileInformation)
return {};
SndfileHandle sndFile(file.string().c_str());
// FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same
const auto frames = static_cast<uint32_t>(sndFile.frames());
const auto existingFile = loadedFiles.find(filename);
if (existingFile != loadedFiles.end()) {
return existingFile->second;
} else {
fileInformation->sampleRate = static_cast<float>(oversamplingFactor) * static_cast<float>(sndFile.samplerate());
FileDataHandle handle {
readFromFile<float>(sndFile, frames, oversamplingFactor),
*fileInformation
};
loadedFiles.insert_or_assign(filename, handle);
return handle;
}
}
sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept
{
if (emptyPromises.empty()) {
@ -244,7 +273,7 @@ sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) n
auto promise = emptyPromises.back();
promise->filename = preloaded->first;
promise->preloadedData = preloaded->second.preloadedData;
promise->sampleRate = preloaded->second.sampleRate;
promise->sampleRate = preloaded->second.information.sampleRate;
promise->oversamplingFactor = oversamplingFactor;
promise->creationTime = std::chrono::high_resolution_clock::now();
@ -396,7 +425,7 @@ void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept
fs::path file { rootDirectory / std::string(preloadedFile.first) };
SndfileHandle sndFile(file.string().c_str());
preloadedFile.second.preloadedData = readFromFile<float>(sndFile, preloadSize + maxOffset, factor);
preloadedFile.second.sampleRate *= samplerateChange;
preloadedFile.second.information.sampleRate *= samplerateChange;
}
this->oversamplingFactor = factor;

View file

@ -43,11 +43,19 @@ namespace sfz {
using AudioBufferPtr = std::shared_ptr<AudioBuffer<float>>;
struct FileInformation {
uint32_t end { Default::sampleEndRange.getEnd() };
uint32_t loopBegin { Default::loopRange.getStart() };
uint32_t loopEnd { Default::loopRange.getEnd() };
double sampleRate { config::defaultSampleRate };
int numChannels { 0 };
};
// Strict C++11 disallows member initialization if aggregate initialization is to be used...
struct PreloadedFileHandle
struct FileDataHandle
{
std::shared_ptr<AudioBuffer<float>> preloadedData;
float sampleRate;
FileInformation information;
};
struct FilePromise
@ -142,14 +150,6 @@ public:
*/
size_t getNumPreloadedSamples() const noexcept { return preloadedFiles.size(); }
struct FileInformation {
uint32_t end { Default::sampleEndRange.getEnd() };
uint32_t loopBegin { Default::loopRange.getStart() };
uint32_t loopEnd { Default::loopRange.getEnd() };
double sampleRate { config::defaultSampleRate };
int numChannels { 0 };
};
/**
* @brief Get metadata information about a file.
*
@ -159,7 +159,7 @@ public:
absl::optional<FileInformation> getFileInformation(const std::string& filename) noexcept;
/**
* @brief Check that a file is preloaded with the proper offset bounds
* @brief Preload a file with the proper offset bounds
*
* @param filename
* @param offset the maximum offset to consider for preloading. The total preloaded
@ -169,6 +169,15 @@ public:
*/
bool preloadFile(const std::string& filename, uint32_t maxOffset) noexcept;
/**
* @brief Load a file and return its information. The file pool will store this
* data for future requests so use this function responsibly.
*
* @param filename
* @return A handle on the file data
*/
absl::optional<sfz::FileDataHandle> loadFile(const std::string& filename) noexcept;
/**
* @brief Check that the sample exists. If not, try to find it in a case insensitive way.
*
@ -258,7 +267,9 @@ private:
std::atomic<bool> addingPromisesToClear { false };
std::atomic<bool> canAddPromisesToClear { true };
absl::flat_hash_map<absl::string_view, PreloadedFileHandle> preloadedFiles;
// Preloaded data
absl::flat_hash_map<absl::string_view, FileDataHandle> preloadedFiles;
absl::flat_hash_map<absl::string_view, FileDataHandle> loadedFiles;
std::vector<std::thread> threadPool { };
LEAK_DETECTOR(FilePool);
};