Skip to content

Commit

Permalink
LPG perf tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Segfault1602 committed Jan 21, 2024
1 parent 3f3bf94 commit 319765f
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 8 deletions.
14 changes: 13 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,19 @@
"request": "launch",
"program": "${workspaceFolder}/build/tests/Debug/libdsp_tests.exe",
"args": [
"--gtest_filter=Phase*"
"--gtest_filter=Buchla*"
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"console": "externalTerminal"
},
{
"name": "PerfTests",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/build/tests/perf/Debug/perf_tests.exe",
"args": [
],
"stopAtEntry": false,
"cwd": "${fileDirname}",
Expand Down
7 changes: 7 additions & 0 deletions include/buchla_lpg.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ float GetCurrent(float vc, float offset, bool smoothed);

float ProcessCurrent(float c);

void GetCurrent(const float* vc_in, float* vc_out, size_t size, bool smoothed);

/// @brief Buchla Low Pass Gate based on Josh Rohs' MATLAB implementation
/// Original paper: http://www.music.mcgill.ca/~gary/courses/projects/618_2018/rohs/MUMT618_Buchla_LPG_Report.pdf
class BuchlaLPG
Expand All @@ -28,10 +30,15 @@ class BuchlaLPG
/// @param size The size of the input and output buffer
void ProcessBlock(const float* cv_in, const float* in, float* out, size_t size);

void ProcessCurrent(const float* vc_in, float* vc_out, size_t size);

void ProcessAudio(const float* vc_in, const float* in, float* out, size_t size);

private:
float samplerate_;
float dt_ = 0.f;
float f_ = 0.f;
float f_inv_ = 0.f;

float prev_current_ = 0.f;

Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ endif()

target_include_directories(dsp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

target_compile_options(dsp PRIVATE -g -gcodeview)
target_link_options(dsp PRIVATE -fuse-ld=lld -g -Wl,--pdb=)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
target_compile_options(dsp PRIVATE ${CLANG_COMPILER_OPTION})
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
Expand Down
89 changes: 89 additions & 0 deletions src/buchla_lpg.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// Original MATLAB implementation:
/// http://www.music.mcgill.ca/~gary/courses/projects/618_2018/rohs/MUMT618_Buchla_LPG_Report.pdf
#include "buchla_lpg.h"

#include <algorithm>
Expand Down Expand Up @@ -70,12 +72,21 @@ float GetCurrent(float vc, float offset, bool smoothed)
return current;
}

void GetCurrent(const float* vc_in, float* vc_out, size_t size, bool smoothed)
{
for (size_t i = 0; i < size; ++i)
{
vc_out[i] = GetCurrent(vc_in[i], kOffset, smoothed);
}
}

void BuchlaLPG::Init(float samplerate)
{
samplerate_ = samplerate;
dt_ = 1.f / samplerate_;

f_ = 1.f / (2.f * samplerate_);
f_inv_ = 1.f / f_;
}

void BuchlaLPG::ProcessBlock(const float* cv_in, const float* in, float* out, size_t size)
Expand Down Expand Up @@ -151,4 +162,82 @@ void BuchlaLPG::ProcessBlock(const float* cv_in, const float* in, float* out, si
}
}

void BuchlaLPG::ProcessCurrent(const float* vc_in, float* vc_out, size_t size)
{
for (size_t i = 0; i < size; ++i)
{
const float current = vc_in[i];
int8_t dir = sign(current - prev_current_);
if (dir != prev_dir_)
{
switch (dir)
{
case 1:
wc_ = kWcOn * (1.f - kRhoOn + kRhoOn * current / 0.040f);
break;
case -1:
wc_ = kWcOff * (1.f + kRhoOff - (kRhoOff) * (yc_) / 0.040f);
break;
case 0:
break;
default:
assert(false);
break;
}
}

const float g = wc_ * dt_ * 0.5f;
const float vc = (current - sc_) * g / (1.f + g);
yc_ = vc + sc_;
sc_ = yc_ + vc;

prev_current_ = current;
prev_dir_ = dir;

const float processed_current = std::max(yc_, 1.01e-5f);
vc_out[i] = 1.f / (std::pow(processed_current, 1.4f)) * kAVac + kBVac;
}
}

void BuchlaLPG::ProcessAudio(const float* vc_in, const float* in, float* out, size_t size)
{
for (size_t i = 0; i < size; ++i)
{
const float rf = vc_in[i];

const float a = 0.f; // kNormalizedFb * amax;
const float a1 = 1.f / (rf * kC1);
const float a2 = -1.f / kC1 * (1 / rf + 1.f / kRa);
const float b1 = 1.f / (rf * kC2);
const float b2 = -2.f / (rf * kC2);
const float b3 = b1;
const float d1 = a;

const float yi = in[i];
const float d_o = 1.f / (1.f - f_ * a2);
const float d_x = 1.f / (1.f - f_ * b2);

constexpr float kD2 = -1.f;

if (non_lin_)
{
}
else
{
float yx = (sx_ + f_ * b1 * yi + f_ * b3 * d_o * so_ + f_ * kB4 * sd_ + kB4 * d1 * d_o * so_) * d_x;
yx = yx / (1 - d_x * (f_ * f_ * b3 * d_o * a1 + kB4 * f_ * d1 * d_o * a1 + kB4 * kD2));
yo_ = (so_ + f_ * a1 * yx) * d_o;
const float yd = sd_ + (1.f / f_) * (d1 * yo_ + kD2 * yx);

sx_ = sx_ + 2.f * f_ * (b1 * yi + b2 * yx + b3 * yo_ + kB4 * yd);

so_ = so_ + 2.f * f_ * (a1 * yx + a2 * yo_);

sd_ = -sd_ - (2.f / f_) * (d1 * yo_ + kD2 * yx);
}

out[i] = yo_;
}
}

} // namespace sfdsp
43 changes: 43 additions & 0 deletions tests/buchla_lpg_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,47 @@ TEST(BuchlaLPGTest, Current)
info.samplerate = kSamplerate;
info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
WriteWavFile("out_lpg.wav", audio_buffer.get(), info, kSize);
}

TEST(BuchlaLPGTestPerf, Current)
{
constexpr float kSamplerate = 96000.f;

sfdsp::BasicOscillator cv;
cv.Init(kSamplerate, 4, sfdsp::OscillatorType::Square);
cv.SetDuty(0.05f);

constexpr size_t kSize = 4 * kSamplerate;
auto cv_out = std::make_unique<float[]>(kSize);
cv.ProcessBlock(cv_out.get(), kSize);

constexpr float kModDepth = 8.f;

for (size_t i = 0; i < kSize; ++i)
{
cv_out[i] = kModDepth / 2.f * (1.f + cv_out[i]);
}

auto audio_buffer = std::make_unique<float[]>(kSize);
sfdsp::BasicOscillator audio;
audio.Init(kSamplerate, 440, sfdsp::OscillatorType::Square);
audio.ProcessBlock(audio_buffer.get(), kSize);

for (size_t i = 0; i < kSize; ++i)
{
audio_buffer[i] = 0.3f * audio_buffer[i];
}

sfdsp::BuchlaLPG lpg;
lpg.Init(kSamplerate);

sfdsp::GetCurrent(cv_out.get(), cv_out.get(), kSize, false);
lpg.ProcessCurrent(cv_out.get(), cv_out.get(), kSize);
lpg.ProcessAudio(cv_out.get(), audio_buffer.get(), audio_buffer.get(), kSize);

SF_INFO info;
info.channels = 1;
info.samplerate = kSamplerate;
info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
WriteWavFile("perf_lpg.wav", audio_buffer.get(), info, kSize);
}
62 changes: 55 additions & 7 deletions tests/perf/buchla_lpg_perf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ using namespace ankerl;
using namespace std::chrono_literals;

constexpr size_t kSamplerate = 96000;
constexpr size_t kOutputSize = 960;
constexpr size_t kOutputSize = 96000;

TEST_CASE("BuchlaLPG")
{
nanobench::Bench bench;
bench.title("Buchla LPG");
// bench.relative(true);
bench.timeUnit(1ms, "ms");
bench.performanceCounters(true);
bench.minEpochIterations(50);
bench.warmup(10);

sfdsp::BasicOscillator cv;
cv.Init(kSamplerate, 4, sfdsp::OscillatorType::Square);
Expand All @@ -45,15 +46,62 @@ TEST_CASE("BuchlaLPG")
sfdsp::BuchlaLPG lpg;
lpg.Init(kSamplerate);

lpg.ProcessBlock(cv_out.get(), audio_buffer.get(), audio_buffer.get(), kOutputSize);
// lpg.ProcessBlock(cv_out.get(), audio_buffer.get(), audio_buffer.get(), kOutputSize);

std::unique_ptr<float[]> audio_out = std::make_unique<float[]>(kOutputSize);
constexpr size_t kBlockSize = 128;

bench.run("Buchla LPG", [&]() {
for (size_t i = 0; i < kOutputSize; i += kBlockSize)
{
lpg.ProcessBlock(cv_out.get() + i, audio_buffer.get() + i, audio_out.get() + i, kBlockSize);
}
// lpg.ProcessBlock(cv_out.get(), audio_buffer.get(), audio_out.get(), kOutputSize);
sfdsp::GetCurrent(cv_out.get(), cv_out.get(), kOutputSize, false);
lpg.ProcessCurrent(cv_out.get(), cv_out.get(), kOutputSize);
lpg.ProcessAudio(cv_out.get(), audio_buffer.get(), audio_buffer.get(), kOutputSize);
});
}

TEST_CASE("BuchlaLPG_Detailed")
{
nanobench::Bench bench;
bench.title("Buchla LPG - Detailed");
bench.relative(true);
bench.timeUnit(1ms, "ms");
bench.performanceCounters(true);
bench.minEpochIterations(50);
bench.warmup(10);

sfdsp::BasicOscillator cv;
cv.Init(kSamplerate, 4, sfdsp::OscillatorType::Square);
cv.SetDuty(0.05f);
auto cv_out = std::make_unique<float[]>(kOutputSize);
cv.ProcessBlock(cv_out.get(), kOutputSize);

constexpr float kModDepth = 8.f;
for (size_t i = 0; i < kOutputSize; ++i)
{
cv_out[i] = kModDepth / 2.f * (1.f + cv_out[i]);
}

auto audio_buffer = std::make_unique<float[]>(kOutputSize);
sfdsp::BasicOscillator audio;
audio.Init(kSamplerate, 440, sfdsp::OscillatorType::Square);
audio.ProcessBlock(audio_buffer.get(), kOutputSize);

for (size_t i = 0; i < kOutputSize; ++i)
{
audio_buffer[i] = 0.3f * audio_buffer[i];
}

sfdsp::BuchlaLPG lpg;
lpg.Init(kSamplerate);

lpg.ProcessBlock(cv_out.get(), audio_buffer.get(), audio_buffer.get(), kOutputSize);

std::unique_ptr<float[]> audio_out = std::make_unique<float[]>(kOutputSize);

bench.run("GetCurrent", [&]() { sfdsp::GetCurrent(cv_out.get(), cv_out.get(), kOutputSize, false); });

bench.run("ProcessCurrent", [&]() { lpg.ProcessCurrent(cv_out.get(), cv_out.get(), kOutputSize); });

bench.run("ProcessAudio",
[&]() { lpg.ProcessAudio(cv_out.get(), audio_buffer.get(), audio_out.get(), kOutputSize); });
}

0 comments on commit 319765f

Please sign in to comment.