aboutsummaryrefslogblamecommitdiff
path: root/src/plugins/Effect/ladspa/ladspaplugin.cpp
blob: 99392a5d03c4b1ad6443094cfc7164956717ee27 (plain) (tree)























                                                                             

                      



                      
                         














































































































































































































































































                                                                                               






































































                                                                                         
                                                    






                                                               

                                                      
 
                                  















                                                                                                      


                                                               





















































































                                                                         
                                                                                                   
 


                                                                                 
     


                                             




                                                                             
       
 
/***************************************************************************
 *   Copyright (C) 2009 by Ilya Kotov                                      *
 *   <forkotov02@hotmail.ru>                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <QSettings>
#include <QByteArray>
#include <QDir>
#include <QFileInfo>
#include <QFormLayout>
#include <QWidget>
#include <math.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <qmmp/qmmp.h>
#include "ladspaslider.h"
#include "ladspaplugin.h"

#ifndef PATH_MAX
#define PATH_MAX 4096
#endif

#undef	CLAMP
#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))

LADSPAHost *LADSPAHost::m_instance = 0;

LADSPAHost::LADSPAHost(QObject* parent) : Effect(parent)
{
    m_instance = this;
    findAllPlugins();
}

LADSPAHost::~LADSPAHost()
{
    m_instance = 0;
    foreach(LADSPAEffect *instance, m_effects)
    {
        unload(instance);
    }
}

ulong LADSPAHost::process(char *in_data, const ulong size, char **out_data)
{
    applyEffect((qint16 *) in_data, size);
    memcpy(*out_data, in_data, size);
    return size;
}

void LADSPAHost::configure(quint32 freq, int chan, int res)
{
    Effect::configure(freq, chan, res);
}

LADSPAHost* LADSPAHost::instance()
{
    return m_instance;
}

QList <LADSPAPlugin *> LADSPAHost::plugins()
{
    return m_plugins;
}

QList <LADSPAEffect *> LADSPAHost::runningPlugins()
{
    return m_effects;
}

/*!!!!*/
void LADSPAHost::findAllPlugins()
{
    while(!m_plugins.isEmpty()) /* empty list */
        delete m_plugins.takeFirst();

    QString ladspa_path = qgetenv("LADSPA_PATH");
    QStringList directories;

    if (ladspa_path.isEmpty())
    {
        /* Fallback, look in obvious places */
        directories << "/usr/lib/ladspa";
        directories << "/usr/local/lib/ladspa";
    }
    else
        directories = ladspa_path.split(':');
    foreach(QString dir, directories)
        findPlugins(dir);
}

LADSPAEffect *LADSPAHost::load(const QString &filename, long int num)
{
    LADSPA_Descriptor_Function descriptor_fn;
    LADSPAEffect *instance = new LADSPAEffect;

    instance->fileName = filename;
    instance->library = dlopen(qPrintable(filename), RTLD_NOW);
    instance->handle = 0;
    instance->handle2 = 0;
    if (!instance->library)
    {
        delete instance;
        return 0;
    }
    descriptor_fn = (LADSPA_Descriptor_Function) dlsym(instance->library, "ladspa_descriptor");
    if (!descriptor_fn)
    {
        dlclose(instance->library);
        delete instance;
        return 0;
    }
    instance->descriptor = descriptor_fn(num);

    return instance;
}

void LADSPAHost::unload(LADSPAEffect *instance)
{
    const LADSPA_Descriptor *descriptor = instance->descriptor;

    if (instance->handle)
    {
        if (descriptor->deactivate)
            descriptor->deactivate(instance->handle);
        descriptor->cleanup(instance->handle);
        instance->handle = 0;
    }
    if (instance->handle2)
    {
        if (descriptor->deactivate)
            descriptor->deactivate(instance->handle2);
        descriptor->cleanup(instance->handle2);
        instance->handle2 = 0;
    }

    if (instance->library)
    {
        dlclose(instance->library);
        instance->library = 0;
    }
    m_effects.removeAll(instance);
    delete instance;
}

void LADSPAHost::bootPlugin(LADSPAEffect *instance)
{
    const LADSPA_Descriptor *descriptor = instance->descriptor;

    instance->handle = descriptor->instantiate(descriptor, (int)sampleRate());
    if (channels() > 1 && !instance->stereo)
    {
        /* Create an additional instance */
        instance->handle2 = descriptor->instantiate(descriptor, (int)sampleRate());
    }

    portAssign(instance);

    if (descriptor->activate)
    {
        descriptor->activate(instance->handle);
        if (instance->handle2)
            descriptor->activate(instance->handle2);
    }
}

