251 lines
7.1 KiB
C++
251 lines
7.1 KiB
C++
|
#include <cstdint>
|
||
|
#include "ladspa.h"
|
||
|
#include <array>
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "effect.h"
|
||
|
#include "pitchshifter.h"
|
||
|
#include "formantshifter.h"
|
||
|
#include "timestretcher.h"
|
||
|
|
||
|
#define PORT_COUNT 6
|
||
|
#define ML_INPUT 0
|
||
|
#define ML_OUTPUT 1
|
||
|
#define ML_PITCH_CHANGE 3
|
||
|
#define ML_TIMBRE_CHANGE 5
|
||
|
#define ML_TIMBRE_IND 4
|
||
|
#define ML_PITCH_IND 2
|
||
|
|
||
|
using namespace Mengu;
|
||
|
using namespace dsp;
|
||
|
|
||
|
struct PluginHandler {
|
||
|
std::array<PitchShifter *, 3> pitch_shifters;
|
||
|
std::array<Effect *, 2> formant_shifters;
|
||
|
const float *in_buffer;
|
||
|
float *out_buffer;
|
||
|
const float *pitch_shifter_ind;
|
||
|
const float *pitch_shift;
|
||
|
const float *formant_shifter_ind;
|
||
|
const float *formant_shift;
|
||
|
};
|
||
|
|
||
|
enum PitchShiftInd {
|
||
|
WSOLAPitch = 0,
|
||
|
PhaseVocoderPitch = 1,
|
||
|
PhaseVocoderDoneRightPitch = 2,
|
||
|
};
|
||
|
enum FormantShiftInd {
|
||
|
LPCFormant = 0,
|
||
|
PSOLAFormant = 1,
|
||
|
};
|
||
|
|
||
|
/* internal core methods */
|
||
|
static LADSPA_Handle instantiate(const LADSPA_Descriptor *descriptor, unsigned long sample_rate) {
|
||
|
PluginHandler *plugin = new PluginHandler;
|
||
|
plugin->pitch_shifters = {
|
||
|
new TimeStretchPitchShifter(new WSOLATimeStretcher(), 1),
|
||
|
new TimeStretchPitchShifter(new PhaseVocoderTimeStretcher(), 1),
|
||
|
new TimeStretchPitchShifter(new PhaseVocoderDoneRightTimeStretcher(), 1),
|
||
|
};
|
||
|
plugin->formant_shifters = {
|
||
|
new LPCFormantShifter(),
|
||
|
new TimeStretchPitchShifter(new PSOLATimeStretcher(), 1),
|
||
|
};
|
||
|
|
||
|
return plugin;
|
||
|
}
|
||
|
|
||
|
static void connect_port(LADSPA_Handle instance, unsigned long port, LADSPA_Data *data_location) {
|
||
|
PluginHandler* plugin = (PluginHandler*) instance;
|
||
|
if (plugin == nullptr) return;
|
||
|
|
||
|
switch (port)
|
||
|
{
|
||
|
case 0:
|
||
|
plugin->in_buffer = (const float*) data_location;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
plugin->out_buffer = (float*) data_location;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
plugin->pitch_shifter_ind = (const float*) data_location;
|
||
|
|
||
|
case 3:
|
||
|
plugin->pitch_shift = (const float*) data_location;
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
plugin->formant_shifter_ind = (const float*) data_location;
|
||
|
break;
|
||
|
|
||
|
case 5:
|
||
|
plugin->formant_shift = (const float*) data_location;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void activate (LADSPA_Handle instance)
|
||
|
{
|
||
|
/* not needed here */
|
||
|
}
|
||
|
|
||
|
static void run (LADSPA_Handle instance, unsigned long sample_count)
|
||
|
{
|
||
|
PluginHandler* plugin = (PluginHandler*) instance;
|
||
|
if (plugin == nullptr) return;
|
||
|
if ((!plugin->in_buffer) || (!plugin->out_buffer) || (!plugin->pitch_shift)) return;
|
||
|
|
||
|
// apply effects
|
||
|
Effect *pitch_shifter = plugin->pitch_shifters[static_cast<PitchShiftInd>(*plugin->pitch_shifter_ind)];
|
||
|
Effect *formant_shifter = plugin->formant_shifters[static_cast<FormantShiftInd>(*plugin->formant_shifter_ind)];
|
||
|
|
||
|
pitch_shifter->set_property(0, EffectPropPayload {
|
||
|
.type = Slider,
|
||
|
.value = *plugin->pitch_shift,
|
||
|
});
|
||
|
formant_shifter->set_property(0, EffectPropPayload {
|
||
|
.type = Slider,
|
||
|
.value = *plugin->formant_shift,
|
||
|
});
|
||
|
|
||
|
static constexpr uint32_t ProcSize = 1 << 10;
|
||
|
Complex cbuffer[ProcSize];
|
||
|
uint32_t num_processed = 0;
|
||
|
|
||
|
while (num_processed < sample_count) {
|
||
|
uint32_t num_this_pass = MIN(ProcSize, sample_count - num_processed);
|
||
|
for (uint32_t i = 0; i < num_this_pass; i++) {
|
||
|
cbuffer[i] = (Complex) plugin->in_buffer[num_processed + i];
|
||
|
}
|
||
|
|
||
|
pitch_shifter->push_signal(cbuffer, num_this_pass);
|
||
|
pitch_shifter->pop_transformed_signal(cbuffer, num_this_pass);
|
||
|
|
||
|
formant_shifter->push_signal(cbuffer, num_this_pass);
|
||
|
formant_shifter->pop_transformed_signal(cbuffer, num_this_pass);
|
||
|
|
||
|
|
||
|
|
||
|
for (uint32_t i = 0; i < num_this_pass; i++) {
|
||
|
plugin->out_buffer[num_processed + i] = cbuffer[i].real();
|
||
|
}
|
||
|
|
||
|
num_processed += num_this_pass;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
static void deactivate (LADSPA_Handle instance)
|
||
|
{
|
||
|
/* not needed here */
|
||
|
}
|
||
|
|
||
|
static void cleanup (LADSPA_Handle instance) {
|
||
|
PluginHandler *plugin = (PluginHandler *) instance;
|
||
|
if (plugin == nullptr) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (auto pitch_shifter: plugin->pitch_shifters) {
|
||
|
delete pitch_shifter;
|
||
|
}
|
||
|
|
||
|
for (auto formant_shifter: plugin->formant_shifters) {
|
||
|
delete formant_shifter;
|
||
|
}
|
||
|
|
||
|
delete plugin;
|
||
|
}
|
||
|
|
||
|
static LADSPA_Descriptor *descriptor = NULL;
|
||
|
|
||
|
// On plugin load
|
||
|
static void __attribute__ ((constructor)) init() {
|
||
|
LADSPA_PortDescriptor * portDescriptors;
|
||
|
LADSPA_PortRangeHint * portRangeHints;
|
||
|
char ** portNames;
|
||
|
|
||
|
descriptor = (LADSPA_Descriptor *)malloc(sizeof(LADSPA_Descriptor));
|
||
|
|
||
|
if (!descriptor) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
descriptor->UniqueID = 109124; // should be unique
|
||
|
descriptor->Label = "pitcher";
|
||
|
descriptor->Name = "pitchplug";
|
||
|
descriptor->Maker = "Rysertio";
|
||
|
descriptor->Copyright = "(c) 2023, Rysertio";
|
||
|
|
||
|
descriptor->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
|
||
|
|
||
|
descriptor->PortCount = PORT_COUNT;
|
||
|
|
||
|
portDescriptors = (LADSPA_PortDescriptor *) calloc(PORT_COUNT, sizeof(LADSPA_PortDescriptor));
|
||
|
|
||
|
portDescriptors[ML_PITCH_CHANGE] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
|
||
|
portDescriptors[ML_TIMBRE_CHANGE] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
|
||
|
portDescriptors[ML_INPUT] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
|
||
|
portDescriptors[ML_OUTPUT] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
|
||
|
portDescriptors[ML_PITCH_IND] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
|
||
|
portDescriptors[ML_TIMBRE_IND] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
|
||
|
|
||
|
descriptor->PortDescriptors = portDescriptors;
|
||
|
|
||
|
portNames = (char **) calloc(PORT_COUNT, sizeof(char *));
|
||
|
portNames[ML_PITCH_CHANGE] = "pitch change factor";
|
||
|
portNames[ML_TIMBRE_CHANGE] = "timbre change factor";
|
||
|
portNames[ML_INPUT] = "Input";
|
||
|
portNames[ML_OUTPUT] = "Output";
|
||
|
portNames[ML_PITCH_IND] = "pitch change algorithm";
|
||
|
portNames[ML_TIMBRE_IND] = "timbre change algorithm";
|
||
|
|
||
|
|
||
|
descriptor->PortNames = (const char * const *) portNames;
|
||
|
|
||
|
portRangeHints = (LADSPA_PortRangeHint *) calloc(PORT_COUNT, sizeof(LADSPA_PortRangeHint));
|
||
|
|
||
|
portRangeHints[ML_PITCH_CHANGE].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_1;
|
||
|
portRangeHints[ML_PITCH_CHANGE].LowerBound = 1;
|
||
|
portRangeHints[ML_TIMBRE_CHANGE].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_1;
|
||
|
portRangeHints[ML_TIMBRE_CHANGE].LowerBound = 1;
|
||
|
portRangeHints[ML_INPUT].HintDescriptor = 0;
|
||
|
portRangeHints[ML_OUTPUT].HintDescriptor = 0;
|
||
|
|
||
|
descriptor->PortRangeHints = portRangeHints;
|
||
|
|
||
|
descriptor->instantiate = instantiate;
|
||
|
descriptor->connect_port = connect_port;
|
||
|
descriptor->activate = activate;
|
||
|
descriptor->run = run;
|
||
|
descriptor->run_adding = NULL;
|
||
|
descriptor->set_run_adding_gain = NULL;
|
||
|
descriptor->deactivate = NULL;
|
||
|
descriptor->cleanup = cleanup;
|
||
|
}
|
||
|
|
||
|
// On plugin unload
|
||
|
static void __attribute__ ((destructor)) fini() {
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
// we only have one type of plugin
|
||
|
const LADSPA_Descriptor * ladspa_descriptor(unsigned long index) {
|
||
|
if (index != 0) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return descriptor;
|
||
|
}
|