From c1d2384e093f2792e6251041bc4c3f38782a59b9 Mon Sep 17 00:00:00 2001 From: paulfd Date: Sat, 7 Sep 2019 01:12:20 +0200 Subject: [PATCH] Added an AudioSpan and AudioBuffer, to better handle the memory for mono/stereo samples --- clients/jack_client.cpp | 16 +++--- sfizz/AudioBuffer.h | 72 +++++++++++++++------------ sfizz/AudioSpan.h | 67 ++++++++++++++++++------- sfizz/Config.h | 2 +- sfizz/FilePool.cpp | 30 ++++++----- sfizz/FilePool.h | 7 ++- sfizz/Region.cpp | 6 ++- sfizz/Region.h | 6 +-- sfizz/Synth.cpp | 6 +-- sfizz/Synth.h | 6 +-- sfizz/Voice.cpp | 107 ++++++++++++++++++++++++++-------------- sfizz/Voice.h | 16 +++--- tests/FilesT.cpp | 2 - 13 files changed, 204 insertions(+), 139 deletions(-) diff --git a/clients/jack_client.cpp b/clients/jack_client.cpp index 6e825005..c6bc369e 100644 --- a/clients/jack_client.cpp +++ b/clients/jack_client.cpp @@ -21,7 +21,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "StereoSpan.h" +#include "AudioSpan.h" #include "Synth.h" #include #include @@ -116,13 +116,9 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]]) } } - StereoSpan output { - jack_port_get_buffer(outputPort1, numFrames), - jack_port_get_buffer(outputPort2, numFrames), - numFrames - }; - - synth->renderBlock(output); + auto leftOutput = reinterpret_cast(jack_port_get_buffer(outputPort1, numFrames)); + auto rightOutput = reinterpret_cast(jack_port_get_buffer(outputPort2, numFrames)); + synth->renderBlock({ { leftOutput, rightOutput }, numFrames }); return 0; } @@ -156,7 +152,7 @@ static void done(int sig [[maybe_unused]]) std::cout << "Signal received" << '\n'; shouldClose = true; // if (client != nullptr) - + // exit(0); } @@ -259,7 +255,7 @@ int main(int argc, char** argv) while (!shouldClose) sleep(1); - + std::cout << "Closing..." << '\n'; jack_client_close(client); return 0; diff --git a/sfizz/AudioBuffer.h b/sfizz/AudioBuffer.h index eef3b738..30735f07 100644 --- a/sfizz/AudioBuffer.h +++ b/sfizz/AudioBuffer.h @@ -50,6 +50,14 @@ public: buffers[i] = std::make_unique(numFrames); } + bool resize(size_type newSize) + { + bool returnedOK = true; + for (auto i = 0; i < numChannels; ++i) + returnedOK &= buffers[i]->resize(newSize); + return returnedOK; + } + iterator channelWriter(int channelIndex) { ASSERT(channelIndex < numChannels) @@ -68,7 +76,7 @@ public: return {}; } - const_iterator channelReader(int channelIndex) + const_iterator channelReader(int channelIndex) const { ASSERT(channelIndex < numChannels) if (channelIndex < numChannels) @@ -77,7 +85,7 @@ public: return {}; } - const_iterator channelReaderEnd(int channelIndex) + const_iterator channelReaderEnd(int channelIndex) const { ASSERT(channelIndex < numChannels) if (channelIndex < numChannels) @@ -86,7 +94,7 @@ public: return {}; } - absl::Span getSpan(int channelIndex) + absl::Span getSpan(int channelIndex) const { ASSERT(channelIndex < numChannels) if (channelIndex < numChannels) @@ -95,45 +103,45 @@ public: return {}; } - absl::Span getConstSpan(int channelIndex) + absl::Span getConstSpan(int channelIndex) const { return getSpan(channelIndex); } - void addChannel() - { - if (numChannels < MaxChannels) - buffers[numChannels++] = std::make_unique(numFrames); - } + void addChannel() + { + if (numChannels < MaxChannels) + buffers[numChannels++] = std::make_unique(numFrames); + } - size_type getNumFrames() - { - return numFrames; - } + size_type getNumFrames() const + { + return numFrames; + } - size_type getNumChannels() - { - return numChannels; - } + int getNumChannels() const + { + return numChannels; + } - bool empty() - { - return numFrames == 0; - } + bool empty() const + { + return numFrames == 0; + } - Type& getSample(int channelIndex, size_type frameIndex) - { - // Uhoh - ASSERT(buffers[channelIndex] != nullptr); - ASSERT(frameIndex < numFrames); + Type& getSample(int channelIndex, size_type frameIndex) + { + // Uhoh + ASSERT(buffers[channelIndex] != nullptr); + ASSERT(frameIndex < numFrames); - return *(buffers[channelIndex]->data() + frameIndex); - } + return *(buffers[channelIndex]->data() + frameIndex); + } - Type& operator()(int channelIndex, size_type frameIndex) - { - return getSample(channelIndex, frameIndex); - } + Type& operator()(int channelIndex, size_type frameIndex) + { + return getSample(channelIndex, frameIndex); + } private: using buffer_type = Buffer; diff --git a/sfizz/AudioSpan.h b/sfizz/AudioSpan.h index 4cd6bb71..d796bf46 100644 --- a/sfizz/AudioSpan.h +++ b/sfizz/AudioSpan.h @@ -39,37 +39,48 @@ public: { } + AudioSpan(const std::array& spans, int numChannels, size_type offset, size_type size) + : numFrames(size) + , numChannels(numChannels) + { + ASSERT(static_cast(numChannels) <= MaxChannels); + for (auto i = 0; i < numChannels; ++i) + this->spans[i] = spans[i] + offset; + } + AudioSpan(std::initializer_list spans, size_type numFrames) : numFrames(numFrames) , numChannels(spans.size()) { - static_assert(spans.size() <= MaxChannels); - for (auto i = 0; i < spans.size(); i++) { + ASSERT(spans.size() <= MaxChannels); + auto newSpan = spans.begin(); + auto thisSpan = this->spans.begin(); + for (; newSpan < spans.end() && thisSpan < this->spans.end(); thisSpan++, newSpan++) { // This will not end well... - ASSERT(spans[i] != nullptr); - this->spans[i] = spans[i]; + ASSERT(*newSpan != nullptr); + *thisSpan = *newSpan; } } AudioSpan(std::initializer_list> spans) : numChannels(spans.size()) { - static_assert(spans.size() <= MaxChannels); + ASSERT(spans.size() <= MaxChannels); auto size = absl::Span::npos; - for (auto i = 0; i < spans.size(); i++) { - // This will not end well... - ASSERT(spans[i] != nullptr); - this->spans[i] = spans[i].data(); - size = std::min(size, spans[i].size()); + auto newSpan = spans.begin(); + auto thisSpan = this->spans.begin(); + for (; newSpan < spans.end() && thisSpan < this->spans.end(); thisSpan++, newSpan++) { + *thisSpan = newSpan->data(); + size = std::min(size, newSpan->size()); } } template > - AudioSpan(const AudioBuffer& audioBuffer) + AudioSpan(AudioBuffer& audioBuffer) : numFrames(audioBuffer.getNumFrames()) , numChannels(audioBuffer.getNumChannels()) { - for (auto i = 0; i < N; i++) { + for (int i = 0; i < numChannels; i++) { if constexpr (std::is_const::value) this->spans[i] = audioBuffer.channelReader(i); else @@ -82,7 +93,7 @@ public: : numFrames(other.getNumFrames()) , numChannels(other.getNumChannels()) { - for (auto i = 0; i < N; i++) { + for (int i = 0; i < numChannels; i++) { this->spans[i] = other.getChannel(i); } } @@ -105,7 +116,7 @@ public: return {}; } - absl::Span> getConstSpan(int channelIndex) + absl::Span getConstSpan(int channelIndex) { ASSERT(channelIndex < numChannels); if (channelIndex < numChannels) @@ -117,23 +128,23 @@ public: void fill(Type value) noexcept { for (int i = 0; i < numChannels; ++i) - ::fill({ getChannel(i), numFrames }, value); + ::fill(getSpan(i), value); } void applyGain(absl::Span gain) noexcept { for (int i = 0; i < numChannels; ++i) - ::applyGain({ getChannel(i), numFrames }, gain); + ::applyGain(gain, getSpan(i)); } void applyGain(Type gain) noexcept { for (int i = 0; i < numChannels; ++i) - ::applyGain({ getChannel(i), numFrames }, gain); + ::applyGain(gain, getSpan(i)); } template > - void add(const AudioSpan& other) + void add(AudioSpan& other) { ASSERT(other.getNumChannels() == numChannels); if (other.getNumChannels() == numChannels) { @@ -143,7 +154,7 @@ public: } template > - void copy(const AudioSpan& other) + void copy(AudioSpan& other) { ASSERT(other.getNumChannels() == numChannels); if (other.getNumChannels() == numChannels) { @@ -162,6 +173,24 @@ public: return numChannels; } + AudioSpan first(size_type length) + { + ASSERT(length <= numFrames); + return { spans, numChannels, 0, length }; + } + + AudioSpan last(size_type length) + { + ASSERT(length <= numFrames); + return { spans, numChannels, numFrames - length, length }; + } + + AudioSpan subspan(size_type offset, size_type length) + { + ASSERT(length + offset <= numFrames); + return { spans, numChannels, offset, length }; + } + private: std::array spans; size_type numFrames { 0 }; diff --git a/sfizz/Config.h b/sfizz/Config.h index 1bb304d7..b5854847 100644 --- a/sfizz/Config.h +++ b/sfizz/Config.h @@ -28,7 +28,7 @@ namespace sfz { namespace config { constexpr float defaultSampleRate { 48000 }; constexpr int defaultSamplesPerBlock { 1024 }; - constexpr int preloadSize { 8192 * 2 }; + constexpr int preloadSize { 8192 }; constexpr int numChannels { 2 }; constexpr int numVoices { 64 }; constexpr int numLoadingThreads { 4 }; diff --git a/sfizz/FilePool.cpp b/sfizz/FilePool.cpp index edbab518..6dd709b4 100644 --- a/sfizz/FilePool.cpp +++ b/sfizz/FilePool.cpp @@ -22,26 +22,27 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FilePool.h" +#include "AudioBuffer.h" #include "Config.h" #include "Debug.h" #include "absl/types/span.h" #include +#include #include using namespace std::chrono_literals; template -void readFromFile(SndfileHandle& sndFile, int numFrames, StereoBuffer& output) +std::unique_ptr> readFromFile(SndfileHandle& sndFile, int numFrames) { + auto returnedBuffer = std::make_unique>(sndFile.channels(), numFrames); if (sndFile.channels() == 1) { - auto tempReadBuffer = std::make_unique>(numFrames); - sndFile.readf(tempReadBuffer->data(), numFrames); - std::copy(tempReadBuffer->begin(), tempReadBuffer->end(), output.begin(Channel::left)); - std::copy(tempReadBuffer->begin(), tempReadBuffer->end(), output.begin(Channel::right)); + sndFile.readf(returnedBuffer->channelWriter(0), numFrames); } else if (sndFile.channels() == 2) { - auto tempReadBuffer = std::make_unique>(2 * numFrames); - sndFile.readf(tempReadBuffer->data(), numFrames); - output.readInterleaved(*tempReadBuffer); + auto tempReadBuffer = std::make_unique>(1, 2 * numFrames); + sndFile.readf(tempReadBuffer->channelWriter(0), numFrames); + ::readInterleaved(tempReadBuffer->getSpan(0), returnedBuffer->getSpan(0), returnedBuffer->getSpan(1)); } + return returnedBuffer; } std::optional sfz::FilePool::getFileInformation(std::string_view filename) noexcept @@ -52,12 +53,11 @@ std::optional sfz::FilePool::getFileInformation( SndfileHandle sndFile(reinterpret_cast(file.c_str())); if (sndFile.channels() != 1 && sndFile.channels() != 2) { - DBG("Missing logic for " << sndFile.channels() << ", discarding sample " << filename); + DBG("Missing logic for " << sndFile.channels() << " channels, discarding sample " << filename); return {}; } FileInformation returnedValue; - returnedValue.numChannels = sndFile.channels(); returnedValue.end = static_cast(sndFile.frames()); returnedValue.sampleRate = static_cast(sndFile.samplerate()); @@ -78,8 +78,7 @@ std::optional sfz::FilePool::getFileInformation( if (preloadedData.contains(filename)) { returnedValue.preloadedData = preloadedData[filename]; } else { - returnedValue.preloadedData = std::make_shared>(preloadedSize); - readFromFile(sndFile, preloadedSize, *returnedValue.preloadedData); + returnedValue.preloadedData = std::shared_ptr>(readFromFile(sndFile, preloadedSize)); preloadedData[filename] = returnedValue.preloadedData; } @@ -97,7 +96,7 @@ void sfz::FilePool::loadingThread() noexcept { FileLoadingInformation fileToLoad {}; while (!quitThread) { - if (!loadingQueue.wait_dequeue_timed(fileToLoad, 1ms)) + if (!loadingQueue.wait_dequeue_timed(fileToLoad, 100ms)) continue; if (fileToLoad.voice == nullptr) { @@ -113,8 +112,7 @@ void sfz::FilePool::loadingThread() noexcept } SndfileHandle sndFile(reinterpret_cast(file.c_str())); - auto fileLoaded = std::make_unique>(fileToLoad.numFrames); - readFromFile(sndFile, fileToLoad.numFrames, *fileLoaded); - fileToLoad.voice->setFileData(std::move(fileLoaded)); + auto fileLoaded = std::make_unique>(sndFile.channels(), fileToLoad.numFrames); + fileToLoad.voice->setFileData(readFromFile(sndFile, fileToLoad.numFrames)); } } \ No newline at end of file diff --git a/sfizz/FilePool.h b/sfizz/FilePool.h index bb4912b6..f27bb870 100644 --- a/sfizz/FilePool.h +++ b/sfizz/FilePool.h @@ -24,7 +24,7 @@ #pragma once #include "Defaults.h" #include "LeakDetector.h" -#include "StereoBuffer.h" +#include "AudioBuffer.h" #include "Voice.h" #include "readerwriterqueue.h" #include @@ -50,12 +50,11 @@ public: size_t getNumPreloadedSamples() const noexcept { return preloadedData.size(); } struct FileInformation { - int numChannels { 1 }; uint32_t end { Default::sampleEndRange.getEnd() }; uint32_t loopBegin { Default::loopRange.getStart() }; uint32_t loopEnd { Default::loopRange.getEnd() }; double sampleRate { config::defaultSampleRate }; - std::shared_ptr> preloadedData; + std::shared_ptr> preloadedData; }; std::optional getFileInformation(std::string_view filename) noexcept; void enqueueLoading(Voice* voice, std::string_view sample, int numFrames) noexcept; @@ -71,7 +70,7 @@ private: void loadingThread() noexcept; std::thread fileLoadingThread; bool quitThread { false }; - absl::flat_hash_map>> preloadedData; + absl::flat_hash_map>> preloadedData; LEAK_DETECTOR(FilePool); }; } \ No newline at end of file diff --git a/sfizz/Region.cpp b/sfizz/Region.cpp index 67e11e2b..02df3818 100644 --- a/sfizz/Region.cpp +++ b/sfizz/Region.cpp @@ -22,6 +22,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Region.h" +#include "MathHelpers.h" #include "Debug.h" #include "StringViewHelpers.h" #include "absl/strings/str_replace.h" @@ -651,7 +652,10 @@ bool sfz::Region::canUsePreloadedData() const noexcept bool sfz::Region::isStereo() const noexcept { - return this->numChannels == 2; + if (isGenerator()) + return 1; + + return (this->preloadedData->getNumChannels() == 2); } template diff --git a/sfizz/Region.h b/sfizz/Region.h index 567364b7..b4bec2dd 100644 --- a/sfizz/Region.h +++ b/sfizz/Region.h @@ -27,7 +27,7 @@ #include "Defaults.h" #include "EGDescription.h" #include "Opcode.h" -#include "StereoBuffer.h" +#include "AudioBuffer.h" #include #include #include @@ -148,9 +148,7 @@ struct Region { EGDescription filterEG; double sampleRate { config::defaultSampleRate }; - int numChannels { 1 }; - std::shared_ptr> preloadedData { nullptr }; - + std::shared_ptr> preloadedData { nullptr }; private: bool keySwitched { true }; bool previousKeySwitched { true }; diff --git a/sfizz/Synth.cpp b/sfizz/Synth.cpp index 79f9c751..976aa4e8 100644 --- a/sfizz/Synth.cpp +++ b/sfizz/Synth.cpp @@ -197,8 +197,6 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename) lastRegion--; continue; } - - region->numChannels = fileInformation->numChannels; region->sampleEnd = std::min(region->sampleEnd, fileInformation->end); region->loopRange.shrinkIfSmaller(fileInformation->loopBegin, fileInformation->loopEnd); region->preloadedData = fileInformation->preloadedData; @@ -274,11 +272,11 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept voice->setSampleRate(sampleRate); } -void sfz::Synth::renderBlock(StereoSpan buffer) noexcept +void sfz::Synth::renderBlock(AudioSpan buffer) noexcept { ScopedFTZ ftz; buffer.fill(0.0f); - StereoSpan tempSpan { tempBuffer, buffer.size() }; + auto tempSpan = AudioSpan(tempBuffer).first(buffer.getNumFrames()); for (auto& voice : voices) { voice->renderBlock(tempSpan); buffer.add(tempSpan); diff --git a/sfizz/Synth.h b/sfizz/Synth.h index f0e82832..7c7f58d9 100644 --- a/sfizz/Synth.h +++ b/sfizz/Synth.h @@ -26,7 +26,7 @@ #include "Parser.h" #include "Region.h" #include "LeakDetector.h" -#include "StereoSpan.h" +#include "AudioSpan.h" #include "absl/types/span.h" #include #include @@ -51,7 +51,7 @@ public: void setSamplesPerBlock(int samplesPerBlock) noexcept; void setSampleRate(float sampleRate) noexcept; - void renderBlock(StereoSpan buffer) noexcept; + void renderBlock(AudioSpan buffer) noexcept; void noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept; void noteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept; void cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept; @@ -91,7 +91,7 @@ private: std::array noteActivationLists; std::array ccActivationLists; - StereoBuffer tempBuffer { config::defaultSamplesPerBlock }; + AudioBuffer tempBuffer { 2, config::defaultSamplesPerBlock }; int samplesPerBlock { config::defaultSamplesPerBlock }; float sampleRate { config::defaultSampleRate }; diff --git a/sfizz/Voice.cpp b/sfizz/Voice.cpp index d235150c..77480901 100644 --- a/sfizz/Voice.cpp +++ b/sfizz/Voice.cpp @@ -57,7 +57,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, if (region->amplitudeCC) baseGain *= normalizeCC(ccState[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second); amplitudeEnvelope.reset(baseGain); - + sourcePosition = region->getOffset(); initialDelay = delay + region->getDelay(); baseFrequency = midiNoteFrequency(number) * pitchRatio; @@ -80,8 +80,9 @@ void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept normalizePercents(region->amplitudeEG.getStart(ccState, velocity))); } -void sfz::Voice::setFileData(std::unique_ptr> file) noexcept +void sfz::Voice::setFileData(std::unique_ptr> file) noexcept { + // DBG("File data set for sample " << region->sample); fileData = std::move(file); dataReady.store(true); } @@ -161,10 +162,9 @@ void sfz::Voice::setSamplesPerBlock(int samplesPerBlock) noexcept indexSpan = absl::MakeSpan(indexBuffer); } -void sfz::Voice::renderBlock(StereoSpan buffer) noexcept +void sfz::Voice::renderBlock(AudioSpan buffer) noexcept { - const auto numSamples = buffer.size(); - ASSERT(static_cast(numSamples) <= samplesPerBlock); + ASSERT(static_cast(buffer.getNumFrames()) <= samplesPerBlock); buffer.fill(0.0f); if (state == State::idle || region == nullptr) @@ -175,34 +175,59 @@ void sfz::Voice::renderBlock(StereoSpan buffer) noexcept else fillWithData(buffer); - auto envelopeSpan = tempSpan1.first(numSamples); - amplitudeEnvelope.getBlock(envelopeSpan); - buffer.applyGain(envelopeSpan); - - egEnvelope.getBlock(envelopeSpan); - buffer.applyGain(envelopeSpan); - + if (region->isStereo()) + processStereo(buffer); + else + processMono(buffer); + if (!egEnvelope.isSmoothing()) reset(); } -void sfz::Voice::fillWithData(StereoSpan buffer) noexcept +void sfz::Voice::processMono(AudioSpan buffer) noexcept +{ + const auto numSamples = buffer.getNumFrames(); + auto leftBuffer = buffer.getSpan(0); + auto rightBuffer = buffer.getSpan(1); + + auto envelopeSpan = tempSpan1.first(numSamples); + amplitudeEnvelope.getBlock(envelopeSpan); + ::applyGain(envelopeSpan, leftBuffer); + + egEnvelope.getBlock(envelopeSpan); + ::applyGain(envelopeSpan, leftBuffer); + + ::copy(leftBuffer, rightBuffer); +} +void sfz::Voice::processStereo(AudioSpan buffer) noexcept +{ + const auto numSamples = buffer.getNumFrames(); + auto envelopeSpan = tempSpan1.first(numSamples); + + amplitudeEnvelope.getBlock(envelopeSpan); + buffer.applyGain(envelopeSpan); + + egEnvelope.getBlock(envelopeSpan); + buffer.applyGain(envelopeSpan); +} + +void sfz::Voice::fillWithData(AudioSpan buffer) noexcept { auto source { [&]() { if (region->canUsePreloadedData() || !dataReady) - return StereoSpan(*region->preloadedData); + return AudioSpan(*region->preloadedData); else - return StereoSpan(*fileData); + return AudioSpan(*fileData); }() }; - auto indices = indexSpan.first(buffer.size()); - auto jumps = tempSpan1.first(buffer.size()); - auto leftCoeffs = tempSpan1.first(buffer.size()); - auto rightCoeffs = tempSpan2.first(buffer.size()); + auto indices = indexSpan.first(buffer.getNumFrames()); + auto jumps = tempSpan1.first(buffer.getNumFrames()); + auto leftCoeffs = tempSpan1.first(buffer.getNumFrames()); + auto rightCoeffs = tempSpan2.first(buffer.getNumFrames()); ::fill(jumps, pitchRatio * speedRatio); - if (region->shouldLoop() && region->trueSampleEnd() <= source.size()) { + if (region->shouldLoop() && region->trueSampleEnd() <= source.getNumFrames()) { floatPosition = ::loopingSFZIndex( jumps, leftCoeffs, @@ -218,32 +243,42 @@ void sfz::Voice::fillWithData(StereoSpan buffer) noexcept rightCoeffs, indices, floatPosition, - source.size() - 1); + source.getNumFrames() - 1); } auto ind = indices.data(); auto leftCoeff = leftCoeffs.data(); auto rightCoeff = rightCoeffs.data(); - auto left = buffer.left().data(); - auto right = buffer.right().data(); - while (ind < indices.end()) { - *left = source.left()[*ind] * (*leftCoeff) + source.left()[*ind + 1] * (*rightCoeff); - *right = source.right()[*ind] * (*leftCoeff) + source.right()[*ind + 1] * (*rightCoeff); - left++; - right++; - ind++; - leftCoeff++; - rightCoeff++; + auto left = buffer.getChannel(0); + if (source.getNumChannels() == 1) { + while (ind < indices.end()) { + *left = source.getChannel(0)[*ind] * (*leftCoeff) + source.getChannel(0)[*ind + 1] * (*rightCoeff); + left++; + ind++; + leftCoeff++; + rightCoeff++; + } + } else { + auto right = buffer.getChannel(1); + while (ind < indices.end()) { + *left = source.getChannel(0)[*ind] * (*leftCoeff) + source.getChannel(0)[*ind + 1] * (*rightCoeff); + *right = source.getChannel(1)[*ind] * (*leftCoeff) + source.getChannel(1)[*ind + 1] * (*rightCoeff); + left++; + right++; + ind++; + leftCoeff++; + rightCoeff++; + } } - if (!region->shouldLoop() && (floatPosition + 1.01) > source.size()) { + if (!region->shouldLoop() && (floatPosition + 1.01) > source.getNumFrames()) { DBG("Releasing " << region->sample); auto last = std::distance(indices.begin(), absl::c_find(indices, region->trueSampleEnd() - 1)); release(last); } } -void sfz::Voice::fillWithGenerator(StereoSpan buffer) noexcept +void sfz::Voice::fillWithGenerator(AudioSpan buffer) noexcept { if (region->sample != "*sine") return; @@ -251,10 +286,10 @@ void sfz::Voice::fillWithGenerator(StereoSpan buffer) noexcept float step = baseFrequency * twoPi / sampleRate; phase = ::linearRamp(tempSpan1, phase, step); - ::sin(tempSpan1.first(buffer.size()), buffer.left()); - absl::c_copy(buffer.left(), buffer.right().begin()); + ::sin(tempSpan1.first(buffer.getNumFrames()), buffer.getSpan(0)); + ::copy(buffer.getSpan(0), buffer.getSpan(1)); - sourcePosition += buffer.size(); + sourcePosition += buffer.getNumFrames(); } bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept diff --git a/sfizz/Voice.h b/sfizz/Voice.h index 9f940bc2..da4e1ab7 100644 --- a/sfizz/Voice.h +++ b/sfizz/Voice.h @@ -26,8 +26,8 @@ #include "Config.h" #include "LinearEnvelope.h" #include "Region.h" -#include "StereoBuffer.h" -#include "StereoSpan.h" +#include "AudioBuffer.h" +#include "AudioSpan.h" #include "LeakDetector.h" #include #include @@ -47,7 +47,7 @@ public: void startVoice(Region* region, int delay, int channel, int number, uint8_t value, TriggerType triggerType) noexcept; - void setFileData(std::unique_ptr> file) noexcept; + void setFileData(std::unique_ptr> file) 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; @@ -55,7 +55,7 @@ public: void registerTempo(int delay, float secondsPerQuarter) noexcept; bool checkOffGroup(int delay, uint32_t group) noexcept; - void renderBlock(StereoSpan buffer) noexcept; + void renderBlock(AudioSpan buffer) noexcept; bool isFree() const noexcept; int getTriggerNumber() const noexcept; @@ -66,9 +66,11 @@ public: void reset() noexcept; void garbageCollect() noexcept; private: - void fillWithData(StereoSpan buffer) noexcept; - void fillWithGenerator(StereoSpan buffer) noexcept; + void fillWithData(AudioSpan buffer) noexcept; + void fillWithGenerator(AudioSpan buffer) noexcept; void prepareEGEnvelope(int delay, uint8_t velocity) noexcept; + void processMono(AudioSpan buffer) noexcept; + void processStereo(AudioSpan buffer) noexcept; void release(int delay) noexcept; Region* region { nullptr }; @@ -96,7 +98,7 @@ private: uint32_t initialDelay { 0 }; std::atomic dataReady { false }; - std::unique_ptr> fileData { nullptr }; + std::unique_ptr> fileData { nullptr }; Buffer tempBuffer1; Buffer tempBuffer2; diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index c43de0fb..a68f3b26 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -257,10 +257,8 @@ TEST_CASE("[Files] Channels (channels.sfz)") synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/channels.sfz"); REQUIRE(synth.getNumRegions() == 2); REQUIRE(synth.getRegionView(0)->sample == "mono_sample.wav"); - REQUIRE(synth.getRegionView(0)->numChannels == 1); REQUIRE(!synth.getRegionView(0)->isStereo()); REQUIRE(synth.getRegionView(1)->sample == "stereo_sample.wav"); - REQUIRE(synth.getRegionView(1)->numChannels == 2); REQUIRE(synth.getRegionView(1)->isStereo()); }