Started implementing voices

This commit is contained in:
paul 2019-08-23 01:27:39 +02:00
parent df0783aec6
commit 7815623115
13 changed files with 254 additions and 15 deletions

View file

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

View file

@ -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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)

View file

@ -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++)

View file

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

View file

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

View file

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