int LADSPAHost::applyEffect(qint16 *d, int length)
{
    qint16 *raw16 = d;
    LADSPAEffect *instance;
    int k;
    int nch = channels();

    if (m_effects.isEmpty())
        return length;

    if (nch == 1)
    {
        for (k = 0; k < length / 2; ++k)
            m_left[k] = ((LADSPA_Data) raw16[k]) * (1.0f / 32768.0f);
        foreach(instance, m_effects)
        {
            if (instance->handle)
                instance->descriptor->run(instance->handle, length / 2);
        }
        for (k = 0; k < length / 2; ++k)
            raw16[k] = CLAMP((int)(m_left[k] * 32768.0f), -32768, 32767);
    }
    else
    {
        for (k = 0; k < length / 2; k += 2)
        {
            m_left[k/2] = ((LADSPA_Data) raw16[k]) * (1.0f / 32768.0f);
            m_right[(k+1)/2] = ((LADSPA_Data) raw16[k+1]) * (1.0f / 32768.0f);
        }
        foreach(instance, m_effects)
        {
            if (instance->handle)
                instance->descriptor->run(instance->handle, length/4);
            if (instance->handle2)
                instance->descriptor->run(instance->handle2, length/4);
        }
        for (k = 0; k < length / 2; k += 2)
        {
            raw16[k] = CLAMP((int)(m_left[k/2] * 32768.0f), -32768, 32767);
            raw16[k+1] = CLAMP((int)(m_right[(k+1)/2] * 32768.0f), -32768, 32767);

        }
    }
    return length;
}

void LADSPAHost::portAssign(LADSPAEffect *instance)
{
    unsigned long port;
    unsigned long inputs = 0, outputs = 0;
    const LADSPA_Descriptor *plugin = instance->descriptor;

    for (port = 0; port < plugin->PortCount; ++port)
    {
        if (LADSPA_IS_PORT_CONTROL(plugin->PortDescriptors[port]))
        {
            if (port < MAX_KNOBS)
            {
                plugin->connect_port(instance->handle, port, &(instance->knobs[port]));
                if (instance->handle2)
                    plugin->connect_port(instance->handle2, port, &(instance->knobs[port]));
            }
            else
            {
                plugin->connect_port(instance->handle, port, m_trash);
                if (instance->handle2)
                    plugin->connect_port(instance->handle2, port, m_trash);
            }

        }
        else if (LADSPA_IS_PORT_AUDIO(plugin->PortDescriptors[port]))
        {

            if (LADSPA_IS_PORT_INPUT(plugin->PortDescriptors[port]))
            {
                if (inputs == 0)
                {
                    plugin->connect_port(instance->handle, port, m_left);
                    if (instance->handle2)
                        plugin->connect_port(instance->handle2, port, m_right);
                }
                else if (inputs == 1 && instance->stereo)
                {
                    plugin->connect_port(instance->handle, port, m_right);
                }
                else
                {
                    plugin->connect_port(instance->handle, port, m_trash);
                    if (instance->handle2)
                        plugin->connect_port(instance->handle2, port, m_trash);
                }
                inputs++;

            }
            else if (LADSPA_IS_PORT_OUTPUT(plugin->PortDescriptors[port]))
            {
                if (outputs == 0)
                {
                    plugin->connect_port(instance->handle, port, m_left);
                    if (instance->handle2)
                        plugin->connect_port(instance->handle2, port, m_right);
                }
                else if (outputs == 1 && instance->stereo)
                {
                    plugin->connect_port(instance->handle, port, m_right);
                }
                else
                {
                    plugin->connect_port(instance->handle, port, m_trash);
                    if (instance->handle2)
                        plugin->connect_port(instance->handle2, port, m_trash);
                }
                outputs++;
            }
        }
    }
}

void LADSPAHost::findPlugins(const QString &path_entry)
{
    LADSPAPlugin *plugin;
    void *library = 0;
    LADSPA_Descriptor_Function descriptor_fn;
    const LADSPA_Descriptor *descriptor;
    long int k;
    unsigned long int port, input, output;

    QDir dir (path_entry);
    dir.setFilter(QDir::Files | QDir::Hidden);
    dir.setSorting(QDir::Name);
    QFileInfoList files = dir.entryInfoList((QStringList() << "*.so"));

    foreach(QFileInfo file, files)
    {
        library = dlopen(qPrintable(file.absoluteFilePath ()), RTLD_LAZY);
        if (library == 0)
        {
            continue;
        }
        descriptor_fn = (LADSPA_Descriptor_Function) dlsym(library, "ladspa_descriptor");
        if (descriptor_fn == 0)
        {
            dlclose(library);
            continue;
        }

        for (k = 0;; ++k)
        {
            descriptor = descriptor_fn(k);
            if (descriptor == 0)
            {
                break;
            }
            plugin = new LADSPAPlugin;
            plugin->name = strdup(descriptor->Name);
            plugin->fileName = file.absoluteFilePath ();
            plugin->id = k;
            plugin->unique_id = descriptor->UniqueID;
            for (input = output = port = 0; port < descriptor->PortCount; ++port)
            {
                if (LADSPA_IS_PORT_AUDIO(descriptor->PortDescriptors[port]))
                {
                    if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[port]))
                        input++;
                    if (LADSPA_IS_PORT_OUTPUT(descriptor->PortDescriptors[port]))
                        output++;
                }
                else if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[port]))
                {
                }
            }
            plugin->stereo = (input >= 2 && output >= 2);
            m_plugins.append(plugin);
        }
        dlclose(library);
    }
}

