From b6a437e4a4206fc2ca86a58909fd310e64cf7a01 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 15 Apr 2020 21:35:16 +0200 Subject: [PATCH] Support the direction opcode --- src/CMakeLists.txt | 1 + src/sfizz/FileId.cpp | 24 +++++++++ src/sfizz/FileId.h | 67 +++++++++++++++++++++++++ src/sfizz/FilePool.cpp | 108 ++++++++++++++++++++++++----------------- src/sfizz/FilePool.h | 28 +++++------ src/sfizz/Region.cpp | 7 ++- src/sfizz/Region.h | 5 +- src/sfizz/Synth.cpp | 12 ++--- src/sfizz/Voice.cpp | 10 ++-- 9 files changed, 189 insertions(+), 73 deletions(-) create mode 100644 src/sfizz/FileId.cpp create mode 100644 src/sfizz/FileId.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b096fa3..1a02ee53 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(external/cpuid) set (SFIZZ_SOURCES sfizz/Synth.cpp + sfizz/FileId.cpp sfizz/FilePool.cpp sfizz/FilterPool.cpp sfizz/EQPool.cpp diff --git a/src/sfizz/FileId.cpp b/src/sfizz/FileId.cpp new file mode 100644 index 00000000..8355ab70 --- /dev/null +++ b/src/sfizz/FileId.cpp @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "FileId.h" +#include "StringViewHelpers.h" +#include + +size_t std::hash::operator()(const sfz::FileId &id) const +{ + uint64_t h = ::hash(id.filename); + h = ::hash(id.reverse ? "!" : "", h); + return h; +} + +std::ostream &operator<<(std::ostream &os, const sfz::FileId &fileId) +{ + os << fileId.filename; + if (fileId.reverse) + os << " (reverse)"; + return os; +} diff --git a/src/sfizz/FileId.h b/src/sfizz/FileId.h new file mode 100644 index 00000000..7193bdd0 --- /dev/null +++ b/src/sfizz/FileId.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include +#include + +namespace sfz { + +/** + * @brief Sample file identifier within a file pool. + */ +struct FileId { + std::string filename; + bool reverse = false; + + /** + * @brief Construct a null identifier. + */ + FileId() + { + } + + /** + * @brief Construct a file identifier, optionally reversed. + * + * @param filename + * @param reverse + */ + FileId(std::string filename, bool reverse = false) + : filename(std::move(filename)), reverse(reverse) + { + } + + /** + * @brief Check equality with another identifier. + * + * @param other + */ + bool operator==(const FileId &other) const + { + return reverse == other.reverse && filename == other.filename; + } + + /** + * @brief Check inequality with another identifier. + * + * @param other + */ + bool operator!=(const FileId &other) const + { + return !operator==(other); + } +}; + +} + +namespace std { + template <> struct hash { + size_t operator()(const sfz::FileId &id) const; + }; +} + +std::ostream &operator<<(std::ostream &os, const sfz::FileId &fileId); diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index c474b634..f9f38ae7 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -32,32 +32,47 @@ #include "absl/types/span.h" #include "absl/strings/match.h" #include "absl/memory/memory.h" +#include #include #include #include template -void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer& output, uint32_t numFrames) +void readBaseFile(SndfileHandle& sndFile, sfz::AudioBuffer& output, uint32_t numFrames, bool reverse) { output.reset(); output.resize(numFrames); - if (sndFile.channels() == 1) { + + if (reverse) + sndFile.seek(-static_cast(numFrames), SEEK_END); + + const unsigned channels = sndFile.channels(); + + if (channels == 1) { output.addChannel(); sndFile.readf(output.channelWriter(0), numFrames); - } else if (sndFile.channels() == 2) { + } else if (channels == 2) { output.addChannel(); output.addChannel(); sfz::Buffer tempReadBuffer { 2 * numFrames }; sndFile.readf(tempReadBuffer.data(), numFrames); sfz::readInterleaved(tempReadBuffer, output.getSpan(0), output.getSpan(1)); } + + if (reverse) { + for (unsigned c = 0; c < channels; ++c) { + // TODO: consider optimizing with SIMD + absl::Span channel = output.getSpan(c); + std::reverse(channel.begin(), channel.end()); + } + } } template -std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor) +std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, bool reverse) { auto baseBuffer = absl::make_unique>(); - readBaseFile(sndFile, *baseBuffer, numFrames); + readBaseFile(sndFile, *baseBuffer, numFrames, reverse); if (factor == sfz::Oversampling::x1) return baseBuffer; @@ -69,16 +84,16 @@ std::unique_ptr> readFromFile(SndfileHandle& sndFile, uint32 } template -void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, sfz::AudioBuffer& output, std::atomic* filledFrames=nullptr) +void streamFromFile(SndfileHandle& sndFile, uint32_t numFrames, sfz::Oversampling factor, bool reverse, sfz::AudioBuffer& output, std::atomic* filledFrames = nullptr) { if (factor == sfz::Oversampling::x1) { - readBaseFile(sndFile, output, numFrames); + readBaseFile(sndFile, output, numFrames, reverse); if (filledFrames != nullptr) filledFrames->store(numFrames); return; } - auto baseBuffer = readFromFile(sndFile, numFrames, sfz::Oversampling::x1); + auto baseBuffer = readFromFile(sndFile, numFrames, sfz::Oversampling::x1, reverse); output.reset(); output.addChannels(baseBuffer->getNumChannels()); output.resize(numFrames * static_cast(factor)); @@ -175,16 +190,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 FileId& fileId) noexcept { - fs::path file { rootDirectory / filename }; + const fs::path file { rootDirectory / fileId.filename }; if (!fs::exists(file)) return {}; SndfileHandle sndFile(file.string().c_str()); if (sndFile.channels() != 1 && sndFile.channels() != 2) { - DBG("[sfizz] Missing logic for " << sndFile.channels() << " channels, discarding sample " << filename); + DBG("[sfizz] Missing logic for " << sndFile.channels() << " channels, discarding sample " << fileId); return {}; } @@ -193,23 +208,28 @@ absl::optional sfz::FilePool::getFileInformation(const std returnedValue.sampleRate = static_cast(sndFile.samplerate()); returnedValue.numChannels = sndFile.channels(); - SF_INSTRUMENT instrumentInfo; - sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); - if (instrumentInfo.loop_count > 0) { - returnedValue.loopBegin = instrumentInfo.loops[0].start; - returnedValue.loopEnd = min(returnedValue.end, instrumentInfo.loops[0].end - 1); + if (!fileId.reverse) { + SF_INSTRUMENT instrumentInfo; + sndFile.command(SFC_GET_INSTRUMENT, &instrumentInfo, sizeof(instrumentInfo)); + if (instrumentInfo.loop_count > 0) { + returnedValue.loopBegin = instrumentInfo.loops[0].start; + returnedValue.loopEnd = min(returnedValue.end, instrumentInfo.loops[0].end - 1); + } + } else { + // TODO loops ignored when reversed + // prehaps it can make use of SF_LOOP_BACKWARD? } return returnedValue; } -bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) noexcept +bool sfz::FilePool::preloadFile(const FileId& fileId, uint32_t maxOffset) noexcept { - fs::path file { rootDirectory / filename }; - auto fileInformation = getFileInformation(filename); + auto fileInformation = getFileInformation(fileId); if (!fileInformation) return false; + const fs::path file { rootDirectory / fileId.filename }; SndfileHandle sndFile(file.string().c_str()); // FIXME: Large offsets will require large preloading; is this OK in practice? Apparently sforzando does the same @@ -221,69 +241,69 @@ bool sfz::FilePool::preloadFile(const std::string& filename, uint32_t maxOffset) return min(frames, maxOffset + preloadSize); }(); - const auto existingFile = preloadedFiles.find(filename); + const auto existingFile = preloadedFiles.find(fileId); if (existingFile != preloadedFiles.end()) { if (framesToLoad > existingFile->second.preloadedData->getNumFrames()) { - preloadedFiles[filename].preloadedData = readFromFile(sndFile, framesToLoad, oversamplingFactor); + preloadedFiles[fileId].preloadedData = readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.reverse); } } else { fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); FileDataHandle handle { - readFromFile(sndFile, framesToLoad, oversamplingFactor), + readFromFile(sndFile, framesToLoad, oversamplingFactor, fileId.reverse), *fileInformation }; - preloadedFiles.insert_or_assign(filename, handle); + preloadedFiles.insert_or_assign(fileId, handle); } return true; } -absl::optional sfz::FilePool::loadFile(const std::string& filename) noexcept +absl::optional sfz::FilePool::loadFile(const FileId& fileId) noexcept { - fs::path file { rootDirectory / filename }; - auto fileInformation = getFileInformation(filename); + auto fileInformation = getFileInformation(fileId); if (!fileInformation) return {}; + const fs::path file { rootDirectory / fileId.filename }; 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); + const auto existingFile = loadedFiles.find(fileId); if (existingFile != loadedFiles.end()) { return existingFile->second; } else { fileInformation->sampleRate = static_cast(oversamplingFactor) * static_cast(sndFile.samplerate()); FileDataHandle handle { - readFromFile(sndFile, frames, oversamplingFactor), + readFromFile(sndFile, frames, oversamplingFactor, fileId.reverse), *fileInformation }; - loadedFiles.insert_or_assign(filename, handle); + loadedFiles.insert_or_assign(fileId, handle); return handle; } } -sfz::FilePromisePtr sfz::FilePool::getFilePromise(const std::string& filename) noexcept +sfz::FilePromisePtr sfz::FilePool::getFilePromise(const FileId& fileId) noexcept { if (emptyPromises.empty()) { - DBG("[sfizz] No empty promises left to honor the one for " << filename); + DBG("[sfizz] No empty promises left to honor the one for " << fileId); return {}; } - const auto preloaded = preloadedFiles.find(filename); + const auto preloaded = preloadedFiles.find(fileId); if (preloaded == preloadedFiles.end()) { - DBG("[sfizz] File not found in the preloaded files: " << filename); + DBG("[sfizz] File not found in the preloaded files: " << fileId); return {}; } auto promise = emptyPromises.back(); - promise->filename = preloaded->first; + promise->fileId = preloaded->first; promise->preloadedData = preloaded->second.preloadedData; promise->sampleRate = static_cast(preloaded->second.information.sampleRate); promise->oversamplingFactor = oversamplingFactor; promise->creationTime = std::chrono::high_resolution_clock::now(); if (!promiseQueue.try_push(promise)) { - DBG("[sfizz] Could not enqueue the promise for " << filename << " (queue capacity " << promiseQueue.capacity() << ")"); + DBG("[sfizz] Could not enqueue the promise for " << fileId << " (queue capacity " << promiseQueue.capacity() << ")"); return {}; } @@ -302,9 +322,9 @@ void sfz::FilePool::setPreloadSize(uint32_t preloadSize) noexcept for (auto& preloadedFile : preloadedFiles) { const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / static_cast(oversamplingFactor); const auto maxOffset = numFrames > this->preloadSize ? static_cast(numFrames) - this->preloadSize : 0; - fs::path file { rootDirectory / std::string(preloadedFile.first) }; + fs::path file { rootDirectory / preloadedFile.first.filename }; SndfileHandle sndFile(file.string().c_str()); - preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, oversamplingFactor); + preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, oversamplingFactor, preloadedFile.first.reverse); } this->preloadSize = preloadSize; } @@ -352,23 +372,23 @@ void sfz::FilePool::loadingThread() noexcept const auto loadStartTime = std::chrono::high_resolution_clock::now(); const auto waitDuration = loadStartTime - promise->creationTime; - fs::path file { rootDirectory / std::string(promise->filename) }; + const fs::path file { rootDirectory / promise->fileId.filename }; SndfileHandle sndFile(file.string().c_str()); if (sndFile.error() != 0) { - DBG("[sfizz] libsndfile errored for " << promise->filename << " with message " << sndFile.strError()); + DBG("[sfizz] libsndfile errored for " << promise->fileId << " with message " << sndFile.strError()); promise->dataStatus = FilePromise::DataStatus::Error; continue; } const auto frames = static_cast(sndFile.frames()); - streamFromFile(sndFile, frames, oversamplingFactor, promise->fileData, &promise->availableFrames); + streamFromFile(sndFile, frames, oversamplingFactor, promise->fileId.reverse, promise->fileData, &promise->availableFrames); promise->dataStatus = FilePromise::DataStatus::Ready; const auto loadDuration = std::chrono::high_resolution_clock::now() - loadStartTime; - logger.logFileTime(waitDuration, loadDuration, frames, promise->filename); + logger.logFileTime(waitDuration, loadDuration, frames, promise->fileId.filename); threadsLoading--; while (!filledPromiseQueue.try_push(promise)) { - DBG("[sfizz] Error enqueuing the promise for " << promise->filename << " in the filledPromiseQueue"); + DBG("[sfizz] Error enqueuing the promise for " << promise->fileId << " in the filledPromiseQueue"); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } @@ -431,9 +451,9 @@ void sfz::FilePool::setOversamplingFactor(sfz::Oversampling factor) noexcept for (auto& preloadedFile : preloadedFiles) { const auto numFrames = preloadedFile.second.preloadedData->getNumFrames() / static_cast(this->oversamplingFactor); const uint32_t maxOffset = numFrames > this->preloadSize ? static_cast(numFrames) - this->preloadSize : 0; - fs::path file { rootDirectory / std::string(preloadedFile.first) }; + fs::path file { rootDirectory / preloadedFile.first.filename }; SndfileHandle sndFile(file.string().c_str()); - preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, factor); + preloadedFile.second.preloadedData = readFromFile(sndFile, preloadSize + maxOffset, factor, preloadedFile.first.reverse); preloadedFile.second.information.sampleRate *= samplerateChange; } diff --git a/src/sfizz/FilePool.h b/src/sfizz/FilePool.h index c2600ece..631dd6f6 100644 --- a/src/sfizz/FilePool.h +++ b/src/sfizz/FilePool.h @@ -30,6 +30,7 @@ #include "RTSemaphore.h" #include "AudioBuffer.h" #include "AudioSpan.h" +#include "FileId.h" #include "SIMDHelpers.h" #include "ghc/fs_std.hpp" #include @@ -44,7 +45,6 @@ namespace sfz { using AudioBufferPtr = std::shared_ptr>; - struct FileInformation { uint32_t end { Default::sampleEndRange.getEnd() }; uint32_t loopBegin { Default::loopRange.getStart() }; @@ -76,7 +76,7 @@ struct FilePromise { fileData.reset(); preloadedData.reset(); - filename = ""; + fileId = FileId {}; availableFrames = 0; dataStatus = DataStatus::Wait; oversamplingFactor = config::defaultOversamplingFactor; @@ -95,7 +95,7 @@ struct FilePromise Error, }; - absl::string_view filename {}; + FileId fileId {}; AudioBufferPtr preloadedData {}; AudioBuffer fileData {}; float sampleRate { config::defaultSampleRate }; @@ -155,30 +155,30 @@ public: /** * @brief Get metadata information about a file. * - * @param filename + * @param fileId * @return absl::optional */ - absl::optional getFileInformation(const std::string& filename) noexcept; + absl::optional getFileInformation(const FileId& fileId) noexcept; /** * @brief Preload a file with the proper offset bounds * - * @param filename - * @param offset the maximum offset to consider for preloading. The total preloaded + * @param fileId + * @param maxOffset the maximum offset to consider for preloading. The total preloaded * size will be preloadSize + offset * @return true if the preloading went fine * @return false if something went wrong () */ - bool preloadFile(const std::string& filename, uint32_t maxOffset) noexcept; + bool preloadFile(const FileId& fileId, 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 + * @param fileId * @return A handle on the file data */ - absl::optional loadFile(const std::string& filename) noexcept; + absl::optional loadFile(const FileId& fileId) noexcept; /** * @brief Check that the sample exists. If not, try to find it in a case insensitive way. @@ -204,10 +204,10 @@ public: /** * @brief Get a file promise * - * @param filename the file to preload + * @param fileId the file to preload * @return FilePromisePtr a file promise */ - FilePromisePtr getFilePromise(const std::string& filename) noexcept; + FilePromisePtr getFilePromise(const FileId& fileId) noexcept; /** * @brief Change the preloading size. This will trigger a full * reload of all samples, so don't call it on the audio thread. @@ -270,8 +270,8 @@ private: std::mutex promiseGuard; // Preloaded data - absl::flat_hash_map preloadedFiles; - absl::flat_hash_map loadedFiles; + absl::flat_hash_map preloadedFiles; + absl::flat_hash_map loadedFiles; std::vector threadPool { }; LEAK_DETECTOR(FilePool); }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 99261839..2d95854a 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -42,11 +42,14 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; if (trimmedSample[0] == '*') - sample = std::string(trimmedSample); + sampleId.filename = std::string(trimmedSample); else - sample = absl::StrCat(defaultPath, absl::StrReplaceAll(trimmedSample, { { "\\", "/" } })); + sampleId.filename = absl::StrCat(defaultPath, absl::StrReplaceAll(trimmedSample, { { "\\", "/" } })); } break; + case hash("direction"): + sampleId.reverse = opcode.value == "reverse"; + break; case hash("delay"): setValueFromOpcode(opcode, delay, Default::delayRange); break; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 9540b1cc..cd954671 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -14,6 +14,7 @@ #include "Opcode.h" #include "AudioBuffer.h" #include "MidiState.h" +#include "FileId.h" #include "absl/types/optional.h" #include #include @@ -57,7 +58,7 @@ struct Region { * @return true * @return false */ - bool isGenerator() const noexcept { return sample.size() > 0 ? sample[0] == '*' : false; } + bool isGenerator() const noexcept { return sampleId.filename.size() > 0 ? sampleId.filename[0] == '*' : false; } /** * @brief Is stereo (has stereo sample or is unison oscillator)? * @@ -227,7 +228,7 @@ struct Region { float getGainToEffectBus(unsigned number) const noexcept; // Sound source: sample playback - std::string sample {}; // Sample + FileId sampleId {}; // Sample float delay { Default::delay }; // delay float delayRandom { Default::delayRandom }; // delay_random int64_t offset { Default::offset }; // offset diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d52aa86c..8f9f1b4f 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -332,7 +332,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) if (currentRegion->get() == nullptr) return; - DBG("Removing the region with sample " << currentRegion->get()->sample); + DBG("Removing the region with sample " << currentRegion->get()->sampleId); std::iter_swap(currentRegion, lastRegion); ++lastRegion; }; @@ -344,12 +344,12 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) auto region = currentRegion->get(); if (!region->oscillator && !region->isGenerator()) { - if (!resources.filePool.checkSample(region->sample)) { + if (!resources.filePool.checkSample(region->sampleId.filename)) { removeCurrentRegion(); continue; } - const auto fileInformation = resources.filePool.getFileInformation(region->sample); + const auto fileInformation = resources.filePool.getFileInformation(region->sampleId); if (!fileInformation) { removeCurrentRegion(); continue; @@ -381,16 +381,16 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) return Default::offsetCCRange.clamp(sumOffsetCC); }(); - if (!resources.filePool.preloadFile(region->sample, maxOffset)) + if (!resources.filePool.preloadFile(region->sampleId, maxOffset)) removeCurrentRegion(); } else if (region->oscillator && !region->isGenerator()) { - if (!resources.filePool.checkSample(region->sample)) { + if (!resources.filePool.checkSample(region->sampleId.filename)) { removeCurrentRegion(); continue; } - if (!resources.wavePool.createFileWave(resources.filePool, region->sample)) { + if (!resources.wavePool.createFileWave(resources.filePool, region->sampleId.filename)) { removeCurrentRegion(); continue; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index cfa8bfbe..68650ef0 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -40,7 +40,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, if (region->isGenerator()) { const WavetableMulti* wave = nullptr; - switch (hash(region->sample)) { + switch (hash(region->sampleId.filename)) { default: case hash("*silence"): break; @@ -64,14 +64,14 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, } setupOscillatorUnison(); } else if (region->oscillator) { - const WavetableMulti* wave = resources.wavePool.getFileWave(region->sample); + const WavetableMulti* wave = resources.wavePool.getFileWave(region->sampleId.filename); for (WavetableOscillator& osc : waveOscillators) { osc.setWavetable(wave); osc.setPhase(region->getPhase()); } setupOscillatorUnison(); } else { - currentPromise = resources.filePool.getFilePromise(region->sample); + currentPromise = resources.filePool.getFilePromise(region->sampleId); if (currentPromise == nullptr) { reset(); return; @@ -483,7 +483,7 @@ void sfz::Voice::fillWithData(AudioSpan buffer) noexcept DBG("[sfizz] Underflow: source available samples " << source.getNumFrames() << "/" << region->trueSampleEnd(currentPromise->oversamplingFactor) - << " for sample " << region->sample); + << " for sample " << region->sampleId); } fill(indices->last(remainingElements), sampleEnd); fill(leftCoeffs->last(remainingElements), 0.0f); @@ -529,7 +529,7 @@ void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept const auto leftSpan = buffer.getSpan(0); const auto rightSpan = buffer.getSpan(1); - if (region->sample == "*noise") { + if (region->sampleId.filename == "*noise") { absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); }); } else {