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

rosegardensequencer.cpp

// -*- c-indentation-style:"stroustrup" 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>

#include <iostream>

#include <klocale.h>
#include <kstandarddirs.h>

#include <dcopclient.h>
#include <qdatetime.h>
#include <qstring.h>
#include <qdir.h>
#include <qbuffer.h>
#include <qvaluevector.h>

#include "rosedebug.h"
#include "rosegardensequencer.h"
#include "mmappedcontrolblock.h"
#include "mmappedsegment.h"
#include "rosegardendcop.h"
#include "ControlBlock.h"
#include "SoundDriver.h"
#include "SoundDriverFactory.h"
#include "MappedInstrument.h"
#include "Profiler.h"
#include "PluginFactory.h"

// The default latency and read-ahead values are actually sent
// down from the GUI every time playback or recording starts
// so the local values are kind of meaningless.
//
//
RosegardenSequencerApp::RosegardenSequencerApp() :
    DCOPObject("RosegardenSequencerIface"),
    m_driver(0),
    m_transportStatus(STOPPED),
    m_songPosition(0, 0),
    m_lastFetchSongPosition(0, 0),
    m_readAhead(0, 80000000),         // default value
    m_audioMix(0, 60000000),         // default value
    m_audioRead(0, 100000000),         // default value
    m_audioWrite(0, 200000000),         // default value
    m_smallFileSize(128),
    m_loopStart(0, 0),
    m_loopEnd(0, 0),
    m_studio(new Rosegarden::MappedStudio()),
    m_segmentFilesPath(KGlobal::dirs()->resourceDirs("tmp").last()),
    m_metaIterator(0),
    m_controlBlockMmapper(0),
    m_transportToken(1),
    m_isEndOfCompReached(false)
{
    SEQUENCER_DEBUG << "Registering with DCOP server" << endl;

    // Without DCOP we are nothing
    QCString realAppId = kapp->dcopClient()->registerAs(kapp->name(), false);

    if (realAppId.isNull())
    {
        SEQUENCER_DEBUG << "RosegardenSequencer cannot register "
                        << "with DCOP server" << endl;
        close();
    }

    // Initialise the MappedStudio
    //
    initialiseStudio();

    // Creating this object also initialises the Rosegarden ALSA/JACK 
    // interface for both playback and recording. MappedStudio
    // aduio faders are also created.
    //
    m_driver = Rosegarden::SoundDriverFactory::createDriver(m_studio);
    m_studio->setSoundDriver(m_driver);

    if (!m_driver)
    {
        SEQUENCER_DEBUG << "RosegardenSequencer object could not be allocated"
                        << endl;
        close();
    }

    m_driver->setAudioBufferSizes(m_audioMix, m_audioRead, m_audioWrite,
                             m_smallFileSize);

    m_driver->setSequencerDataBlock(m_sequencerMapper.getSequencerDataBlock());
    m_driver->setExternalTransportControl(this);

    // Check for new clients every so often
    //
    m_newClientTimer = new QTimer(this);
    connect(m_newClientTimer, SIGNAL(timeout()),
            this, SLOT(slotCheckForNewClients()));

    m_newClientTimer->start(3000); // every 3 seconds
}

RosegardenSequencerApp::~RosegardenSequencerApp()
{
    SEQUENCER_DEBUG << "RosegardenSequencer - shutting down" << endl;
    m_driver->shutdown();
    delete m_studio;
    delete m_driver;
    delete m_controlBlockMmapper;
}

void
RosegardenSequencerApp::quit()
{
    close();

    // and break out of the loop next time around
    m_transportStatus = QUIT;
}


void
RosegardenSequencerApp::stop()
{
    // set our state at this level to STOPPING (pending any
    // unfinished NOTES)
    m_transportStatus = STOPPING;

    // report
    //
    SEQUENCER_DEBUG << "RosegardenSequencerApp::stop() - stopping" << endl;

    // process pending NOTE OFFs and stop the Sequencer
    m_driver->stopPlayback();

    // the Sequencer doesn't need to know these once
    // we've stopped.
    //
    m_songPosition.sec = 0;
    m_songPosition.nsec = 0;
    m_lastFetchSongPosition.sec = 0;
    m_lastFetchSongPosition.nsec = 0;

    cleanupMmapData();

    Rosegarden::Profiles::getInstance()->dump();
    
    incrementTransportToken();
}

// Get a slice of events from the GUI
//
void
RosegardenSequencerApp::fetchEvents(Rosegarden::MappedComposition &composition,
                            const Rosegarden::RealTime &start,
                                    const Rosegarden::RealTime &end,
                                    bool firstFetch)
{
    // Always return nothing if we're stopped
    //
    if ( m_transportStatus == STOPPED || m_transportStatus == STOPPING )
        return;

    // If we're looping then we should get as much of the rest of
    // the right hand of the loop as possible and also events from
    // the beginning of the loop.  We can do this in two fetches.
    // Make sure that we delete all returned pointers when we've
    // finished with them.
    //
    //
/*
    if (isLooping() == true && end >= m_loopEnd)
    {
        Rosegarden::RealTime loopOverlap = end - m_loopEnd;

        Rosegarden::MappedComposition *endLoop = 0;

      if (m_loopEnd > start) {
          endLoop = getSlice(start, m_loopEnd, firstFetch);
      }

      if (loopOverlap > Rosegarden::RealTime::zeroTime) { 

          Rosegarden::MappedComposition *beginLoop =
            getSlice(m_loopStart, m_loopStart + loopOverlap, true);

          // move the start time of the begin section one loop width
          // into the future and ensure that we keep the clocks level
          // until this time has passed
          //
          beginLoop->moveStartTime(m_loopEnd - m_loopStart);

          if (endLoop) {
            (*endLoop) = (*endLoop) + (*beginLoop);
            delete beginLoop;
          } else {
            endLoop = beginLoop;
          }
      }

      if (endLoop) return endLoop;
      else return new Rosegarden::MappedComposition();
    }
    else
*/
    getSlice(composition, start, end, firstFetch);
    applyLatencyCompensation(composition);
}


void
RosegardenSequencerApp::getSlice(Rosegarden::MappedComposition &composition,
                         const Rosegarden::RealTime &start,
                                 const Rosegarden::RealTime &end,
                                 bool firstFetch)
{
//    SEQUENCER_DEBUG << "RosegardenSequencerApp::getSlice (" << start << " -> " << end << ", " << firstFetch << ")" << endl;

    if (firstFetch || (start < m_lastStartTime)) {
      SEQUENCER_DEBUG << "[calling jumpToTime on start]" << endl;
        m_metaIterator->jumpToTime(start);
    }

    (void)m_metaIterator->fillCompositionWithEventsUntil
      (firstFetch, &composition, start, end);

//     setEndOfCompReached(eventsRemaining); // don't do that, it breaks recording because
// playing stops right after it starts.

    m_lastStartTime = start;
}


void
RosegardenSequencerApp::applyLatencyCompensation(Rosegarden::MappedComposition &composition)
{
    Rosegarden::RealTime maxLatency = m_driver->getMaximumPlayLatency();
    if (maxLatency == Rosegarden::RealTime::zeroTime) return;

    for (Rosegarden::MappedComposition::iterator i = composition.begin();
       i != composition.end(); ++i) {

      Rosegarden::RealTime instrumentLatency =
          m_driver->getInstrumentPlayLatency((*i)->getInstrument());

//    std::cerr << "RosegardenSequencerApp::applyLatencyCompensation: maxLatency " << maxLatency << ", instrumentLatency " << instrumentLatency << ", moving " << (*i)->getEventTime() << " to " << (*i)->getEventTime() + maxLatency - instrumentLatency << std::endl;

      (*i)->setEventTime((*i)->getEventTime() +
                     maxLatency - instrumentLatency);
    }
}
           

// The first fetch of events from the core/ and initialisation for
// this session of playback.  We fetch up to m_readAhead ahead at
// first at then top up at each slice.
//
bool
RosegardenSequencerApp::startPlaying()
{
    // Fetch up to m_readHead microseconds worth of events
    //
    m_lastFetchSongPosition = m_songPosition + m_readAhead;

    // This will reset the Sequencer's internal clock
    // ready for new playback
    m_driver->initialisePlayback(m_songPosition);

    m_mC.clear();
    fetchEvents(m_mC, m_songPosition, m_songPosition + m_readAhead, true);

    // process whether we need to or not as this also processes
    // the audio queue for us
    //
    m_driver->processEventsOut(m_mC, m_songPosition, m_songPosition + m_readAhead);

    std::vector<Rosegarden::MappedEvent> audioEvents;
    m_metaIterator->getAudioEvents(audioEvents);
    m_driver->initialiseAudioQueue(audioEvents);

//    SEQUENCER_DEBUG << "RosegardenSequencerApp::startPlaying: pausing to simulate high-load environment" << endl;
//    ::sleep(2);

    // and only now do we signal to start the clock
    //
    m_driver->startClocks();

    incrementTransportToken();

    return true; // !isEndOfCompReached();
}

bool
RosegardenSequencerApp::keepPlaying()
{
    Rosegarden::Profiler profiler("RosegardenSequencerApp::keepPlaying");

    m_mC.clear();

    Rosegarden::RealTime fetchEnd = m_songPosition + m_readAhead;
    if (isLooping() && fetchEnd >= m_loopEnd) {
      fetchEnd = m_loopEnd - Rosegarden::RealTime(0, 1);
    }
    if (fetchEnd > m_lastFetchSongPosition) {
      fetchEvents(m_mC, m_lastFetchSongPosition, fetchEnd, false);
    }

    // Again, process whether we need to or not to keep
    // the Sequencer up-to-date with audio events
    //
    m_driver->processEventsOut(m_mC, m_lastFetchSongPosition, fetchEnd);

    if (fetchEnd > m_lastFetchSongPosition) {
      m_lastFetchSongPosition = fetchEnd;
    }
    
    return true; // !isEndOfCompReached(); - until we sort this out, we don't stop at end of comp.
}

// Return current Sequencer time in GUI compatible terms
//
void
RosegardenSequencerApp::updateClocks()
{
    Rosegarden::Profiler profiler("RosegardenSequencerApp::updateClocks");

    m_driver->runTasks();

    checkExternalTransport();

//SEQUENCER_DEBUG << "RosegardenSequencerApp::updateClocks" << endl; 

    // If we're not playing etc. then that's all we need to do
    //
    if (m_transportStatus != PLAYING &&
        m_transportStatus != RECORDING)
        return;

    Rosegarden::RealTime newPosition = m_driver->getSequencerTime();

    // Go around the loop if we've reached the end
    //
    if (isLooping() && newPosition >= m_loopEnd)
    {
      Rosegarden::RealTime oldPosition = m_songPosition;

        // Remove the loop width from the song position and send
        // this position to the GUI
        //
        newPosition = m_songPosition = m_lastFetchSongPosition = m_loopStart;

      m_driver->stopClocks();

        // Reset playback using this jump
        //
        m_driver->resetPlayback(oldPosition, m_songPosition);

      m_mC.clear();
      fetchEvents(m_mC, m_songPosition, m_songPosition + m_readAhead, true);

      m_driver->processEventsOut(m_mC, m_songPosition, m_songPosition + m_readAhead);

      m_driver->startClocks();
    }
    else
    {
        m_songPosition = newPosition;

        if (m_songPosition <= m_driver->getStartPosition())
            newPosition = m_driver->getStartPosition();
    }

    Rosegarden::RealTime maxLatency = m_driver->getMaximumPlayLatency();
    if (maxLatency != Rosegarden::RealTime::zeroTime) {
//    std::cerr << "RosegardenSequencerApp::updateClocks: latency compensation moving " << newPosition << " to " << newPosition - maxLatency << std::endl;
      newPosition = newPosition - maxLatency;
    }

    // Remap the position pointer
    //
    m_sequencerMapper.updatePositionPointer(newPosition);
}

void
RosegardenSequencerApp::notifySequencerStatus()
{
    QByteArray data, replyData;
    QCString replyType;
    QDataStream arg(data, IO_WriteOnly);

    arg << (int)m_transportStatus;

    if (!kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                                  ROSEGARDEN_GUI_IFACE_NAME,
                                  "notifySequencerStatus(int)",
                                  data)) 
    {
        SEQUENCER_DEBUG << "RosegardenSequencer::notifySequencerStatus()"
                        << " - can't send to RosegardenGUI client"
                        << endl;

        // Stop the sequencer
        //
        stop();
    }
}

void
RosegardenSequencerApp::sleep(const Rosegarden::RealTime &rt)
{
    m_driver->sleep(rt);
}


// Sets the Sequencer object and this object to the new time 
// from where playback can continue.
//
void
RosegardenSequencerApp::jumpTo(long posSec, long posNsec)
{
    SEQUENCER_DEBUG << "RosegardenSequencerApp::jumpTo(" << posSec << ", " << posNsec << ")\n";

    if (posSec < 0 && posNsec < 0)
        return;

    m_driver->stopClocks();

    Rosegarden::RealTime oldPosition = m_songPosition;

    m_songPosition = m_lastFetchSongPosition =
      Rosegarden::RealTime(posSec, posNsec);

    if (m_sequencerMapper.getSequencerDataBlock()) {
      m_sequencerMapper.getSequencerDataBlock()->setPositionPointer
          (m_songPosition);
    }

    m_driver->resetPlayback(oldPosition, m_songPosition);

    if (m_driver->isPlaying()) {
    
      // Now prebuffer as in startPlaying:

      m_mC.clear();
      fetchEvents(m_mC, m_songPosition, m_songPosition + m_readAhead, true);

      // process whether we need to or not as this also processes
      // the audio queue for us
      //
      m_driver->processEventsOut(m_mC, m_songPosition, m_songPosition + m_readAhead);
    }
    
    incrementTransportToken();

//    SEQUENCER_DEBUG << "RosegardenSequencerApp::jumpTo: pausing to simulate high-load environment" << endl;
//    ::sleep(1);

    m_driver->startClocks();

    return;
}

// Send the last recorded MIDI block
//
void
RosegardenSequencerApp::processRecordedMidi()
{
    Rosegarden::MappedComposition *mC = m_driver->getMappedComposition();

    if (mC->empty() || !m_controlBlockMmapper) return;

    applyFiltering(mC, m_controlBlockMmapper->getRecordFilter(), false);
    m_sequencerMapper.updateRecordingBuffer(mC);

    if (m_controlBlockMmapper->isMidiRoutingEnabled()) {
        applyFiltering(mC, m_controlBlockMmapper->getThruFilter(), true);
        routeEvents(mC, false);
    }
}

void
RosegardenSequencerApp::routeEvents(Rosegarden::MappedComposition *mC, bool useSelectedTrack)
{
    InstrumentId instrumentId;
           
    if(useSelectedTrack) {
        instrumentId = m_controlBlockMmapper->getInstrumentForTrack
            (m_controlBlockMmapper->getSelectedTrack());
        for (Rosegarden::MappedComposition::iterator i = mC->begin();
            i != mC->end(); ++i) {
            (*i)->setInstrument(instrumentId);
      }
    } else {
        for (Rosegarden::MappedComposition::iterator i = mC->begin();
            i != mC->end(); ++i) {
            instrumentId = m_controlBlockMmapper->getInstrumentForEvent
                ((*i)->getRecordedDevice(), (*i)->getRecordedChannel());
                (*i)->setInstrument(instrumentId);
        }
    }
    m_driver->processEventsOut(*mC);
}

// Send an update
//
void
RosegardenSequencerApp::processRecordedAudio()
{
    // Nothing to do here: the recording time is sent back to the GUI
    // in the sequencer mapper as a normal case.
}


// This method is called during STOPPED or PLAYING operations
// to mop up any async (unexpected) incoming MIDI or Audio events
// and forward them to the GUI for display
//
void
RosegardenSequencerApp::processAsynchronousEvents()
{
    if (!m_controlBlockMmapper) {

      // If the control block mmapper doesn't exist, we'll just
      // return here.  But we want to ensure we don't check again
      // immediately, because we're probably waiting for the GUI to
      // start up.

      static bool lastChecked = false;
      static struct timeval lastCheckedAt;

      struct timeval tv;
      (void)gettimeofday(&tv, 0);

      if (lastChecked &&
          tv.tv_sec == lastCheckedAt.tv_sec) {
          lastCheckedAt = tv;
          return;
      }

      lastChecked = true;
      lastCheckedAt = tv;

      try {
          m_controlBlockMmapper = new ControlBlockMmapper(KGlobal::dirs()->resourceDirs("tmp").last()
                                                            + "/rosegarden_control_block");
      } catch (Rosegarden::Exception e) {
          // Assume that the control block simply hasn't been
          // created yet because the GUI's still starting up.
          // If there's a real problem with the mmapper, it
          // will show up in play() instead.
          return;
      }
      m_sequencerMapper.setControlBlock(m_controlBlockMmapper->getControlBlock());
    }

    Rosegarden::MappedComposition *mC = m_driver->getMappedComposition();

    if (mC->empty()) {
      m_driver->processPending();
      return;
    }

//    std::cerr << "processAsynchronousEvents: have " << mC->size() << " events" << std::endl;

    QByteArray data;
    QDataStream arg(data, IO_WriteOnly);
    arg << mC;

    if (m_controlBlockMmapper->isMidiRoutingEnabled()) {
        applyFiltering(mC, m_controlBlockMmapper->getThruFilter(), true);
        routeEvents(mC, true);
    }

//    std::cerr << "processAsynchronousEvents: sent " << mC->size() << " events" << std::endl;

    if (!kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                                 ROSEGARDEN_GUI_IFACE_NAME,
                                 "processAsynchronousMidi(Rosegarden::MappedComposition)", data))
    {
        SEQUENCER_DEBUG << "RosegardenSequencer::processAsynchronousEvents() - "
                        << "can't call RosegardenGUI client" << endl;

        // Stop the sequencer so we can see if we can try again later
        //
        stop();
    }

    // Process any pending events (Note Offs or Audio) as part of
    // same procedure.
    //
    m_driver->processPending();
}


void
RosegardenSequencerApp::applyFiltering(Rosegarden::MappedComposition *mC,
                               Rosegarden::MidiFilter filter,
                               bool filterControlDevice)
{
    for (Rosegarden::MappedComposition::iterator i = mC->begin();
       i != mC->end(); ) { // increment in loop
      Rosegarden::MappedComposition::iterator j = i;
      ++j;
      if (((*i)->getType() & filter) ||
          (filterControlDevice && ((*i)->getRecordedDevice() ==
                             Rosegarden::Device::CONTROL_DEVICE))) {
          mC->erase(i);
      }
      i = j;
    }
}    


int
RosegardenSequencerApp::record(const Rosegarden::RealTime &time,
                               const Rosegarden::RealTime &readAhead,
                               const Rosegarden::RealTime &audioMix,
                               const Rosegarden::RealTime &audioRead,
                               const Rosegarden::RealTime &audioWrite,
                         long smallFileSize,
                               long recordMode)
{
    TransportStatus localRecordMode = (TransportStatus) recordMode;

    SEQUENCER_DEBUG << "RosegardenSequencerApp::record - recordMode is " << recordMode << ", transport status is " << m_transportStatus << endl;

    // punch in recording
    if (m_transportStatus == PLAYING) {
        if (localRecordMode == STARTING_TO_RECORD) {
          SEQUENCER_DEBUG << "RosegardenSequencerApp::record: punching in" << endl;
          localRecordMode = RECORDING; // no need to start playback
      }
    }

    // For audio recording we need to retrieve audio
    // file names from the GUI
    //
    if (localRecordMode == STARTING_TO_RECORD ||
      localRecordMode == RECORDING)
    {
        SEQUENCER_DEBUG << "RosegardenSequencerApp::record()"
                        << " - starting to record" << endl;

      QValueVector<Rosegarden::InstrumentId> armedInstruments;
      QValueVector<QString> audioFileNames;

      {
          QByteArray data, replyData;
          QCString replyType;
          QDataStream arg(data, IO_WriteOnly);
          
          if (!kapp->dcopClient()->call(ROSEGARDEN_GUI_APP_NAME,
                                ROSEGARDEN_GUI_IFACE_NAME,
                                "getArmedInstruments()",
                                data, replyType, replyData, true)) {
            SEQUENCER_DEBUG << "RosegardenSequencer::record()"
                        << " - can't call RosegardenGUI client for getArmedInstruments"
                        << endl;
          }

          QDataStream reply(replyData, IO_ReadOnly);
          if (replyType == "QValueVector<Rosegarden::InstrumentId>") {
            reply >> armedInstruments;
          } else {
            SEQUENCER_DEBUG << "RosegardenSequencer::record() - "
                        << "unrecognised type returned for getArmedInstruments" << endl;
          }
      }

      QValueVector<Rosegarden::InstrumentId> audioInstruments;

      for (unsigned int i = 0; i < armedInstruments.size(); ++i) {
          if (armedInstruments[i] >= Rosegarden::AudioInstrumentBase &&
            armedInstruments[i] <  Rosegarden::MidiInstrumentBase) {
            audioInstruments.push_back(armedInstruments[i]);
          }
      }

      if (audioInstruments.size() > 0) {
          
          QByteArray data, replyData;
          QCString replyType;
          QDataStream arg(data, IO_WriteOnly);
          
          arg << audioInstruments;

          if (!kapp->dcopClient()->call(ROSEGARDEN_GUI_APP_NAME,
                                ROSEGARDEN_GUI_IFACE_NAME,
                                "createRecordAudioFiles(QValueVector<Rosegarden::InstrumentId>)",
                                data, replyType, replyData, true)) {
            SEQUENCER_DEBUG << "RosegardenSequencer::record()"
                        << " - can't call RosegardenGUI client for createNewAudioFiles"
                        << endl;
          }

          QDataStream reply(replyData, IO_ReadOnly);
          if (replyType == "QValueVector<QString>") {
            reply >> audioFileNames;
          } else {
            SEQUENCER_DEBUG << "RosegardenSequencer::record() - "
                        << "unrecognised type returned for createNewAudioFiles" << endl;
          }

          if (audioFileNames.size() != audioInstruments.size()) {
            std::cerr << "ERROR: RosegardenSequencer::record(): Failed to create correct number of audio files (wanted " << audioInstruments.size() << ", got " << audioFileNames.size() << ")" << std::endl;
            stop();
            return 0;
          }
      }

      std::vector<Rosegarden::InstrumentId> armedInstrumentsVec;
      std::vector<QString> audioFileNamesVec;
      for (int i = 0; i < armedInstruments.size(); ++i) {
          armedInstrumentsVec.push_back(armedInstruments[i]);
      }
      for (int i = 0; i < audioFileNames.size(); ++i) {
          audioFileNamesVec.push_back(audioFileNames[i]);
      }

        // Get the Sequencer to prepare itself for recording - if
        // this fails we stop.
        //
        if (m_driver->record(Rosegarden::RECORD_ON,
                       &armedInstrumentsVec,
                       &audioFileNamesVec) == false) {
            stop();
            return 0;
        }
    }
    else
    {
        // unrecognised type - return a problem
        return 0;
    }

    // Now set the local transport status to the record mode
    //
    //
    m_transportStatus = localRecordMode;

    if (localRecordMode == RECORDING) { // punch in
      return 1;
    } else {

      // Ensure that playback is initialised
      //
      m_driver->initialisePlayback(m_songPosition);

      return play(time, readAhead, audioMix, audioRead, audioWrite, smallFileSize);
    }
}

// We receive a starting time from the GUI which we use as the
// basis of our first fetch of events from the GUI core.  Assuming
// this works we set our internal state to PLAYING and go ahead
// and play the piece until we get a signal to stop.
// 
// DCOP wants us to use an int as a return type instead of a bool.
//
int
RosegardenSequencerApp::play(const Rosegarden::RealTime &time,
                             const Rosegarden::RealTime &readAhead,
                             const Rosegarden::RealTime &audioMix,
                             const Rosegarden::RealTime &audioRead,
                             const Rosegarden::RealTime &audioWrite,
                       long smallFileSize)
{
    if (m_transportStatus == PLAYING ||
      m_transportStatus == STARTING_TO_PLAY)
        return true;

    // Check for record toggle (punch out)
    //
    if (m_transportStatus == RECORDING) {
        m_transportStatus = PLAYING;
      return punchOut();
    }

    // To play from the given song position sets up the internal
    // play state to "STARTING_TO_PLAY" which is then caught in
    // the main event loop
    //
    m_songPosition = time;

    if (m_sequencerMapper.getSequencerDataBlock()) {
      m_sequencerMapper.getSequencerDataBlock()->setPositionPointer
          (m_songPosition);
    }

    if (m_transportStatus != RECORDING &&
        m_transportStatus != STARTING_TO_RECORD)
    {
        m_transportStatus = STARTING_TO_PLAY;
    }

    m_driver->stopClocks();

    // Set up buffer size
    //
    m_readAhead = readAhead;
    if (m_readAhead == Rosegarden::RealTime::zeroTime)
        m_readAhead.sec = 1;

    m_audioMix = audioMix;
    m_audioRead = audioRead;
    m_audioWrite = audioWrite;
    m_smallFileSize = smallFileSize;

    m_driver->setAudioBufferSizes(m_audioMix, m_audioRead, m_audioWrite,
                             m_smallFileSize);

    cleanupMmapData();

    // Map all segments
    //
    QDir segmentsDir(m_segmentFilesPath, "segment_*");
    for (unsigned int i = 0; i < segmentsDir.count(); ++i) {
        mmapSegment(m_segmentFilesPath + "/" + segmentsDir[i]);
    }

    QString tmpDir = KGlobal::dirs()->resourceDirs("tmp").last();

    // Map metronome
    //
    QString metronomeFileName =  tmpDir + "/rosegarden_metronome";
    QFileInfo metronomeFileInfo(metronomeFileName);
    if (metronomeFileInfo.exists())
        mmapSegment(metronomeFileName);
    else 
        SEQUENCER_DEBUG << "RosegardenSequencerApp::play() - no metronome found\n";

    // Map tempo segment
    //
    QString tempoSegmentFileName = tmpDir + "/rosegarden_tempo";
    QFileInfo tempoSegmentFileInfo(tempoSegmentFileName);
    if (tempoSegmentFileInfo.exists())
        mmapSegment(tempoSegmentFileName);
    else 
        SEQUENCER_DEBUG << "RosegardenSequencerApp::play() - no tempo segment found\n";

    // Map time sig segment
    //
    QString timeSigSegmentFileName = tmpDir + "/rosegarden_timesig";
    QFileInfo timeSigSegmentFileInfo(timeSigSegmentFileName);
    if (timeSigSegmentFileInfo.exists())
        mmapSegment(timeSigSegmentFileName);
    else 
        SEQUENCER_DEBUG << "RosegardenSequencerApp::play() - no time sig segment found\n";

    // Map control block if necessary
    //
    if (!m_controlBlockMmapper) {
      m_controlBlockMmapper = new ControlBlockMmapper(tmpDir + "/rosegarden_control_block");
      m_sequencerMapper.setControlBlock(m_controlBlockMmapper->getControlBlock());
    }

    initMetaIterator();

    // report
    //
    SEQUENCER_DEBUG << "RosegardenSequencerApp::play() - starting to play\n";

    // Test bits
//     m_metaIterator = new MmappedSegmentsMetaIterator(m_mmappedSegments);
//     Rosegarden::MappedComposition testCompo;
//     m_metaIterator->fillCompositionWithEventsUntil(&testCompo,
//                                                    Rosegarden::RealTime(2,0));

//     dumpFirstSegment();

    // keep it simple
    return true;
}

