Lv2Plugin::~Lv2Plugin () { deactivate(); slv2_instance_free( m_instance ); slv2_value_free( m_name ); slv2_value_free( m_authorName ); slv2_value_free( m_authorEmail ); slv2_value_free( m_authorHomepage ); for (int i=0; i < m_ports.count(); ++i) { delete m_ports[i]; } m_instance = NULL; }
/* private */ void slv2_port_free(SLV2Port port) { slv2_values_free(port->classes); slv2_value_free(port->symbol); free(port); }
static void gst_lv2_base_init (gpointer g_class) { GstLV2Class *klass = (GstLV2Class *) g_class; GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class); GstElementDetails *details; SLV2Plugin lv2plugin; SLV2Value val; guint j, audio_in_count, audio_out_count, control_in_count, control_out_count; gchar *klass_tags; GST_DEBUG ("base_init %p", g_class); lv2plugin = (SLV2Plugin) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), GST_SLV2_PLUGIN_QDATA); g_assert (lv2plugin); /* pad templates */ gsp_class->num_audio_in = 0; gsp_class->num_audio_out = 0; /* properties */ gsp_class->num_control_in = 0; gsp_class->num_control_out = 0; for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) { SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j); if (slv2_port_is_a (lv2plugin, port, audio_class)) { gchar *name = g_strdup (slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port))); GST_DEBUG ("LV2 port name: \"%s\"", name); if (slv2_port_is_a (lv2plugin, port, input_class)) gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK, gsp_class->num_audio_in++); else if (slv2_port_is_a (lv2plugin, port, output_class)) gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SRC, gsp_class->num_audio_out++); /* TODO: else ignore plugin */ g_free (name); } else if (slv2_port_is_a (lv2plugin, port, control_class)) { if (slv2_port_is_a (lv2plugin, port, input_class)) gsp_class->num_control_in++; else if (slv2_port_is_a (lv2plugin, port, output_class)) gsp_class->num_control_out++; /* TODO: else ignore plugin */ } /* TODO: else ignore plugin */ } /* construct the element details struct */ details = g_new0 (GstElementDetails, 1); val = slv2_plugin_get_name (lv2plugin); if (val) { details->longname = g_strdup (slv2_value_as_string (val)); slv2_value_free (val); } else { details->longname = g_strdup ("no description available"); } details->description = details->longname; val = slv2_plugin_get_author_name (lv2plugin); if (val) { details->author = g_strdup (slv2_value_as_string (val)); slv2_value_free (val); } else { details->author = g_strdup ("no author available"); } if (gsp_class->num_audio_in == 0) klass_tags = "Source/Audio/LV2"; else if (gsp_class->num_audio_out == 0) { if (gsp_class->num_control_out == 0) klass_tags = "Sink/Audio/LV2"; else klass_tags = "Sink/Analyzer/Audio/LV2"; } else klass_tags = "Filter/Effect/Audio/LV2"; details->klass = klass_tags; GST_INFO ("tags : %s", details->klass); gst_element_class_set_details (element_class, details); g_free (details->longname); g_free (details->author); g_free (details); klass->audio_in_portnums = g_new0 (gint, gsp_class->num_audio_in); klass->audio_out_portnums = g_new0 (gint, gsp_class->num_audio_out); klass->control_in_portnums = g_new0 (gint, gsp_class->num_control_in); klass->control_out_portnums = g_new0 (gint, gsp_class->num_control_out); audio_in_count = audio_out_count = control_in_count = control_out_count = 0; for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) { SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j); gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class); if (slv2_port_is_a (lv2plugin, port, audio_class)) { if (is_input) klass->audio_in_portnums[audio_in_count++] = j; else klass->audio_out_portnums[audio_out_count++] = j; } else if (slv2_port_is_a (lv2plugin, port, control_class)) { if (is_input) klass->control_in_portnums[control_in_count++] = j; else klass->control_out_portnums[control_out_count++] = j; } } g_assert (audio_in_count == gsp_class->num_audio_in); g_assert (audio_out_count == gsp_class->num_audio_out); g_assert (control_in_count == gsp_class->num_control_in); g_assert (control_out_count == gsp_class->num_control_out); /*if (!LV2_IS_INPLACE_BROKEN (desc->Properties)) GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass); */ klass->plugin = lv2plugin; }
LV2EffectDialog::LV2EffectDialog(LV2Effect *eff, wxWindow * parent, SLV2Plugin data, int sampleRate, double length, double noteLength, unsigned char noteVelocity, unsigned char noteKey) :wxDialog(parent, -1, LAT1CTOWX(slv2_value_as_string(slv2_plugin_get_name(data))), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), effect(eff), mControls(eff->GetControls()) { mLength = length; this->mData = data; this->sampleRate = sampleRate; #ifdef __WXMSW__ // On Windows, for some reason, wxWindows calls OnTextCtrl during creation // of the text control, and LV2EffectDialog::OnTextCtrl calls HandleText, // which assumes all the fields have been initialized. // This can give us a bad pointer crash, so manipulate inSlider to // no-op HandleText during creation. inSlider = true; #else inSlider = false; #endif inText = false; // Allocate memory for the user parameter controls toggles = new wxCheckBox*[mControls.size()]; sliders = new wxSlider*[mControls.size()]; fields = new wxTextCtrl*[mControls.size()]; labels = new wxStaticText*[mControls.size()]; wxControl *item; wxBoxSizer *vSizer = new wxBoxSizer(wxVERTICAL); // Add information about the plugin SLV2Value tmpValue = slv2_plugin_get_author_name(data); if (tmpValue) { const char* author = slv2_value_as_string(tmpValue); item = new wxStaticText(this, 0, wxString(_("Author: "))+LAT1CTOWX(author)); vSizer->Add(item, 0, wxALL, 5); slv2_value_free(tmpValue); } wxScrolledWindow *w = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL | wxTAB_TRAVERSAL); // Try to give the window a sensible default/minimum size w->SetMinSize(wxSize( wxMax(600, parent->GetSize().GetWidth() * 2/3), parent->GetSize().GetHeight() / 2)); w->SetScrollRate(0, 20); vSizer->Add(w, 1, wxEXPAND|wxALL, 5); // Preview, OK, & Cancel buttons vSizer->Add(CreateStdButtonSizer(this, ePreviewButton|eCancelButton|eOkButton), 0, wxEXPAND); SetSizer(vSizer); wxSizer *paramSizer = new wxStaticBoxSizer(wxVERTICAL, w, _("Effect Settings")); wxFlexGridSizer *gridSizer = new wxFlexGridSizer(5, 0, 0); gridSizer->AddGrowableCol(3); const LV2PortGroup& rootGroup = eff->GetPortGroups(); const ScalePointMap& scalePoints = eff->GetScalePoints(); // Now add the length control if (effect->GetEffectFlags() & INSERT_EFFECT) { item = new wxStaticText(w, 0, _("Length (seconds)")); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); mSeconds = new wxTextCtrl(w, LADSPA_SECONDS_ID, Internat::ToDisplayString(length)); mSeconds->SetName(_("Length (seconds)")); gridSizer->Add(mSeconds, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); ConnectFocus(mSeconds); } // The note controls if the plugin is a synth if (effect->IsSynth()) { // Note length control item = new wxStaticText(w, 0, _("Note length (seconds)")); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); mNoteSeconds = new wxTextCtrl(w, LADSPA_SECONDS_ID, Internat::ToDisplayString(length / 2)); mNoteSeconds->SetName(_("Note length (seconds)")); gridSizer->Add(mNoteSeconds, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); ConnectFocus(mNoteSeconds); // Note velocity control item = new wxStaticText(w, 0, _("Note velocity")); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); mNoteVelocity = new wxTextCtrl(w, LADSPA_SECONDS_ID, Internat::ToDisplayString(64)); mNoteVelocity->SetName(_("Note velocity")); gridSizer->Add(mNoteVelocity, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); ConnectFocus(mNoteVelocity); // Note key control item = new wxStaticText(w, 0, _("Note key")); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); mNoteKey = new wxTextCtrl(w, LADSPA_SECONDS_ID, Internat::ToDisplayString(64)); mNoteKey->SetName(_("Note key")); gridSizer->Add(mNoteKey, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); ConnectFocus(mNoteKey); } paramSizer->Add(gridSizer, 1, wxEXPAND | wxALL, 5); // Create user parameter controls std::queue<const LV2PortGroup*> groups; groups.push(&rootGroup); while (!groups.empty()) { const LV2PortGroup* pg = groups.front(); groups.pop(); if (pg->GetName() != wxT("")) { wxSizer *groupSizer = new wxStaticBoxSizer(wxVERTICAL, w, pg->GetName()); paramSizer->Add(groupSizer, 0, wxEXPAND | wxALL, 5); gridSizer = new wxFlexGridSizer(5, 0, 0); gridSizer->AddGrowableCol(3); groupSizer->Add(gridSizer, 1, wxEXPAND | wxALL, 5); } std::vector<LV2PortGroup>::const_iterator iter; for (iter = pg->GetSubGroups().begin(); iter != pg->GetSubGroups().end(); ++iter) { groups.push(&*iter); } const std::vector<uint32_t>& params = pg->GetParameters(); for (uint32_t k = 0; k < params.size(); ++k) { uint32_t p = params[k]; wxString labelText = mControls[p].mName; item = new wxStaticText(w, 0, labelText + wxT(":")); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); wxString fieldText; if (mControls[p].mToggle) { toggles[p] = new wxCheckBox(w, p, wxT("")); toggles[p]->SetName(labelText); toggles[p]->SetValue(mControls[p].mControlBuffer > 0); gridSizer->Add(toggles[p], 0, wxALL, 5); ConnectFocus(toggles[p]); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); } else { if (mControls[p].mInteger) fieldText.Printf(wxT("%d"), (int)(mControls[p].mControlBuffer + 0.5)); else fieldText = Internat::ToDisplayString(mControls[p].mControlBuffer); fields[p] = new wxTextCtrl(w, p, fieldText); fields[p]->SetName(labelText); gridSizer->Add(fields[p], 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); ConnectFocus(fields[p]); wxString bound; double lower = 0.0; double upper = 0.0; bool haslo = false; bool hashi = false; bool forceint = false; wxString loLabel; wxString hiLabel; ScalePointMap::const_iterator iter = scalePoints.find(mControls[p].mIndex); if (!std::isnan(mControls[p].mMin)) { lower = mControls[p].mMin; haslo = true; if (iter != scalePoints.end()) { std::map<float, wxString>::const_iterator iter2 = iter->second.find(lower); if (iter2 != iter->second.end()) { loLabel = iter2->second; } } } if (!std::isnan(mControls[p].mMax)) { upper = mControls[p].mMax; hashi = true; if (iter != scalePoints.end()) { std::map<float, wxString>::const_iterator iter2 = iter->second.find(upper); if (iter2 != iter->second.end()) hiLabel = iter2->second; } } if (mControls[p].mSampleRate) { lower *= sampleRate * 1000; upper *= sampleRate; forceint = true; } wxString str; if (haslo) { str = loLabel; if (str.IsEmpty()) { if (mControls[p].mInteger || forceint) str.Printf(wxT("%d"), (int)(lower + 0.5)); else str = Internat::ToDisplayString(lower); } item = new wxStaticText(w, 0, str); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); } else { gridSizer->Add(1, 1, 0); } sliders[p] = new wxSlider(w, p, 0, 0, 1000, wxDefaultPosition, wxSize(200, -1)); sliders[p]->SetName(labelText); gridSizer->Add(sliders[p], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, 5); ConnectFocus(sliders[p]); if (hashi) { str = hiLabel; if (str.IsEmpty()) { if (mControls[p].mInteger || forceint) str.Printf(wxT("%d"), (int)(upper + 0.5)); else str = Internat::ToDisplayString(upper); } item = new wxStaticText(w, 0, str); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 5); } else { gridSizer->Add(1, 1, 0); } } } } // Set all of the sliders based on the value in the // text fields inSlider = false; // Now we're ready for HandleText to actually do something. HandleText(); w->SetSizer(paramSizer); Layout(); Fit(); SetSizeHints(GetSize()); }
LV2Effect::LV2Effect(SLV2Plugin data, const std::set<wxString>& categories) : mValid(true), mCategories(categories), mMidiInput(0), mScalePointsRetrieved(false), mPortGroupsRetrieved(false) { // We don't support any features at all, so if the plugin requires // any we skip it. SLV2Values req = slv2_plugin_get_required_features(data); size_t nFeatures = slv2_values_size(req); slv2_values_free(req); if (nFeatures > 0) { mValid = false; return; } mData = data; pluginName = wxString::FromUTF8(slv2_value_as_string(slv2_plugin_get_name(mData))); fInBuffer = NULL; fOutBuffer = NULL; mLength = 0; uint32_t p; // Allocate buffers for the port indices and the default control values uint32_t numPorts = slv2_plugin_get_num_ports(mData); float* minimumValues = new float [numPorts]; float* maximumValues = new float [numPorts]; float* defaultValues = new float [numPorts]; // Retrieve the port ranges for all ports (some values may be NaN) slv2_plugin_get_port_ranges_float(mData, minimumValues, maximumValues, defaultValues); // Get info about all ports for(p = 0; p < numPorts; p++) { SLV2Port port = slv2_plugin_get_port_by_index(mData, p); LV2Port internalPort; internalPort.mIndex = p; // Get the port name SLV2Value tmpName = slv2_port_get_name(data, port); internalPort.mName = LAT1CTOWX(slv2_value_as_string(tmpName)); slv2_value_free(tmpName); // Get the port type if (slv2_port_is_a(mData, port, gAudioPortClass)) { if (slv2_port_is_a(mData, port, gInputPortClass)) mAudioInputs.push_back(internalPort); else if (slv2_port_is_a(mData, port, gOutputPortClass)) mAudioOutputs.push_back(internalPort); } else if (slv2_port_is_a(mData, port, gControlPortClass) && slv2_port_is_a(mData, port, gInputPortClass)) { internalPort.mControlBuffer = float(1.0); internalPort.mMin = minimumValues[p]; internalPort.mMax = maximumValues[p]; internalPort.mDefault = defaultValues[p]; if (std::isfinite(defaultValues[p])) internalPort.mControlBuffer = defaultValues[p]; else if (std::isfinite(minimumValues[p])) internalPort.mControlBuffer = minimumValues[p]; else if (std::isfinite(maximumValues[p])) internalPort.mControlBuffer = maximumValues[p]; if (slv2_port_has_property(data, port, gPortToggled)) internalPort.mToggle = true; if (slv2_port_has_property(data, port, gPortIsInteger)) internalPort.mInteger = true; if (slv2_port_has_property(data, port, gPortIsSampleRate)) internalPort.mSampleRate = true; mControlInputs.push_back(internalPort); } else if (slv2_port_is_a(mData, port, gMidiPortClass) && slv2_port_is_a(mData, port, gInputPortClass)) { // If there are more than one MIDI input ports, the plugin is invalid if (mMidiInput) { mValid = false; continue; } mMidiInput = new LV2Port(internalPort); } else { // Unknown port type, we set the invalid flag mValid = false; } } delete [] minimumValues; delete [] maximumValues; delete [] defaultValues; // MIDI synths may not have any audio inputs. if (mMidiInput && mAudioInputs.size() > 0) mValid = false; // Determine whether the plugin is a generator, effect or analyser // depending on the number of ports of each type (not completely accurate, // but works most of the time) flags = PLUGIN_EFFECT; if (mAudioInputs.size() == 0) flags |= INSERT_EFFECT; else if (mAudioOutputs.size() == 0) flags |= ANALYZE_EFFECT; else flags |= PROCESS_EFFECT; }