Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Effects #84

Merged
merged 23 commits into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 37 additions & 31 deletions benchmarks/BM_multiplyAddFixedGain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,58 +14,64 @@

class MultiplyAddFixedGain : 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));
gain = dist(gen);
std::fill(output.begin(), output.end(), 1.0f );
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}

void TearDown(const ::benchmark::State& state [[maybe_unused]]) {
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));
gain = dist(gen);
std::fill(output.begin(), output.end(), 1.0f);
std::generate(input.begin(), input.end(), [&]() { return dist(gen); });
}

}
void TearDown(const ::benchmark::State& state [[maybe_unused]])
{
}

float gain = {};
std::vector<float> input;
std::vector<float> output;
float gain = {};
std::vector<float> input;
std::vector<float> output;
};

BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Straight)(benchmark::State& state) {
for (auto _ : state)
{
BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Straight)
(benchmark::State& state)
{
for (auto _ : state) {
for (int i = 0; i < state.range(0); ++i)
output[i] += gain * input[i];
}
}

BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar)(benchmark::State& state) {
for (auto _ : state)
{
BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, false>(gain, input, absl::MakeSpan(output));
}
}

BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD)(benchmark::State& state) {
for (auto _ : state)
{
BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, true>(gain, input, absl::MakeSpan(output));
}
}

BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
BENCHMARK_DEFINE_F(MultiplyAddFixedGain, Scalar_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, false>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}

BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD_Unaligned)(benchmark::State& state) {
for (auto _ : state)
{
BENCHMARK_DEFINE_F(MultiplyAddFixedGain, SIMD_Unaligned)
(benchmark::State& state)
{
for (auto _ : state) {
sfz::multiplyAdd<float, true>(gain, absl::MakeSpan(input).subspan(1), absl::MakeSpan(output).subspan(1));
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ set (SFIZZ_SOURCES
sfizz/FloatEnvelopes.cpp
sfizz/Logger.cpp
sfizz/SfzFilter.cpp
sfizz/Effects.cpp
sfizz/effects/Nothing.cpp
sfizz/effects/Lofi.cpp
)
include (SfizzSIMDSourceFiles)

Expand Down
16 changes: 16 additions & 0 deletions src/sfizz/AudioSpan.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,22 @@ class AudioSpan {
return {};
}

/**
* @brief Convert implicitly to a pointer of channels
*/
operator const float* const*() const noexcept
{
return spans.data();
}

/**
* @brief Convert implicitly to a pointer of channels
*/
operator float* const*() noexcept
{
return spans.data();
}

/**
* @brief Get a Span<Type> corresponding to a specific channel
*
Expand Down
4 changes: 4 additions & 0 deletions src/sfizz/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ namespace config {
*/
const absl::string_view midnamManufacturer { "The Sfizz authors" };
const absl::string_view midnamModel { "Sfizz" };
/**
Limit of how many "fxN" buses are accepted (in SFZv2, maximum is 4)
*/
constexpr int maxEffectBuses { 256 };
} // namespace config

// Enable or disable SIMD accelerators by default
Expand Down
146 changes: 146 additions & 0 deletions src/sfizz/Effects.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: BSD-2-Clause

// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz

#include "Effects.h"
#include "AudioSpan.h"
#include "Opcode.h"
#include "SIMDHelpers.h"
#include "Config.h"
#include "effects/Nothing.h"
#include "effects/Lofi.h"
#include <algorithm>

namespace sfz {

void EffectFactory::registerStandardEffectTypes()
{
// TODO
registerEffectType("lofi", fx::Lofi::makeInstance);
}

void EffectFactory::registerEffectType(absl::string_view name, Effect::MakeInstance& make)
{
FactoryEntry ent;
ent.name = std::string(name);
ent.make = &make;
_entries.push_back(std::move(ent));
}

std::unique_ptr<Effect> EffectFactory::makeEffect(absl::Span<const Opcode> members)
{
const Opcode* opcode = nullptr;

for (auto it = members.rbegin(); it != members.rend() && !opcode; ++it) {
if (it->lettersOnlyHash == hash("type"))
opcode = &*it;
}

if (!opcode) {
DBG("The effect does not specify a type");
return std::make_unique<sfz::fx::Nothing>();
}

const absl::string_view type = opcode->value;

const auto it = absl::c_find_if(_entries, [&](auto&& entry) { return entry.name == type; });
if (it == _entries.end()) {
DBG("Unsupported effect type: " << type);
return std::make_unique<sfz::fx::Nothing>();
}

auto fx = it->make(members);
if (!fx) {
DBG("Could not instantiate effect of type: " << type);
return std::make_unique<sfz::fx::Nothing>();
}

return fx;
}

///
EffectBus::EffectBus()
{
}

EffectBus::~EffectBus()
{
}

void EffectBus::addEffect(std::unique_ptr<Effect> fx)
{
_effects.emplace_back(std::move(fx));
}

void EffectBus::clearInputs(unsigned nframes)
{
AudioSpan<float>(_inputs).first(nframes).fill(0.0f);
AudioSpan<float>(_outputs).first(nframes).fill(0.0f);
}

void EffectBus::addToInputs(const float* const addInput[], float addGain, unsigned nframes)
{
if (addGain == 0)
return;

for (unsigned c = 0; c < EffectChannels; ++c) {
absl::Span<const float> addIn { addInput[c], nframes };
sfz::multiplyAdd(addGain, addIn, _inputs.getSpan(c));
}
}

void EffectBus::setSampleRate(double sampleRate)
{
for (const auto& effectPtr : _effects)
effectPtr->setSampleRate(sampleRate);
}

void EffectBus::clear()
{
for (const auto& effectPtr : _effects)
effectPtr->clear();
}

void EffectBus::process(unsigned nframes)
{
size_t numEffects = _effects.size();

if (numEffects > 0 && hasNonZeroOutput()) {
_effects[0]->process(
AudioSpan<float>(_inputs), AudioSpan<float>(_outputs), nframes);
for (size_t i = 1; i < numEffects; ++i)
_effects[i]->process(
AudioSpan<float>(_outputs), AudioSpan<float>(_outputs), nframes);
} else
fx::Nothing().process(
AudioSpan<float>(_inputs), AudioSpan<float>(_outputs), nframes);
}

void EffectBus::mixOutputsTo(float* const mainOutput[], float* const mixOutput[], unsigned nframes)
{
const float gainToMain = _gainToMain;
const float gainToMix = _gainToMix;

for (unsigned c = 0; c < EffectChannels; ++c) {
auto fxOut = _outputs.getConstSpan(c);
sfz::multiplyAdd(gainToMain, fxOut, absl::Span<float>(mainOutput[c], nframes));
sfz::multiplyAdd(gainToMix, fxOut, absl::Span<float>(mixOutput[c], nframes));
}
}

size_t EffectBus::numEffects() const noexcept
{
return _effects.size();
}
void EffectBus::setSamplesPerBlock(int samplesPerBlock) noexcept
{
_inputs.resize(samplesPerBlock);
_outputs.resize(samplesPerBlock);

for (const auto& effectPtr : _effects)
effectPtr->setSamplesPerBlock(samplesPerBlock);
}

} // namespace sfz
Loading