int
RosegardenSequencerApp::punchOut()
{
    // Check for record toggle (punch out)
    //
    if (m_transportStatus == RECORDING) {
      m_driver->punchOut();
        m_transportStatus = PLAYING;
        return true;
    }
    return false;
}

MmappedSegment* RosegardenSequencerApp::mmapSegment(const QString& file)
{
    MmappedSegment* m = 0;
    
    try {
        m = new MmappedSegment(file);
    } catch (Rosegarden::Exception e) {
        SEQUENCER_DEBUG << "RosegardenSequencerApp::mmapSegment() - couldn't map file " << file
                        << " : " << e.getMessage().c_str() << endl;
        return 0;
    }
    
    
    m_mmappedSegments[file] = m;
    return m;
}

void RosegardenSequencerApp::initMetaIterator()
{
    delete m_metaIterator;
    m_metaIterator = new MmappedSegmentsMetaIterator(m_mmappedSegments, m_controlBlockMmapper);
}

void RosegardenSequencerApp::cleanupMmapData()
{
    for(MmappedSegmentsMetaIterator::mmappedsegments::iterator i = 
            m_mmappedSegments.begin(); i != m_mmappedSegments.end(); ++i)
        delete i->second;

    m_mmappedSegments.clear();

    delete m_metaIterator;
    m_metaIterator = 0;
}

void RosegardenSequencerApp::remapSegment(const QString& filename, size_t newSize)
{
    if (m_transportStatus != PLAYING) return;

    SEQUENCER_DEBUG << "RosegardenSequencerApp::remapSegment(" << filename << ")\n";

    MmappedSegment* m = m_mmappedSegments[filename];
    if (m->remap(newSize) && m_metaIterator)
        m_metaIterator->resetIteratorForSegment(filename);
}

void RosegardenSequencerApp::addSegment(const QString& filename)
{
    if (m_transportStatus != PLAYING) return;

    SEQUENCER_DEBUG << "MmappedSegment::addSegment(" << filename << ")\n";

    MmappedSegment* m = mmapSegment(filename);

    if (m_metaIterator)
        m_metaIterator->addSegment(m);
}

void RosegardenSequencerApp::deleteSegment(const QString& filename)
{
    if (m_transportStatus != PLAYING) return;

    SEQUENCER_DEBUG << "MmappedSegment::deleteSegment(" << filename << ")\n";

    MmappedSegment* m = m_mmappedSegments[filename];

    if (m_metaIterator)
        m_metaIterator->deleteSegment(m);

    delete m;

    // #932415
    m_mmappedSegments.erase(filename);
}

void RosegardenSequencerApp::closeAllSegments()
{
    SEQUENCER_DEBUG << "MmappedSegment::closeAllSegments()\n";

    for(MmappedSegmentsMetaIterator::mmappedsegments::iterator 
            i = m_mmappedSegments.begin();
            i != m_mmappedSegments.end(); ++i) {
        if (m_metaIterator)
            m_metaIterator->deleteSegment(i->second);

        delete i->second;
    }

    m_mmappedSegments.clear();

    m_sequencerMapper.setControlBlock(0);
    delete m_controlBlockMmapper;
    m_controlBlockMmapper = 0;
}

void RosegardenSequencerApp::remapTracks()
{
    SEQUENCER_DEBUG << "RosegardenSequencerApp::remapTracks" << endl;

    rationalisePlayingAudio();
}

// DCOP Wrapper for play(Rosegarden::RealTime,
//                       Rosegarden::RealTime,
//                       Rosegarden::RealTime)
//
//
int
RosegardenSequencerApp::play(long timeSec,
                             long timeNSec,
                             long readAheadSec,
                             long readAheadNSec,
                       long audioMixSec,
                       long audioMixNsec,
                       long audioReadSec,
                       long audioReadNsec,
                       long audioWriteSec,
                       long audioWriteNsec,
                       long smallFileSize)

{
    return play(Rosegarden::RealTime(timeSec, timeNSec),
                Rosegarden::RealTime(readAheadSec, readAheadNSec),
            Rosegarden::RealTime(audioMixSec, audioMixNsec),
            Rosegarden::RealTime(audioReadSec, audioReadNsec),
            Rosegarden::RealTime(audioWriteSec, audioWriteNsec),
            smallFileSize);
}



// Wrapper for record(Rosegarden::RealTime,
//                    Rosegarden::RealTime,
//                    Rosegarden::RealTime,
//                    recordMode);
//
//
int
RosegardenSequencerApp::record(long timeSec,
                               long timeNSec,
                               long readAheadSec,
                               long readAheadNSec,
                         long audioMixSec,
                         long audioMixNsec,
                         long audioReadSec,
                         long audioReadNsec,
                         long audioWriteSec,
                         long audioWriteNsec,
                         long smallFileSize,
                               long recordMode)

{
    return record(Rosegarden::RealTime(timeSec, timeNSec),
                  Rosegarden::RealTime(readAheadSec, readAheadNSec),
              Rosegarden::RealTime(audioMixSec, audioMixNsec),
              Rosegarden::RealTime(audioReadSec, audioReadNsec),
              Rosegarden::RealTime(audioWriteSec, audioWriteNsec),
              smallFileSize,
                  recordMode);
}


void
RosegardenSequencerApp::setLoop(const Rosegarden::RealTime &loopStart,
                                const Rosegarden::RealTime &loopEnd)
{
    m_loopStart = loopStart;
    m_loopEnd = loopEnd;

    m_driver->setLoop(loopStart, loopEnd);
}


void
RosegardenSequencerApp::setLoop(long loopStartSec,
                                long loopStartNSec,
                                long loopEndSec,
                                long loopEndNSec)
{
    setLoop(Rosegarden::RealTime(loopStartSec, loopStartNSec),
            Rosegarden::RealTime(loopEndSec, loopEndNSec));
}


// Return the status of the sound systems (audio and MIDI)
//
unsigned int
RosegardenSequencerApp::getSoundDriverStatus(const QString &guiVersion)
{
    unsigned int driverStatus = m_driver->getStatus();
    if (guiVersion == VERSION) driverStatus |= Rosegarden::VERSION_OK;
    else {
      std::cerr << "WARNING: RosegardenSequencerApp::getSoundDriverStatus: "
              << "GUI version \"" << guiVersion
              << "\" does not match sequencer version \"" << VERSION
              << "\"" << std::endl;
    }
    return driverStatus;
}


// Add an audio file to the sequencer
int
RosegardenSequencerApp::addAudioFile(const QString &fileName, int id)
{
    return((int)m_driver->addAudioFile(fileName.utf8().data(), id));
}

int
RosegardenSequencerApp::removeAudioFile(int id)
{
    return((int)m_driver->removeAudioFile(id));
}

void
RosegardenSequencerApp::clearAllAudioFiles()
{
    m_driver->clearAudioFiles();
}

void
RosegardenSequencerApp::setMappedInstrument(int type, unsigned char channel,
                                            unsigned int id)
{
    InstrumentId mID = (InstrumentId)id;
    Rosegarden::Instrument::InstrumentType mType = 
        (Rosegarden::Instrument::InstrumentType)type;
    Rosegarden::MidiByte mChannel = (Rosegarden::MidiByte)channel;

    m_driver->setMappedInstrument(
            new Rosegarden::MappedInstrument (mType, mChannel, mID));

}

// Process a MappedComposition sent from Sequencer with
// immediate effect
//
void
RosegardenSequencerApp::processSequencerSlice(Rosegarden::MappedComposition mC)
{
    // Use the "now" API
    //
    m_driver->processEventsOut(mC);
}

void
RosegardenSequencerApp::processMappedEvent(unsigned int id,
                                           int type,
                                           unsigned char pitch,
                                           unsigned char velocity,
                                           long absTimeSec,
                                           long absTimeNsec,
                                           long durationSec,
                                           long durationNsec,
                                           long audioStartMarkerSec,
                                           long audioStartMarkerNSec)
{
    MappedEvent *mE =
        new MappedEvent(
            (InstrumentId)id,
            (MappedEvent::MappedEventType)type,
            (Rosegarden::MidiByte)pitch,
            (Rosegarden::MidiByte)velocity,
            Rosegarden::RealTime(absTimeSec, absTimeNsec),
            Rosegarden::RealTime(durationSec, durationNsec),
            Rosegarden::RealTime(audioStartMarkerSec, audioStartMarkerNSec));

    Rosegarden::MappedComposition mC;

//    SEQUENCER_DEBUG << "processMappedEvent(data) - sending out single event at time " << mE->getEventTime() << endl;

    /*
    std::cout << "ID = " << mE->getInstrument() << std::endl;
    std::cout << "TYPE = " << mE->getType() << std::endl;
    std::cout << "D1 = " << (int)mE->getData1() << std::endl;
    std::cout << "D2 = " << (int)mE->getData2() << std::endl;
    */

    mC.insert(mE);

    m_driver->processEventsOut(mC);
}

void
RosegardenSequencerApp::processMappedEvent(MappedEvent mE)
{
    Rosegarden::MappedComposition mC;
    mC.insert(new MappedEvent(mE));
    SEQUENCER_DEBUG << "processMappedEvent(ev) - sending out single event at time " << mE.getEventTime() << endl;

    m_driver->processEventsOut(mC);
}

// Get the MappedDevice (DCOP wrapped vector of MappedInstruments)
//
Rosegarden::MappedDevice
RosegardenSequencerApp::getMappedDevice(unsigned int id)
{
    return m_driver->getMappedDevice(id);
}

unsigned int
RosegardenSequencerApp::getDevices()
{
    return m_driver->getDevices();
}

int
RosegardenSequencerApp::canReconnect(int type)
{
    return m_driver->canReconnect((Rosegarden::Device::DeviceType)type);
}

unsigned int
RosegardenSequencerApp::addDevice(int type, unsigned int direction)
{
    return m_driver->addDevice((Rosegarden::Device::DeviceType)type,
                         (Rosegarden::MidiDevice::DeviceDirection)direction);
}

void
RosegardenSequencerApp::removeDevice(unsigned int deviceId)
{
    m_driver->removeDevice(deviceId);
}

void
RosegardenSequencerApp::renameDevice(unsigned int deviceId, QString name)
{
    m_driver->renameDevice(deviceId, name);
}

unsigned int
RosegardenSequencerApp::getConnections(int type, unsigned int direction)
{
    return m_driver->getConnections((Rosegarden::Device::DeviceType)type,
                            (Rosegarden::MidiDevice::DeviceDirection)direction);
}

QString
RosegardenSequencerApp::getConnection(int type, unsigned int direction,
                              unsigned int connectionNo)
{
    return m_driver->getConnection((Rosegarden::Device::DeviceType)type,
                           (Rosegarden::MidiDevice::DeviceDirection)direction,
                           connectionNo);
}

void
RosegardenSequencerApp::setConnection(unsigned int deviceId,
                              QString connection)
{
    m_driver->setConnection(deviceId, connection);
}

void
RosegardenSequencerApp::setPlausibleConnection(unsigned int deviceId,
                                     QString connection)
{
    m_driver->setPlausibleConnection(deviceId, connection);
}

unsigned int
RosegardenSequencerApp::getTimers()
{
    return m_driver->getTimers();
}

QString
RosegardenSequencerApp::getTimer(unsigned int n)
{
    return m_driver->getTimer(n);
}

QString
RosegardenSequencerApp::getCurrentTimer()
{
    return m_driver->getCurrentTimer();
}

void
RosegardenSequencerApp::setCurrentTimer(QString timer)
{
    m_driver->setCurrentTimer(timer);
}

void
RosegardenSequencerApp::setLowLatencyMode(bool ll)
{
    m_driver->setLowLatencyMode(ll);
}

void
RosegardenSequencerApp::sequencerAlive()
{
    if (!kapp->dcopClient()->
        isApplicationRegistered(QCString(ROSEGARDEN_GUI_APP_NAME)))
    {
        SEQUENCER_DEBUG << "RosegardenSequencerApp::sequencerAlive() - "
                        << "waiting for GUI to register" << endl;
        return;
    }

    QByteArray data;

    if (!kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                                  ROSEGARDEN_GUI_IFACE_NAME,
                                 "alive()",
                                  data))
    {
        SEQUENCER_DEBUG << "RosegardenSequencer::alive()"
                        << " - can't call RosegardenGUI client"
                        << endl;
    }

    SEQUENCER_DEBUG << "RosegardenSequencerApp::sequencerAlive() - "
                    << "trying to tell GUI that we're alive" << endl;
}

Rosegarden::MappedRealTime
RosegardenSequencerApp::getAudioPlayLatency()
{
    return Rosegarden::MappedRealTime(m_driver->getAudioPlayLatency());
}

Rosegarden::MappedRealTime
RosegardenSequencerApp::getAudioRecordLatency()
{
    return Rosegarden::MappedRealTime(m_driver->getAudioRecordLatency());
}

// Initialise the virtual studio with a few audio faders and
// create a plugin manager.  For the moment this is pretty
// arbitrary but eventually we'll drive this from the gui
// and rg file "Studio" entries.
//
void
RosegardenSequencerApp::initialiseStudio()
{
    // clear down the studio before we start adding anything
    //
    m_studio->clear();
}


void
RosegardenSequencerApp::setMappedProperty(int id,
                                          const QString &property,
                                          float value)
{

//    SEQUENCER_DEBUG << "setProperty: id = " << id
//                    << " : property = \"" << property << "\""
//              << ", value = " << value << endl;


    Rosegarden::MappedObject *object = m_studio->getObjectById(id);

    if (object)
        object->setProperty(property, value);
}

void
RosegardenSequencerApp::setMappedProperties(const Rosegarden::MappedObjectIdList &ids,
                                  const Rosegarden::MappedObjectPropertyList &properties,
                                  const Rosegarden::MappedObjectValueList &values)
{
    Rosegarden::MappedObject *object = 0;
    Rosegarden::MappedObjectId prevId = 0;

    for (size_t i = 0;
       i < ids.size() && i < properties.size() && i < values.size();
       ++i) {

      if (i == 0 || ids[i] != prevId) {
          object = m_studio->getObjectById(ids[i]);
          prevId = ids[i];
      }

      if (object) {
          object->setProperty(properties[i], values[i]);
      }
    }
}

void
RosegardenSequencerApp::setMappedProperty(int id,
                                          const QString &property,
                                          const QString &value)
{

    SEQUENCER_DEBUG << "setProperty: id = " << id
                    << " : property = \"" << property << "\""
                << ", value = " << value << endl;


    Rosegarden::MappedObject *object = m_studio->getObjectById(id);

    if (object)
        object->setProperty(property, value);
}

void
RosegardenSequencerApp::setMappedPropertyList(int id, const QString &property,
                                    const Rosegarden::MappedObjectPropertyList &values)
{
    SEQUENCER_DEBUG << "setPropertyList: id = " << id
                    << " : property list size = \"" << values.size()
                    << "\"" << endl;

    Rosegarden::MappedObject *object = m_studio->getObjectById(id);

    if (object) {
      try {
          object->setPropertyList(property, values);
      } catch (QString err) {
          QByteArray data;
          QDataStream arg(data, IO_WriteOnly);
          arg << err;
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "showError(QString)",
                             data);
      }
    }
}
      

int
RosegardenSequencerApp::getMappedObjectId(int type)
{
    int value = -1;

    Rosegarden::MappedObject *object =
        m_studio->getObjectOfType(
                Rosegarden::MappedObject::MappedObjectType(type));

    if (object)
    {
        value = int(object->getId());
    }

    return value;
}


std::vector<QString>
RosegardenSequencerApp::getPropertyList(int id,
                                        const QString &property)
{
    std::vector<QString> list;

    Rosegarden::MappedObject *object =
        m_studio->getObjectById(id);

    if (object)
    {
        list = object->getPropertyList(property);
    }

    SEQUENCER_DEBUG << "getPropertyList - return " << list.size()
                    << " items" << endl;

    return list;
}

std::vector<QString>
RosegardenSequencerApp::getPluginInformation()
{
    std::vector<QString> list;

    Rosegarden::PluginFactory::enumerateAllPlugins(list);

    return list;
}

QString
RosegardenSequencerApp::getPluginProgram(int id, int bank, int program)
{
    Rosegarden::MappedObject *object = m_studio->getObjectById(id);

    if (object) {
      Rosegarden::MappedPluginSlot *slot =
          dynamic_cast<Rosegarden::MappedPluginSlot *>(object);
      if (slot) {
          return slot->getProgram(bank, program);
      }
    }

    return QString();
}

unsigned long
RosegardenSequencerApp::getPluginProgram(int id, const QString &name)
{
    Rosegarden::MappedObject *object = m_studio->getObjectById(id);

    if (object) {
      Rosegarden::MappedPluginSlot *slot =
          dynamic_cast<Rosegarden::MappedPluginSlot *>(object);
      if (slot) {
          return slot->getProgram(name);
      }
    }

    return 0;
}

unsigned int
RosegardenSequencerApp::getSampleRate() const
{
    if (m_driver)
        return m_driver->getSampleRate();

    return 0;
}

// Creates an object of a type
//
int 
RosegardenSequencerApp::createMappedObject(int type)
{
    Rosegarden::MappedObject *object =
              m_studio->createObject(
                      Rosegarden::MappedObject::MappedObjectType(type));

    if (object)
    {
        SEQUENCER_DEBUG << "createMappedObject - type = "
                        << type << ", object id = "
                        << object->getId() << endl;
        return object->getId();
    }

    return 0;
}

// Destroy an object
//
int 
RosegardenSequencerApp::destroyMappedObject(int id)
{
    return(int(m_studio->destroyObject(Rosegarden::MappedObjectId(id))));
}

// Connect two objects
//
void
RosegardenSequencerApp::connectMappedObjects(int id1, int id2)
{
    m_studio->connectObjects(Rosegarden::MappedObjectId(id1),
                       Rosegarden::MappedObjectId(id2));
    
    // When this happens we need to resynchronise our audio processing,
    // and this is the easiest (and most brutal) way to do it.
    if (m_transportStatus == PLAYING ||
      m_transportStatus == RECORDING) {
      Rosegarden::RealTime seqTime = m_driver->getSequencerTime();
      jumpTo(seqTime.sec, seqTime.nsec);
    }
}
    
// Disconnect two objects
//
void
RosegardenSequencerApp::disconnectMappedObjects(int id1, int id2)
{
    m_studio->disconnectObjects(Rosegarden::MappedObjectId(id1),
                        Rosegarden::MappedObjectId(id2));
}

