Started implementing voices
This commit is contained in:
parent
df0783aec6
commit
7815623115
13 changed files with 254 additions and 15 deletions
|
|
@ -173,4 +173,7 @@ add_executable(bm_ramp benchmarks/BM_ramp.cpp sources/SIMDSSE.cpp)
|
|||
target_link_libraries(bm_ramp benchmark absl::span absl::algorithm)
|
||||
|
||||
add_executable(bm_ADSR benchmarks/BM_ADSR.cpp sources/SIMDSSE.cpp)
|
||||
target_link_libraries(bm_ADSR benchmark absl::span absl::algorithm)
|
||||
target_link_libraries(bm_ADSR benchmark absl::span absl::algorithm)
|
||||
|
||||
add_executable(bm_add benchmarks/BM_add.cpp sources/SIMDSSE.cpp)
|
||||
target_link_libraries(bm_add benchmark absl::span absl::algorithm)
|
||||
62
benchmarks/BM_add.cpp
Normal file
62
benchmarks/BM_add.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include "../sources/SIMDHelpers.h"
|
||||
|
||||
class AddArray : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state) {
|
||||
std::random_device rd { };
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> dist { 0, 1 };
|
||||
input = std::vector<float>(state.range(0));
|
||||
output = std::vector<float>(state.range(0));
|
||||
std::generate(output.begin(), output.end(), [&]() { return dist(gen); });
|
||||
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
|
||||
|
||||
}
|
||||
|
||||
std::vector<float> input;
|
||||
std::vector<float> output;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Scalar)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
add<float, false>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, SIMD)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
add<float, true>(input, absl::MakeSpan(output));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, Scalar_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
add<float, false>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(AddArray, SIMD_Unaligned)(benchmark::State& state) {
|
||||
for (auto _ : state)
|
||||
{
|
||||
add<float, true>(absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(AddArray, Scalar)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(AddArray, SIMD)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(AddArray, Scalar_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_REGISTER_F(AddArray, SIMD_Unaligned)->RangeMultiplier(4)->Range(1 << 2, 1 << 12);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -45,7 +45,7 @@ void sfz::FilePool::loadingThread()
|
|||
FileLoadingInformation fileToLoad {};
|
||||
while (!quitThread)
|
||||
{
|
||||
if (!loadingQueue.wait_dequeue_timed(fileToLoad, 1s))
|
||||
if (!loadingQueue.wait_dequeue_timed(fileToLoad, 1ms))
|
||||
continue;
|
||||
|
||||
if (fileToLoad.voice == nullptr)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ namespace SIMDConfig
|
|||
constexpr bool loopingSFZIndex { true };
|
||||
constexpr bool linearRamp { false };
|
||||
constexpr bool multiplicativeRamp { true };
|
||||
constexpr bool add { false };
|
||||
#if USE_SIMD
|
||||
constexpr bool useSIMD { true };
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include "Opcode.h"
|
||||
#include "FilePool.h"
|
||||
#include "EGDescription.h"
|
||||
#include "StereoBuffer.h"
|
||||
#include "Defaults.h"
|
||||
#include "CCMap.h"
|
||||
#include <bitset>
|
||||
|
|
|
|||
|
|
@ -77,4 +77,10 @@ template<>
|
|||
float multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
|
||||
{
|
||||
return multiplicativeRamp<float, false>(output, start, step);
|
||||
}
|
||||
|
||||
template<>
|
||||
void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
|
||||
{
|
||||
add<float, false>(input, output);
|
||||
}
|
||||
|
|
@ -231,9 +231,28 @@ T multiplicativeRamp(absl::Span<T> output, T start, T step) noexcept
|
|||
return start;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
float linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
|
||||
template<>
|
||||
float multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
float multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
|
||||
template<class T>
|
||||
inline void snippetAdd(const T*& input, T*& output)
|
||||
{
|
||||
*output++ += *input++;
|
||||
}
|
||||
|
||||
template<class T, bool SIMD=SIMDConfig::add>
|
||||
void add(absl::Span<const T> input, absl::Span<T> output) noexcept
|
||||
{
|
||||
ASSERT(output.size() >= input.size());
|
||||
auto* in = input.begin();
|
||||
auto* out = output.begin();
|
||||
auto* sentinel = out + min(input.size(), output.size());
|
||||
while (out < sentinel)
|
||||
snippetAdd(in, out);
|
||||
}
|
||||
|
||||
template<>
|
||||
void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept;
|
||||
|
|
@ -363,4 +363,27 @@ float multiplicativeRamp<float, true>(absl::Span<float> output, float value, flo
|
|||
while(out < output.end())
|
||||
snippetRampMultiplicative<float>(out, value, step);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<>
|
||||
void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
|
||||
{
|
||||
ASSERT(output.size() >= input.size());
|
||||
auto* in = input.begin();
|
||||
auto* out = output.begin();
|
||||
auto* sentinel = out + min(input.size(), output.size());
|
||||
const auto* lastAligned = prevAligned(sentinel);
|
||||
|
||||
while(unaligned(in, out) && out < lastAligned)
|
||||
snippetAdd<float>(in, out);
|
||||
|
||||
while(out < lastAligned)
|
||||
{
|
||||
_mm_store_ps(out, _mm_add_ps(_mm_load_ps(in), _mm_load_ps(out)));
|
||||
out += TypeAlignment;
|
||||
in += TypeAlignment;
|
||||
}
|
||||
|
||||
while(out < sentinel)
|
||||
snippetAdd<float>(in, out);
|
||||
}
|
||||
|
|
@ -34,6 +34,19 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
absl::Span<const Type> getSpan(Channel channel) const
|
||||
{
|
||||
switch(channel)
|
||||
{
|
||||
case Channel::left: return leftBuffer;
|
||||
case Channel::right: return rightBuffer;
|
||||
// Should not be here by construction...
|
||||
default:
|
||||
ASSERTFALSE;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Type& getSample(Channel channel, int sampleIndex) noexcept
|
||||
{
|
||||
ASSERT(sampleIndex >= 0);
|
||||
|
|
@ -66,6 +79,12 @@ public:
|
|||
::writeInterleaved<Type>(leftBuffer, rightBuffer, output);
|
||||
}
|
||||
|
||||
void add(const StereoBuffer<Type>& buffer)
|
||||
{
|
||||
::add<Type>(buffer.getSpan(Channel::left), absl::MakeSpan(leftBuffer));
|
||||
::add<Type>(buffer.getSpan(Channel::right), absl::MakeSpan(rightBuffer));
|
||||
}
|
||||
|
||||
Type* getChannel(Channel channel) noexcept
|
||||
{
|
||||
switch(channel)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ void sfz::Synth::callback(std::string_view header, std::vector<Opcode> members)
|
|||
|
||||
void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||
{
|
||||
auto lastRegion = std::make_shared<Region>();
|
||||
auto lastRegion = std::make_unique<Region>();
|
||||
|
||||
auto parseOpcodes = [&](const auto& opcodes) {
|
||||
for (auto& opcode: opcodes)
|
||||
|
|
@ -57,7 +57,7 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
|||
parseOpcodes(groupOpcodes);
|
||||
parseOpcodes(regionOpcodes);
|
||||
|
||||
regions.push_back(lastRegion);
|
||||
regions.push_back(std::move(lastRegion));
|
||||
}
|
||||
|
||||
void sfz::Synth::clear()
|
||||
|
|
@ -127,7 +127,7 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
|
|||
auto currentRegion = regions.begin();
|
||||
while (currentRegion <= lastRegion)
|
||||
{
|
||||
auto region = *currentRegion;
|
||||
auto region = &**currentRegion;
|
||||
|
||||
if (region->isGenerator())
|
||||
{
|
||||
|
|
@ -150,10 +150,10 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
|
|||
region->sampleRate = fileInformation->sampleRate;
|
||||
|
||||
for (auto note = region->keyRange.getStart(); note <= region->keyRange.getEnd(); note++)
|
||||
noteActivationLists[note].push_back(*currentRegion);
|
||||
noteActivationLists[note].push_back(region);
|
||||
|
||||
for (auto cc = region->keyRange.getStart(); cc <= region->keyRange.getEnd(); cc++)
|
||||
ccActivationLists[cc].push_back(*currentRegion);
|
||||
ccActivationLists[cc].push_back(region);
|
||||
|
||||
// Defaults
|
||||
for (int ccIndex = 1; ccIndex < 128; ccIndex++)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ namespace sfz
|
|||
class Synth: public Parser
|
||||
{
|
||||
public:
|
||||
Synth()
|
||||
{
|
||||
for (int i = 0; i < config::numVoices; ++i)
|
||||
voices.push_back(std::make_unique<Voice>());
|
||||
}
|
||||
bool loadSfzFile(const std::filesystem::path& file) final;
|
||||
int getNumRegions() const noexcept { return static_cast<int>(regions.size()); }
|
||||
int getNumGroups() const noexcept { return numGroups; }
|
||||
|
|
@ -22,8 +27,30 @@ public:
|
|||
const Region* getRegionView(int idx) const noexcept { return (size_t)idx < regions.size() ? regions[idx].get() : nullptr; }
|
||||
auto getUnknownOpcodes() { return unknownOpcodes; }
|
||||
size_t getNumPreloadedSamples() { return filePool.getNumPreloadedSamples(); }
|
||||
void prepareToPlay(int samplesPerBlock, double sampleRate);
|
||||
void renderBlock(StereoBuffer<float>& buffer);
|
||||
|
||||
void prepareToPlay(int samplesPerBlock, double sampleRate)
|
||||
{
|
||||
this->samplesPerBlock = samplesPerBlock;
|
||||
this->sampleRate = sampleRate;
|
||||
this->tempBuffer = StereoBuffer<float>(samplesPerBlock);
|
||||
}
|
||||
|
||||
void renderBlock(StereoBuffer<float>& buffer)
|
||||
{
|
||||
for (auto& voice: voices)
|
||||
{
|
||||
voice->renderBlock(tempBuffer);
|
||||
buffer.add(tempBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void noteOn(int delay, int channel, int noteNumber, uint8_t velocity);
|
||||
void noteOff(int delay, int channel, int noteNumber, uint8_t velocity);
|
||||
void cc(int delay, int channel, int ccNumber, uint8_t ccValue);
|
||||
void pitchWheel(int delay, int channel, int pitch);
|
||||
void aftertouch(int delay, int channel, uint8_t aftertouch);
|
||||
void tempo(int delay, float secondsPerQuarter);
|
||||
|
||||
protected:
|
||||
void callback(std::string_view header, std::vector<Opcode> members) final;
|
||||
private:
|
||||
|
|
@ -44,11 +71,16 @@ private:
|
|||
std::vector<CCNamePair> ccNames;
|
||||
std::optional<uint8_t> defaultSwitch;
|
||||
std::set<std::string_view> unknownOpcodes;
|
||||
using RegionPtrVector = std::vector<std::shared_ptr<Region>>;
|
||||
std::vector<std::shared_ptr<Region>> regions;
|
||||
using RegionPtrVector = std::vector<Region*>;
|
||||
std::vector<std::unique_ptr<Region>> regions;
|
||||
std::vector<std::unique_ptr<Voice>> voices;
|
||||
std::array<RegionPtrVector, 128> noteActivationLists;
|
||||
std::array<RegionPtrVector, 128> ccActivationLists;
|
||||
void buildRegion(const std::vector<Opcode>& regionOpcodes);
|
||||
|
||||
StereoBuffer<float> tempBuffer { config::defaultSamplesPerBlock };
|
||||
int samplesPerBlock { config::defaultSamplesPerBlock };
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
#include "StereoBuffer.h"
|
||||
#include "Globals.h"
|
||||
#include "Region.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -8,13 +10,53 @@ namespace sfz
|
|||
class Voice
|
||||
{
|
||||
public:
|
||||
enum class TriggerType { NoteOn, NoteOff, CC };
|
||||
void startVoice(Region* region, int channel, int number, uint8_t value, TriggerType triggerType)
|
||||
{
|
||||
this->triggerType = triggerType;
|
||||
triggerNumber = number;
|
||||
triggerChannel = channel;
|
||||
triggerValue = value;
|
||||
|
||||
this->region = region;
|
||||
}
|
||||
|
||||
void setFileData(std::unique_ptr<StereoBuffer<float>> file)
|
||||
{
|
||||
fileData = std::move(file);
|
||||
dataReady.store(true);
|
||||
}
|
||||
|
||||
bool registerNoteOn(int delay, int channel, int noteNumber, uint8_t velocity);
|
||||
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity);
|
||||
bool registerCC(int delay, int channel, int ccNumber, uint8_t ccValue);
|
||||
void registerPitchWheel(int delay, int channel, int pitch);
|
||||
void registerAftertouch(int delay, int channel, uint8_t aftertouch);
|
||||
void registerTempo(int delay, float secondsPerQuarter);
|
||||
|
||||
void prepareToPlay(int samplesPerBlock, double sampleRate);
|
||||
void renderBlock(StereoBuffer<float>& buffer)
|
||||
{
|
||||
buffer.fill(0.0f);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
dataReady.store(false);
|
||||
}
|
||||
private:
|
||||
Region* region;
|
||||
|
||||
TriggerType triggerType;
|
||||
int triggerNumber;
|
||||
int triggerChannel;
|
||||
uint8_t triggerValue;
|
||||
|
||||
std::atomic<bool> dataReady;
|
||||
std::unique_ptr<StereoBuffer<float>> fileData;
|
||||
|
||||
int samplesPerBlock { config::defaultSamplesPerBlock };
|
||||
double sampleRate { config::defaultSampleRate };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -486,4 +486,36 @@ TEST_CASE("[Helpers] Multiplicative Ramp unaligned (SIMD vs scalar)")
|
|||
multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
|
||||
multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
|
||||
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[Helpers] Add")
|
||||
{
|
||||
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
|
||||
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
std::array<float, 5> expected { 2.0, 3.0, 4.0, 5.0, 6.0 };
|
||||
add<float, false>(input, absl::MakeSpan(output));
|
||||
REQUIRE( output == expected );
|
||||
}
|
||||
|
||||
TEST_CASE("[Helpers] Add (SIMD)")
|
||||
{
|
||||
std::array<float, 5> input { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
|
||||
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
std::array<float, 5> expected { 2.0, 3.0, 4.0, 5.0, 6.0 };
|
||||
add<float, true>(input, absl::MakeSpan(output));
|
||||
REQUIRE( output == expected );
|
||||
}
|
||||
|
||||
TEST_CASE("[Helpers] Add (SIMD vs scalar)")
|
||||
{
|
||||
std::vector<float> input(bigBufferSize);
|
||||
std::vector<float> outputScalar(bigBufferSize);
|
||||
std::vector<float> outputSIMD(bigBufferSize);
|
||||
absl::c_iota(input, 0.0);
|
||||
absl::c_fill(outputScalar, 0.0);
|
||||
absl::c_fill(outputSIMD, 0.0);
|
||||
|
||||
add<float, false>(input, absl::MakeSpan(outputScalar));
|
||||
add<float, true>(input, absl::MakeSpan(outputSIMD));
|
||||
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) );
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue