You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1207 lines
34 KiB
1207 lines
34 KiB
/*
|
|
Rosegarden
|
|
A sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <bownie@bownie.com>
|
|
|
|
The moral right of the authors to claim authorship of this work
|
|
has been asserted.
|
|
|
|
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. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
|
|
#include "DSSIPluginInstance.h"
|
|
#include "PluginIdentifier.h"
|
|
#include "LADSPAPluginFactory.h"
|
|
|
|
#ifdef HAVE_DSSI
|
|
|
|
//#define DEBUG_DSSI 1
|
|
//#define DEBUG_DSSI_PROCESS 1
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
#define EVENT_BUFFER_SIZE 1023
|
|
|
|
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);
|
|
|
|
|
|
DSSIPluginInstance::DSSIPluginInstance(PluginFactory *factory,
|
|
InstrumentId instrument,
|
|
TQString identifier,
|
|
int position,
|
|
unsigned long sampleRate,
|
|
size_t blockSize,
|
|
int idealChannelCount,
|
|
const DSSI_Descriptor* descriptor) :
|
|
RunnablePluginInstance(factory, identifier),
|
|
m_instrument(instrument),
|
|
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_runSinceReset(false),
|
|
m_bypassed(false),
|
|
m_grouped(false)
|
|
{
|
|
pthread_mutex_t initialisingMutex = PTHREAD_MUTEX_INITIALIZER;
|
|
memcpy(&m_processLock, &initialisingMutex, sizeof(pthread_mutex_t));
|
|
|
|
#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(PluginFactory *factory,
|
|
InstrumentId instrument,
|
|
TQString identifier,
|
|
int position,
|
|
unsigned long sampleRate,
|
|
size_t blockSize,
|
|
sample_t **inputBuffers,
|
|
sample_t **outputBuffers,
|
|
const DSSI_Descriptor* descriptor) :
|
|
RunnablePluginInstance(factory, identifier),
|
|
m_instrument(instrument),
|
|
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_runSinceReset(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);
|
|
m_portChangedSinceProgramChange.push_back(false);
|
|
|
|
} 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()
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
// std::cerr << "DSSIPluginInstance::getLatency(): m_latencyPort " << m_latencyPort << ", m_run " << m_run << std::endl;
|
|
#endif
|
|
|
|
if (m_latencyPort) {
|
|
if (!m_run) {
|
|
for (int i = 0; i < getAudioInputCount(); ++i) {
|
|
for (int j = 0; j < m_blockSize; ++j) {
|
|
m_inputBuffers[i][j] = 0.f;
|
|
}
|
|
}
|
|
run(RealTime::zeroTime);
|
|
}
|
|
#ifdef DEBUG_DSSI
|
|
|
|
std::cerr << "DSSIPluginInstance::getLatency(): latency is " << (size_t)(*m_latencyPort + 0.1) << std::endl;
|
|
#endif
|
|
|
|
return (size_t)(*m_latencyPort + 0.1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
DSSIPluginInstance::silence()
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance::silence: m_run " << m_run << ", m_runSinceReset " << m_runSinceReset << std::endl;
|
|
#endif
|
|
|
|
if (m_run && !m_runSinceReset) {
|
|
return ;
|
|
}
|
|
if (m_instanceHandle != 0) {
|
|
deactivate();
|
|
activate();
|
|
}
|
|
m_runSinceReset = false;
|
|
}
|
|
|
|
void
|
|
DSSIPluginInstance::discardEvents()
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance::discardEvents" << std::endl;
|
|
#endif
|
|
|
|
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;
|
|
|
|
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()
|
|
{
|
|
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 = TQString("%1. %2").arg(index).arg(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;
|
|
}
|
|
|
|
TQStringList
|
|
DSSIPluginInstance::getPrograms()
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance::getPrograms" << std::endl;
|
|
#endif
|
|
|
|
if (!m_descriptor)
|
|
return TQStringList();
|
|
|
|
checkProgramCache();
|
|
|
|
TQStringList programs;
|
|
|
|
for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
|
|
i != m_cachedPrograms.end(); ++i) {
|
|
programs.push_back(i->name);
|
|
}
|
|
|
|
return programs;
|
|
}
|
|
|
|
TQString
|
|
DSSIPluginInstance::getProgram(int bank, int program)
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance::getProgram(" << bank << "," << program << ")" << std::endl;
|
|
#endif
|
|
|
|
if (!m_descriptor)
|
|
return TQString();
|
|
|
|
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 TQString();
|
|
}
|
|
|
|
unsigned long
|
|
DSSIPluginInstance::getProgram(TQString name)
|
|
{
|
|
#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;
|
|
}
|
|
|
|
TQString
|
|
DSSIPluginInstance::getCurrentProgram()
|
|
{
|
|
return m_program;
|
|
}
|
|
|
|
void
|
|
DSSIPluginInstance::selectProgram(TQString program)
|
|
{
|
|
selectProgramAux(program, true);
|
|
}
|
|
|
|
void
|
|
DSSIPluginInstance::selectProgramAux(TQString program, bool backupPortValues)
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance[" << this << "]::selectProgram(" << program << ", " << backupPortValues << ")" << 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
|
|
pthread_mutex_lock(&m_processLock);
|
|
m_descriptor->select_program(m_instanceHandle, bankNo, programNo);
|
|
pthread_mutex_unlock(&m_processLock);
|
|
|
|
#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;
|
|
m_portChangedSinceProgramChange[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
DSSIPluginInstance::activate()
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance[" << this << "]::activate" << std::endl;
|
|
#endif
|
|
|
|
if (!m_descriptor || !m_descriptor->LADSPA_Plugin->activate)
|
|
return ;
|
|
m_descriptor->LADSPA_Plugin->activate(m_instanceHandle);
|
|
|
|
for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
|
|
if (m_portChangedSinceProgramChange[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];
|
|
}
|
|
}
|
|
|
|
if (!m_program.isNull()) {
|
|
#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) {
|
|
if (m_portChangedSinceProgramChange[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 ;
|
|
#ifdef DEBUG_DSSI
|
|
|
|
std::cerr << "DSSIPluginInstance::connectPorts: " << m_audioPortsIn.size()
|
|
<< " audio ports in, " << m_audioPortsOut.size() << " out, "
|
|
<< m_outputBufferCount << " output buffers" << std::endl;
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void
|
|
DSSIPluginInstance::setPortValue(unsigned int portNumber, float value)
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance[" << this << "]::setPortValue(" << portNumber << ") to " << value << std::endl;
|
|
#endif
|
|
|
|
for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
|
|
if (m_controlPortsIn[i].first == portNumber) {
|
|
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[i].second) = value;
|
|
m_backupControlPortsIn[i] = value;
|
|
m_portChangedSinceProgramChange[i] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
setPortValue(port, value);
|
|
}
|
|
|
|
float
|
|
DSSIPluginInstance::getPortValue(unsigned int portNumber)
|
|
{
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance::getPortValue(" << portNumber << ")" << std::endl;
|
|
#endif
|
|
|
|
for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
|
|
if (m_controlPortsIn[i].first == portNumber) {
|
|
return (*m_controlPortsIn[i].second);
|
|
}
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
TQString
|
|
DSSIPluginInstance::configure(TQString key,
|
|
TQString value)
|
|
{
|
|
if (!m_descriptor || !m_descriptor->configure)
|
|
return TQString();
|
|
|
|
if (key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
|
|
#ifdef DSSI_PROJECT_DIRECTORY_KEY
|
|
key = DSSI_PROJECT_DIRECTORY_KEY;
|
|
#else
|
|
|
|
return TQString();
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#ifdef DEBUG_DSSI
|
|
std::cerr << "DSSIPluginInstance::configure(" << key << "," << value << ")" << std::endl;
|
|
#endif
|
|
|
|
char *message = m_descriptor->configure(m_instanceHandle, key.ascii(), value.ascii());
|
|
|
|
m_programCacheValid = false;
|
|
|
|
TQString 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 = TQString(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 (pthread_mutex_trylock(&m_processLock) != 0) {
|
|
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;
|
|
m_runSinceReset = true;
|
|
if (needLock)
|
|
pthread_mutex_unlock(&m_processLock);
|
|
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.peek();
|
|
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)
|
|
pthread_mutex_unlock(&m_processLock);
|
|
|
|
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;
|
|
m_runSinceReset = 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.peek();
|
|
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);
|
|
}
|
|
|
|
|
|
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
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif // HAVE_DSSI
|
|
|
|
|