#include #include "ladspa.h" #include #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 pitch_shifters; std::array 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(*plugin->pitch_shifter_ind)]; Effect *formant_shifter = plugin->formant_shifters[static_cast(*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; }