Skip to content

Commit

Permalink
Remove libsamplerate dependency (#7728)
Browse files Browse the repository at this point in the history
After porting Nimbus ot lanczos in 4b3ffe6 we can now remove the
libsamplerate dependency altogether as long as we fix up the twist
fm (which we did with a simple application of another lanczos ds)
and remove src code from the unit tests (where it was explicitly
tested)
  • Loading branch information
baconpaul authored Jul 25, 2024
1 parent 4b3ffe6 commit a4e2a65
Show file tree
Hide file tree
Showing 7 changed files with 12 additions and 214 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
[submodule "libs/JUCE"]
path = libs/JUCE
url = https://github.com/surge-synthesizer/JUCE
[submodule "libs/libsamplerate"]
path = libs/libsamplerate
url = https://github.com/libsndfile/libsamplerate.git
[submodule "libs/luajitlib/LuaJIT"]
path = libs/luajitlib/LuaJIT
url = https://github.com/LuaJIT/LuaJIT.git
Expand Down
1 change: 0 additions & 1 deletion libs/libsamplerate
Submodule libsamplerate deleted from d0ea53
6 changes: 0 additions & 6 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ else()
add_library(surge::oddsound-mts ALIAS oddsound-mts)
endif()

if(MINGW)
set(HAVE_VISIBILITY 0 CACHE INTERNAL "Force-disable libsamplerate's visibility check on MinGW")
endif()

surge_add_lib_subdirectory(libsamplerate)
surge_add_lib_subdirectory(pffft)
surge_add_lib_subdirectory(tuning-library)
surge_add_lib_subdirectory(sqlite-3.23.3)
Expand Down Expand Up @@ -369,7 +364,6 @@ target_link_libraries(${PROJECT_NAME}
PUBLIC
fmt
luajit-5.1
samplerate
surge::airwindows
surge::eurorack

Expand Down
131 changes: 10 additions & 121 deletions src/common/dsp/oscillators/TwistOscillator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
#endif
#include "plaits/dsp/voice.h"

#include "samplerate.h"

std::string twist_engine_name(int i)
{
switch (i)
Expand Down Expand Up @@ -282,32 +280,15 @@ TwistOscillator::TwistOscillator(SurgeStorage *storage, OscillatorStorage *oscda
pdata *localcopy)
: Oscillator(storage, oscdata, localcopy), charFilt(storage)
{
#if SAMPLERATE_LANCZOS
lancRes = std::make_unique<sst::basic_blocks::dsp::LanczosResampler<BLOCK_SIZE>>(
48000, storage->dsamplerate_os);
srcstate = nullptr;
#else
int error;
srcstate = src_new(SRC_SINC_FASTEST, 2, &error);
// srcstate = src_new(SRC_LINEAR, 2, &error);
if (error != 0)
{
srcstate = nullptr;
}
#endif
lancRes = std::make_unique<resamp_t>(48000, storage->dsamplerate_os);
voice = std::make_unique<plaits::Voice>();
shared_buffer = new char[16384];
alloc = std::make_unique<stmlib::BufferAllocator>(shared_buffer, 16384);
patch = std::make_unique<plaits::Patch>();
mod = std::make_unique<plaits::Modulations>();

// FM downsampling with a linear interpolator is absolutely fine
int error;
fmdownsamplestate = src_new(SRC_LINEAR, 1, &error);
if (error != 0)
{
fmdownsamplestate = nullptr;
}
fmDownSampler = std::make_unique<resamp_t>(storage->dsamplerate_os, 48000);
}

float TwistOscillator::tuningAwarePitch(float pitch)
Expand Down Expand Up @@ -362,24 +343,13 @@ TwistOscillator::~TwistOscillator()
{
if (shared_buffer)
delete[] shared_buffer;

if (srcstate)
srcstate = src_delete(srcstate);

if (fmdownsamplestate)
fmdownsamplestate = src_delete(fmdownsamplestate);
}

template <bool FM, bool throwaway>
void TwistOscillator::process_block_internal(float pitch, float drift, bool stereo, float FMdepth,
int throwawayBlocks)
{
#if SAMPLERATE_SRC
if (!srcstate)
return;
#endif

if (FM && !fmdownsamplestate)
if (FM && !fmDownSampler)
return;

pitch = tuningAwarePitch(pitch);
Expand Down Expand Up @@ -408,64 +378,34 @@ void TwistOscillator::process_block_internal(float pitch, float drift, bool ster
// This setting allows us to correct it in VCV Rack.
if (lpgIsOn && useCorrectLPGBlockSize)
subblock = 12;
#if SAMPLERATE_SRC
float src_in[subblock][2];
float src_out[BLOCK_SIZE_OS][2];

SRC_DATA sdata;
sdata.end_of_input = 0;
sdata.src_ratio = dsamplerate_os / 48000.0;
#endif

float normFMdepth = 0;

if (FM)
{
float dsmaster[BLOCK_SIZE_OS << 2];
SRC_DATA fmdata;
fmdata.end_of_input = 0;
fmdata.src_ratio = 48000.0 / storage->dsamplerate_os; // going INTO the plaits rate
fmdata.data_in = master_osc;
fmdata.data_out = &(dsmaster[0]);
fmdata.input_frames = BLOCK_SIZE_OS;
fmdata.output_frames = BLOCK_SIZE_OS << 2;
src_process(fmdownsamplestate, &fmdata);
float dsmaster[2][BLOCK_SIZE_OS << 2];
for (int i = 0; i < BLOCK_SIZE_OS; ++i)
fmDownSampler->push(master_osc[i], 0.f);

const float bl = -143.5, bhi = 71.7, oos = 1.0 / (bhi - bl);
float adb = limit_range(amp_to_db(FMdepth), bl, bhi);
float nfm = (adb - bl) * oos;

normFMdepth = limit_range(nfm, 0.f, 1.f);

for (int i = 0; i < fmdata.output_frames_gen; ++i)
auto outputFramesGen =
fmDownSampler->populateNext(dsmaster[0], dsmaster[1], BLOCK_SIZE_OS << 2);
for (int i = 0; i < outputFramesGen; ++i)
{
fmlagbuffer[fmwp] = dsmaster[i];
fmlagbuffer[fmwp] = dsmaster[0][i];
fmwp = (fmwp + 1) & ((BLOCK_SIZE_OS << 1) - 1);
}
}

int required_blocks = throwaway ? throwawayBlocks : BLOCK_SIZE_OS;

#if SAMPLERATE_SRC
for (int i = 0; i < carrover_size; ++i)
{
if (oscdata->p[twist_aux_mix].extend_range)
{
output[i] = auxmix.v * carryover[i][1] + (1.0 - auxmix.v) * carryover[i][0];
outputR[i] = auxmix.v * carryover[i][0] + (1.0 - auxmix.v) * carryover[i][1];
}
else
{
output[i] = auxmix.v * carryover[i][1] + (1.0 - auxmix.v) * carryover[i][0];
outputR[i] = output[i];
}
}
int total_generated = carrover_size;
carrover_size = 0;
#else
int total_generated =
required_blocks - lancRes->inputsRequiredToGenerateOutputs(required_blocks);
#endif

if (lpgIsOn)
{
Expand All @@ -487,9 +427,6 @@ void TwistOscillator::process_block_internal(float pitch, float drift, bool ster
morph.process();
lpgdec.process();
lpgcol.process();
#if SAMPLERATE_SRC
auxmix.process();
#endif

if (FM)
{
Expand All @@ -506,61 +443,14 @@ void TwistOscillator::process_block_internal(float pitch, float drift, bool ster

voice->Render(*patch, *mod, poutput, subblock);

#if SAMPLERATE_LANCZOS
for (int i = 0; i < subblock; ++i)
{
lancRes->push(poutput[i].out / 32768.f, poutput[i].aux / 32768.f);
}
total_generated =
required_blocks - lancRes->inputsRequiredToGenerateOutputs(required_blocks);
#else
for (int i = 0; i < subblock; ++i)
{
src_in[i][0] = poutput[i].out / 32768.f;
src_in[i][1] = poutput[i].aux / 32768.f;
}
sdata.data_in = &(src_in[0][0]);
sdata.data_out = &(src_out[0][0]);
sdata.input_frames = subblock;
sdata.output_frames = BLOCK_SIZE_OS;
auto res = src_process(srcstate, &sdata);
// FIXME - check res

for (int i = 0; i < sdata.output_frames_gen; ++i)
{
if (i + total_generated >= required_blocks)
{
carryover[carrover_size][0] = src_out[i][0];
carryover[carrover_size][1] = src_out[i][1];
carrover_size++;
}
else if (!throwaway)
{
if (oscdata->p[twist_aux_mix].extend_range)
{
output[total_generated + i] =
auxmix.v * src_out[i][1] + (1 - auxmix.v) * src_out[i][0];
outputR[total_generated + i] =
auxmix.v * src_out[i][0] + (1 - auxmix.v) * src_out[i][1];
}
else
{
output[total_generated + i] =
auxmix.v * src_out[i][1] + (1 - auxmix.v) * src_out[i][0];
outputR[total_generated + i] = output[total_generated + i];
}
}
}
total_generated += sdata.output_frames_gen;
if (sdata.input_frames_used != subblock)
{
// FIXME
std::cout << "DEAL " << std::endl;
}
#endif
}

#if SAMPLERATE_LANCZOS
if (throwaway)
{
lancRes->advanceReadPointer(required_blocks);
Expand All @@ -586,7 +476,6 @@ void TwistOscillator::process_block_internal(float pitch, float drift, bool ster
}
}
lancRes->renormalizePhases();
#endif

if (!throwaway && charFilt.doFilter)
{
Expand Down
12 changes: 2 additions & 10 deletions src/common/dsp/oscillators/TwistOscillator.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,15 @@
* What's our samplerate strategy
*/
#include "globals.h"
#define SAMPLERATE_SRC 0
#define SAMPLERATE_LANCZOS 1

#include "OscillatorBase.h"
#include <memory>
#include "basic_dsp.h"
#include "DSPUtils.h"
#include "OscillatorCommonFunctions.h"

#if SAMPLERATE_LANCZOS
// #include "LanczosResampler.h"
#include "sst/basic-blocks/dsp/LanczosResampler.h"
#endif

namespace plaits
{
Expand All @@ -50,8 +46,6 @@ namespace stmlib
class BufferAllocator;
}

struct SRC_STATE_tag;

class TwistOscillator : public Oscillator
{
public:
Expand Down Expand Up @@ -95,15 +89,13 @@ class TwistOscillator : public Oscillator
char *shared_buffer{nullptr};

// Keep this here for now even if using lanczos since I'm using SRC for FM still
SRC_STATE_tag *srcstate, *fmdownsamplestate;
float fmlagbuffer[BLOCK_SIZE_OS << 1];
int fmwp, fmrp;

bool useCorrectLPGBlockSize{false}; // See #6760

#if SAMPLERATE_LANCZOS
std::unique_ptr<sst::basic_blocks::dsp::LanczosResampler<BLOCK_SIZE>> lancRes;
#endif
using resamp_t = sst::basic_blocks::dsp::LanczosResampler<BLOCK_SIZE>;
std::unique_ptr<resamp_t> lancRes, fmDownSampler;

float carryover[BLOCK_SIZE_OS][2];
int carrover_size = 0;
Expand Down
1 change: 0 additions & 1 deletion src/surge-testrunner/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ add_executable(${PROJECT_NAME}
)

target_link_libraries(${PROJECT_NAME} PRIVATE
samplerate
surge-lua-src
surge::catch2_v3
surge::surge-common
Expand Down
72 changes: 0 additions & 72 deletions src/surge-testrunner/UnitTestsDSP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@

#include "UnitTestUtilities.h"

#include "samplerate.h"

#include "SSEComplex.h"
#include <complex>
#include "sst/basic-blocks/mechanics/simd-ops.h"
Expand Down Expand Up @@ -341,76 +339,6 @@ TEST_CASE("All Patches Have Bounded Output", "[dsp]")
// Surge::Headless::playOnNRandomPatches(surge, scale, 100, callBack);
}

TEST_CASE("libsamplerate Basics", "[dsp]")
{
for (auto tsr : {44100, 48000}) // { 44100, 48000, 88200, 96000, 192000 })
{
for (auto ssr : {44100, 48000, 88200})
{
DYNAMIC_SECTION("libsamplerate from " << ssr << " to " << tsr)
{
int error;
auto state = src_new(SRC_SINC_FASTEST, 1, &error);
REQUIRE(state);
REQUIRE(error == 0);

static constexpr int buffer_size = 1024 * 100;
static constexpr int output_block = 64;

float input_data[buffer_size];
float copied_output[buffer_size];

int cwp = 0, irp = 0;
float output_data[output_block];

float dPhase = 440.0 / ssr * 2.0 * M_PI;
float phase = 0;
for (int i = 0; i < buffer_size; ++i)
{
input_data[i] = std::sin(phase);
phase += dPhase;
if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
}

SRC_DATA sdata;
sdata.end_of_input = 0;
while (irp + output_block < buffer_size && cwp + output_block < buffer_size)
{
sdata.data_in = &(input_data[irp]);
sdata.data_out = output_data;
sdata.input_frames = 64;
sdata.output_frames = 64;
sdata.src_ratio = 1.0 * tsr / ssr;

auto res = src_process(state, &sdata);
memcpy((void *)(copied_output + cwp), (void *)output_data,
sdata.output_frames_gen * sizeof(float));
irp += sdata.input_frames_used;
cwp += sdata.output_frames_gen;
REQUIRE(res == 0);
REQUIRE(sdata.input_frames_used + sdata.output_frames_gen > 0);
}

state = src_delete(state);
REQUIRE(!state);

// At this point the output block should be a 440hz sine wave at the target rate
dPhase = 440.0 / tsr * 2.0 * M_PI;
phase = 0;
for (int i = 0; i < cwp; ++i)
{
auto cw = std::sin(phase);
REQUIRE(copied_output[i] == Approx(cw).margin(1e-2));
phase += dPhase;
if (phase >= 2.0 * M_PI)
phase -= 2.0 * M_PI;
}
}
}
}
}

TEST_CASE("Every Oscillator Plays", "[dsp]")
{
for (int i = 0; i < n_osc_types; ++i)
Expand Down

0 comments on commit a4e2a65

Please sign in to comment.