Added an AudioSpan and AudioBuffer, to better handle the memory for mono/stereo samples

This commit is contained in:
paulfd 2019-09-07 01:12:20 +02:00
parent b1ef2c26bd
commit c1d2384e09
13 changed files with 204 additions and 139 deletions

View file

@ -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 <absl/flags/parse.h>
#include <absl/types/span.h>
@ -116,13 +116,9 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]])
}
}
StereoSpan<float> output {
jack_port_get_buffer(outputPort1, numFrames),
jack_port_get_buffer(outputPort2, numFrames),
numFrames
};
synth->renderBlock(output);
auto leftOutput = reinterpret_cast<float*>(jack_port_get_buffer(outputPort1, numFrames));
auto rightOutput = reinterpret_cast<float*>(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;

View file

@ -50,6 +50,14 @@ public:
buffers[i] = std::make_unique<buffer_type>(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<value_type> getSpan(int channelIndex)
absl::Span<value_type> getSpan(int channelIndex) const
{
ASSERT(channelIndex < numChannels)
if (channelIndex < numChannels)
@ -95,45 +103,45 @@ public:
return {};
}
absl::Span<const value_type> getConstSpan(int channelIndex)
absl::Span<const value_type> getConstSpan(int channelIndex) const
{
return getSpan(channelIndex);
}
void addChannel()
{
if (numChannels < MaxChannels)
buffers[numChannels++] = std::make_unique<buffer_type>(numFrames);
}
void addChannel()
{
if (numChannels < MaxChannels)
buffers[numChannels++] = std::make_unique<buffer_type>(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<Type, Alignment>;

View file

@ -39,37 +39,48 @@ public:
{
}
AudioSpan(const std::array<Type*, MaxChannels>& spans, int numChannels, size_type offset, size_type size)
: numFrames(size)
, numChannels(numChannels)
{
ASSERT(static_cast<unsigned int>(numChannels) <= MaxChannels);
for (auto i = 0; i < numChannels; ++i)
this->spans[i] = spans[i] + offset;
}
AudioSpan(std::initializer_list<Type*> 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<absl::Span<Type>> spans)
: numChannels(spans.size())
{
static_assert(spans.size() <= MaxChannels);
ASSERT(spans.size() <= MaxChannels);
auto size = absl::Span<Type>::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 <class U, unsigned int N, unsigned int Alignment, typename = std::enable_if<N <= MaxChannels>>
AudioSpan(const AudioBuffer<U, N, Alignment>& audioBuffer)
AudioSpan(AudioBuffer<U, N, Alignment>& 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<Type>::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<const std::remove_cv<Type>> getConstSpan(int channelIndex)
absl::Span<const Type> 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<Type>({ getChannel(i), numFrames }, value);
::fill<Type>(getSpan(i), value);
}
void applyGain(absl::Span<const Type> gain) noexcept
{
for (int i = 0; i < numChannels; ++i)
::applyGain<Type>({ getChannel(i), numFrames }, gain);
::applyGain<Type>(gain, getSpan(i));
}
void applyGain(Type gain) noexcept
{
for (int i = 0; i < numChannels; ++i)
::applyGain<Type>({ getChannel(i), numFrames }, gain);
::applyGain<Type>(gain, getSpan(i));
}
template <class U, unsigned int N, typename = std::enable_if<N <= MaxChannels>>
void add(const AudioSpan<U, N>& other)
void add(AudioSpan<U, N>& other)
{
ASSERT(other.getNumChannels() == numChannels);
if (other.getNumChannels() == numChannels) {
@ -143,7 +154,7 @@ public:
}
template <class U, unsigned int N, typename = std::enable_if<N <= MaxChannels>>
void copy(const AudioSpan<U, N>& other)
void copy(AudioSpan<U, N>& other)
{
ASSERT(other.getNumChannels() == numChannels);
if (other.getNumChannels() == numChannels) {
@ -162,6 +173,24 @@ public:
return numChannels;
}
AudioSpan<Type> first(size_type length)
{
ASSERT(length <= numFrames);
return { spans, numChannels, 0, length };
}
AudioSpan<Type> last(size_type length)
{
ASSERT(length <= numFrames);
return { spans, numChannels, numFrames - length, length };
}
AudioSpan<Type> subspan(size_type offset, size_type length)
{
ASSERT(length + offset <= numFrames);
return { spans, numChannels, offset, length };
}
private:
std::array<Type*, MaxChannels> spans;
size_type numFrames { 0 };

View file

@ -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 };

View file

@ -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 <chrono>
#include <memory>
#include <sndfile.hh>
using namespace std::chrono_literals;
template <class T>
void readFromFile(SndfileHandle& sndFile, int numFrames, StereoBuffer<T>& output)
std::unique_ptr<AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, int numFrames)
{
auto returnedBuffer = std::make_unique<AudioBuffer<T>>(sndFile.channels(), numFrames);
if (sndFile.channels() == 1) {
auto tempReadBuffer = std::make_unique<Buffer<float>>(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<Buffer<float>>(2 * numFrames);
sndFile.readf(tempReadBuffer->data(), numFrames);
output.readInterleaved(*tempReadBuffer);
auto tempReadBuffer = std::make_unique<AudioBuffer<float>>(1, 2 * numFrames);
sndFile.readf(tempReadBuffer->channelWriter(0), numFrames);
::readInterleaved<float>(tempReadBuffer->getSpan(0), returnedBuffer->getSpan(0), returnedBuffer->getSpan(1));
}
return returnedBuffer;
}
std::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(std::string_view filename) noexcept
@ -52,12 +53,11 @@ std::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(
SndfileHandle sndFile(reinterpret_cast<const char*>(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<uint32_t>(sndFile.frames());
returnedValue.sampleRate = static_cast<double>(sndFile.samplerate());
@ -78,8 +78,7 @@ std::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(
if (preloadedData.contains(filename)) {
returnedValue.preloadedData = preloadedData[filename];
} else {
returnedValue.preloadedData = std::make_shared<StereoBuffer<float>>(preloadedSize);
readFromFile(sndFile, preloadedSize, *returnedValue.preloadedData);
returnedValue.preloadedData = std::shared_ptr<AudioBuffer<float>>(readFromFile<float>(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<const char*>(file.c_str()));
auto fileLoaded = std::make_unique<StereoBuffer<float>>(fileToLoad.numFrames);
readFromFile(sndFile, fileToLoad.numFrames, *fileLoaded);
fileToLoad.voice->setFileData(std::move(fileLoaded));
auto fileLoaded = std::make_unique<AudioBuffer<float>>(sndFile.channels(), fileToLoad.numFrames);
fileToLoad.voice->setFileData(readFromFile<float>(sndFile, fileToLoad.numFrames));
}
}

View file

@ -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 <absl/container/flat_hash_map.h>
@ -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<StereoBuffer<float>> preloadedData;
std::shared_ptr<AudioBuffer<float>> preloadedData;
};
std::optional<FileInformation> 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<std::string_view, std::shared_ptr<StereoBuffer<float>>> preloadedData;
absl::flat_hash_map<std::string_view, std::shared_ptr<AudioBuffer<float>>> preloadedData;
LEAK_DETECTOR(FilePool);
};
}

View file

@ -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<class T, class U>

View file

@ -27,7 +27,7 @@
#include "Defaults.h"
#include "EGDescription.h"
#include "Opcode.h"
#include "StereoBuffer.h"
#include "AudioBuffer.h"
#include <bitset>
#include <optional>
#include <random>
@ -148,9 +148,7 @@ struct Region {
EGDescription filterEG;
double sampleRate { config::defaultSampleRate };
int numChannels { 1 };
std::shared_ptr<StereoBuffer<float>> preloadedData { nullptr };
std::shared_ptr<AudioBuffer<float>> preloadedData { nullptr };
private:
bool keySwitched { true };
bool previousKeySwitched { true };

View file

@ -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<float> buffer) noexcept
void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
{
ScopedFTZ ftz;
buffer.fill(0.0f);
StereoSpan<float> tempSpan { tempBuffer, buffer.size() };
auto tempSpan = AudioSpan<float>(tempBuffer).first(buffer.getNumFrames());
for (auto& voice : voices) {
voice->renderBlock(tempSpan);
buffer.add(tempSpan);

View file

@ -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 <optional>
#include <random>
@ -51,7 +51,7 @@ public:
void setSamplesPerBlock(int samplesPerBlock) noexcept;
void setSampleRate(float sampleRate) noexcept;
void renderBlock(StereoSpan<float> buffer) noexcept;
void renderBlock(AudioSpan<float> 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<RegionPtrVector, 128> noteActivationLists;
std::array<RegionPtrVector, 128> ccActivationLists;
StereoBuffer<float> tempBuffer { config::defaultSamplesPerBlock };
AudioBuffer<float> tempBuffer { 2, config::defaultSamplesPerBlock };
int samplesPerBlock { config::defaultSamplesPerBlock };
float sampleRate { config::defaultSampleRate };

View file

@ -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<StereoBuffer<float>> file) noexcept
void sfz::Voice::setFileData(std::unique_ptr<AudioBuffer<float>> 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<float> buffer) noexcept
void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
{
const auto numSamples = buffer.size();
ASSERT(static_cast<int>(numSamples) <= samplesPerBlock);
ASSERT(static_cast<int>(buffer.getNumFrames()) <= samplesPerBlock);
buffer.fill(0.0f);
if (state == State::idle || region == nullptr)
@ -175,34 +175,59 @@ void sfz::Voice::renderBlock(StereoSpan<float> 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<float> buffer) noexcept
void sfz::Voice::processMono(AudioSpan<float> 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<float>(envelopeSpan, leftBuffer);
egEnvelope.getBlock(envelopeSpan);
::applyGain<float>(envelopeSpan, leftBuffer);
::copy<float>(leftBuffer, rightBuffer);
}
void sfz::Voice::processStereo(AudioSpan<float> 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<float> buffer) noexcept
{
auto source { [&]() {
if (region->canUsePreloadedData() || !dataReady)
return StereoSpan<const float>(*region->preloadedData);
return AudioSpan<const float>(*region->preloadedData);
else
return StereoSpan<const float>(*fileData);
return AudioSpan<const float>(*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<float>(jumps, pitchRatio * speedRatio);
if (region->shouldLoop() && region->trueSampleEnd() <= source.size()) {
if (region->shouldLoop() && region->trueSampleEnd() <= source.getNumFrames()) {
floatPosition = ::loopingSFZIndex<float, false>(
jumps,
leftCoeffs,
@ -218,32 +243,42 @@ void sfz::Voice::fillWithData(StereoSpan<float> 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<float> buffer) noexcept
void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
{
if (region->sample != "*sine")
return;
@ -251,10 +286,10 @@ void sfz::Voice::fillWithGenerator(StereoSpan<float> buffer) noexcept
float step = baseFrequency * twoPi<float> / sampleRate;
phase = ::linearRamp<float>(tempSpan1, phase, step);
::sin<float>(tempSpan1.first(buffer.size()), buffer.left());
absl::c_copy(buffer.left(), buffer.right().begin());
::sin<float>(tempSpan1.first(buffer.getNumFrames()), buffer.getSpan(0));
::copy<float>(buffer.getSpan(0), buffer.getSpan(1));
sourcePosition += buffer.size();
sourcePosition += buffer.getNumFrames();
}
bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept

View file

@ -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 <absl/types/span.h>
#include <atomic>
@ -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<StereoBuffer<float>> file) noexcept;
void setFileData(std::unique_ptr<AudioBuffer<float>> 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<float> buffer) noexcept;
void renderBlock(AudioSpan<float, 2> 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<float> buffer) noexcept;
void fillWithGenerator(StereoSpan<float> buffer) noexcept;
void fillWithData(AudioSpan<float> buffer) noexcept;
void fillWithGenerator(AudioSpan<float> buffer) noexcept;
void prepareEGEnvelope(int delay, uint8_t velocity) noexcept;
void processMono(AudioSpan<float> buffer) noexcept;
void processStereo(AudioSpan<float> buffer) noexcept;
void release(int delay) noexcept;
Region* region { nullptr };
@ -96,7 +98,7 @@ private:
uint32_t initialDelay { 0 };
std::atomic<bool> dataReady { false };
std::unique_ptr<StereoBuffer<float>> fileData { nullptr };
std::unique_ptr<AudioBuffer<float>> fileData { nullptr };
Buffer<float> tempBuffer1;
Buffer<float> tempBuffer2;

View file

@ -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());
}