Added an AudioSpan and AudioBuffer, to better handle the memory for mono/stereo samples
This commit is contained in:
parent
b1ef2c26bd
commit
c1d2384e09
13 changed files with 204 additions and 139 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
||||
|
|
|
|||
107
sfizz/Voice.cpp
107
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<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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue