Logo Search packages:      
Sourcecode: rosegarden version File versions  Download package

AudioProcess.cpp

// -*- c-basic-offset: 4 -*-

/*
    Rosegarden-4
    A sequencer and musical notation editor.

    This program is Copyright 2000-2006
        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 "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, QString identifier)
{
    // Not RT safe

    std::cerr << "AudioInstrumentMixer::setPlugin(" << id << ", " << position << ", " << identifier << ")" << 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 << 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);
}

QStringList
AudioInstrumentMixer::getPluginPrograms(InstrumentId id, int position)
{
    // Not RT safe

    QStringList programs;
    RunnablePluginInstance *instance = getPluginInstance(id, position);
    if (instance) programs = instance->getPrograms();
    return programs;
}

QString
AudioInstrumentMixer::getPluginProgram(InstrumentId id, int position)
{
    // Not RT safe

    QString program;
    RunnablePluginInstance *instance = getPluginInstance(id, position);
    if (instance) program = instance->getCurrentProgram();
    return program;
}

QString
AudioInstrumentMixer::getPluginProgram(InstrumentId id, int position, int bank,
                               int program)
{
    // Not RT safe

    QString programName;
    RunnablePluginInstance *instance = getPluginInstance(id, position);
    if (instance) programName = instance->getProgram(bank, program);
    return programName;
}

unsigned long
AudioInstrumentMixer::getPluginProgram(InstrumentId id, int position, QString 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, QString program)
{
    // Not RT safe

    RunnablePluginInstance *instance = getPluginInstance(id, position);
    if (instance) instance->selectProgram(program);
}

QString
AudioInstrumentMixer::configurePlugin(InstrumentId id, int position, QString key, QString value)
{
    // Not RT safe

    RunnablePluginInstance *instance = getPluginInstance(id, position);
    if (instance) return instance->configure(key, value);
    return QString();
}

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;
#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

//    Rosegarden::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

//    Rosegarden::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 sample
                         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();
    }
}


}


Generated by  Doxygen 1.6.0   Back to index