diff plugin/DSSIPluginInstance.cpp @ 0:da6937383da8

initial import
author Chris Cannam
date Tue, 10 Jan 2006 16:33:16 +0000
parents
children d86891498eef
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/DSSIPluginInstance.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,1182 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include <iostream>
+#include <cassert>
+
+#include "DSSIPluginInstance.h"
+#include "PluginIdentifier.h"
+#include "LADSPAPluginFactory.h"
+
+#define DEBUG_DSSI 1
+//#define DEBUG_DSSI_PROCESS 1
+
+#define EVENT_BUFFER_SIZE 1023
+
+#ifdef DEBUG_DSSI
+static std::ostream &operator<<(std::ostream& o, const QString &s)
+{
+    o << s.toLocal8Bit().data();
+    return o;
+}
+#endif
+
+DSSIPluginInstance::GroupMap DSSIPluginInstance::m_groupMap;
+snd_seq_event_t **DSSIPluginInstance::m_groupLocalEventBuffers = 0;
+size_t DSSIPluginInstance::m_groupLocalEventBufferCount = 0;
+Scavenger<ScavengerArrayWrapper<snd_seq_event_t *> > DSSIPluginInstance::m_bufferScavenger(2, 10);
+std::map<LADSPA_Handle, std::set<DSSIPluginInstance::NonRTPluginThread *> > DSSIPluginInstance::m_threads;
+
+
+DSSIPluginInstance::DSSIPluginInstance(RealTimePluginFactory *factory,
+				       int clientId,
+				       QString identifier,
+				       int position,
+				       unsigned long sampleRate,
+				       size_t blockSize,
+				       int idealChannelCount,
+				       const DSSI_Descriptor* descriptor) :
+    RealTimePluginInstance(factory, identifier),
+    m_client(clientId),
+    m_position(position),
+    m_descriptor(descriptor),
+    m_programCacheValid(false),
+    m_eventBuffer(EVENT_BUFFER_SIZE),
+    m_blockSize(blockSize),
+    m_idealChannelCount(idealChannelCount),
+    m_sampleRate(sampleRate),
+    m_latencyPort(0),
+    m_run(false),
+    m_bypassed(false),
+    m_grouped(false)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::DSSIPluginInstance(" << identifier << ")"
+	      << std::endl;
+#endif
+
+    init();
+
+    m_inputBuffers  = new sample_t*[m_audioPortsIn.size()];
+    m_outputBuffers = new sample_t*[m_outputBufferCount];
+
+    for (size_t i = 0; i < m_audioPortsIn.size(); ++i) {
+	m_inputBuffers[i] = new sample_t[blockSize];
+    }
+    for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	m_outputBuffers[i] = new sample_t[blockSize];
+    }
+
+    m_ownBuffers = true;
+
+    m_pending.lsb = m_pending.msb = m_pending.program = -1;
+
+    instantiate(sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+	initialiseGroupMembership();
+    }
+}
+
+DSSIPluginInstance::DSSIPluginInstance(RealTimePluginFactory *factory,
+				       int clientId,
+				       QString identifier,
+				       int position,
+				       unsigned long sampleRate,
+				       size_t blockSize,
+				       sample_t **inputBuffers,
+				       sample_t **outputBuffers,
+				       const DSSI_Descriptor* descriptor) :
+    RealTimePluginInstance(factory, identifier),
+    m_client(clientId),
+    m_position(position),
+    m_descriptor(descriptor),
+    m_eventBuffer(EVENT_BUFFER_SIZE),
+    m_blockSize(blockSize),
+    m_inputBuffers(inputBuffers),
+    m_outputBuffers(outputBuffers),
+    m_ownBuffers(false),
+    m_idealChannelCount(0),
+    m_sampleRate(sampleRate),
+    m_latencyPort(0),
+    m_run(false),
+    m_bypassed(false),
+    m_grouped(false)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::DSSIPluginInstance[buffers supplied](" << identifier << ")"
+	      << std::endl;
+#endif
+
+    init();
+
+    m_pending.lsb = m_pending.msb = m_pending.program = -1;
+
+    instantiate(sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+	if (m_descriptor->run_multiple_synths) {
+	    m_grouped = true;
+	    initialiseGroupMembership();
+	}
+    }
+}
+
+
+void
+DSSIPluginInstance::init()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::init" << std::endl;
+#endif
+
+    // Discover ports numbers and identities
+    //
+    const LADSPA_Descriptor *descriptor = m_descriptor->LADSPA_Plugin;
+
+    for (unsigned long i = 0; i < descriptor->PortCount; ++i)
+    {
+        if (LADSPA_IS_PORT_AUDIO(descriptor->PortDescriptors[i]))
+        {
+            if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+                m_audioPortsIn.push_back(i);
+	    } else {
+                m_audioPortsOut.push_back(i);
+	    }
+        }
+        else
+        if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i]))
+        {
+	    if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+
+		LADSPA_Data *data = new LADSPA_Data(0.0);
+
+		m_controlPortsIn.push_back(std::pair<unsigned long, LADSPA_Data*>
+					   (i, data));
+
+		m_backupControlPortsIn.push_back(0.0);
+
+	    } else {
+		LADSPA_Data *data = new LADSPA_Data(0.0);
+		m_controlPortsOut.push_back(
+                    std::pair<unsigned long, LADSPA_Data*>(i, data));
+		if (!strcmp(descriptor->PortNames[i], "latency") ||
+		    !strcmp(descriptor->PortNames[i], "_latency")) {
+#ifdef DEBUG_DSSI
+		    std::cerr << "Wooo! We have a latency port!" << std::endl;
+#endif
+		    m_latencyPort = data;
+		}
+	    }
+        }
+#ifdef DEBUG_DSSI
+        else
+            std::cerr << "DSSIPluginInstance::DSSIPluginInstance - "
+                      << "unrecognised port type" << std::endl;
+#endif
+    }
+
+    m_outputBufferCount = std::max(m_idealChannelCount, m_audioPortsOut.size());
+}
+
+size_t
+DSSIPluginInstance::getLatency()
+{
+    size_t latency = 0;
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::getLatency(): m_latencyPort " << m_latencyPort << ", m_run " << m_run << std::endl;
+#endif
+
+    if (m_latencyPort) {
+	if (!m_run) run(RealTime::zeroTime);
+	latency = (size_t)(*m_latencyPort + 0.1);
+    }
+    
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::getLatency(): latency is " << latency << std::endl;
+#endif
+
+    return latency;
+}
+
+void
+DSSIPluginInstance::silence()
+{
+    if (m_instanceHandle != 0) {
+	deactivate();
+	activate();
+    }
+}
+
+void
+DSSIPluginInstance::discardEvents()
+{
+    m_eventBuffer.reset();
+}
+
+void
+DSSIPluginInstance::setIdealChannelCount(size_t channels)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::setIdealChannelCount: channel count "
+	      << channels << " (was " << m_idealChannelCount << ")" << std::endl;
+#endif
+
+    if (channels == m_idealChannelCount) {
+	silence();
+	return;
+    }
+
+    if (m_instanceHandle != 0) {
+	deactivate();
+    }
+
+    m_idealChannelCount = channels;
+
+    if (channels > m_outputBufferCount) {
+
+	for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	    delete[] m_outputBuffers[i];
+	}
+
+	delete[] m_outputBuffers;
+
+	m_outputBufferCount = channels;
+
+	m_outputBuffers = new sample_t*[m_outputBufferCount];
+
+	for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	    m_outputBuffers[i] = new sample_t[m_blockSize];
+	}
+
+	connectPorts();
+    }
+
+    if (m_instanceHandle != 0) {
+	activate();
+    }
+}
+
+void
+DSSIPluginInstance::detachFromGroup()
+{
+    if (!m_grouped) return;
+    m_groupMap[m_identifier].erase(this);
+    m_grouped = false;
+}
+
+void
+DSSIPluginInstance::initialiseGroupMembership()
+{
+    if (!m_descriptor->run_multiple_synths) {
+	m_grouped = false;
+	return;
+    }
+
+    //!!! GroupMap is not actually thread-safe.
+
+    size_t pluginsInGroup = m_groupMap[m_identifier].size();
+
+    if (++pluginsInGroup > m_groupLocalEventBufferCount) {
+
+	size_t nextBufferCount = pluginsInGroup * 2;
+
+	snd_seq_event_t **eventLocalBuffers = new snd_seq_event_t *[nextBufferCount];
+
+	for (size_t i = 0; i < m_groupLocalEventBufferCount; ++i) {
+	    eventLocalBuffers[i] = m_groupLocalEventBuffers[i];
+	}
+	for (size_t i = m_groupLocalEventBufferCount; i < nextBufferCount; ++i) {
+	    eventLocalBuffers[i] = new snd_seq_event_t[EVENT_BUFFER_SIZE];
+	}
+
+	if (m_groupLocalEventBuffers) {
+	    m_bufferScavenger.claim(new ScavengerArrayWrapper<snd_seq_event_t *>
+				    (m_groupLocalEventBuffers));
+	}
+
+	m_groupLocalEventBuffers = eventLocalBuffers;
+	m_groupLocalEventBufferCount = nextBufferCount;
+    }
+
+    m_grouped = true;
+    m_groupMap[m_identifier].insert(this);
+}
+
+DSSIPluginInstance::~DSSIPluginInstance()
+{
+    std::cerr << "DSSIPluginInstance::~DSSIPluginInstance" << std::endl;
+
+    if (m_threads.find(m_instanceHandle) != m_threads.end()) {
+
+	for (std::set<NonRTPluginThread *>::iterator i =
+		 m_threads[m_instanceHandle].begin();
+	     i != m_threads[m_instanceHandle].end(); ++i) {
+
+	    (*i)->setExiting();
+	    (*i)->wait();
+	    delete *i;
+	}
+
+	m_threads.erase(m_instanceHandle);
+    }
+
+    detachFromGroup();
+
+    if (m_instanceHandle != 0) {
+	deactivate();
+    }
+
+    cleanup();
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i)
+        delete m_controlPortsIn[i].second;
+
+    for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i)
+        delete m_controlPortsOut[i].second;
+
+    m_controlPortsIn.clear();
+    m_controlPortsOut.clear();
+
+    if (m_ownBuffers) {
+	for (size_t i = 0; i < m_audioPortsIn.size(); ++i) {
+	    delete[] m_inputBuffers[i];
+	}
+	for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	    delete[] m_outputBuffers[i];
+	}
+
+	delete[] m_inputBuffers;
+	delete[] m_outputBuffers;
+    }
+
+    m_audioPortsIn.clear();
+    m_audioPortsOut.clear();
+}
+
+
+void
+DSSIPluginInstance::instantiate(unsigned long sampleRate)
+{
+#ifdef DEBUG_DSSI
+    std::cout << "DSSIPluginInstance::instantiate - plugin \"unique\" id = "
+              << m_descriptor->LADSPA_Plugin->UniqueID << std::endl;
+#endif
+    if (!m_descriptor) return;
+
+    const LADSPA_Descriptor *descriptor = m_descriptor->LADSPA_Plugin;
+
+    if (!descriptor->instantiate) {
+	std::cerr << "Bad plugin: plugin id " << descriptor->UniqueID
+		  << ":" << descriptor->Label
+		  << " has no instantiate method!" << std::endl;
+	return;
+    }
+
+    m_instanceHandle = descriptor->instantiate(descriptor, sampleRate);
+
+    if (m_instanceHandle) {
+
+	if (m_descriptor->get_midi_controller_for_port) {
+
+	    for (unsigned long i = 0; i < descriptor->PortCount; ++i) {
+
+		if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i]) &&
+		    LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+
+		    int controller = m_descriptor->get_midi_controller_for_port
+			(m_instanceHandle, i);
+
+		    if (controller != 0 && controller != 32 &&
+			DSSI_IS_CC(controller)) {
+
+			m_controllerMap[DSSI_CC_NUMBER(controller)] = i;
+		    }
+		}
+	    }
+	}
+    }
+}
+
+void
+DSSIPluginInstance::checkProgramCache() const
+{
+    if (m_programCacheValid) return;
+    m_cachedPrograms.clear();
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::checkProgramCache" << std::endl;
+#endif
+
+    if (!m_descriptor || !m_descriptor->get_program) {
+	m_programCacheValid = true;
+	return;
+    }
+
+    unsigned long index = 0;
+    const DSSI_Program_Descriptor *programDescriptor;
+    while ((programDescriptor = m_descriptor->get_program(m_instanceHandle, index))) {
+	++index;
+	ProgramDescriptor d;
+	d.bank = programDescriptor->Bank;
+	d.program = programDescriptor->Program;
+//!!!	d.name = QString("%1. %2").arg(index).arg(programDescriptor->Name);
+	d.name = programDescriptor->Name;
+	m_cachedPrograms.push_back(d);
+    }
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::checkProgramCache: have " << m_cachedPrograms.size() << " programs" << std::endl;
+#endif
+
+    m_programCacheValid = true;
+}
+
+QStringList
+DSSIPluginInstance::getPrograms() const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getPrograms" << std::endl;
+#endif
+
+    if (!m_descriptor) return QStringList();
+
+    checkProgramCache();
+
+    QStringList programs;
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+	programs.push_back(i->name);
+    }
+
+    return programs;
+}
+
+QString
+DSSIPluginInstance::getProgram(int bank, int program) const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getProgram(" << bank << "," << program << ")" << std::endl;
+#endif
+
+    if (!m_descriptor) return QString();
+
+    checkProgramCache();
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+	if (i->bank == bank && i->program == program) return i->name;
+    }
+
+    return QString();
+}
+
+unsigned long
+DSSIPluginInstance::getProgram(QString name) const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getProgram(" << name << ")" << std::endl;
+#endif
+
+    if (!m_descriptor) return 0;
+
+    checkProgramCache();
+
+    unsigned long rv;
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+	if (i->name == name) {
+	    rv = i->bank;
+	    rv = (rv << 16) + i->program;
+	    return rv;
+	}
+    }
+
+    return 0;
+}
+
+QString
+DSSIPluginInstance::getCurrentProgram() const
+{
+    return m_program;
+}
+
+void
+DSSIPluginInstance::selectProgram(QString program)
+{
+    selectProgramAux(program, true);
+}
+
+void
+DSSIPluginInstance::selectProgramAux(QString program, bool backupPortValues)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::selectProgram(" << program << ")" << std::endl;
+#endif
+
+    if (!m_descriptor) return;
+
+    checkProgramCache();
+
+    if (!m_descriptor->select_program) return;
+
+    bool found = false;
+    unsigned long bankNo = 0, programNo = 0;
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+
+	if (i->name == program) {
+
+	    bankNo = i->bank;
+	    programNo = i->program;
+	    found = true;
+
+#ifdef DEBUG_DSSI
+	    std::cerr << "DSSIPluginInstance::selectProgram(" << program << "): found at bank " << bankNo << ", program " << programNo << std::endl;
+#endif
+
+	    break;
+	}
+    }
+
+    if (!found) return;
+    m_program = program;
+
+    // DSSI select_program is an audio context call
+    m_processLock.lock();
+    m_descriptor->select_program(m_instanceHandle, bankNo, programNo);
+    m_processLock.unlock();
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::selectProgram(" << program << "): made select_program(" << bankNo << "," << programNo << ") call" << std::endl;
+#endif
+
+    if (backupPortValues) {
+	for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
+	    m_backupControlPortsIn[i] = *m_controlPortsIn[i].second;
+	}
+    }
+}
+
+void
+DSSIPluginInstance::activate()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::activate" << std::endl;
+#endif
+
+    if (!m_descriptor || !m_descriptor->LADSPA_Plugin->activate) return;
+    m_descriptor->LADSPA_Plugin->activate(m_instanceHandle);
+
+    if (!m_program.isEmpty()) {
+#ifdef DEBUG_DSSI
+	std::cerr << "DSSIPluginInstance::activate: restoring program " << m_program << std::endl;
+#endif
+	selectProgramAux(m_program, false);
+    }
+
+    for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
+#ifdef DEBUG_DSSI
+	std::cerr << "DSSIPluginInstance::activate: setting port " << m_controlPortsIn[i].first << " to " << m_backupControlPortsIn[i] << std::endl;
+#endif
+	*m_controlPortsIn[i].second = m_backupControlPortsIn[i];
+    }
+}
+
+void
+DSSIPluginInstance::connectPorts()
+{
+    if (!m_descriptor || !m_descriptor->LADSPA_Plugin->connect_port) return;
+    std::cerr << "DSSIPluginInstance::connectPorts: " << m_audioPortsIn.size() 
+	      << " audio ports in, " << m_audioPortsOut.size() << " out, "
+	      << m_outputBufferCount << " output buffers" << std::endl;
+
+    assert(sizeof(LADSPA_Data) == sizeof(float));
+    assert(sizeof(sample_t) == sizeof(float));
+
+    int inbuf = 0, outbuf = 0;
+
+    for (unsigned int i = 0; i < m_audioPortsIn.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_audioPortsIn[i],
+	     (LADSPA_Data *)m_inputBuffers[inbuf]);
+	++inbuf;
+    }
+
+    for (unsigned int i = 0; i < m_audioPortsOut.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_audioPortsOut[i],
+	     (LADSPA_Data *)m_outputBuffers[outbuf]);
+	++outbuf;
+    }
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_controlPortsIn[i].first,
+	     m_controlPortsIn[i].second);
+    }
+
+    for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_controlPortsOut[i].first,
+	     m_controlPortsOut[i].second);
+    }
+}
+
+unsigned int
+DSSIPluginInstance::getParameterCount() const
+{
+    return m_controlPortsIn.size();
+}
+
+void
+DSSIPluginInstance::setParameterValue(unsigned int parameter, float value)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::setParameterValue(" << parameter << ") to " << value << std::endl;
+#endif
+    if (parameter >= m_controlPortsIn.size()) return;
+
+    unsigned int portNumber = m_controlPortsIn[parameter].first;
+
+    LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory);
+    if (f) {
+	if (value < f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber)) {
+	    value = f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber);
+	}
+	if (value > f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber)) {
+	    value = f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber);
+	}
+    }
+
+    (*m_controlPortsIn[parameter].second) = value;
+    m_backupControlPortsIn[parameter] = value;
+}
+
+void
+DSSIPluginInstance::setPortValueFromController(unsigned int port, int cv)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::setPortValueFromController(" << port << ") to " << cv << std::endl;
+#endif
+
+    const LADSPA_Descriptor *p = m_descriptor->LADSPA_Plugin;
+    LADSPA_PortRangeHintDescriptor d = p->PortRangeHints[port].HintDescriptor;
+    LADSPA_Data lb = p->PortRangeHints[port].LowerBound;
+    LADSPA_Data ub = p->PortRangeHints[port].UpperBound;
+
+    float value = (float)cv;
+
+    if (!LADSPA_IS_HINT_BOUNDED_BELOW(d)) {
+	if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	    /* unbounded: might as well leave the value alone. */
+	} else {
+	    /* bounded above only. just shift the range. */
+	    value = ub - 127.0f + value;
+	}
+    } else {
+	if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	    /* bounded below only. just shift the range. */
+	    value = lb + value;
+	} else {
+	    /* bounded both ends.  more interesting. */
+	    /* XXX !!! todo: fill in logarithmic, sample rate &c */
+	    value = lb + ((ub - lb) * value / 127.0f);
+	}
+    }
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
+	if (m_controlPortsIn[i].first == port) {
+	    setParameterValue(i, value);
+	}
+    }
+}
+
+float
+DSSIPluginInstance::getParameterValue(unsigned int parameter) const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getParameterValue(" << parameter << ")" << std::endl;
+#endif
+    if (parameter >= m_controlPortsIn.size()) return 0.0;
+    return (*m_controlPortsIn[parameter].second);
+}
+
+float
+DSSIPluginInstance::getParameterDefault(unsigned int parameter) const
+{
+    if (parameter >= m_controlPortsIn.size()) return 0.0;
+
+    LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory);
+    if (f) {
+	return f->getPortDefault(m_descriptor->LADSPA_Plugin,
+				 m_controlPortsIn[parameter].first);
+    } else {
+	return 0.0f;
+    }
+}
+
+QString
+DSSIPluginInstance::configure(QString key,
+			      QString value)
+{
+    if (!m_descriptor || !m_descriptor->configure) return QString();
+
+    if (key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
+#ifdef DSSI_PROJECT_DIRECTORY_KEY
+	key = DSSI_PROJECT_DIRECTORY_KEY;
+#else
+	return QString();
+#endif
+    }
+	
+    
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::configure(" << key << "," << value << ")" << std::endl;
+#endif
+
+    char *message = m_descriptor->configure(m_instanceHandle,
+					    key.toStdString().c_str(),
+					    value.toStdString().c_str());
+
+    m_programCacheValid = false;
+
+    QString qm;
+
+    // Ignore return values from reserved key configuration calls such
+    // as project directory
+#ifdef DSSI_RESERVED_CONFIGURE_PREFIX
+    if (key.startsWith(DSSI_RESERVED_CONFIGURE_PREFIX)) {
+	return qm;
+    }
+#endif
+
+    if (message) {
+	if (m_descriptor->LADSPA_Plugin && m_descriptor->LADSPA_Plugin->Label) {
+	    qm = QString(m_descriptor->LADSPA_Plugin->Label) + ": ";
+	}
+	qm = qm + message;
+	free(message);
+    }
+
+    return qm;
+}
+
+void
+DSSIPluginInstance::sendEvent(const RealTime &eventTime,
+			      const void *e)
+{
+    snd_seq_event_t *event = (snd_seq_event_t *)e;
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::sendEvent at " << eventTime << std::endl;
+#endif
+    snd_seq_event_t ev(*event);
+
+    ev.time.time.tv_sec = eventTime.sec;
+    ev.time.time.tv_nsec = eventTime.nsec;
+
+    // DSSI doesn't use MIDI channels, it uses run_multiple_synths instead.
+    ev.data.note.channel = 0;
+
+    m_eventBuffer.write(&ev, 1);
+}
+
+bool
+DSSIPluginInstance::handleController(snd_seq_event_t *ev)
+{
+    int controller = ev->data.control.param;
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::handleController " << controller << std::endl;
+#endif
+
+    if (controller == 0) { // bank select MSB
+	
+	m_pending.msb = ev->data.control.value;
+
+    } else if (controller == 32) { // bank select LSB
+
+	m_pending.lsb = ev->data.control.value;
+
+    } else if (controller > 0 && controller < 128) {
+	
+	if (m_controllerMap.find(controller) != m_controllerMap.end()) {
+	    int port = m_controllerMap[controller];
+	    setPortValueFromController(port, ev->data.control.value);
+	} else {
+	    return true; // pass through to plugin
+	}
+    }
+
+    return false;
+}
+
+void
+DSSIPluginInstance::run(const RealTime &blockTime)
+{
+    static snd_seq_event_t localEventBuffer[EVENT_BUFFER_SIZE];
+    int evCount = 0;
+
+    bool needLock = false;
+    if (m_descriptor->select_program) needLock = true;
+
+    if (needLock) {
+	if (!m_processLock.tryLock()) {
+	    for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) {
+		memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t));
+	    }
+	    return;
+	}
+    }
+
+    if (m_grouped) {
+	runGrouped(blockTime);
+	goto done;
+    }
+
+    if (!m_descriptor || !m_descriptor->run_synth) {
+	m_eventBuffer.skip(m_eventBuffer.getReadSpace());
+	if (m_descriptor->LADSPA_Plugin->run) {
+	    m_descriptor->LADSPA_Plugin->run(m_instanceHandle, m_blockSize);
+	} else {
+	    for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) {
+		memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t));
+	    }
+	}
+	m_run = true;
+	if (needLock) m_processLock.unlock();
+	return;
+    }
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::run(" << blockTime << ")" << std::endl;
+#endif
+
+#ifdef DEBUG_DSSI_PROCESS
+    if (m_eventBuffer.getReadSpace() > 0) {
+	std::cerr << "DSSIPluginInstance::run: event buffer has "
+		  << m_eventBuffer.getReadSpace() << " event(s) in it" << std::endl;
+    }
+#endif
+
+    while (m_eventBuffer.getReadSpace() > 0) {
+
+	snd_seq_event_t *ev = localEventBuffer + evCount;
+	*ev = m_eventBuffer.peekOne();
+	bool accept = true;
+
+	RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec);
+
+	int frameOffset = 0;
+	if (evTime > blockTime) {
+	    frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate);
+	}
+
+#ifdef DEBUG_DSSI_PROCESS
+	std::cerr << "DSSIPluginInstance::run: evTime " << evTime << ", blockTime " << blockTime << ", frameOffset " << frameOffset
+		  << ", blockSize " << m_blockSize << std::endl;
+	std::cerr << "Type: " << int(ev->type) << ", pitch: " << int(ev->data.note.note) << ", velocity: " << int(ev->data.note.velocity) << std::endl;
+#endif
+
+	if (frameOffset >= int(m_blockSize)) break;
+	if (frameOffset < 0) frameOffset = 0;
+
+	ev->time.tick = frameOffset;
+	m_eventBuffer.skip(1);
+
+	if (ev->type == SND_SEQ_EVENT_CONTROLLER) {
+	    accept = handleController(ev);
+	} else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) {
+	    m_pending.program = ev->data.control.value;
+	    accept = false;
+	}
+
+	if (accept) {
+	    if (++evCount >= EVENT_BUFFER_SIZE) break;
+	}
+    }
+
+    if (m_pending.program >= 0 && m_descriptor->select_program) {
+
+	int program = m_pending.program;
+	int bank = m_pending.lsb + 128 * m_pending.msb;
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::run: making select_program(" << bank << "," << program << ") call" << std::endl;
+#endif
+
+	m_pending.lsb = m_pending.msb = m_pending.program = -1;
+	m_descriptor->select_program(m_instanceHandle, bank, program);
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::run: made select_program(" << bank << "," << program << ") call" << std::endl;
+#endif
+    }
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::run: running with " << evCount << " events"
+	      << std::endl;
+#endif
+
+    m_descriptor->run_synth(m_instanceHandle, m_blockSize,
+			    localEventBuffer, evCount);
+
+#ifdef DEBUG_DSSI_PROCESS
+//    for (int i = 0; i < m_blockSize; ++i) {
+//	std::cout << m_outputBuffers[0][i] << " ";
+//	if (i % 8 == 0) std::cout << std::endl;
+//    }
+#endif
+
+ done:
+    if (needLock) m_processLock.unlock();
+
+    if (m_audioPortsOut.size() == 0) {
+	// copy inputs to outputs
+	for (size_t ch = 0; ch < m_idealChannelCount; ++ch) {
+	    size_t sch = ch % m_audioPortsIn.size();
+	    for (size_t i = 0; i < m_blockSize; ++i) {
+		m_outputBuffers[ch][i] = m_inputBuffers[sch][i];
+	    }
+	}
+    } else if (m_idealChannelCount < m_audioPortsOut.size()) {
+	if (m_idealChannelCount == 1) {
+	    // mix down to mono
+	    for (size_t ch = 1; ch < m_audioPortsOut.size(); ++ch) {
+		for (size_t i = 0; i < m_blockSize; ++i) {
+		    m_outputBuffers[0][i] += m_outputBuffers[ch][i];
+		}
+	    }
+	}
+    } else if (m_idealChannelCount > m_audioPortsOut.size()) {
+	// duplicate
+	for (size_t ch = m_audioPortsOut.size(); ch < m_idealChannelCount; ++ch) {
+	    size_t sch = (ch - m_audioPortsOut.size()) % m_audioPortsOut.size();
+	    for (size_t i = 0; i < m_blockSize; ++i) {
+		m_outputBuffers[ch][i] = m_outputBuffers[sch][i];
+	    }
+	}
+    }	
+
+    m_lastRunTime = blockTime;
+    m_run = true;
+}
+
+void
+DSSIPluginInstance::runGrouped(const RealTime &blockTime)
+{
+    // If something else in our group has just been called for this
+    // block time (but we haven't) then we should just write out the
+    // results and return; if we have just been called for this block
+    // time or nothing else in the group has been, we should run the
+    // whole group.
+
+    bool needRun = true;
+
+    PluginSet &s = m_groupMap[m_identifier];
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): this is " << this << "; " << s.size() << " elements in m_groupMap[" << m_identifier << "]" << std::endl;
+#endif
+
+    if (m_lastRunTime != blockTime) {
+	for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) {
+	    DSSIPluginInstance *instance = *i;
+	    if (instance != this && instance->m_lastRunTime == blockTime) {
+#ifdef DEBUG_DSSI_PROCESS
+		std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): plugin " << instance << " has already been run" << std::endl;
+#endif
+		needRun = false;
+	    }
+	}
+    }
+
+    if (!needRun) {
+#ifdef DEBUG_DSSI_PROCESS
+	std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): already run, returning" << std::endl;
+#endif
+	return;
+    }
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): I'm the first, running" << std::endl;
+#endif
+
+    size_t index = 0;
+    unsigned long *counts = (unsigned long *)
+	alloca(m_groupLocalEventBufferCount * sizeof(unsigned long));
+    LADSPA_Handle *instances = (LADSPA_Handle *)
+	alloca(m_groupLocalEventBufferCount * sizeof(LADSPA_Handle));
+
+    for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) {
+
+	if (index >= m_groupLocalEventBufferCount) break;
+
+	DSSIPluginInstance *instance = *i;
+	counts[index] = 0;
+	instances[index] = instance->m_instanceHandle;
+
+#ifdef DEBUG_DSSI_PROCESS
+	std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): running " << instance << std::endl;
+#endif
+
+	if (instance->m_pending.program >= 0 &&
+	    instance->m_descriptor->select_program) {
+	    int program = instance->m_pending.program;
+	    int bank = instance->m_pending.lsb + 128 * instance->m_pending.msb;
+	    instance->m_pending.lsb = instance->m_pending.msb = instance->m_pending.program = -1;
+	    instance->m_descriptor->select_program
+		(instance->m_instanceHandle, bank, program);
+	}
+
+	while (instance->m_eventBuffer.getReadSpace() > 0) {
+
+	    snd_seq_event_t *ev = m_groupLocalEventBuffers[index] + counts[index];
+	    *ev = instance->m_eventBuffer.peekOne();
+	    bool accept = true;
+
+	    RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec);
+
+	    int frameOffset = 0;
+	    if (evTime > blockTime) {
+		frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate);
+	    }
+
+#ifdef DEBUG_DSSI_PROCESS
+	    std::cerr << "DSSIPluginInstance::runGrouped: evTime " << evTime << ", frameOffset " << frameOffset
+		      << ", block size " << m_blockSize << std::endl;
+#endif
+
+	    if (frameOffset >= int(m_blockSize)) break;
+	    if (frameOffset < 0) frameOffset = 0;
+
+	    ev->time.tick = frameOffset;
+	    instance->m_eventBuffer.skip(1);
+
+	    if (ev->type == SND_SEQ_EVENT_CONTROLLER) {
+		accept = instance->handleController(ev);
+	    } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) {
+		instance->m_pending.program = ev->data.control.value;
+		accept = false;
+	    }
+
+	    if (accept) {
+		if (++counts[index] >= EVENT_BUFFER_SIZE) break;
+	    }
+	}
+
+	++index;
+    }
+
+    m_descriptor->run_multiple_synths(index,
+				      instances,
+				      m_blockSize,
+				      m_groupLocalEventBuffers,
+				      counts);
+}
+
+int
+DSSIPluginInstance::requestMidiSend(LADSPA_Handle instance,
+				    unsigned char ports,
+				    unsigned char channels)
+{
+    // This is called from a non-RT context (during instantiate)
+
+    std::cerr << "DSSIPluginInstance::requestMidiSend" << std::endl;
+    return 1;
+}
+
+void
+DSSIPluginInstance::midiSend(LADSPA_Handle instance,
+			     snd_seq_event_t *events,
+			     unsigned long eventCount)
+{
+    // This is likely to be called from an RT context
+
+    std::cerr << "DSSIPluginInstance::midiSend" << std::endl;
+}
+
+void
+DSSIPluginInstance::NonRTPluginThread::run()
+{
+    while (!m_exiting) {
+	m_runFunction(m_handle);
+	usleep(100000);
+    }
+}
+
+int
+DSSIPluginInstance::requestNonRTThread(LADSPA_Handle instance,
+				       void (*runFunction)(LADSPA_Handle))
+{
+    NonRTPluginThread *thread = new NonRTPluginThread(instance, runFunction);
+    m_threads[instance].insert(thread);
+    thread->start();
+    return 0;
+}
+
+void
+DSSIPluginInstance::deactivate()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::deactivate " << m_identifier << std::endl;
+#endif
+    if (!m_descriptor || !m_descriptor->LADSPA_Plugin->deactivate) return;
+
+    for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
+	m_backupControlPortsIn[i] = *m_controlPortsIn[i].second;
+    }
+
+    m_descriptor->LADSPA_Plugin->deactivate(m_instanceHandle);
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::deactivate " << m_identifier << " done" << std::endl;
+#endif
+
+    m_bufferScavenger.scavenge();
+}
+
+void
+DSSIPluginInstance::cleanup()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::cleanup " << m_identifier << std::endl;
+#endif
+    if (!m_descriptor) return;
+
+    if (!m_descriptor->LADSPA_Plugin->cleanup) {
+	std::cerr << "Bad plugin: plugin id "
+		  << m_descriptor->LADSPA_Plugin->UniqueID
+		  << ":" << m_descriptor->LADSPA_Plugin->Label
+		  << " has no cleanup method!" << std::endl;
+	return;
+    }
+
+    m_descriptor->LADSPA_Plugin->cleanup(m_instanceHandle);
+    m_instanceHandle = 0;
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::cleanup " << m_identifier << " done" << std::endl;
+#endif
+}
+