diff --git a/src/carla/carla_plugin.h b/src/carla/carla_plugin.h index ca5e8f7..692915f 100644 --- a/src/carla/carla_plugin.h +++ b/src/carla/carla_plugin.h @@ -33,6 +33,8 @@ #include #include +// TODO - check and try '#ifndef BUILD_BRIDGE' in all global_jack_client + #define CARLA_PROCESS_CONTINUE_CHECK if (m_id != plugin_id) { return callback_action(CALLBACK_DEBUG, plugin_id, m_id, 0, 0.0); } const unsigned short MAX_POST_EVENTS = 128; @@ -652,7 +654,7 @@ public: { } - virtual void set_program(uint32_t index, bool, bool osc_send, bool callback_send, bool) + virtual void set_program(int32_t index, bool, bool osc_send, bool callback_send, bool) { prog.current = index; @@ -683,7 +685,7 @@ public: callback_action(CALLBACK_PROGRAM_CHANGED, m_id, prog.current, 0, 0.0); } - virtual void set_midi_program(uint32_t index, bool, bool osc_send, bool callback_send, bool) + virtual void set_midi_program(int32_t index, bool, bool osc_send, bool callback_send, bool) { midiprog.current = index; @@ -754,11 +756,11 @@ public: callback_action(onoff ? CALLBACK_NOTE_ON : CALLBACK_NOTE_OFF, m_id, note, velo, 0.0); } - virtual void set_gui_data(int, void*) + virtual void set_gui_data(int /*data*/, void* /*ptr*/) { } - virtual void show_gui(bool) + virtual void show_gui(bool /*yesno*/) { } diff --git a/src/carla/dssi.cpp b/src/carla/dssi.cpp index 110ac65..66e085e 100644 --- a/src/carla/dssi.cpp +++ b/src/carla/dssi.cpp @@ -211,20 +211,23 @@ public: descriptor->set_custom_data(handle, chunk.data(), chunk.size()); } - virtual void set_midi_program(uint32_t index, bool gui_send, bool osc_send, bool callback_send, bool block) + virtual void set_midi_program(int32_t index, bool gui_send, bool osc_send, bool callback_send, bool block) { if (! descriptor->select_program) return; - // TODO - go for id -1 so we don't block audio - if (block) carla_proc_lock(); - descriptor->select_program(handle, midiprog.data[index].bank, midiprog.data[index].program); - if (block) carla_proc_unlock(); + if (index >= 0) + { + // TODO - go for id -1 so we don't block audio + if (block) carla_proc_lock(); + descriptor->select_program(handle, midiprog.data[index].bank, midiprog.data[index].program); + if (block) carla_proc_unlock(); #ifndef BUILD_BRIDGE - if (gui_send) - osc_send_program_as_midi(&osc.data, midiprog.data[index].bank, midiprog.data[index].program); + if (gui_send) + osc_send_program_as_midi(&osc.data, midiprog.data[index].bank, midiprog.data[index].program); #endif + } CarlaPlugin::set_midi_program(index, gui_send, osc_send, callback_send, block); } @@ -315,6 +318,7 @@ public: if (LADSPA_IS_PORT_AUDIO(PortType)) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); @@ -322,6 +326,7 @@ public: strncat(port_name, ldescriptor->PortNames[i], port_name_size/2); } else +#endif strncpy(port_name, ldescriptor->PortNames[i], port_name_size/2); if (LADSPA_IS_PORT_INPUT(PortType)) @@ -530,12 +535,14 @@ public: if (needs_cin) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); strcat(port_name, ":control-in"); } else +#endif strcpy(port_name, "control-in"); param.port_cin = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); @@ -543,12 +550,14 @@ public: if (needs_cout) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); strcat(port_name, ":control-out"); } else +#endif strcpy(port_name, "control-out"); param.port_cout = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); @@ -556,12 +565,14 @@ public: if (mins == 1) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); strcat(port_name, ":midi-in"); } else +#endif strcpy(port_name, "midi-in"); midi.port_min = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); diff --git a/src/carla/ladspa.cpp b/src/carla/ladspa.cpp index 074601f..2535dc2 100644 --- a/src/carla/ladspa.cpp +++ b/src/carla/ladspa.cpp @@ -335,6 +335,7 @@ public: if (LADSPA_IS_PORT_AUDIO(PortType)) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); @@ -342,6 +343,7 @@ public: strncat(port_name, descriptor->PortNames[i], port_name_size/2); } else +#endif strncpy(port_name, descriptor->PortNames[i], port_name_size/2); if (LADSPA_IS_PORT_INPUT(PortType)) @@ -549,12 +551,14 @@ public: if (needs_cin) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); strcat(port_name, ":control-in"); } else +#endif strcpy(port_name, "control-in"); param.port_cin = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); @@ -562,12 +566,14 @@ public: if (needs_cout) { +#ifndef BUILD_BRIDGE if (carla_options.global_jack_client) { strcpy(port_name, m_name); strcat(port_name, ":control-out"); } else +#endif strcpy(port_name, "control-out"); param.port_cout = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); diff --git a/src/carla/sf2.cpp b/src/carla/sf2.cpp index bd7b4eb..3a2fe03 100644 --- a/src/carla/sf2.cpp +++ b/src/carla/sf2.cpp @@ -15,6 +15,10 @@ * For a full copy of the GNU General Public License see the COPYING file */ +#ifdef BUILD_BRIDGE +#error You should not use bridge for soundfonts +#endif + #include "carla_plugin.h" #include @@ -261,9 +265,10 @@ public: CarlaPlugin::set_parameter_value(param_id, value, gui_send, osc_send, callback_send); } - virtual void set_midi_program(uint32_t index, bool gui_send, bool osc_send, bool callback_send, bool block) + virtual void set_midi_program(int32_t index, bool gui_send, bool osc_send, bool callback_send, bool block) { - fluid_synth_program_select(f_synth, 0, f_id, midiprog.data[index].bank, midiprog.data[index].program); + if (index >= 0) + fluid_synth_program_select(f_synth, 0, f_id, midiprog.data[index].bank, midiprog.data[index].program); CarlaPlugin::set_midi_program(index, gui_send, osc_send, callback_send, block); } @@ -1033,6 +1038,18 @@ public: m_active_before = m_active; } + virtual void delete_buffers() + { + qDebug("Sf2Plugin::delete_buffers() - start"); + + if (param.count > 0) + delete[] param_buffers; + + param_buffers = nullptr; + + qDebug("Sf2Plugin::delete_buffers() - end"); + } + bool init(const char* filename, const char* label) { f_id = fluid_synth_sfload(f_synth, filename, 0); diff --git a/src/carla/vst.cpp b/src/carla/vst.cpp index ddbee77..930ec91 100644 --- a/src/carla/vst.cpp +++ b/src/carla/vst.cpp @@ -17,6 +17,9 @@ #include "carla_plugin.h" +#include +#include + #define VST_FORCE_DEPRECATED 0 #include "aeffectx.h" @@ -39,6 +42,13 @@ #define kPlugCategGenerator 11 #endif +typedef AEffect* (*VST_Function)(audioMasterCallback); + +bool VstPluginCanDo(AEffect* effect, const char* feature) +{ + return (effect->dispatcher(effect, effCanDo, 0, 0, (void*)feature, 0.0f) == 1); +} + class VstPlugin : public CarlaPlugin { public: @@ -78,7 +88,6 @@ public: } } -#if 0 virtual PluginCategory category() { intptr_t VstCategory = effect->dispatcher(effect, effGetPlugCategory, 0, 0, nullptr, 0.0f); @@ -111,6 +120,16 @@ public: return effect->uniqueID; } + virtual int32_t chunk_data(void** data_ptr) + { + return effect->dispatcher(effect, effGetChunk, 0 /* bank */, 0, data_ptr, 0.0f); + } + + virtual double get_parameter_value(uint32_t param_id) + { + return effect->getParameter(effect, param_id); + } + virtual void get_label(char* buf_str) { effect->dispatcher(effect, effGetProductString, 0, 0, buf_str, 0.0f); @@ -130,7 +149,997 @@ public: { effect->dispatcher(effect, effGetEffectName, 0, 0, buf_str, 0.0f); } + + virtual void get_parameter_name(uint32_t param_id, char* buf_str) + { + effect->dispatcher(effect, effGetParamName, param_id, 0, buf_str, 0.0f); + } + + virtual void get_parameter_label(uint32_t param_id, char* buf_str) + { + effect->dispatcher(effect, effGetParamLabel, param_id, 0, buf_str, 0.0f); + } + + virtual void get_gui_info(GuiInfo* info) + { + if (effect->flags & effFlagsHasEditor) + info->type = GUI_INTERNAL_QT4; + else + info->type = GUI_NONE; + } + + virtual void set_parameter_value(uint32_t param_id, double value, bool gui_send, bool osc_send, bool callback_send) + { + effect->setParameter(effect, param_id, value); + CarlaPlugin::set_parameter_value(param_id, value, gui_send, osc_send, callback_send); + } + + void set_chunk_data(const char* string_data) + { + QByteArray chunk = QByteArray::fromBase64(string_data); + effect->dispatcher(effect, effSetChunk, 0 /* bank */, chunk.size(), chunk.data(), 0.0f); + } + + virtual void set_program(int32_t index, bool gui_send, bool osc_send, bool callback_send, bool block) + { + // TODO - go for id -1 so we don't block audio + if (index >= 0) + { + if (block) carla_proc_lock(); + effect->dispatcher(effect, effSetProgram, 0, index, nullptr, 0.0f); + if (block) carla_proc_unlock(); + } + + CarlaPlugin::set_program(index, gui_send, osc_send, callback_send, block); + } + + virtual void set_gui_data(int data, void* ptr) + { + if (effect->dispatcher(effect, effEditOpen, 0, data, (void*)((QDialog*)ptr)->winId(), 0.0f) == 1) + { +#ifndef ERect + struct ERect { + short top; + short left; + short bottom; + short right; + }; +#endif + ERect* vst_rect; + + if (effect->dispatcher(effect, effEditGetRect, 0, 0, &vst_rect, 0.0f)) + { + int width = vst_rect->right - vst_rect->left; + int height = vst_rect->bottom - vst_rect->top; + + if (width <= 0 || height <= 0) + { + qCritical("Failed to get proper Plugin Window size"); + return; + } + + // TODO + //gui.width = width; + //gui.height = height; + } + else + qCritical("Failed to get Plugin Window size"); + } + else + { + // failed to open UI + m_hints &= ~PLUGIN_HAS_GUI; + callback_action(CALLBACK_SHOW_GUI, m_id, -1, 0, 0.0); + + effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0.0f); + } + } + + virtual void show_gui(bool /*yesno*/) + { + // TODO +#if 0 + gui.visible = yesno; + + if (gui.visible && gui.width > 0 && gui.height > 0) + callback_action(CALLBACK_RESIZE_GUI, id, gui.width, gui.height, 0.0); +#endif + } + + virtual void idle_gui() + { + //effect->dispatcher(effect, effIdle, 0, 0, nullptr, 0.0f); + + // TODO + //if (gui.visible) + effect->dispatcher(effect, effEditIdle, 0, 0, nullptr, 0.0f); + } + + virtual void reload() + { + qDebug("DssiPlugin::reload()"); + short _id = m_id; + + // Safely disable plugin for reload + carla_proc_lock(); + m_id = -1; + carla_proc_unlock(); + + // Unregister previous jack ports if needed + if (_id >= 0) + remove_from_jack(); + + // Delete old data + delete_buffers(); + + uint32_t ains, aouts, mins, mouts, params, j; + ains = aouts = mins = mouts = params = 0; + + ains = effect->numInputs; + aouts = effect->numOutputs; + params = effect->numParams; + + if (VstPluginCanDo(effect, "receiveVstEvents") || VstPluginCanDo(effect, "receiveVstMidiEvent") || effect->flags & effFlagsIsSynth) + mins = 1; + + if (VstPluginCanDo(effect, "sendVstEvents") || VstPluginCanDo(effect, "sendVstMidiEvent")) + mouts = 1; + + if (ains > 0) + ain.ports = new jack_port_t*[ains]; + + if (aouts > 0) + aout.ports = new jack_port_t*[aouts]; + + if (params > 0) + { + param.data = new ParameterData[params]; + param.ranges = new ParameterRanges[params]; + } + + const int port_name_size = jack_port_name_size(); + char port_name[port_name_size]; + bool needs_cin = (aouts > 0 || params > 0); // TODO - try to apply this <- to others + + for (j=0; jdispatcher(effect, effGetParameterProperties, j, 0, &prop, 0)) + { + if (prop.flags & kVstParameterUsesIntegerMinMax) + { + min = prop.minInteger; + max = prop.maxInteger; + } + else + { + min = 0.0; + max = 1.0; + } + + if (prop.flags & kVstParameterUsesIntStep) + { + step = prop.stepInteger; + step_small = prop.stepInteger; + step_large = prop.largeStepInteger; + } + else if (prop.flags & kVstParameterUsesFloatStep) + { + step = prop.stepFloat; + step_small = prop.smallStepFloat; + step_large = prop.largeStepFloat; + } + else if (prop.flags & kVstParameterIsSwitch) + { + step = max - min; + step_small = step; + step_large = step; + } + else + { + double range = max - min; + step = range/100.0; + step_small = range/1000.0; + step_large = range/10.0; + } + } + else + { + min = 0.0; + max = 1.0; + step = 0.001; + step_small = 0.0001; + step_large = 0.1; + } + + if (min > max) + max = min; + else if (max < min) + min = max; + + // no such thing as VST default parameters + def = effect->getParameter(effect, j); + + if (def < min) + def = min; + else if (def > max) + def = max; + + if (max - min == 0.0) + { + qWarning("Broken plugin parameter -> max - min == 0"); + max = min + 0.1; + } + + param.data[j].type = PARAMETER_INPUT; + param.ranges[j].min = min; + param.ranges[j].max = max; + param.ranges[j].def = def; + param.ranges[j].step = step; + param.ranges[j].step_small = step_small; + param.ranges[j].step_large = step_large; + + // hints + param.data[j].hints |= PARAMETER_IS_ENABLED; + + if (effect->dispatcher(effect, effCanBeAutomated, j, 0, nullptr, 0.0f) == 1) + param.data[j].hints |= PARAMETER_IS_AUTOMABLE; + } + + if (needs_cin) + { +#ifndef BUILD_BRIDGE + if (carla_options.global_jack_client) + { + strcpy(port_name, m_name); + strcat(port_name, ":control-in"); + } + else #endif + strcpy(port_name, "control-in"); + + param.port_cin = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); + } + + if (mins == 1) + { +#ifndef BUILD_BRIDGE + if (carla_options.global_jack_client) + { + strcpy(port_name, m_name); + strcat(port_name, ":midi-in"); + } + else +#endif + strcpy(port_name, "midi-in"); + + midi.port_min = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); + } + + if (mouts == 1) + { +#ifndef BUILD_BRIDGE + if (carla_options.global_jack_client) + { + strcpy(port_name, m_name); + strcat(port_name, ":midi-in"); + } + else +#endif + strcpy(port_name, "midi-in"); + + midi.port_mout = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + } + + ain.count = ains; + aout.count = aouts; + param.count = params; + + reload_programs(true); + + // plugin checks + m_hints &= ~(PLUGIN_IS_SYNTH | PLUGIN_USES_CHUNKS | PLUGIN_CAN_DRYWET | PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE); + + intptr_t VstCategory = effect->dispatcher(effect, effGetPlugCategory, 0, 0, nullptr, 0.0f); + + if (VstCategory == kPlugCategSynth || VstCategory == kPlugCategGenerator) + m_hints |= PLUGIN_IS_SYNTH; + + if (effect->flags & effFlagsProgramChunks) + m_hints |= PLUGIN_USES_CHUNKS; + + if (aouts > 0 && (ains == aouts || ains == 1)) + m_hints |= PLUGIN_CAN_DRYWET; + + if (aouts > 0) + m_hints |= PLUGIN_CAN_VOLUME; + + if (aouts >= 2 && aouts%2 == 0) + m_hints |= PLUGIN_CAN_BALANCE; + + carla_proc_lock(); + m_id = _id; + carla_proc_unlock(); + + if (carla_options.global_jack_client == false) + jack_activate(jack_client); + } + + virtual void reload_programs(bool init) + { + qDebug("VstPlugin::reload_programs(%s)", bool2str(init)); + uint32_t i, old_count = prog.count; + + // Delete old programs + if (prog.count > 0) + { + for (uint32_t i=0; i < prog.count; i++) + free((void*)prog.names[i]); + + delete[] prog.names; + } + + prog.count = 0; + prog.names = nullptr; + + // Query new programs + prog.count = effect->numPrograms; + + if (prog.count > 0) + prog.names = new const char* [prog.count]; + + // Update names + for (i=0; i < prog.count; i++) + { + char buf_str[STR_MAX] = { 0 }; + if (effect->dispatcher(effect, effGetProgramNameIndexed, i, 0, buf_str, 0.0f) != 1) + { + // program will be [re-]changed later + effect->dispatcher(effect, effSetProgram, 0, i, nullptr, 0.0f); + effect->dispatcher(effect, effGetProgramName, 0, 0, buf_str, 0.0f); + } + prog.names[i] = strdup(buf_str); + } + + // Update OSC Names + // etc etc + + callback_action(CALLBACK_RELOAD_PROGRAMS, m_id, 0, 0, 0.0); + + if (init) + { + if (prog.count > 0) + set_program(0, false, false, false, true); + } + else + { + callback_action(CALLBACK_UPDATE, m_id, 0, 0, 0.0); + + // Check if current program is invalid + bool program_changed = false; + + if (prog.count == old_count+1) + { + // one program added, probably created by user + prog.current = old_count; + program_changed = true; + } + else if (prog.current >= (int32_t)prog.count) + { + // current program > count + prog.current = 0; + program_changed = true; + } + else if (prog.current < 0 && prog.count > 0) + { + // programs exist now, but not before + prog.current = 0; + program_changed = true; + } + else if (prog.current >= 0 && prog.count == 0) + { + // programs existed before, but not anymore + prog.current = -1; + program_changed = true; + } + + if (program_changed) + set_program(prog.current, true, true, true, true); + else + { + // Program was changed during update, re-set it + if (prog.current >= 0) + effect->dispatcher(effect, effSetProgram, 0, prog.current, nullptr, 0.0f); + } + } + } + + virtual void process(jack_nframes_t nframes) + { + uint32_t i, k; + unsigned short plugin_id = m_id; + uint32_t midi_event_count = 0; + + double ains_peak_tmp[2] = { 0.0 }; + double aouts_peak_tmp[2] = { 0.0 }; + + jack_default_audio_sample_t* ains_buffer[ain.count]; + jack_default_audio_sample_t* aouts_buffer[aout.count]; + void* min_buffer = nullptr; + void* mout_buffer = nullptr; + + for (i=0; i < ain.count; i++) + ains_buffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(ain.ports[i], nframes); + + for (i=0; i < aout.count; i++) + aouts_buffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(aout.ports[i], nframes); + + if (midi.port_min > 0) + min_buffer = jack_port_get_buffer(midi.port_min, nframes); + + if (midi.port_mout > 0) + mout_buffer = jack_port_get_buffer(midi.port_mout, nframes); + + // -------------------------------------------------------------------------------------------------------- + // Input VU + + if (ain.count > 0) + { + short j2 = (ain.count == 1) ? 0 : 1; + + for (k=0; k ains_peak_tmp[0]) + ains_peak_tmp[0] = abs_d(ains_buffer[0][k]); + if (abs_d(ains_buffer[j2][k]) > ains_peak_tmp[1]) + ains_peak_tmp[1] = abs_d(ains_buffer[j2][k]); + } + } + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // Parameters Input [Automation] + + if (param.port_cin) + { + jack_default_audio_sample_t* pin_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cin, nframes); + + jack_midi_event_t pin_event; + uint32_t n_pin_events = jack_midi_get_event_count(pin_buffer); + + for (i=0; i 0) + { + // Dry/Wet (using '0x09', undefined) + set_drywet(velo_per, false, false); + postpone_event(PostEventParameterChange, PARAMETER_DRYWET, velo_per); + } + else if (status == 0x07 && (m_hints & PLUGIN_CAN_VOLUME) > 0) + { + // Volume + value = double(velo)/100; + set_volume(value, false, false); + postpone_event(PostEventParameterChange, PARAMETER_VOLUME, value); + } + else if (status == 0x08 && (m_hints & PLUGIN_CAN_BALANCE) > 0) + { + // Balance + double left, right; + value = (double(velo)-63.5)/63.5; + + if (value < 0) + { + left = -1.0; + right = (value*2)+1.0; + } + else if (value > 0) + { + left = (value*2)-1.0; + right = 1.0; + } + else + { + left = -1.0; + right = 1.0; + } + + set_balance_left(left, false, false); + set_balance_right(right, false, false); + postpone_event(PostEventParameterChange, PARAMETER_BALANCE_LEFT, left); + postpone_event(PostEventParameterChange, PARAMETER_BALANCE_RIGHT, right); + } + } + + // Control plugin parameters + for (k=0; k < param.count; k++) + { + if (param.data[k].type == PARAMETER_INPUT && (param.data[k].hints & PARAMETER_IS_AUTOMABLE) > 0 && + param.data[k].midi_channel == channel && param.data[k].midi_cc == status) + { + value = (velo_per * (param.ranges[k].max - param.ranges[k].min)) + param.ranges[k].min; + set_parameter_value(k, value, false, false, false); + postpone_event(PostEventParameterChange, k, value); + } + } + } + // Program change + else if (mode == 0xC0) + { + uint32_t prog_id = pin_event.buffer[1] & 0x7F; + + if (prog_id < prog.count) + { + set_program(prog_id, false, false, false, false); + postpone_event(PostEventMidiProgramChange, prog_id, 0.0); + } + } + } + } // End of Parameters Input + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // MIDI Input (External) + + if (midi.port_min) + { + carla_midi_lock(); + + for (i=0; i < MAX_MIDI_EVENTS && midi_event_count < MAX_MIDI_EVENTS; i++) + { + if (ext_midi_notes[i].valid) + { + ExternalMidiNote* enote = &ext_midi_notes[i]; + enote->valid = false; + + VstMidiEvent* midi_event = &midi_events[midi_event_count]; + memset(midi_event, 0, sizeof(VstMidiEvent)); + + midi_event->type = kVstMidiType; + midi_event->byteSize = sizeof(VstMidiEvent); + midi_event->midiData[0] = enote->onoff ? 0x90 : 0x80; + midi_event->midiData[1] = enote->note; + midi_event->midiData[2] = enote->velo; + + midi_event_count += 1; + } + else + break; + } + + carla_midi_unlock(); + + } // End of MIDI Input (External) + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // MIDI Input (JACK) + + if (midi.port_min) + { + jack_midi_event_t min_event; + uint32_t n_min_events = jack_midi_get_event_count(min_buffer); + + for (k=0; k < n_min_events && midi_event_count < MAX_MIDI_EVENTS; k++) + { + if (jack_midi_event_get(&min_event, min_buffer, k) != 0) + break; + + if (min_event.size != 3) + continue; + + unsigned char channel = min_event.buffer[0] & 0x0F; + unsigned char mode = min_event.buffer[0] & 0xF0; + unsigned char note = min_event.buffer[1] & 0x7F; + unsigned char velo = min_event.buffer[2] & 0x7F; + + // fix bad note off + if (mode == 0x90 && velo == 0) + { + mode = 0x80; + velo = 64; + } + + VstMidiEvent* midi_event = &midi_events[midi_event_count]; + memset(midi_event, 0, sizeof(VstMidiEvent)); + + if (mode == 0x80 || mode == 0x90) + { + midi_event->type = kVstMidiType; + midi_event->byteSize = sizeof(VstMidiEvent); + midi_event->deltaFrames = min_event.time; + midi_event->midiData[0] = mode+channel; + midi_event->midiData[1] = note; + midi_event->midiData[2] = velo; + + if (mode == 0x90) + postpone_event(PostEventNoteOn, note, velo); + else + postpone_event(PostEventNoteOff, note, 0.0); + } + // TODO - more types, but not status + + midi_event_count += 1; + } + } // End of MIDI Input (JACK) + + // VST Events + if (midi_event_count > 0) + { + events.numEvents = midi_event_count; + events.reserved = 0; + effect->dispatcher(effect, effProcessEvents, 0, 0, &events, 0.0f); + } + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // Plugin processing + + if (m_active) + { + if (m_active_before == false) + { + effect->dispatcher(effect, effMainsChanged, 0, 1, nullptr, 0.0f); + effect->dispatcher(effect, effStartProcess, 0, 0, nullptr, 0.0f); + } + + if (effect->flags & effFlagsCanReplacing) + { + effect->processReplacing(effect, ains_buffer, aouts_buffer, nframes); + } + else + { + for (i=0; i < aout.count; i++) + memset(aouts_buffer[i], 0, sizeof(jack_default_audio_sample_t)*nframes); + + // FIXME - missing macro check + effect->process(effect, ains_buffer, aouts_buffer, nframes); + } + } + else + { + if (m_active_before) + { + effect->dispatcher(effect, effStopProcess, 0, 0, nullptr, 0.0f); + effect->dispatcher(effect, effMainsChanged, 0, 0, nullptr, 0.0f); + } + } + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // Post-processing (dry/wet, volume and balance) + + if (m_active) + { + double bal_rangeL, bal_rangeR; + jack_default_audio_sample_t old_bal_left[nframes]; + + for (i=0; i < aout.count; i++) + { + // Dry/Wet and Volume + for (k=0; k 0 && x_drywet != 1.0) + { + if (aout.count == 1) + aouts_buffer[i][k] = (aouts_buffer[i][k]*x_drywet)+(ains_buffer[0][k]*(1.0-x_drywet)); + else + aouts_buffer[i][k] = (aouts_buffer[i][k]*x_drywet)+(ains_buffer[i][k]*(1.0-x_drywet)); + } + + if (m_hints & PLUGIN_CAN_VOLUME) + aouts_buffer[i][k] *= x_vol; + } + + // Balance + if (m_hints & PLUGIN_CAN_BALANCE) + { + if (i%2 == 0) + memcpy(&old_bal_left, aouts_buffer[i], sizeof(jack_default_audio_sample_t)*nframes); + + bal_rangeL = (x_bal_left+1.0)/2; + bal_rangeR = (x_bal_right+1.0)/2; + + for (k=0; k aouts_peak_tmp[i]) + aouts_peak_tmp[i] = abs_d(aouts_buffer[i][k]); + } + } + } + } + else + { + // disable any output sound if not active + for (i=0; i < aout.count; i++) + memset(aouts_buffer[i], 0.0f, sizeof(jack_default_audio_sample_t)*nframes); + + aouts_peak_tmp[0] = 0.0; + aouts_peak_tmp[1] = 0.0; + + } // End of Post-processing + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // MIDI Output + if (midi.port_mout) + { + jack_midi_clear_buffer(mout_buffer); + + // TODO - read jack ringuffer events + //jack_midi_event_write(mout_buffer, midi_event->deltaFrames, (unsigned char*)midi_event->midiData, midi_event->byteSize); + + } // End of MIDI Output + + CARLA_PROCESS_CONTINUE_CHECK; + + // -------------------------------------------------------------------------------------------------------- + // Peak Values + + ains_peak[(plugin_id*2)+0] = ains_peak_tmp[0]; + ains_peak[(plugin_id*2)+1] = ains_peak_tmp[1]; + aouts_peak[(plugin_id*2)+0] = aouts_peak_tmp[0]; + aouts_peak[(plugin_id*2)+1] = aouts_peak_tmp[1]; + + m_active_before = m_active; + } + + virtual void buffer_size_changed(jack_nframes_t new_buffer_size) + { + if (m_active) + { + effect->dispatcher(effect, effStopProcess, 0, 0, nullptr, 0.0f); + effect->dispatcher(effect, effMainsChanged, 0, 0, nullptr, 0.0f); + } + + effect->dispatcher(effect, effSetBlockSize, 0, new_buffer_size, nullptr, 0.0f); + + if (m_active) + { + effect->dispatcher(effect, effMainsChanged, 0, 1, nullptr, 0.0f); + effect->dispatcher(effect, effStartProcess, 0, 0, nullptr, 0.0f); + } + } + + bool init(const char* filename, const char* label) + { + if (lib_open(filename)) + { + VST_Function vstfn = (VST_Function)lib_symbol("VSTPluginMain"); + + if (vstfn == nullptr) + { + if (vstfn == nullptr) + { +#ifdef TARGET_API_MAC_CARBON + + vstfn = (VST_Function)lib_symbol("main_macho"); + if (vstfn == nullptr) +#endif + vstfn = (VST_Function)lib_symbol("main"); + } + } + + if (vstfn) + { + effect = vstfn(VstHostCallback); + + if (effect && effect->magic == kEffectMagic) + { + m_filename = strdup(filename); + + char buf_str[STR_MAX] = { 0 }; + effect->dispatcher(effect, effGetEffectName, 0, 0, buf_str, 0.0f); + + if (buf_str[0] != 0) + m_name = get_unique_name(buf_str); + else + m_name = get_unique_name(label); + + // Init plugin + effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f); +#if !VST_FORCE_DEPRECATED + effect->dispatcher(effect, effSetBlockSizeAndSampleRate, 0, get_buffer_size(), nullptr, get_sample_rate()); +#endif + effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr, get_sample_rate()); + effect->dispatcher(effect, effSetBlockSize, 0, get_buffer_size(), nullptr, 0.0f); + effect->dispatcher(effect, effSetProcessPrecision, 0, kVstProcessPrecision32, nullptr, 0.0f); + effect->user = this; + + if (carla_jack_register_plugin(this, &jack_client)) + return true; + else + set_last_error("Failed to register plugin in JACK"); + } + else + set_last_error("Plugin failed to initialize"); + } + else + set_last_error("Could not find the VST main entry in the plugin library"); + } + else + set_last_error(lib_error()); + + return false; + } + + static intptr_t VstHostCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt) + { + qDebug("VstHostCallback() - code: %02i, index: %02i, value: " P_INTPTR ", opt: %03f", opcode, index, value, opt); + + switch (opcode) + { + case audioMasterAutomate: + if (effect) + effect->setParameter(effect, index, opt); + break; + + case audioMasterVersion: + return kVstVersion; + + //case audioMasterCurrentId: + // return VstCurrentUniqueId; + + case audioMasterGetTime: + static VstTimeInfo timeInfo; + memset(&timeInfo, 0, sizeof(VstTimeInfo)); + timeInfo.sampleRate = get_sample_rate(); + return (intptr_t)&timeInfo; + + case audioMasterGetSampleRate: + return get_sample_rate(); + + case audioMasterGetBlockSize: + return get_buffer_size(); + + case audioMasterGetVendorString: + if (ptr) + strcpy((char*)ptr, "falkTX"); + break; + + case audioMasterGetProductString: + if (ptr) + strcpy((char*)ptr, "Carla-Discovery"); + break; + + case audioMasterGetVendorVersion: + return 0x05; // 0.5 + + case audioMasterCanDo: +#if DEBUG + qDebug("VstHostCallback:audioMasterCanDo - %s", (char*)ptr); +#endif + + if (strcmp((char*)ptr, "sendVstEvents") == 0) + return 1; + else if (strcmp((char*)ptr, "sendVstMidiEvent") == 0) + return 1; + else if (strcmp((char*)ptr, "sendVstTimeInfo") == 0) + return 1; + else if (strcmp((char*)ptr, "receiveVstEvents") == 0) + return 1; + else if (strcmp((char*)ptr, "receiveVstMidiEvent") == 0) + return 1; +#if !VST_FORCE_DEPRECATED + else if (strcmp((char*)ptr, "receiveVstTimeInfo") == 0) + return -1; +#endif + else if (strcmp((char*)ptr, "reportConnectionChanges") == 0) + return 1; + else if (strcmp((char*)ptr, "acceptIOChanges") == 0) + return 1; + else if (strcmp((char*)ptr, "sizeWindow") == 0) + return 1; + else if (strcmp((char*)ptr, "offline") == 0) + return -1; + else if (strcmp((char*)ptr, "openFileSelector") == 0) + return -1; + else if (strcmp((char*)ptr, "closeFileSelector") == 0) + return -1; + else if (strcmp((char*)ptr, "startStopProcess") == 0) + return 1; + else if (strcmp((char*)ptr, "shellCategory") == 0) + return 1; + else if (strcmp((char*)ptr, "sendVstMidiEventFlagIsRealtime") == 0) + return -1; + else + { + qWarning("VstHostCallback:audioMasterCanDo - Got uninplemented feature request '%s'", (char*)ptr); + return 0; + } + + case audioMasterGetLanguage: + return kVstLangEnglish; + + default: + break; + } + + return 0; + } private: AEffect* effect; @@ -150,8 +1159,25 @@ short add_plugin_vst(const char* filename, const char* label) if (id >= 0) { - set_last_error("Not implemented yet"); - id = -1; + VstPlugin* plugin = new VstPlugin; + + if (plugin->init(filename, label)) + { + plugin->reload(); + plugin->set_id(id); + + unique_names[id] = plugin->name(); + CarlaPlugins[id] = plugin; + +#ifndef BUILD_BRIDGE + //osc_new_plugin(plugin); +#endif + } + else + { + delete plugin; + id = -1; + } } else set_last_error("Maximum number of plugins reached");