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.
rosegarden/src/sound/AudioProcess.cpp

2462 lines
68 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 "AudioProcess.h"
#include "RunnablePluginInstance.h"
#include "PlayableAudioFile.h"
#include "RecordableAudioFile.h"
#include "WAVAudioFile.h"
#include "MappedStudio.h"
#include "Profiler.h"
#include "AudioLevel.h"
#include "AudioPlayQueue.h"
#include "PluginFactory.h"
#include <sys/time.h>
#include <pthread.h>
#include <cmath>
//#define DEBUG_THREAD_CREATE_DESTROY 1
//#define DEBUG_BUSS_MIXER 1
//#define DEBUG_MIXER 1
//#define DEBUG_MIXER_LIGHTWEIGHT 1
//#define DEBUG_LOCKS 1
//#define DEBUG_READER 1
//#define DEBUG_WRITER 1
namespace Rosegarden
{
/* Branch-free optimizer-resistant denormal killer courtesy of Simon
Jenkins on LAD: */
static inline float flushToZero(volatile float f)
{
f += 9.8607615E-32f;
return f - 9.8607615E-32f;
}
static inline void denormalKill(float *buffer, int size)
{
for (int i = 0; i < size; ++i) {
buffer[i] = flushToZero(buffer[i]);
}
}
AudioThread::AudioThread(std::string name,
SoundDriver *driver,
unsigned int sampleRate) :
m_name(name),
m_driver(driver),
m_sampleRate(sampleRate),
m_thread(0),
m_running(false),
m_exiting(false)
{
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << "AudioThread::AudioThread() [" << m_name << "]" << std::endl;
#endif
pthread_mutex_t initialisingMutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&m_lock, &initialisingMutex, sizeof(pthread_mutex_t));
pthread_cond_t initialisingCondition = PTHREAD_COND_INITIALIZER;
memcpy(&m_condition, &initialisingCondition, sizeof(pthread_cond_t));
}
AudioThread::~AudioThread()
{
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << "AudioThread::~AudioThread() [" << m_name << "]" << std::endl;
#endif
if (m_thread) {
pthread_mutex_destroy(&m_lock);
m_thread = 0;
}
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << "AudioThread::~AudioThread() exiting" << std::endl;
#endif
}
void
AudioThread::run()
{
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << m_name << "::run()" << std::endl;
#endif
pthread_attr_t attr;
pthread_attr_init(&attr);
int priority = getPriority();
if (priority > 0) {
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) {
std::cerr << m_name << "::run: WARNING: couldn't set FIFO scheduling "
<< "on new thread" << std::endl;
pthread_attr_init(&attr); // reset to safety
} else {
struct sched_param param;
memset(&param, 0, sizeof(struct sched_param));
param.sched_priority = priority;
if (pthread_attr_setschedparam(&attr, &param)) {
std::cerr << m_name << "::run: WARNING: couldn't set priority "
<< priority << " on new thread" << std::endl;
pthread_attr_init(&attr); // reset to safety
}
}
}
pthread_attr_setstacksize(&attr, 1048576);
int rv = pthread_create(&m_thread, &attr, staticThreadRun, this);
if (rv != 0 && priority > 0) {
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << m_name << "::run: WARNING: unable to start RT thread;"
<< "\ntrying again with normal scheduling" << std::endl;
#endif
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 1048576);
rv = pthread_create(&m_thread, &attr, staticThreadRun, this);
}
if (rv != 0) {
// This is quite fatal.
std::cerr << m_name << "::run: ERROR: failed to start thread!" << std::endl;
::exit(1);
}
m_running = true;
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << m_name << "::run() done" << std::endl;
#endif
}
void
AudioThread::terminate()
{
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::string name = m_name;
std::cerr << name << "::terminate()" << std::endl;
#endif
m_running = false;
if (m_thread) {
pthread_cancel(m_thread);
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << name << "::terminate(): cancel requested" << std::endl;
#endif
int rv = pthread_join(m_thread, 0);
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << name << "::terminate(): thread exited with return value " << rv << std::endl;
#endif
}
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << name << "::terminate(): done" << std::endl;
#endif
}
void *
AudioThread::staticThreadRun(void *arg)
{
AudioThread *inst = static_cast<AudioThread *>(arg);
if (!inst)
return 0;
pthread_cleanup_push(staticThreadCleanup, arg);
inst->getLock();
inst->m_exiting = false;
inst->threadRun();
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << inst->m_name << "::staticThreadRun(): threadRun exited" << std::endl;
#endif
inst->releaseLock();
pthread_cleanup_pop(0);
return 0;
}
void
AudioThread::staticThreadCleanup(void *arg)
{
AudioThread *inst = static_cast<AudioThread *>(arg);
if (!inst || inst->m_exiting)
return ;
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::string name = inst->m_name;
std::cerr << name << "::staticThreadCleanup()" << std::endl;
#endif
inst->m_exiting = true;
inst->releaseLock();
#ifdef DEBUG_THREAD_CREATE_DESTROY
std::cerr << name << "::staticThreadCleanup() done" << std::endl;
#endif
}
int
AudioThread::getLock()
{
int rv;
#ifdef DEBUG_LOCKS
std::cerr << m_name << "::getLock()" << std::endl;
#endif
rv = pthread_mutex_lock(&m_lock);
#ifdef DEBUG_LOCKS
std::cerr << "OK" << std::endl;
#endif
return rv;
}
int
AudioThread::tryLock()
{
int rv;
#ifdef DEBUG_LOCKS
std::cerr << m_name << "::tryLock()" << std::endl;
#endif
rv = pthread_mutex_trylock(&m_lock);
#ifdef DEBUG_LOCKS
std::cerr << "OK (rv is " << rv << ")" << std::endl;
#endif
return rv;
}
int
AudioThread::releaseLock()
{
int rv;
#ifdef DEBUG_LOCKS
std::cerr << m_name << "::releaseLock()" << std::endl;
#endif
rv = pthread_mutex_unlock(&m_lock);
#ifdef DEBUG_LOCKS
std::cerr << "OK" << std::endl;
#endif
return rv;
}
void
AudioThread::signal()
{
#ifdef DEBUG_LOCKS
std::cerr << m_name << "::signal()" << std::endl;
#endif
pthread_cond_signal(&m_condition);
}
AudioBussMixer::AudioBussMixer(SoundDriver *driver,
AudioInstrumentMixer *instrumentMixer,
unsigned int sampleRate,
unsigned int blockSize) :
AudioThread("AudioBussMixer", driver, sampleRate),
m_instrumentMixer(instrumentMixer),
m_blockSize(blockSize),
m_bussCount(0)
{
// nothing else here
}
AudioBussMixer::~AudioBussMixer()
{
for (unsigned int i = 0; i < m_processBuffers.size(); ++i) {
delete[] m_processBuffers[i];
}
}
AudioBussMixer::BufferRec::~BufferRec()
{
for (size_t i = 0; i < buffers.size(); ++i)
delete buffers[i];
}
void
AudioBussMixer::generateBuffers()
{
// Not RT safe
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::generateBuffers" << std::endl;
#endif
// This returns one too many, as the master is counted as buss 0
m_bussCount =
m_driver->getMappedStudio()->getObjectCount(MappedStudio::AudioBuss) - 1;
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::generateBuffers: have " << m_bussCount << " busses" << std::endl;
#endif
int bufferSamples = m_blockSize;
if (!m_driver->getLowLatencyMode()) {
RealTime bufferLength = m_driver->getAudioMixBufferLength();
int bufferSamples = RealTime::realTime2Frame(bufferLength, m_sampleRate);
bufferSamples = ((bufferSamples / m_blockSize) + 1) * m_blockSize;
}
for (int i = 0; i < m_bussCount; ++i) {
BufferRec &rec = m_bufferMap[i];
if (rec.buffers.size() == 2)
continue;
for (unsigned int ch = 0; ch < 2; ++ch) {
RingBuffer<sample_t> *rb = new RingBuffer<sample_t>(bufferSamples);
if (!rb->mlock()) {
// std::cerr << "WARNING: AudioBussMixer::generateBuffers: couldn't lock ring buffer into real memory, performance may be impaired" << std::endl;
}
rec.buffers.push_back(rb);
}
MappedAudioBuss *mbuss =
m_driver->getMappedStudio()->getAudioBuss(i + 1); // master is 0
if (mbuss) {
float level = 0.0;
(void)mbuss->getProperty(MappedAudioBuss::Level, level);
float pan = 0.0;
(void)mbuss->getProperty(MappedAudioBuss::Pan, pan);
setBussLevels(i + 1, level, pan);
}
}
if (m_processBuffers.size() == 0) {
m_processBuffers.push_back(new sample_t[m_blockSize]);
m_processBuffers.push_back(new sample_t[m_blockSize]);
}
}
void
AudioBussMixer::fillBuffers(const RealTime &currentTime)
{
// Not RT safe
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::fillBuffers" << std::endl;
#endif
emptyBuffers();
m_instrumentMixer->fillBuffers(currentTime);
kick();
}
void
AudioBussMixer::emptyBuffers()
{
// Not RT safe
getLock();
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::emptyBuffers" << std::endl;
#endif
// We can't generate buffers before this, because we don't know how
// many busses there are
generateBuffers();
for (int i = 0; i < m_bussCount; ++i) {
m_bufferMap[i].dormant = true;
for (int ch = 0; ch < 2; ++ch) {
if (int(m_bufferMap[i].buffers.size()) > ch) {
m_bufferMap[i].buffers[ch]->reset();
}
}
}
releaseLock();
}
void
AudioBussMixer::kick(bool wantLock, bool signalInstrumentMixer)
{
// Needs to be RT safe if wantLock is not specified
if (wantLock)
getLock();
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::kick" << std::endl;
#endif
processBlocks();
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::kick: processed" << std::endl;
#endif
if (wantLock)
releaseLock();
if (signalInstrumentMixer) {
m_instrumentMixer->signal();
}
}
void
AudioBussMixer::setBussLevels(int bussId, float dB, float pan)
{
// No requirement to be RT safe
if (bussId == 0)
return ; // master
int buss = bussId - 1;
BufferRec &rec = m_bufferMap[buss];
float volume = AudioLevel::dB_to_multiplier(dB);
rec.gainLeft = volume * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
rec.gainRight = volume * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
}
void
AudioBussMixer::updateInstrumentConnections()
{
// Not RT safe
if (m_bussCount <= 0)
generateBuffers();
InstrumentId audioInstrumentBase;
int audioInstruments;
m_driver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
InstrumentId synthInstrumentBase;
int synthInstruments;
m_driver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
for (int buss = 0; buss < m_bussCount; ++buss) {
MappedAudioBuss *mbuss =
m_driver->getMappedStudio()->getAudioBuss(buss + 1); // master is 0
if (!mbuss) {
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::updateInstrumentConnections: buss " << buss << " not found" << std::endl;
#endif
continue;
}
BufferRec &rec = m_bufferMap[buss];
while (int(rec.instruments.size()) < audioInstruments + synthInstruments) {
rec.instruments.push_back(false);
}
std::vector<InstrumentId> instruments = mbuss->getInstruments();
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
size_t j = 0;
for (j = 0; j < instruments.size(); ++j) {
if (instruments[j] == id) {
rec.instruments[i] = true;
break;
}
}
if (j == instruments.size())
rec.instruments[i] = false;
}
}
}
void
AudioBussMixer::processBlocks()
{
// Needs to be RT safe
if (m_bussCount == 0)
return ;
#ifdef DEBUG_BUSS_MIXER
if (m_driver->isPlaying())
std::cerr << "AudioBussMixer::processBlocks" << std::endl;
#endif
InstrumentId audioInstrumentBase;
int audioInstruments;
m_driver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
InstrumentId synthInstrumentBase;
int synthInstruments;
m_driver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
bool *processedInstruments = (bool *)alloca
((audioInstruments + synthInstruments) * sizeof(bool));
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
processedInstruments[i] = false;
}
int minBlocks = 0;
bool haveMinBlocks = false;
for (int buss = 0; buss < m_bussCount; ++buss) {
BufferRec &rec = m_bufferMap[buss];
float gain[2];
gain[0] = rec.gainLeft;
gain[1] = rec.gainRight;
// The dormant calculation here depends on the buffer length
// for this mixer being the same as that for the instrument mixer
size_t minSpace = 0;
for (int ch = 0; ch < 2; ++ch) {
size_t w = rec.buffers[ch]->getWriteSpace();
if (ch == 0 || w < minSpace)
minSpace = w;
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::processBlocks: buss " << buss << ": write space " << w << " on channel " << ch << std::endl;
#endif
if (minSpace == 0)
break;
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
// is this instrument on this buss?
if (int(rec.instruments.size()) <= i ||
!rec.instruments[i])
continue;
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
if (m_instrumentMixer->isInstrumentEmpty(id))
continue;
RingBuffer<sample_t, 2> *rb =
m_instrumentMixer->getRingBuffer(id, ch);
if (rb) {
size_t r = rb->getReadSpace(1);
if (r < minSpace)
minSpace = r;
#ifdef DEBUG_BUSS_MIXER
if (id == 1000) {
std::cerr << "AudioBussMixer::processBlocks: buss " << buss << ": read space " << r << " on instrument " << id << ", channel " << ch << std::endl;
}
#endif
if (minSpace == 0)
break;
}
}
if (minSpace == 0)
break;
}
int blocks = minSpace / m_blockSize;
if (!haveMinBlocks || (blocks < minBlocks)) {
minBlocks = blocks;
haveMinBlocks = true;
}
#ifdef DEBUG_BUSS_MIXER
if (m_driver->isPlaying())
std::cerr << "AudioBussMixer::processBlocks: doing " << blocks << " blocks at block size " << m_blockSize << std::endl;
#endif
for (int block = 0; block < blocks; ++block) {
memset(m_processBuffers[0], 0, m_blockSize * sizeof(sample_t));
memset(m_processBuffers[1], 0, m_blockSize * sizeof(sample_t));
bool dormant = true;
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
// is this instrument on this buss?
if (int(rec.instruments.size()) <= i ||
!rec.instruments[i])
continue;
if (processedInstruments[i]) {
// we aren't set up to process any instrument to
// more than one buss
continue;
} else {
processedInstruments[i] = true;
}
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
if (m_instrumentMixer->isInstrumentEmpty(id))
continue;
if (m_instrumentMixer->isInstrumentDormant(id)) {
for (int ch = 0; ch < 2; ++ch) {
RingBuffer<sample_t, 2> *rb =
m_instrumentMixer->getRingBuffer(id, ch);
if (rb)
rb->skip(m_blockSize,
1);
}
} else {
dormant = false;
for (int ch = 0; ch < 2; ++ch) {
RingBuffer<sample_t, 2> *rb =
m_instrumentMixer->getRingBuffer(id, ch);
if (rb)
rb->readAdding(m_processBuffers[ch],
m_blockSize,
1);
}
}
}
if (m_instrumentMixer) {
AudioInstrumentMixer::PluginList &plugins =
m_instrumentMixer->getBussPlugins(buss + 1);
// This will have to do for now!
if (!plugins.empty())
dormant = false;
for (AudioInstrumentMixer::PluginList::iterator pli =
plugins.begin(); pli != plugins.end(); ++pli) {
RunnablePluginInstance *plugin = *pli;
if (!plugin || plugin->isBypassed())
continue;
unsigned int ch = 0;
while (ch < plugin->getAudioInputCount()) {
if (ch < 2) {
memcpy(plugin->getAudioInputBuffers()[ch],
m_processBuffers[ch],
m_blockSize * sizeof(sample_t));
} else {
memset(plugin->getAudioInputBuffers()[ch], 0,
m_blockSize * sizeof(sample_t));
}
++ch;
}
#ifdef DEBUG_BUSS_MIXER
std::cerr << "Running buss plugin with " << plugin->getAudioInputCount()
<< " inputs, " << plugin->getAudioOutputCount() << " outputs" << std::endl;
#endif
// We don't currently maintain a record of our
// frame time in the buss mixer. This will screw
// up any plugin that requires a good frame count:
// at the moment that only means DSSI effects
// plugins using run_multiple_synths, which would
// be an unusual although plausible combination
plugin->run(RealTime::zeroTime);
ch = 0;
while (ch < 2 && ch < plugin->getAudioOutputCount()) {
denormalKill(plugin->getAudioOutputBuffers()[ch],
m_blockSize);
memcpy(m_processBuffers[ch],
plugin->getAudioOutputBuffers()[ch],
m_blockSize * sizeof(sample_t));
++ch;
}
}
}
for (int ch = 0; ch < 2; ++ch) {
if (dormant) {
rec.buffers[ch]->zero(m_blockSize);
} else {
for (size_t j = 0; j < m_blockSize; ++j) {
m_processBuffers[ch][j] *= gain[ch];
}
rec.buffers[ch]->write(m_processBuffers[ch], m_blockSize);
}
}
rec.dormant = dormant;
#ifdef DEBUG_BUSS_MIXER
if (m_driver->isPlaying())
std::cerr << "AudioBussMixer::processBlocks: buss " << buss << (dormant ? " dormant" : " not dormant") << std::endl;
#endif
}
}
// any unprocessed instruments need to be skipped, or they'll block
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
if (processedInstruments[i])
continue;
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
if (m_instrumentMixer->isInstrumentEmpty(id))
continue;
for (int ch = 0; ch < 2; ++ch) {
RingBuffer<sample_t, 2> *rb =
m_instrumentMixer->getRingBuffer(id, ch);
if (rb)
rb->skip(m_blockSize * minBlocks,
1);
}
}
#ifdef DEBUG_BUSS_MIXER
std::cerr << "AudioBussMixer::processBlocks: done" << std::endl;
#endif
}
void
AudioBussMixer::threadRun()
{
while (!m_exiting) {
if (m_driver->areClocksRunning()) {
kick(false);
}
RealTime t = m_driver->getAudioMixBufferLength();
t = t / 2;
if (t < RealTime(0, 10000000))
t = RealTime(0, 10000000); // 10ms minimum
struct timeval now;
gettimeofday(&now, 0);
t = t + RealTime(now.tv_sec, now.tv_usec * 1000);
struct timespec timeout;
timeout.tv_sec = t.sec;
timeout.tv_nsec = t.nsec;
pthread_cond_timedwait(&m_condition, &m_lock, &timeout);
pthread_testcancel();
}
}
AudioInstrumentMixer::AudioInstrumentMixer(SoundDriver *driver,
AudioFileReader *fileReader,
unsigned int sampleRate,
unsigned int blockSize) :
AudioThread("AudioInstrumentMixer", driver, sampleRate),
m_fileReader(fileReader),
m_bussMixer(0),
m_blockSize(blockSize)
{
// Pregenerate empty plugin slots
InstrumentId audioInstrumentBase;
int audioInstruments;
m_driver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
InstrumentId synthInstrumentBase;
int synthInstruments;
m_driver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
PluginList &list = m_plugins[id];
for (int j = 0; j < int(Instrument::PLUGIN_COUNT); ++j) {
list.push_back(0);
}
if (i >= audioInstruments) {
m_synths[id] = 0;
}
}
// Leave the buffer map and process buffer list empty for now.
// The buffer length can change between plays, so we always
// examine the buffers in fillBuffers and are prepared to
// regenerate from scratch if necessary. Don't like it though.
}
AudioInstrumentMixer::~AudioInstrumentMixer()
{
std::cerr << "AudioInstrumentMixer::~AudioInstrumentMixer" << std::endl;
// BufferRec dtor will handle the BufferMap
removeAllPlugins();
for (std::vector<sample_t *>::iterator i = m_processBuffers.begin();
i != m_processBuffers.end(); ++i) {
delete[] *i;
}
std::cerr << "AudioInstrumentMixer::~AudioInstrumentMixer exiting" << std::endl;
}
AudioInstrumentMixer::BufferRec::~BufferRec()
{
for (size_t i = 0; i < buffers.size(); ++i)
delete buffers[i];
}
void
AudioInstrumentMixer::setPlugin(InstrumentId id, int position, TQString identifier)
{
// Not RT safe
std::cerr << "AudioInstrumentMixer::setPlugin(" << id << ", " << position << ", " << identifier.ascii() << ")" << std::endl;
int channels = 2;
if (m_bufferMap.find(id) != m_bufferMap.end()) {
channels = m_bufferMap[id].channels;
}
RunnablePluginInstance *instance = 0;
PluginFactory *factory = PluginFactory::instanceFor(identifier);
if (factory) {
instance = factory->instantiatePlugin(identifier,
id,
position,
m_sampleRate,
m_blockSize,
channels);
if (instance && !instance->isOK()) {
std::cerr << "AudioInstrumentMixer::setPlugin(" << id << ", " << position
<< ": instance is not OK" << std::endl;
delete instance;
instance = 0;
}
} else {
std::cerr << "AudioInstrumentMixer::setPlugin: No factory for identifier "
<< identifier.ascii() << std::endl;
}
RunnablePluginInstance *oldInstance = 0;
if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
oldInstance = m_synths[id];
m_synths[id] = instance;
} else {
PluginList &list = m_plugins[id];
if (position < Instrument::PLUGIN_COUNT) {
while (position >= (int)list.size()) {
list.push_back(0);
}
oldInstance = list[position];
list[position] = instance;
} else {
std::cerr << "AudioInstrumentMixer::setPlugin: No position "
<< position << " for instrument " << id << std::endl;
delete instance;
}
}
if (oldInstance) {
m_driver->claimUnwantedPlugin(oldInstance);
}
}
void
AudioInstrumentMixer::removePlugin(InstrumentId id, int position)
{
// Not RT safe
std::cerr << "AudioInstrumentMixer::removePlugin(" << id << ", " << position << ")" << std::endl;
RunnablePluginInstance *oldInstance = 0;
if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
if (m_synths[id]) {
oldInstance = m_synths[id];
m_synths[id] = 0;
}
} else {
PluginList &list = m_plugins[id];
if (position < (int)list.size()) {
oldInstance = list[position];
list[position] = 0;
}
}
if (oldInstance) {
m_driver->claimUnwantedPlugin(oldInstance);
}
}
void
AudioInstrumentMixer::removeAllPlugins()
{
// Not RT safe
std::cerr << "AudioInstrumentMixer::removeAllPlugins" << std::endl;
for (SynthPluginMap::iterator i = m_synths.begin();
i != m_synths.end(); ++i) {
if (i->second) {
RunnablePluginInstance *instance = i->second;
i->second = 0;
m_driver->claimUnwantedPlugin(instance);
}
}
for (PluginMap::iterator j = m_plugins.begin();
j != m_plugins.end(); ++j) {
PluginList &list = j->second;
for (PluginList::iterator i = list.begin(); i != list.end(); ++i) {
RunnablePluginInstance *instance = *i;
*i = 0;
m_driver->claimUnwantedPlugin(instance);
}
}
}
RunnablePluginInstance *
AudioInstrumentMixer::getPluginInstance(InstrumentId id, int position)
{
// Not RT safe
if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
return m_synths[id];
} else {
PluginList &list = m_plugins[id];
if (position < int(list.size()))
return list[position];
}
return 0;
}
void
AudioInstrumentMixer::setPluginPortValue(InstrumentId id, int position,
unsigned int port, float value)
{
// Not RT safe
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance) {
instance->setPortValue(port, value);
}
}
float
AudioInstrumentMixer::getPluginPortValue(InstrumentId id, int position,
unsigned int port)
{
// Not RT safe
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance) {
return instance->getPortValue(port);
}
return 0;
}
void
AudioInstrumentMixer::setPluginBypass(InstrumentId id, int position, bool bypass)
{
// Not RT safe
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
instance->setBypassed(bypass);
}
TQStringList
AudioInstrumentMixer::getPluginPrograms(InstrumentId id, int position)
{
// Not RT safe
TQStringList programs;
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
programs = instance->getPrograms();
return programs;
}
TQString
AudioInstrumentMixer::getPluginProgram(InstrumentId id, int position)
{
// Not RT safe
TQString program;
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
program = instance->getCurrentProgram();
return program;
}
TQString
AudioInstrumentMixer::getPluginProgram(InstrumentId id, int position, int bank,
int program)
{
// Not RT safe
TQString programName;
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
programName = instance->getProgram(bank, program);
return programName;
}
unsigned long
AudioInstrumentMixer::getPluginProgram(InstrumentId id, int position, TQString name)
{
// Not RT safe
unsigned long program = 0;
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
program = instance->getProgram(name);
return program;
}
void
AudioInstrumentMixer::setPluginProgram(InstrumentId id, int position, TQString program)
{
// Not RT safe
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
instance->selectProgram(program);
}
TQString
AudioInstrumentMixer::configurePlugin(InstrumentId id, int position, TQString key, TQString value)
{
// Not RT safe
RunnablePluginInstance *instance = getPluginInstance(id, position);
if (instance)
return instance->configure(key, value);
return TQString();
}
void
AudioInstrumentMixer::discardPluginEvents()
{
getLock();
if (m_bussMixer) m_bussMixer->getLock();
for (SynthPluginMap::iterator j = m_synths.begin();
j != m_synths.end(); ++j) {
RunnablePluginInstance *instance = j->second;
if (instance) instance->discardEvents();
}
for (PluginMap::iterator j = m_plugins.begin();
j != m_plugins.end(); ++j) {
InstrumentId id = j->first;
for (PluginList::iterator i = m_plugins[id].begin();
i != m_plugins[id].end(); ++i) {
RunnablePluginInstance *instance = *i;
if (instance) instance->discardEvents();
}
}
if (m_bussMixer) m_bussMixer->releaseLock();
releaseLock();
}
void
AudioInstrumentMixer::resetAllPlugins(bool discardEvents)
{
// Not RT safe
// lock required here to protect against calling
// activate/deactivate at the same time as run()
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::resetAllPlugins!" << std::endl;
if (discardEvents) std::cerr << "(discardEvents true)" << std::endl;
#endif
getLock();
if (m_bussMixer)
m_bussMixer->getLock();
for (SynthPluginMap::iterator j = m_synths.begin();
j != m_synths.end(); ++j) {
InstrumentId id = j->first;
int channels = 2;
if (m_bufferMap.find(id) != m_bufferMap.end()) {
channels = m_bufferMap[id].channels;
}
RunnablePluginInstance *instance = j->second;
if (instance) {
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::resetAllPlugins: (re)setting " << channels << " channels on synth for instrument " << id << std::endl;
#endif
if (discardEvents)
instance->discardEvents();
instance->setIdealChannelCount(channels);
}
}
for (PluginMap::iterator j = m_plugins.begin();
j != m_plugins.end(); ++j) {
InstrumentId id = j->first;
int channels = 2;
if (m_bufferMap.find(id) != m_bufferMap.end()) {
channels = m_bufferMap[id].channels;
}
for (PluginList::iterator i = m_plugins[id].begin();
i != m_plugins[id].end(); ++i) {
RunnablePluginInstance *instance = *i;
if (instance) {
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::resetAllPlugins: (re)setting " << channels << " channels on plugin for instrument " << id << std::endl;
#endif
if (discardEvents)
instance->discardEvents();
instance->setIdealChannelCount(channels);
}
}
}
if (m_bussMixer)
m_bussMixer->releaseLock();
releaseLock();
}
void
AudioInstrumentMixer::destroyAllPlugins()
{
// Not RT safe
getLock();
if (m_bussMixer)
m_bussMixer->getLock();
// Delete immediately, as we're probably exiting here -- don't use
// the scavenger.
std::cerr << "AudioInstrumentMixer::destroyAllPlugins" << std::endl;
for (SynthPluginMap::iterator j = m_synths.begin();
j != m_synths.end(); ++j) {
RunnablePluginInstance *instance = j->second;
j->second = 0;
delete instance;
}
for (PluginMap::iterator j = m_plugins.begin();
j != m_plugins.end(); ++j) {
InstrumentId id = j->first;
for (PluginList::iterator i = m_plugins[id].begin();
i != m_plugins[id].end(); ++i) {
RunnablePluginInstance *instance = *i;
*i = 0;
delete instance;
}
}
// and tell the driver to get rid of anything already scavenged.
m_driver->scavengePlugins();
if (m_bussMixer)
m_bussMixer->releaseLock();
releaseLock();
}
size_t
AudioInstrumentMixer::getPluginLatency(unsigned int id)
{
// Not RT safe
size_t latency = 0;
RunnablePluginInstance *synth = m_synths[id];
if (synth)
latency += m_synths[id]->getLatency();
for (PluginList::iterator i = m_plugins[id].begin();
i != m_plugins[id].end(); ++i) {
RunnablePluginInstance *plugin = *i;
if (plugin)
latency += plugin->getLatency();
}
return latency;
}
void
AudioInstrumentMixer::generateBuffers()
{
// Not RT safe
InstrumentId audioInstrumentBase;
int audioInstruments;
m_driver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
InstrumentId synthInstrumentBase;
int synthInstruments;
m_driver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
unsigned int maxChannels = 0;
int bufferSamples = m_blockSize;
if (!m_driver->getLowLatencyMode()) {
RealTime bufferLength = m_driver->getAudioMixBufferLength();
int bufferSamples = RealTime::realTime2Frame(bufferLength, m_sampleRate);
bufferSamples = ((bufferSamples / m_blockSize) + 1) * m_blockSize;
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::generateBuffers: Buffer length is " << bufferLength << "; buffer samples " << bufferSamples << " (sample rate " << m_sampleRate << ")" << std::endl;
#endif
}
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
// Get a fader for this instrument - if we can't then this
// isn't a valid audio track.
MappedAudioFader *fader = m_driver->getMappedStudio()->getAudioFader(id);
if (!fader) {
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::generateBuffers: no fader for audio instrument " << id << std::endl;
#endif
continue;
}
float fch = 2;
(void)fader->getProperty(MappedAudioFader::Channels, fch);
unsigned int channels = (unsigned int)fch;
BufferRec &rec = m_bufferMap[id];
rec.channels = channels;
// We always have stereo buffers (for output of pan)
// even on a mono instrument.
if (channels < 2)
channels = 2;
if (channels > maxChannels)
maxChannels = channels;
bool replaceBuffers = (rec.buffers.size() > channels);
if (!replaceBuffers) {
for (size_t i = 0; i < rec.buffers.size(); ++i) {
if (rec.buffers[i]->getSize() != bufferSamples) {
replaceBuffers = true;
break;
}
}
}
if (replaceBuffers) {
for (size_t i = 0; i < rec.buffers.size(); ++i) {
delete rec.buffers[i];
}
rec.buffers.clear();
}
while (rec.buffers.size() < channels) {
// All our ringbuffers are set up for two readers: the
// buss mix thread and the main process thread for
// e.g. JACK. The main process thread gets the zero-id
// reader, so it gets the same API as if this was a
// single-reader buffer; the buss mixer has to remember to
// explicitly request reader 1.
RingBuffer<sample_t, 2> *rb =
new RingBuffer<sample_t, 2>(bufferSamples);
if (!rb->mlock()) {
// std::cerr << "WARNING: AudioInstrumentMixer::generateBuffers: couldn't lock ring buffer into real memory, performance may be impaired" << std::endl;
}
rec.buffers.push_back(rb);
}
float level = 0.0;
(void)fader->getProperty(MappedAudioFader::FaderLevel, level);
float pan = 0.0;
(void)fader->getProperty(MappedAudioFader::Pan, pan);
setInstrumentLevels(id, level, pan);
}
// Make room for up to 16 busses here, to avoid reshuffling later
int busses = 16;
if (m_bussMixer)
busses = std::max(busses, m_bussMixer->getBussCount());
for (int i = 0; i < busses; ++i) {
PluginList &list = m_plugins[i + 1];
while (list.size() < Instrument::PLUGIN_COUNT) {
list.push_back(0);
}
}
while (m_processBuffers.size() > maxChannels) {
std::vector<sample_t *>::iterator bi = m_processBuffers.end();
--bi;
delete[] *bi;
m_processBuffers.erase(bi);
}
while (m_processBuffers.size() < maxChannels) {
m_processBuffers.push_back(new sample_t[m_blockSize]);
}
}
void
AudioInstrumentMixer::fillBuffers(const RealTime &currentTime)
{
// Not RT safe
emptyBuffers(currentTime);
getLock();
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::fillBuffers(" << currentTime << ")" << std::endl;
#endif
bool discard;
processBlocks(discard);
releaseLock();
}
void
AudioInstrumentMixer::allocateBuffers()
{
// Not RT safe
getLock();
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::allocateBuffers()" << std::endl;
#endif
generateBuffers();
releaseLock();
}
void
AudioInstrumentMixer::emptyBuffers(RealTime currentTime)
{
// Not RT safe
getLock();
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::emptyBuffers(" << currentTime << ")" << std::endl;
#endif
generateBuffers();
InstrumentId audioInstrumentBase;
int audioInstruments;
m_driver->getAudioInstrumentNumbers(audioInstrumentBase, audioInstruments);
InstrumentId synthInstrumentBase;
int synthInstruments;
m_driver->getSoftSynthInstrumentNumbers(synthInstrumentBase, synthInstruments);
for (int i = 0; i < audioInstruments + synthInstruments; ++i) {
InstrumentId id;
if (i < audioInstruments)
id = audioInstrumentBase + i;
else
id = synthInstrumentBase + (i - audioInstruments);
m_bufferMap[id].dormant = true;
m_bufferMap[id].muted = false;
m_bufferMap[id].zeroFrames = 0;
m_bufferMap[id].filledTo = currentTime;
for (size_t i = 0; i < m_bufferMap[id].buffers.size(); ++i) {
m_bufferMap[id].buffers[i]->reset();
}
}
releaseLock();
}
void
AudioInstrumentMixer::setInstrumentLevels(InstrumentId id, float dB, float pan)
{
// No requirement to be RT safe
BufferRec &rec = m_bufferMap[id];
float volume = AudioLevel::dB_to_multiplier(dB);
rec.gainLeft = volume * ((pan > 0.0) ? (1.0 - (pan / 100.0)) : 1.0);
rec.gainRight = volume * ((pan < 0.0) ? ((pan + 100.0) / 100.0) : 1.0);
rec.volume = volume;
}
void
AudioInstrumentMixer::updateInstrumentMuteStates()
{
SequencerDataBlock *sdb = m_driver->getSequencerDataBlock();
if (sdb) {
ControlBlock *cb = sdb->getControlBlock();
if (cb) {
for (BufferMap::iterator i = m_bufferMap.begin();
i != m_bufferMap.end(); ++i) {
InstrumentId id = i->first;
BufferRec &rec = i->second;
if (id >= SoftSynthInstrumentBase) {
rec.muted = cb->isInstrumentMuted(id);
} else {
rec.muted = cb->isInstrumentUnused(id);
}
}
}
}
}
void
AudioInstrumentMixer::processBlocks(bool &readSomething)
{
// Needs to be RT safe
#ifdef DEBUG_MIXER
if (m_driver->isPlaying())
std::cerr << "AudioInstrumentMixer::processBlocks" << std::endl;
#endif
// Profiler profiler("processBlocks", true);
const AudioPlayQueue *queue = m_driver->getAudioQueue();
for (BufferMap::iterator i = m_bufferMap.begin();
i != m_bufferMap.end(); ++i) {
InstrumentId id = i->first;
BufferRec &rec = i->second;
// This "muted" flag actually only strictly means muted when
// applied to synth instruments. For audio instruments it's
// only true if the instrument is not in use at all (see
// updateInstrumentMuteStates above). It's not safe to base
// the empty calculation on muted state for audio tracks,
// because that causes buffering problems when the mute is
// toggled for an audio track while it's playing a file.
bool empty = false;
if (rec.muted) {
empty = true;
} else {
if (id >= SoftSynthInstrumentBase) {
empty = (!m_synths[id] || m_synths[id]->isBypassed());
} else {
empty = !queue->haveFilesForInstrument(id);
}
if (empty) {
for (PluginList::iterator j = m_plugins[id].begin();
j != m_plugins[id].end(); ++j) {
if (*j != 0) {
empty = false;
break;
}
}
}
}
if (!empty && rec.empty) {
// This instrument is becoming freshly non-empty. We need
// to set its filledTo field to match that of an existing
// non-empty instrument, if we can find one.
for (BufferMap::iterator j = m_bufferMap.begin();
j != m_bufferMap.end(); ++j) {
if (j->first == i->first)
continue;
if (j->second.empty)
continue;
rec.filledTo = j->second.filledTo;
break;
}
}
rec.empty = empty;
// For a while we were setting empty to true if the volume on
// the track was zero, but that breaks continuity if there is
// actually a file on the track -- processEmptyBlocks won't
// read it, so it'll fall behind if we put the volume up again.
}
bool more = true;
static const int MAX_FILES_PER_INSTRUMENT = 500;
static PlayableAudioFile *playing[MAX_FILES_PER_INSTRUMENT];
RealTime blockDuration = RealTime::frame2RealTime(m_blockSize, m_sampleRate);
while (more) {
more = false;
for (BufferMap::iterator i = m_bufferMap.begin();
i != m_bufferMap.end(); ++i) {
InstrumentId id = i->first;
BufferRec &rec = i->second;
if (rec.empty) {
rec.dormant = true;
continue;
}
size_t playCount = MAX_FILES_PER_INSTRUMENT;
if (id >= SoftSynthInstrumentBase)
playCount = 0;
else {
queue->getPlayingFilesForInstrument(rec.filledTo,
blockDuration, id,
playing, playCount);
}
if (processBlock(id, playing, playCount, readSomething)) {
more = true;
}
}
}
}
bool
AudioInstrumentMixer::processBlock(InstrumentId id,
PlayableAudioFile **playing,
size_t playCount,
bool &readSomething)
{
// Needs to be RT safe
// Profiler profiler("processBlock", true);
BufferRec &rec = m_bufferMap[id];
RealTime bufferTime = rec.filledTo;
#ifdef DEBUG_MIXER
// if (m_driver->isPlaying()) {
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): buffer time is " << bufferTime << std::endl;
// }
#endif
unsigned int channels = rec.channels;
if (channels > rec.buffers.size())
channels = rec.buffers.size();
if (channels > m_processBuffers.size())
channels = m_processBuffers.size();
if (channels == 0) {
#ifdef DEBUG_MIXER
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): nominal channels " << rec.channels << ", ring buffers " << rec.buffers.size() << ", process buffers " << m_processBuffers.size() << std::endl;
#endif
return false; // buffers just haven't been set up yet
}
unsigned int targetChannels = channels;
if (targetChannels < 2)
targetChannels = 2; // fill at least two buffers
size_t minWriteSpace = 0;
for (unsigned int ch = 0; ch < targetChannels; ++ch) {
size_t thisWriteSpace = rec.buffers[ch]->getWriteSpace();
if (ch == 0 || thisWriteSpace < minWriteSpace) {
minWriteSpace = thisWriteSpace;
if (minWriteSpace < m_blockSize) {
#ifdef DEBUG_MIXER
// if (m_driver->isPlaying()) {
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): only " << minWriteSpace << " write space on channel " << ch << " for block size " << m_blockSize << std::endl;
// }
#endif
return false;
}
}
}
PluginList &plugins = m_plugins[id];
#ifdef DEBUG_MIXER
if ((id % 100) == 0 && m_driver->isPlaying())
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): minWriteSpace is " << minWriteSpace << std::endl;
#else
#ifdef DEBUG_MIXER_LIGHTWEIGHT
if ((id % 100) == 0 && m_driver->isPlaying())
std::cout << minWriteSpace << "/" << rec.buffers[0]->getSize() << std::endl;
#endif
#endif
#ifdef DEBUG_MIXER
if ((id % 100) == 0 && playCount > 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): " << playCount << " audio file(s) to consider" << std::endl;
#endif
bool haveBlock = true;
bool haveMore = false;
for (size_t fileNo = 0; fileNo < playCount; ++fileNo) {
bool acceptable = false;
PlayableAudioFile *file = playing[fileNo];
size_t frames = file->getSampleFramesAvailable();
acceptable = ((frames >= m_blockSize) || file->isFullyBuffered());
if (acceptable &&
(minWriteSpace >= m_blockSize * 2) &&
(frames >= m_blockSize * 2)) {
#ifdef DEBUG_MIXER
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): will be asking for more" << std::endl;
#endif
haveMore = true;
}
#ifdef DEBUG_MIXER
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): file has " << frames << " frames available" << std::endl;
#endif
if (!acceptable) {
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): file " << file->getAudioFile()->getFilename() << " has " << frames << " frames available, says isBuffered " << file->isBuffered() << std::endl;
if (!m_driver->getLowLatencyMode()) {
// Not a serious problem, just block on this
// instrument and return to it a little later.
haveBlock = false;
} else {
// In low latency mode, this is a serious problem if
// the file has been buffered and simply isn't filling
// fast enough. Otherwise we have to assume that the
// problem is something like a new file being dropped
// in by unmute during playback, in which case we have
// to accept that it won't be available for a while
// and just read silence from it instead.
if (file->isBuffered()) {
m_driver->reportFailure(MappedEvent::FailureDiscUnderrun);
haveBlock = false;
} else {
// ignore happily.
}
}
}
}
if (!haveBlock) {
return false; // blocked;
}
#ifdef DEBUG_MIXER
if (!haveMore) {
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): won't be asking for more" << std::endl;
}
#endif
for (unsigned int ch = 0; ch < targetChannels; ++ch) {
memset(m_processBuffers[ch], 0, sizeof(sample_t) * m_blockSize);
}
RunnablePluginInstance *synth = m_synths[id];
if (synth && !synth->isBypassed()) {
synth->run(bufferTime);
unsigned int ch = 0;
while (ch < synth->getAudioOutputCount() && ch < channels) {
denormalKill(synth->getAudioOutputBuffers()[ch],
m_blockSize);
memcpy(m_processBuffers[ch],
synth->getAudioOutputBuffers()[ch],
m_blockSize * sizeof(sample_t));
++ch;
}
}
if (haveBlock) {
// Mix in a block from each playing file on this instrument.
for (size_t fileNo = 0; fileNo < playCount; ++fileNo) {
PlayableAudioFile *file = playing[fileNo];
size_t offset = 0;
size_t blockSize = m_blockSize;
if (file->getStartTime() > bufferTime) {
offset = RealTime::realTime2Frame
(file->getStartTime() - bufferTime, m_sampleRate);
if (offset < blockSize)
blockSize -= offset;
else
blockSize = 0;
#ifdef DEBUG_MIXER
std::cerr << "AudioInstrumentMixer::processBlock: file starts at offset " << offset << ", block size now " << blockSize << std::endl;
#endif
}
//!!! This addSamples call is what is supposed to signal
// to a playable audio file when the end of the file has
// been reached. But for some playables it appears the
// file overruns, possibly due to rounding errors in
// sample rate conversion, and so we stop reading from it
// before it's actually done. I don't particularly mind
// that from a sound quality POV (after all it's badly
// resampled already) but unfortunately it means we leak
// pooled buffers.
if (blockSize > 0) {
file->addSamples(m_processBuffers, channels, blockSize, offset);
readSomething = true;
}
}
}
// Apply plugins. There are various copy-reducing
// optimisations available here, but we're not even going to
// think about them yet. Note that we force plugins to mono
// on a mono track, even though we have stereo output buffers
// -- stereo only comes into effect at the pan stage, and
// these are pre-fader plugins.
for (PluginList::iterator pli = plugins.begin();
pli != plugins.end(); ++pli) {
RunnablePluginInstance *plugin = *pli;
if (!plugin || plugin->isBypassed())
continue;
unsigned int ch = 0;
// If a plugin has more input channels than we have
// available, we duplicate up to stereo and leave any
// remaining channels empty.
while (ch < plugin->getAudioInputCount()) {
if (ch < channels || ch < 2) {
memcpy(plugin->getAudioInputBuffers()[ch],
m_processBuffers[ch % channels],
m_blockSize * sizeof(sample_t));
} else {
memset(plugin->getAudioInputBuffers()[ch], 0,
m_blockSize * sizeof(sample_t));
}
++ch;
}
#ifdef DEBUG_MIXER
std::cerr << "Running plugin with " << plugin->getAudioInputCount()
<< " inputs, " << plugin->getAudioOutputCount() << " outputs" << std::endl;
#endif
plugin->run(bufferTime);
ch = 0;
while (ch < plugin->getAudioOutputCount()) {
denormalKill(plugin->getAudioOutputBuffers()[ch],
m_blockSize);
if (ch < channels) {
memcpy(m_processBuffers[ch],
plugin->getAudioOutputBuffers()[ch],
m_blockSize * sizeof(sample_t));
} else if (ch == 1) {
// stereo output from plugin on a mono track
for (size_t i = 0; i < m_blockSize; ++i) {
m_processBuffers[0][i] +=
plugin->getAudioOutputBuffers()[ch][i];
m_processBuffers[0][i] /= 2;
}
} else {
break;
}
++ch;
}
}
// special handling for pan on mono tracks
bool allZeros = true;
if (targetChannels == 2 && channels == 1) {
for (size_t i = 0; i < m_blockSize; ++i) {
sample_t sample = m_processBuffers[0][i];
m_processBuffers[0][i] = sample * rec.gainLeft;
m_processBuffers[1][i] = sample * rec.gainRight;
if (allZeros && sample != 0.0)
allZeros = false;
}
rec.buffers[0]->write(m_processBuffers[0], m_blockSize);
rec.buffers[1]->write(m_processBuffers[1], m_blockSize);
} else {
for (unsigned int ch = 0; ch < targetChannels; ++ch) {
float gain = ((ch == 0) ? rec.gainLeft :
(ch == 1) ? rec.gainRight : rec.volume);
for (size_t i = 0; i < m_blockSize; ++i) {
// handle volume and pan
m_processBuffers[ch][i] *= gain;
if (allZeros && m_processBuffers[ch][i] != 0.0)
allZeros = false;
}
rec.buffers[ch]->write(m_processBuffers[ch], m_blockSize);
}
}
bool dormant = true;
if (allZeros) {
rec.zeroFrames += m_blockSize;
for (unsigned int ch = 0; ch < targetChannels; ++ch) {
if (rec.buffers[ch]->getReadSpace() > rec.zeroFrames) {
dormant = false;
}
}
} else {
rec.zeroFrames = 0;
dormant = false;
}
#ifdef DEBUG_MIXER
if ((id % 100) == 0 && m_driver->isPlaying())
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): setting dormant to " << dormant << std::endl;
#endif
rec.dormant = dormant;
bufferTime = bufferTime + RealTime::frame2RealTime(m_blockSize,
m_sampleRate);
rec.filledTo = bufferTime;
#ifdef DEBUG_MIXER
if ((id % 100) == 0)
std::cerr << "AudioInstrumentMixer::processBlock(" << id << "): done, returning " << haveMore << std::endl;
#endif
return haveMore;
}
void
AudioInstrumentMixer::kick(bool wantLock)
{
// Needs to be RT safe if wantLock is not specified
if (wantLock)
getLock();
bool readSomething = false;
processBlocks(readSomething);
if (readSomething)
m_fileReader->signal();
if (wantLock)
releaseLock();
}
void
AudioInstrumentMixer::threadRun()
{
while (!m_exiting) {
if (m_driver->areClocksRunning()) {
kick(false);
}
RealTime t = m_driver->getAudioMixBufferLength();
t = t / 2;
if (t < RealTime(0, 10000000))
t = RealTime(0, 10000000); // 10ms minimum
struct timeval now;
gettimeofday(&now, 0);
t = t + RealTime(now.tv_sec, now.tv_usec * 1000);
struct timespec timeout;
timeout.tv_sec = t.sec;
timeout.tv_nsec = t.nsec;
pthread_cond_timedwait(&m_condition, &m_lock, &timeout);
pthread_testcancel();
}
}
AudioFileReader::AudioFileReader(SoundDriver *driver,
unsigned int sampleRate) :
AudioThread("AudioFileReader", driver, sampleRate)
{
// nothing else here
}
AudioFileReader::~AudioFileReader()
{}
void
AudioFileReader::fillBuffers(const RealTime &currentTime)
{
getLock();
// Tell every audio file the play start time.
const AudioPlayQueue *queue = m_driver->getAudioQueue();
RealTime bufferLength = m_driver->getAudioReadBufferLength();
int bufferFrames = RealTime::realTime2Frame(bufferLength, m_sampleRate);
int poolSize = queue->getMaxBuffersRequired() * 2 + 4;
PlayableAudioFile::setRingBufferPoolSizes(poolSize, bufferFrames);
const AudioPlayQueue::FileSet &files = queue->getAllScheduledFiles();
#ifdef DEBUG_READER
std::cerr << "AudioFileReader::fillBuffers: have " << files.size() << " audio files total" << std::endl;
#endif
for (AudioPlayQueue::FileSet::const_iterator fi = files.begin();
fi != files.end(); ++fi) {
(*fi)->clearBuffers();
}
int allocated = 0;
for (AudioPlayQueue::FileSet::const_iterator fi = files.begin();
fi != files.end(); ++fi) {
(*fi)->fillBuffers(currentTime);
if ((*fi)->getEndTime() >= currentTime) {
if (++allocated == poolSize)
break;
} // else the file's ring buffers will have been returned
}
releaseLock();
}
bool
AudioFileReader::kick(bool wantLock)
{
if (wantLock)
getLock();
RealTime now = m_driver->getSequencerTime();
const AudioPlayQueue *queue = m_driver->getAudioQueue();
bool someFilled = false;
// Tell files that are playing or will be playing in the next few
// seconds to update.
AudioPlayQueue::FileSet playing;
queue->getPlayingFiles
(now, RealTime(3, 0) + m_driver->getAudioReadBufferLength(), playing);
for (AudioPlayQueue::FileSet::iterator fi = playing.begin();
fi != playing.end(); ++fi) {
if (!(*fi)->isBuffered()) {
// fillBuffers has not been called on this file. This
// happens when a file is unmuted during playback. The
// results are unpredictable because we can no longer
// synchronise with the correct JACK callback slice at
// this point, but this is better than allowing the file
// to update from its start as would otherwise happen.
(*fi)->fillBuffers(now);
someFilled = true;
} else {
if ((*fi)->updateBuffers())
someFilled = true;
}
}
if (wantLock)
releaseLock();
return someFilled;
}
void
AudioFileReader::threadRun()
{
while (!m_exiting) {
// struct timeval now;
// gettimeofday(&now, 0);
// RealTime t = RealTime(now.tv_sec, now.tv_usec * 1000);
bool someFilled = false;
if (m_driver->areClocksRunning()) {
someFilled = kick(false);
}
if (someFilled) {
releaseLock();
getLock();
} else {
RealTime bt = m_driver->getAudioReadBufferLength();
bt = bt / 2;
if (bt < RealTime(0, 10000000))
bt = RealTime(0, 10000000); // 10ms minimum
struct timeval now;
gettimeofday(&now, 0);
RealTime t = bt + RealTime(now.tv_sec, now.tv_usec * 1000);
struct timespec timeout;
timeout.tv_sec = t.sec;
timeout.tv_nsec = t.nsec;
pthread_cond_timedwait(&m_condition, &m_lock, &timeout);
pthread_testcancel();
}
}
}
AudioFileWriter::AudioFileWriter(SoundDriver *driver,
unsigned int sampleRate) :
AudioThread("AudioFileWriter", driver, sampleRate)
{
InstrumentId instrumentBase;
int instrumentCount;
m_driver->getAudioInstrumentNumbers(instrumentBase, instrumentCount);
for (InstrumentId id = instrumentBase;
id < instrumentBase + instrumentCount; ++id) {
// prefill with zero files in all slots, so that we can
// refer to the map without a lock (as the number of
// instruments won't change)
m_files[id] = FilePair(0, 0);
}
}
AudioFileWriter::~AudioFileWriter()
{}
bool
AudioFileWriter::openRecordFile(InstrumentId id,
const std::string &fileName)
{
getLock();
if (m_files[id].first) {
releaseLock();
std::cerr << "AudioFileWriter::openRecordFile: already have record file for instrument " << id << "!" << std::endl;
return false; // already have one
}
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::openRecordFile: instrument id is " << id << std::endl;
#endif
MappedAudioFader *fader = m_driver->getMappedStudio()->getAudioFader(id);
RealTime bufferLength = m_driver->getAudioWriteBufferLength();
int bufferSamples = RealTime::realTime2Frame(bufferLength, m_sampleRate);
bufferSamples = ((bufferSamples / 1024) + 1) * 1024;
if (fader) {
float fch = 2;
(void)fader->getProperty(MappedAudioFader::Channels, fch);
int channels = (int)fch;
RIFFAudioFile::SubFormat format = m_driver->getAudioRecFileFormat();
int bytesPerSample = (format == RIFFAudioFile::PCM ? 2 : 4) * channels;
int bitsPerSample = (format == RIFFAudioFile::PCM ? 16 : 32);
AudioFile *recordFile = 0;
try {
recordFile =
new WAVAudioFile(fileName,
channels, // channels
m_sampleRate, // samples per second
m_sampleRate *
bytesPerSample, // bytes per second
bytesPerSample, // bytes per frame
bitsPerSample); // bits per sample
// open the file for writing
//
if (!recordFile->write()) {
std::cerr << "AudioFileWriter::openRecordFile: failed to open " << fileName << " for writing" << std::endl;
delete recordFile;
releaseLock();
return false;
}
} catch (SoundFile::BadSoundFileException e) {
std::cerr << "AudioFileWriter::openRecordFile: failed to open " << fileName << " for writing: " << e.getMessage() << std::endl;
delete recordFile;
releaseLock();
return false;
}
RecordableAudioFile *raf = new RecordableAudioFile(recordFile,
bufferSamples);
m_files[id].second = raf;
m_files[id].first = recordFile;
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::openRecordFile: created " << channels << "-channel file at " << fileName << " (id is " << recordFile->getId() << ")" << std::endl;
#endif
releaseLock();
return true;
}
std::cerr << "AudioFileWriter::openRecordFile: no audio fader for record instrument " << id << "!" << std::endl;
releaseLock();
return false;
}
void
AudioFileWriter::write(InstrumentId id,
const sample_t *samples,
int channel,
size_t sampleCount)
{
if (!m_files[id].first)
return ; // no file
if (m_files[id].second->buffer(samples, channel, sampleCount) < sampleCount) {
m_driver->reportFailure(MappedEvent::FailureDiscOverrun);
}
}
bool
AudioFileWriter::closeRecordFile(InstrumentId id, AudioFileId &returnedId)
{
if (!m_files[id].first)
return false;
returnedId = m_files[id].first->getId();
m_files[id].second->setStatus(RecordableAudioFile::DEFUNCT);
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::closeRecordFile: instrument " << id << " file set defunct (file ID is " << returnedId << ")" << std::endl;
#endif
// Don't reset the file pointers here; that will be done in the
// next call to kick(). Doesn't really matter when that happens,
// but let's encourage it to happen soon just for certainty.
signal();
return true;
}
bool
AudioFileWriter::haveRecordFileOpen(InstrumentId id)
{
InstrumentId instrumentBase;
int instrumentCount;
m_driver->getAudioInstrumentNumbers(instrumentBase, instrumentCount);
if (id < instrumentBase || id >= instrumentBase + instrumentCount) {
return false;
}
return (m_files[id].first &&
(m_files[id].second->getStatus() != RecordableAudioFile::DEFUNCT));
}
bool
AudioFileWriter::haveRecordFilesOpen()
{
InstrumentId instrumentBase;
int instrumentCount;
m_driver->getAudioInstrumentNumbers(instrumentBase, instrumentCount);
for (InstrumentId id = instrumentBase; id < instrumentBase + instrumentCount; ++id) {
if (m_files[id].first &&
(m_files[id].second->getStatus() != RecordableAudioFile::DEFUNCT)) {
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::haveRecordFilesOpen: found open record file for instrument " << id << std::endl;
#endif
return true;
}
}
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::haveRecordFilesOpen: nope" << std::endl;
#endif
return false;
}
void
AudioFileWriter::kick(bool wantLock)
{
if (wantLock)
getLock();
InstrumentId instrumentBase;
int instrumentCount;
m_driver->getAudioInstrumentNumbers(instrumentBase, instrumentCount);
for (InstrumentId id = instrumentBase;
id < instrumentBase + instrumentCount; ++id) {
if (!m_files[id].first)
continue;
RecordableAudioFile *raf = m_files[id].second;
if (raf->getStatus() == RecordableAudioFile::DEFUNCT) {
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::kick: found defunct file on instrument " << id << std::endl;
#endif
m_files[id].first = 0;
delete raf; // also deletes the AudioFile
m_files[id].second = 0;
} else {
#ifdef DEBUG_WRITER
std::cerr << "AudioFileWriter::kick: writing file on instrument " << id << std::endl;
#endif
raf->write();
}
}
if (wantLock)
releaseLock();
}
void
AudioFileWriter::threadRun()
{
while (!m_exiting) {
kick(false);
RealTime t = m_driver->getAudioWriteBufferLength();
t = t / 2;
if (t < RealTime(0, 10000000))
t = RealTime(0, 10000000); // 10ms minimum
struct timeval now;
gettimeofday(&now, 0);
t = t + RealTime(now.tv_sec, now.tv_usec * 1000);
struct timespec timeout;
timeout.tv_sec = t.sec;
timeout.tv_nsec = t.nsec;
pthread_cond_timedwait(&m_condition, &m_lock, &timeout);
pthread_testcancel();
}
}
}