// Disconnect an object from everything
//
void
RosegardenSequencerApp::disconnectMappedObject(int id)
{
    m_studio->disconnectObject(Rosegarden::MappedObjectId(id));
}


void
RosegardenSequencerApp::clearStudio()
{
    SEQUENCER_DEBUG << "clearStudio()" << endl;
    m_studio->clear();
    m_sequencerMapper.getSequencerDataBlock()->clearTemporaries();

} 

void
RosegardenSequencerApp::setMappedPort(int pluginId,
                                      unsigned long portId,
                                      float value)
{
    Rosegarden::MappedObject *object =
        m_studio->getObjectById(pluginId);

    Rosegarden::MappedPluginSlot *slot = 
        dynamic_cast<Rosegarden::MappedPluginSlot *>(object);

    if (slot) {
        slot->setPort(portId, value);
    } else {
      SEQUENCER_DEBUG << "no such slot" << endl;
    }
}

float
RosegardenSequencerApp::getMappedPort(int pluginId,
                                      unsigned long portId)
{
    Rosegarden::MappedObject *object =
        m_studio->getObjectById(pluginId);

    Rosegarden::MappedPluginSlot *slot = 
        dynamic_cast<Rosegarden::MappedPluginSlot *>(object);

    if (slot) {
        return slot->getPort(portId);
    } else {
      SEQUENCER_DEBUG << "no such slot" << endl;
    }
    
    return 0;
}

void
RosegardenSequencerApp::slotCheckForNewClients()
{
    // Don't do this check if any of these conditions hold
    //
    if (m_transportStatus == PLAYING ||
        m_transportStatus == RECORDING)
        return;

    if (m_driver->checkForNewClients())
    {
        SEQUENCER_DEBUG << "client list changed" << endl;
    }
}


// Set the MIDI Clock period in microseconds
//
void
RosegardenSequencerApp::setQuarterNoteLength(long timeSec, long timeNSec)
{
    SEQUENCER_DEBUG << "RosegardenSequencerApp::setQuarterNoteLength"
                    << Rosegarden::RealTime(timeSec, timeNSec) << endl;

    m_driver->setMIDIClockInterval(
            Rosegarden::RealTime(timeSec, timeNSec) / 24);
}

QString
RosegardenSequencerApp::getStatusLog()
{
    return m_driver->getStatusLog();
}


void RosegardenSequencerApp::dumpFirstSegment()
{
    SEQUENCER_DEBUG << "Dumping 1st segment data :\n";

    unsigned int i = 0;
    MmappedSegment* firstMappedSegment = (*(m_mmappedSegments.begin())).second;

    MmappedSegment::iterator it(firstMappedSegment);

    for (; !it.atEnd(); ++it) {

        MappedEvent evt = (*it);
        SEQUENCER_DEBUG << i << " : inst = "  << evt.getInstrument()
                        << " - type = "       << evt.getType()
                        << " - data1 = "      << (unsigned int)evt.getData1()
                        << " - data2 = "      << (unsigned int)evt.getData2()
                        << " - time = "       << evt.getEventTime()
                        << " - duration = "   << evt.getDuration()
                        << " - audio mark = " << evt.getAudioStartMarker()
                        << endl;

        ++i;
    }

    SEQUENCER_DEBUG << "Dumping 1st segment data - done\n";

}


void
RosegardenSequencerApp::rationalisePlayingAudio()
{
    std::vector<Rosegarden::MappedEvent> audioEvents;
    m_metaIterator->getAudioEvents(audioEvents);
    m_driver->initialiseAudioQueue(audioEvents);
}


Rosegarden::ExternalTransport::TransportToken
RosegardenSequencerApp::transportChange(TransportRequest request)
{
    TransportPair pair(request, Rosegarden::RealTime::zeroTime);
    m_transportRequests.push_back(pair);

    std::cout << "RosegardenSequencerApp::transportChange: " << request << std::endl;

    if (request == TransportNoChange) return m_transportToken;
    else return m_transportToken + 1;
}

Rosegarden::ExternalTransport::TransportToken
RosegardenSequencerApp::transportJump(TransportRequest request,
                              Rosegarden::RealTime rt)
{
    TransportPair pair(request, rt);
    m_transportRequests.push_back(pair);

    std::cout << "RosegardenSequencerApp::transportJump: " << request << ", " << rt << std::endl;

    if (request == TransportNoChange) return m_transportToken + 1;
    else return m_transportToken + 2;
}

bool
RosegardenSequencerApp::isTransportSyncComplete(TransportToken token)
{
    std::cout << "RosegardenSequencerApp::isTransportSyncComplete: token " << token << ", current token " << m_transportToken << std::endl;
    return m_transportToken >= token;
}

bool
RosegardenSequencerApp::checkExternalTransport()
{
    bool rv = (!m_transportRequests.empty());

    while (!m_transportRequests.empty()) {

      TransportPair pair = *m_transportRequests.begin();
      m_transportRequests.pop_front();

      QByteArray data;

      switch (pair.first) {
          
      case TransportNoChange:
          break;
      
      case TransportStop:
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "stop()",
                             data);
          break;
      
      case TransportStart:
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "play()",
                             data);
          break;
      
      case TransportPlay:
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "play()",
                             data);
          break;
      
      case TransportRecord:
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "record()",
                             data);
          break;
      
      case TransportJumpToTime:
      {
          QDataStream arg(data, IO_WriteOnly);
          arg << (int)pair.second.sec;
          arg << (int)pair.second.usec();
          
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "jumpToTime(int, int)",
                             data);

          if (m_transportStatus == PLAYING ||
            m_transportStatus != RECORDING) {
            jumpTo(pair.second.sec, pair.second.usec() * 1000);
          }

          incrementTransportToken();
          break;
      }
      
      case TransportStartAtTime:
      {
          QDataStream arg(data, IO_WriteOnly);
          arg << (int)pair.second.sec;
          arg << (int)pair.second.usec();
          
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "startAtTime(int, int)",
                             data);
          break;
      }
      
      case TransportStopAtTime:
      {
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "stop()",
                             data);

          QDataStream arg(data, IO_WriteOnly);
          arg << (int)pair.second.sec;
          arg << (int)pair.second.usec();
          
          kapp->dcopClient()->send(ROSEGARDEN_GUI_APP_NAME,
                             ROSEGARDEN_GUI_IFACE_NAME,
                             "jumpToTime(int, int)",
                             data);
          break;
      }
      }
    }

    return rv;
}

void
RosegardenSequencerApp::incrementTransportToken()
{
    ++m_transportToken;
    SEQUENCER_DEBUG << "RosegardenSequencerApp::incrementTransportToken: incrementing to " << m_transportToken << endl;
}


#include "rosegardensequencer.moc"

Generated by  Doxygen 1.6.0   Back to index