diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 69ed4c63..3cd57684 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -170,13 +170,16 @@ bool sfz::FilePool::checkSample(std::string& filename) const noexcept #endif } -absl::optional sfz::FilePool::getFileInformation(const std::string& filename) noexcept +absl::optional 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::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(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(sndFile, framesToLoad, oversamplingFactor); } } else { - const float sourceSampleRate { static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()) }; - PreloadedFileHandle handle { readFromFile(sndFile, framesToLoad, oversamplingFactor), sourceSampleRate }; + fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); + FileDataHandle handle { + readFromFile(sndFile, framesToLoad, oversamplingFactor), + *fileInformation + }; preloadedFiles.insert_or_assign(filename, handle); } - return true; } +absl::optional 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(sndFile.frames()); + const auto existingFile = loadedFiles.find(filename); + if (existingFile != loadedFiles.end()) { + return existingFile->second; + } else { + fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); + FileDataHandle handle { + readFromFile(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(sndFile, preloadSize + maxOffset, factor); - preloadedFile.second.sampleRate *= samplerateChange; + preloadedFile.second.information.sampleRate *= samplerateChange; } this->oversamplingFactor = factor; diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index 20f73fc8..450a4c24 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -43,11 +43,19 @@ namespace sfz { using AudioBufferPtr = std::shared_ptr>; +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> 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 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 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 addingPromisesToClear { false }; std::atomic canAddPromisesToClear { true }; - absl::flat_hash_map preloadedFiles; + // Preloaded data + absl::flat_hash_map preloadedFiles; + absl::flat_hash_map loadedFiles; std::vector threadPool { }; LEAK_DETECTOR(FilePool); };