LADSPAEffect *LADSPAHost::addPlugin(LADSPAPlugin *plugin)
{
    if (!plugin)
        return 0;
    LADSPAEffect *instance;
    if (!(instance = load(plugin->fileName, plugin->id)))
        return 0;
    instance->stereo = plugin->stereo;
    if (channels() && sampleRate())
        bootPlugin(instance);
    draw_plugin(instance);
    m_effects.append(instance);
    return instance;
}

void LADSPAHost::draw_plugin(LADSPAEffect *instance)
{
    const LADSPA_Descriptor *plugin = instance->descriptor;
    const LADSPA_PortRangeHint *hints = plugin->PortRangeHints;
    LADSPA_Data fact, min, max, step, start;
    int dp;
    unsigned long k;
    bool no_ui = TRUE;
    QWidget *widget = new QWidget();
    QFormLayout *formLayout = new QFormLayout(widget);

    widget->setLayout(formLayout);

    for (k = 0; k < MAX_KNOBS && k < plugin->PortCount; ++k)
    {
        if (!LADSPA_IS_PORT_CONTROL(plugin->PortDescriptors[k]))
            continue;
        no_ui = FALSE;
        //hbox = gtk_hbox_new(FALSE, 3);
        //widget = gtk_label_new(plugin->PortNames[k]);
        //gtk_container_add(GTK_CONTAINER(hbox), widget);

        if (LADSPA_IS_HINT_TOGGLED(hints[k].HintDescriptor))
        {
            /*widget = gtk_toggle_button_new_with_label("Press");
            g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(toggled), &(instance->knobs[k]));
            gtk_container_add(GTK_CONTAINER(hbox), widget);
            gtk_container_add(GTK_CONTAINER(vbox), hbox);*/


            //formLayout->addRow(plugin->PortNames[k], slider);
            continue;
        }

        if (LADSPA_IS_HINT_SAMPLE_RATE(hints[k].HintDescriptor))
            fact = sampleRate();
        else
            fact = 1.0f;

        if (LADSPA_IS_HINT_BOUNDED_BELOW(hints[k].HintDescriptor))
            min = hints[k].LowerBound * fact;
        else
            min = -10000.0f;

        if (LADSPA_IS_HINT_BOUNDED_ABOVE(hints[k].HintDescriptor))
            max = hints[k].UpperBound * fact;
        else
            max = 10000.0f;

        /* infinity */
        if (10000.0f <= max - min)
        {
            dp = 1;
            step = 5.0f;

            /* 100.0 ... lots */
        }
        else if (100.0f < max - min)
        {
            dp = 0;
            step = 5.0f;

            /* 10.0 ... 100.0 */
        }
        else if (10.0f < max - min)
        {
            dp = 1;
            step = 0.5f;

            /* 1.0 ... 10.0 */
        }
        else if (1.0f < max - min)
        {
            dp = 2;
            step = 0.05f;

            /* 0.0 ... 1.0 */
        }
        else
        {
            dp = 3;
            step = 0.005f;
        }

        if (LADSPA_IS_HINT_INTEGER(hints[k].HintDescriptor))
        {
            dp = 0;
            if (step < 1.0f)
                step = 1.0f;
        }

        if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hints[k].HintDescriptor))
            start = min;
        else if (LADSPA_IS_HINT_DEFAULT_LOW(hints[k].HintDescriptor))
            start = min * 0.75f + max * 0.25f;
        else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hints[k].HintDescriptor))
            start = min * 0.5f + max * 0.5f;
        else if (LADSPA_IS_HINT_DEFAULT_HIGH(hints[k].HintDescriptor))
            start = min * 0.25f + max * 0.75f;
        else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hints[k].HintDescriptor))
            start = max;
        else if (LADSPA_IS_HINT_DEFAULT_0(hints[k].HintDescriptor))
            start = 0.0f;
        else if (LADSPA_IS_HINT_DEFAULT_1(hints[k].HintDescriptor))
            start = 1.0f;
        else if (LADSPA_IS_HINT_DEFAULT_100(hints[k].HintDescriptor))
            start = 100.0f;
        else if (LADSPA_IS_HINT_DEFAULT_440(hints[k].HintDescriptor))
            start = 440.0f;
        else if (LADSPA_IS_HINT_INTEGER(hints[k].HintDescriptor))
            start = min;
        else if (max >= 0.0f && min <= 0.0f)
            start = 0.0f;
        else
            start = min * 0.5f + max * 0.5f;

        instance->knobs[k] = start;
        LADSPASlider *slider = new LADSPASlider(min, max, step, &instance->knobs[k], this, widget);

        //slider->setEnabled(!LADSPA_IS_PORT_OUTPUT(plugin->PortDescriptors[k]));

        formLayout->addRow(plugin->PortNames[k], slider);
    }
    widget->setLayout(formLayout);
    widget->setFixedSize(widget->sizeHint());
    instance->widget = widget;

    /*if (no_ui)
    {
        widget = gtk_label_new(_("This LADSPA plugin has no user controls"));
        gtk_container_add(GTK_CONTAINER(vbox), widget);
    }*/
}