Mercurial > hg > svcore
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 +} +