summaryrefslogtreecommitdiffstats
path: root/src/sequencer
diff options
context:
space:
mode:
Diffstat (limited to 'src/sequencer')
-rw-r--r--src/sequencer/ControlBlockMmapper.cpp81
-rw-r--r--src/sequencer/ControlBlockMmapper.h94
-rw-r--r--src/sequencer/MmappedSegment.cpp702
-rw-r--r--src/sequencer/MmappedSegment.h185
-rw-r--r--src/sequencer/RosegardenSequencerApp.cpp1850
-rw-r--r--src/sequencer/RosegardenSequencerApp.h531
-rw-r--r--src/sequencer/RosegardenSequencerIface.h364
-rw-r--r--src/sequencer/SequencerMmapper.cpp146
-rw-r--r--src/sequencer/SequencerMmapper.h103
-rw-r--r--src/sequencer/main.cpp246
10 files changed, 4302 insertions, 0 deletions
diff --git a/src/sequencer/ControlBlockMmapper.cpp b/src/sequencer/ControlBlockMmapper.cpp
new file mode 100644
index 0000000..d9bf83d
--- /dev/null
+++ b/src/sequencer/ControlBlockMmapper.cpp
@@ -0,0 +1,81 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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 "ControlBlockMmapper.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "misc/Debug.h"
+
+namespace Rosegarden
+{
+
+ControlBlockMmapper::ControlBlockMmapper(QString fileName)
+ : m_fileName(fileName),
+ m_fd( -1),
+ m_mmappedBuffer(0),
+ m_mmappedSize(sizeof(ControlBlock)),
+ m_controlBlock(0)
+{
+ m_fd = ::open(m_fileName.latin1(), O_RDWR);
+
+ if (m_fd < 0) {
+ SEQMAN_DEBUG << "ControlBlockMmapper : Couldn't open " << m_fileName
+ << endl;
+ throw Exception(std::string("Couldn't open ")
+ + m_fileName.latin1());
+ }
+
+ //
+ // mmap() file for reading
+ //
+ m_mmappedBuffer = (char*)::mmap(0, m_mmappedSize,
+ PROT_READ, MAP_SHARED, m_fd, 0);
+
+ if (m_mmappedBuffer == (void*) - 1) {
+
+ SEQUENCER_DEBUG << QString("mmap failed : (%1) %2\n").
+ arg(errno).arg(strerror(errno));
+
+ throw Exception("mmap failed");
+ }
+
+ SEQMAN_DEBUG << "ControlBlockMmapper : mmap size : " << m_mmappedSize
+ << " at " << (void*)m_mmappedBuffer << endl;
+
+ // Create new control block on file
+ m_controlBlock = new (m_mmappedBuffer) ControlBlock;
+}
+
+ControlBlockMmapper::~ControlBlockMmapper()
+{
+ ::munmap(m_mmappedBuffer, m_mmappedSize);
+ ::close(m_fd);
+}
+
+}
diff --git a/src/sequencer/ControlBlockMmapper.h b/src/sequencer/ControlBlockMmapper.h
new file mode 100644
index 0000000..55d4c9f
--- /dev/null
+++ b/src/sequencer/ControlBlockMmapper.h
@@ -0,0 +1,94 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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.
+*/
+
+#ifndef _MMAPPEDCONTROLBLOCK_H_
+#define _MMAPPEDCONTROLBLOCK_H_
+
+#include <qstring.h>
+
+#include "sound/MappedEvent.h"
+#include "sound/ControlBlock.h"
+
+namespace Rosegarden
+{
+
+class ControlBlockMmapper
+{
+public:
+ ControlBlockMmapper(QString fileName);
+ ~ControlBlockMmapper();
+
+ QString getFileName() { return m_fileName; }
+
+ // delegate ControlBlock's interface
+ InstrumentId getInstrumentForTrack(unsigned int trackId)
+ { return m_controlBlock->getInstrumentForTrack(trackId); }
+
+ InstrumentId getInstrumentForEvent(unsigned int dev,
+ unsigned int chan)
+ { return m_controlBlock->getInstrumentForEvent(dev, chan); }
+
+ bool isTrackMuted(unsigned int trackId)
+ { return m_controlBlock->isTrackMuted(trackId); }
+
+ bool isTrackArmed(unsigned int trackId)
+ { return m_controlBlock->isTrackArmed(trackId); }
+
+ InstrumentId getInstrumentForMetronome()
+ { return m_controlBlock->getInstrumentForMetronome(); }
+
+ bool isMetronomeMuted() { return m_controlBlock->isMetronomeMuted(); }
+
+ bool isSolo() { return m_controlBlock->isSolo(); }
+
+ bool isMidiRoutingEnabled()
+ { return m_controlBlock->isMidiRoutingEnabled(); }
+
+ TrackId getSelectedTrack()
+ { return m_controlBlock->getSelectedTrack(); }
+
+ MidiFilter getThruFilter()
+ { return m_controlBlock->getThruFilter(); }
+
+ MidiFilter getRecordFilter()
+ { return m_controlBlock->getRecordFilter(); }
+
+ // for transfer to SequencerMmapper
+ ControlBlock *getControlBlock()
+ { return m_controlBlock; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ QString m_fileName;
+ int m_fd;
+ void* m_mmappedBuffer;
+ size_t m_mmappedSize;
+ ControlBlock* m_controlBlock;
+};
+
+}
+
+#endif // _MMAPPEDCONTROLBLOCK_H_
diff --git a/src/sequencer/MmappedSegment.cpp b/src/sequencer/MmappedSegment.cpp
new file mode 100644
index 0000000..57b3dc9
--- /dev/null
+++ b/src/sequencer/MmappedSegment.cpp
@@ -0,0 +1,702 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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 "MmappedSegment.h"
+#include "misc/Debug.h"
+
+#include "sound/MappedComposition.h"
+#include "sound/Midi.h"
+
+//#define DEBUG_META_ITERATOR 1
+//#define DEBUG_PLAYING_AUDIO_FILES 1
+
+namespace Rosegarden
+{
+
+MmappedSegment::MmappedSegment(const QString filename)
+ : m_fd( -1),
+ m_mmappedSize(0),
+ m_mmappedRegion(0),
+ m_mmappedEventBuffer((MappedEvent*)0),
+ m_filename(filename)
+{
+ SEQUENCER_DEBUG << "mmapping " << filename << endl;
+
+ map();
+}
+
+bool MmappedSegment::isMetronome()
+{
+ return (getFileName().contains("metronome", false) > 0);
+}
+
+
+
+void MmappedSegment::map()
+{
+ QFileInfo fInfo(m_filename);
+ if (!fInfo.exists()) {
+ SEQUENCER_DEBUG << "MmappedSegment::map() : file " << m_filename << " doesn't exist\n";
+ throw Exception("file not found");
+ }
+
+ m_mmappedSize = fInfo.size();
+
+ m_fd = ::open(m_filename.latin1(), O_RDWR);
+
+ m_mmappedRegion = ::mmap(0, m_mmappedSize, PROT_READ, MAP_SHARED, m_fd, 0);
+
+ if (m_mmappedRegion == (void*) - 1) {
+
+ SEQUENCER_DEBUG << QString("mmap failed : (%1) %2\n").
+ arg(errno).arg(strerror(errno));
+
+ throw Exception("mmap failed");
+ }
+
+ m_mmappedEventBuffer = (MappedEvent *)((size_t *)m_mmappedRegion + 1);
+
+ SEQUENCER_DEBUG << "MmappedSegment::map() : "
+ << (void*)m_mmappedRegion << "," << m_mmappedSize << endl;
+
+}
+
+MmappedSegment::~MmappedSegment()
+{
+ unmap();
+}
+
+void MmappedSegment::unmap()
+{
+ ::munmap(m_mmappedRegion, m_mmappedSize);
+ ::close(m_fd);
+}
+
+size_t
+MmappedSegment::getNbMappedEvents() const
+{
+ if (m_mmappedRegion || !m_mmappedSize) {
+
+ // The shared memory area consists of one size_t giving the
+ // number of following mapped events, followed by the mapped
+ // events themselves.
+
+ // So this is the number of mapped events that we expect:
+ size_t nominal = *(size_t *)m_mmappedRegion;
+
+ // But we want to be sure not to read off the end of the
+ // shared memory area, so just in case, this is the number of
+ // events that can actually be accommodated in the memory area
+ // as we see it:
+ size_t actual = (m_mmappedSize - sizeof(size_t)) /
+ sizeof(MappedEvent);
+
+ return std::min(nominal, actual);
+
+ } else
+ return 0;
+}
+
+bool MmappedSegment::remap(size_t newSize)
+{
+ SEQUENCER_DEBUG << "remap() from " << m_mmappedSize << " to "
+ << newSize << endl;
+
+ if (m_mmappedSize == newSize) {
+
+ SEQUENCER_DEBUG << "remap() : sizes are identical, remap not forced - "
+ << "nothing to do\n";
+ return false;
+ }
+
+#ifdef linux
+ void *oldRegion = m_mmappedRegion;
+ m_mmappedRegion = (MappedEvent*)::mremap(m_mmappedRegion, m_mmappedSize, newSize, MREMAP_MAYMOVE);
+ if (m_mmappedRegion != oldRegion) {
+ SEQUENCER_DEBUG << "NOTE: buffer moved from " << oldRegion <<
+ " to " << (void *)m_mmappedRegion << endl;
+ }
+#else
+ ::munmap(m_mmappedRegion, m_mmappedSize);
+ m_mmappedRegion = (MappedEvent*)::mmap(0, newSize, PROT_READ, MAP_SHARED, m_fd, 0);
+#endif
+
+ if (m_mmappedRegion == (void*) - 1) {
+
+ SEQUENCER_DEBUG << QString("mremap failed : (%1) %2\n").
+ arg(errno).arg(strerror(errno));
+
+ throw Exception("mremap failed");
+ }
+
+ m_mmappedEventBuffer = (MappedEvent *)((size_t *)m_mmappedRegion + 1);
+ m_mmappedSize = newSize;
+
+ return true;
+}
+
+MmappedSegment::iterator::iterator(MmappedSegment* s)
+ : m_s(s), m_currentEvent(m_s->getBuffer())
+{}
+
+MmappedSegment::iterator& MmappedSegment::iterator::operator=(const iterator& it)
+{
+ if (&it == this)
+ return * this;
+
+ m_s = it.m_s;
+ m_currentEvent = it.m_currentEvent;
+
+ return *this;
+}
+
+MmappedSegment::iterator& MmappedSegment::iterator::operator++()
+{
+ if (!atEnd()) {
+
+ do
+ ++m_currentEvent;
+ while (!atEnd() && (m_currentEvent->getType() == 0));
+ // skip null events - there can be some if the file has been
+ // zeroed out after events have been deleted
+
+ } else {
+
+ SEQUENCER_DEBUG << "MmappedSegment::iterator::operator++() " << this
+ << " - reached end of stream\n";
+
+ }
+
+ return *this;
+}
+
+MmappedSegment::iterator MmappedSegment::iterator::operator++(int)
+{
+ iterator r = *this;
+
+ if (!atEnd()) {
+ do
+ ++m_currentEvent;
+ while (!atEnd() && m_currentEvent->getType() == 0);
+
+ }
+
+ return r;
+}
+
+MmappedSegment::iterator& MmappedSegment::iterator::operator+=(int offset)
+{
+ m_currentEvent += offset;
+
+ if (atEnd()) {
+ m_currentEvent = m_s->getBuffer() + m_s->getNbMappedEvents();
+ }
+
+ return *this;
+}
+
+MmappedSegment::iterator& MmappedSegment::iterator::operator-=(int offset)
+{
+ m_currentEvent -= offset;
+ if (m_currentEvent < m_s->getBuffer()) {
+ m_currentEvent = m_s->getBuffer();
+ }
+
+ return *this;
+}
+
+
+bool MmappedSegment::iterator::operator==(const iterator& it)
+{
+ return (m_currentEvent == it.m_currentEvent) || (atEnd() == it.atEnd());
+}
+
+void MmappedSegment::iterator::reset()
+{
+ m_currentEvent = m_s->getBuffer();
+}
+
+const MappedEvent &MmappedSegment::iterator::operator*()
+{
+ return *m_currentEvent;
+}
+
+bool MmappedSegment::iterator::atEnd() const
+{
+ return (m_currentEvent == 0) ||
+ (m_currentEvent > (m_s->getBuffer() + m_s->getNbMappedEvents() - 1));
+}
+
+//----------------------------------------
+
+MmappedSegmentsMetaIterator::MmappedSegmentsMetaIterator(
+ mmappedsegments& segments,
+ ControlBlockMmapper* controlBlockMmapper)
+ : m_controlBlockMmapper(controlBlockMmapper),
+ m_segments(segments)
+{
+ for (mmappedsegments::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i)
+ m_iterators.push_back(new MmappedSegment::iterator(i->second));
+}
+
+MmappedSegmentsMetaIterator::~MmappedSegmentsMetaIterator()
+{
+ clear();
+}
+
+void MmappedSegmentsMetaIterator::addSegment(MmappedSegment* ms)
+{
+ MmappedSegment::iterator* iter = new MmappedSegment::iterator(ms);
+ moveIteratorToTime(*iter, m_currentTime);
+ m_iterators.push_back(iter);
+}
+
+void MmappedSegmentsMetaIterator::deleteSegment(MmappedSegment* ms)
+{
+ for (segmentiterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i) {
+ if ((*i)->getSegment() == ms) {
+ SEQUENCER_DEBUG << "deleteSegment : found segment to delete : "
+ << ms->getFileName() << endl;
+ delete (*i);
+ m_iterators.erase(i);
+ break;
+ }
+ }
+}
+
+void MmappedSegmentsMetaIterator::clear()
+{
+ for (unsigned int i = 0; i < m_iterators.size(); ++i)
+ delete m_iterators[i];
+
+ m_iterators.clear();
+}
+
+void MmappedSegmentsMetaIterator::reset()
+{
+ m_currentTime.sec = m_currentTime.nsec = 0;
+
+ for (segmentiterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i) {
+ (*i)->reset();
+ }
+
+}
+
+bool MmappedSegmentsMetaIterator::jumpToTime(const RealTime& startTime)
+{
+ SEQUENCER_DEBUG << "jumpToTime(" << startTime << ")" << endl;
+
+ reset();
+
+ bool res = true;
+
+ m_currentTime = startTime;
+
+ for (segmentiterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i)
+ if (!moveIteratorToTime(*(*i), startTime))
+ res = false;
+
+ return res;
+}
+
+bool MmappedSegmentsMetaIterator::moveIteratorToTime(MmappedSegment::iterator& iter,
+ const RealTime& startTime)
+{
+ while ((!iter.atEnd()) &&
+ (iter.peek()->getEventTime() < startTime) &&
+ ((iter.peek()->getEventTime() + iter.peek()->getDuration()) < startTime)
+ ) {
+ ++iter;
+ }
+ bool res = !iter.atEnd();
+
+ return res;
+}
+
+bool MmappedSegmentsMetaIterator::acceptEvent(MappedEvent *evt, bool evtIsFromMetronome)
+{
+ if (evt->getType() == 0)
+ return false; // discard those right away
+
+ if (evtIsFromMetronome) {
+ if (evt->getType() == MappedEvent::MidiSystemMessage &&
+ evt->getData1() == MIDI_TIMING_CLOCK) {
+ /*
+ std::cout << "MmappedSegmentsMetaIterator::acceptEvent - "
+ << "found clock" << std::endl;
+ */
+ return true;
+ }
+
+ return !m_controlBlockMmapper->isMetronomeMuted();
+ }
+
+ // else, evt is not from metronome : first check if we're soloing (i.e. playing only the selected track)
+ if (m_controlBlockMmapper->isSolo())
+ return (evt->getTrackId() == m_controlBlockMmapper->getSelectedTrack());
+
+ // finally we're not soloing, so check if track is muted
+ TrackId track = evt->getTrackId();
+ bool muted = m_controlBlockMmapper->isTrackMuted(evt->getTrackId());
+
+#ifdef DEBUG_META_ITERATOR
+
+ SEQUENCER_DEBUG << "MSMI::acceptEvent: track " << track << " muted status: " << muted << endl;
+#endif
+
+ return !muted;
+}
+
+
+bool
+MmappedSegmentsMetaIterator::fillCompositionWithEventsUntil(bool /*firstFetch*/,
+ MappedComposition* c,
+ const RealTime& startTime,
+ const RealTime& endTime)
+{
+#ifdef DEBUG_META_ITERATOR
+ SEQUENCER_DEBUG << "MSMI::fillCompositionWithEventsUntil " << startTime << " -> " << endTime << endl;
+#endif
+
+ m_currentTime = endTime;
+
+ // keep track of the segments which still have valid events
+ std::vector<bool> validSegments;
+ for (unsigned int i = 0; i < m_segments.size(); ++i)
+ validSegments.push_back(true);
+
+ bool foundOneEvent = false, eventsRemaining = false;
+
+ do {
+ foundOneEvent = false;
+
+ for (unsigned int i = 0; i < m_iterators.size(); ++i) {
+
+ MmappedSegment::iterator* iter = m_iterators[i];
+
+ //std::cerr << "Iterating on Segment #" << i << std::endl;
+
+#ifdef DEBUG_META_ITERATOR
+
+ SEQUENCER_DEBUG << "MSMI::fillCompositionWithEventsUntil : "
+ << "checking segment #" << i << " "
+ << iter->getSegment()->getFileName() << endl;
+#endif
+
+ if (!validSegments[i]) {
+#ifdef DEBUG_META_ITERATOR
+ SEQUENCER_DEBUG << "MSMI::fillCompositionWithEventsUntil : "
+ << "no more events to get for this slice "
+ << "in segment #" << i << endl;
+#endif
+
+ continue; // skip this segment
+ }
+
+ bool evtIsFromMetronome = iter->getSegment()->isMetronome();
+
+ if (iter->atEnd()) {
+#ifdef DEBUG_META_ITERATOR
+ SEQUENCER_DEBUG << "MSMI::fillCompositionWithEventsUntil : "
+ << endTime
+ << " reached end of segment #"
+ << i << endl;
+#endif
+
+ continue;
+ } else if (!evtIsFromMetronome) {
+ eventsRemaining = true;
+ }
+
+ if ((**iter).getEventTime() < endTime) {
+
+ MappedEvent *evt = new MappedEvent(*(*iter));
+
+ // set event's instrument
+ //
+ if (evtIsFromMetronome) {
+
+ evt->setInstrument(m_controlBlockMmapper->
+ getInstrumentForMetronome());
+
+ } else {
+
+ evt->setInstrument(m_controlBlockMmapper->
+ getInstrumentForTrack(evt->getTrackId()));
+
+ }
+
+#ifdef DEBUG_META_ITERATOR
+ SEQUENCER_DEBUG << "MSMI::fillCompositionWithEventsUntil : " << endTime
+ << " inserting evt from segment #"
+ << i
+ << " : trackId: " << evt->getTrackId()
+ << " - inst: " << evt->getInstrument()
+ << " - type: " << evt->getType()
+ << " - time: " << evt->getEventTime()
+ << " - duration: " << evt->getDuration()
+ << " - data1: " << (unsigned int)evt->getData1()
+ << " - data2: " << (unsigned int)evt->getData2()
+ << " - metronome event: " << evtIsFromMetronome
+ << endl;
+#endif
+
+ if (evt->getType() == MappedEvent::TimeSignature) {
+
+ // Process time sig and tempo changes along with
+ // everything else, as the sound driver probably
+ // wants to know when they happen
+
+ c->insert(evt);
+
+ } else if (evt->getType() == MappedEvent::Tempo) {
+
+ c->insert(evt);
+
+ } else if (evt->getType() == MappedEvent::MidiSystemMessage &&
+
+ // #1048388:
+ // Ensure sysex heeds mute status, but ensure
+ // clocks etc still get through
+ evt->getData1() != MIDI_SYSTEM_EXCLUSIVE) {
+
+ c->insert(evt);
+
+ } else if (acceptEvent(evt, evtIsFromMetronome) &&
+
+ ((evt->getEventTime() + evt->getDuration() > startTime) ||
+ (evt->getDuration() == RealTime::zeroTime &&
+ evt->getEventTime() == startTime))) {
+
+ // std::cout << "inserting event" << std::endl;
+
+ /*
+ std::cout << "Inserting event (type = "
+ << evt->getType() << ")" << std::endl;
+ */
+
+
+ c->insert(evt);
+
+ } else {
+
+#ifdef DEBUG_META_ITERATOR
+ std::cout << "MSMI: skipping event"
+ << " - event time = " << evt->getEventTime()
+ << ", duration = " << evt->getDuration()
+ << ", startTime = " << startTime << std::endl;
+#endif
+
+ delete evt;
+ }
+
+ if (!evtIsFromMetronome)
+ foundOneEvent = true;
+ ++(*iter);
+
+ } else {
+ validSegments[i] = false; // no more events to get from this segment
+#ifdef DEBUG_META_ITERATOR
+
+ SEQUENCER_DEBUG << "fillCompositionWithEventsUntil : no more events to get from segment #"
+ << i << endl;
+#endif
+
+ }
+
+ }
+
+ } while (foundOneEvent);
+
+#ifdef DEBUG_META_ITERATOR
+
+ SEQUENCER_DEBUG << "fillCompositionWithEventsUntil : eventsRemaining = " << eventsRemaining << endl;
+#endif
+
+ return eventsRemaining || foundOneEvent;
+}
+
+void MmappedSegmentsMetaIterator::resetIteratorForSegment(const QString& filename)
+{
+ for (segmentiterators::iterator i = m_iterators.begin(); i != m_iterators.end(); ++i) {
+ MmappedSegment::iterator* iter = *i;
+
+ if (iter->getSegment()->getFileName() == filename) {
+ SEQUENCER_DEBUG << "MSMI::resetIteratorForSegment(" << filename << ") : found iterator\n";
+ // delete iterator and create another one
+ MmappedSegment* ms = (*i)->getSegment();
+ delete iter;
+ m_iterators.erase(i);
+ iter = new MmappedSegment::iterator(ms);
+ m_iterators.push_back(iter);
+ moveIteratorToTime(*iter, m_currentTime);
+ break;
+ }
+
+ }
+}
+
+void
+MmappedSegmentsMetaIterator::getAudioEvents(std::vector<MappedEvent> &v)
+{
+ v.clear();
+
+ for (mmappedsegments::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+
+ MmappedSegment::iterator itr(i->second);
+
+ while (!itr.atEnd()) {
+
+ if ((*itr).getType() != MappedEvent::Audio) {
+ ++itr;
+ continue;
+ }
+
+ MappedEvent evt(*itr);
+ ++itr;
+
+ if (m_controlBlockMmapper->isTrackMuted(evt.getTrackId())) {
+#ifdef DEBUG_PLAYING_AUDIO_FILES
+ std::cout << "MSMI::getAudioEvents - "
+ << "track " << evt.getTrackId() << " is muted" << std::endl;
+#endif
+
+ continue;
+ }
+
+ if (m_controlBlockMmapper->isSolo() == true &&
+ evt.getTrackId() != m_controlBlockMmapper->getSelectedTrack()) {
+#ifdef DEBUG_PLAYING_AUDIO_FILES
+ std::cout << "MSMI::getAudioEvents - "
+ << "track " << evt.getTrackId() << " is not solo track" << std::endl;
+#endif
+
+ continue;
+ }
+
+ v.push_back(evt);
+ }
+ }
+}
+
+
+std::vector<MappedEvent>&
+MmappedSegmentsMetaIterator::getPlayingAudioFiles(const RealTime &
+ songPosition)
+{
+ // Clear playing audio segments
+ //
+ m_playingAudioSegments.clear();
+
+#ifdef DEBUG_PLAYING_AUDIO_FILES
+
+ std::cout << "MSMI::getPlayingAudioFiles" << std::endl;
+#endif
+
+ for (mmappedsegments::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+
+ MmappedSegment::iterator iter(i->second);
+
+ bool found = false;
+
+ //!!! any point to this loop at all? can found ever fail?
+ for (segmentiterators::iterator sI = m_iterators.begin();
+ sI != m_iterators.end(); ++sI) {
+ if ((*sI)->getSegment() == iter.getSegment())
+ found = true;
+ }
+
+ if (!found)
+ continue;
+
+ while (!iter.atEnd()) {
+ if ((*iter).getType() != MappedEvent::Audio) {
+ ++iter;
+ continue;
+ }
+
+ //std::cout << "CONSTRUCTING MAPPEDEVENT" << std::endl;
+ MappedEvent evt(*iter);
+
+ // Check for this track being muted or soloed
+ //
+ if (m_controlBlockMmapper->isTrackMuted(evt.getTrackId()) == true) {
+#ifdef DEBUG_PLAYING_AUDIO_FILES
+ std::cout << "MSMI::getPlayingAudioFiles - "
+ << "track " << evt.getTrackId() << " is muted" << std::endl;
+#endif
+
+ ++iter;
+ continue;
+ }
+
+ if (m_controlBlockMmapper->isSolo() == true &&
+ evt.getTrackId() != m_controlBlockMmapper->getSelectedTrack()) {
+#ifdef DEBUG_PLAYING_AUDIO_FILES
+ std::cout << "MSMI::getPlayingAudioFiles - "
+ << "track " << evt.getTrackId() << " is not solo track" << std::endl;
+#endif
+
+ ++iter;
+ continue;
+ }
+
+ // If there's an audio event and it should be playing at this time
+ // then flag as such.
+ //
+ if (songPosition > evt.getEventTime() - RealTime(1, 0) &&
+ songPosition < evt.getEventTime() + evt.getDuration()) {
+
+#ifdef DEBUG_PLAYING_AUDIO_FILES
+ std::cout << "MSMI::getPlayingAudioFiles - "
+ << "instrument id = " << evt.getInstrument() << std::endl;
+
+
+ std::cout << "MSMI::getPlayingAudioFiles - "
+ << " id " << evt.getRuntimeSegmentId() << ", audio event time = " << evt.getEventTime() << std::endl;
+ std::cout << "MSMI::getPlayingAudioFiles - "
+ << "audio event duration = " << evt.getDuration() << std::endl;
+
+
+#endif // DEBUG_PLAYING_AUDIO_FILES
+
+ m_playingAudioSegments.push_back(evt);
+ }
+
+ ++iter;
+ }
+
+ //std::cout << "END OF ITERATOR" << std::endl << std::endl;
+
+ }
+
+ return m_playingAudioSegments;
+}
+
+}
+
diff --git a/src/sequencer/MmappedSegment.h b/src/sequencer/MmappedSegment.h
new file mode 100644
index 0000000..32e2ea8
--- /dev/null
+++ b/src/sequencer/MmappedSegment.h
@@ -0,0 +1,185 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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.
+*/
+
+#ifndef _MMAPPED_SEGMENT_H_
+#define _MMAPPED_SEGMENT_H_
+
+#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 "ControlBlockMmapper.h"
+#include "sound/MappedInstrument.h"
+
+namespace Rosegarden {
+
+class MappedComposition;
+
+// Seems not to be properly defined under some gcc 2.95 setups
+#ifndef MREMAP_MAYMOVE
+# define MREMAP_MAYMOVE 1
+#endif
+
+/**
+ * An mmap()ed segment
+ */
+class MmappedSegment
+{
+public:
+ MmappedSegment(const QString filename);
+ ~MmappedSegment();
+
+ bool remap(size_t newSize);
+ QString getFileName() const { return m_filename; }
+ bool isMetronome();
+ MappedEvent* getBuffer() { return m_mmappedEventBuffer; }
+ size_t getSize() const { return m_mmappedSize; }
+ size_t getNbMappedEvents() const;
+
+ class iterator
+ {
+ public:
+ iterator(MmappedSegment* s);
+ iterator& operator=(const iterator&);
+ bool operator==(const iterator&);
+ bool operator!=(const iterator& it) { return !operator==(it); }
+
+ bool atEnd() const;
+
+ /// go back to beginning of stream
+ void reset();
+
+ iterator& operator++();
+ iterator operator++(int);
+ iterator& operator+=(int);
+ iterator& operator-=(int);
+
+ const MappedEvent &operator*();
+ const MappedEvent* peek() const { return m_currentEvent; }
+
+ MmappedSegment* getSegment() { return m_s; }
+ const MmappedSegment* getSegment() const { return m_s; }
+
+ private:
+ iterator();
+
+ protected:
+ //--------------- Data members ---------------------------------
+
+ MmappedSegment* m_s;
+ MappedEvent* m_currentEvent;
+ };
+
+protected:
+ void map();
+ void unmap();
+
+ //--------------- Data members ---------------------------------
+ int m_fd;
+ size_t m_mmappedSize;
+// unsigned int m_nbMappedEvents;
+ void *m_mmappedRegion;
+ MappedEvent* m_mmappedEventBuffer;
+ QString m_filename;
+};
+
+class MmappedSegmentsMetaIterator
+{
+public:
+
+ typedef std::map<QString, MmappedSegment*> mmappedsegments;
+
+ MmappedSegmentsMetaIterator(mmappedsegments&,
+ ControlBlockMmapper*);
+ ~MmappedSegmentsMetaIterator();
+
+ /// reset all iterators to beginning
+ void reset();
+ bool jumpToTime(const RealTime&);
+
+ /**
+ * Fill mapped composition with events from current point until
+ * specified time @return true if there are non-metronome events
+ * remaining, false if end of composition was reached
+ */
+ bool fillCompositionWithEventsUntil(bool firstFetch,
+ MappedComposition*,
+ const RealTime& start,
+ const RealTime& end);
+
+ void resetIteratorForSegment(const QString& filename);
+
+ void addSegment(MmappedSegment*);
+ void deleteSegment(MmappedSegment*);
+
+ void getAudioEvents(std::vector<MappedEvent> &);
+
+ // Manipulate a vector of currently mapped audio segments so that we
+ // can cross check them against PlayableAudioFiles (and stop if
+ // necessary). This will account for muting/soloing too I should
+ // hope.
+ //
+ //!!! to be obsoleted, hopefully
+ std::vector<MappedEvent>& getPlayingAudioFiles
+ (const RealTime &songPosition);
+
+protected:
+ bool acceptEvent(MappedEvent*, bool evtIsFromMetronome);
+
+ /// Delete all iterators
+ void clear();
+ bool moveIteratorToTime(MmappedSegment::iterator&,
+ const RealTime&);
+
+ //--------------- Data members ---------------------------------
+
+ ControlBlockMmapper* m_controlBlockMmapper;
+
+ RealTime m_currentTime;
+ mmappedsegments& m_segments;
+
+ typedef std::vector<MmappedSegment::iterator*> segmentiterators;
+ segmentiterators m_iterators;
+
+ std::vector<MappedEvent> m_playingAudioSegments;
+};
+
+}
+
+#endif // _MMAPPED_SEGMENT_H_
diff --git a/src/sequencer/RosegardenSequencerApp.cpp b/src/sequencer/RosegardenSequencerApp.cpp
new file mode 100644
index 0000000..4c26efb
--- /dev/null
+++ b/src/sequencer/RosegardenSequencerApp.cpp
@@ -0,0 +1,1850 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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 "RosegardenSequencerApp.h"
+#include <kapplication.h>
+
+#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 "misc/Debug.h"
+#include "ControlBlockMmapper.h"
+#include "MmappedSegment.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "sound/ControlBlock.h"
+#include "sound/SoundDriver.h"
+#include "sound/SoundDriverFactory.h"
+#include "sound/MappedInstrument.h"
+#include "base/Profiler.h"
+#include "sound/PluginFactory.h"
+
+namespace Rosegarden
+{
+
+// 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 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
+ // audio faders are also created.
+ //
+ m_driver = 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()
+{
+ std::cerr << "RosegardenSequencerApp::quit()" << std::endl;
+
+ 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();
+
+ Profiles::getInstance()->dump();
+
+ incrementTransportToken();
+}
+
+// Get a slice of events from the GUI
+//
+void
+RosegardenSequencerApp::fetchEvents(MappedComposition &composition,
+ const RealTime &start,
+ const 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)
+ {
+ RealTime loopOverlap = end - m_loopEnd;
+
+ MappedComposition *endLoop = 0;
+
+ if (m_loopEnd > start) {
+ endLoop = getSlice(start, m_loopEnd, firstFetch);
+ }
+
+ if (loopOverlap > RealTime::zeroTime) {
+
+ 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 MappedComposition();
+ }
+ else
+ */
+ getSlice(composition, start, end, firstFetch);
+ applyLatencyCompensation(composition);
+}
+
+
+void
+RosegardenSequencerApp::getSlice(MappedComposition &composition,
+ const RealTime &start,
+ const 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(MappedComposition &composition)
+{
+ RealTime maxLatency = m_driver->getMaximumPlayLatency();
+ if (maxLatency == RealTime::zeroTime)
+ return ;
+
+ for (MappedComposition::iterator i = composition.begin();
+ i != composition.end(); ++i) {
+
+ 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<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()
+{
+ Profiler profiler("RosegardenSequencerApp::keepPlaying");
+
+ m_mC.clear();
+
+ RealTime fetchEnd = m_songPosition + m_readAhead;
+ if (isLooping() && fetchEnd >= m_loopEnd) {
+ fetchEnd = m_loopEnd - 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()
+{
+ 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 ;
+
+ RealTime newPosition = m_driver->getSequencerTime();
+
+ // Go around the loop if we've reached the end
+ //
+ if (isLooping() && newPosition >= m_loopEnd) {
+
+ 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();
+ }
+
+ RealTime maxLatency = m_driver->getMaximumPlayLatency();
+ if (maxLatency != 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 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();
+
+ RealTime oldPosition = m_songPosition;
+
+ m_songPosition = m_lastFetchSongPosition = 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()
+{
+ 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(MappedComposition *mC, bool useSelectedTrack)
+{
+ InstrumentId instrumentId;
+
+ if (useSelectedTrack) {
+ instrumentId = m_controlBlockMmapper->getInstrumentForTrack
+ (m_controlBlockMmapper->getSelectedTrack());
+ for (MappedComposition::iterator i = mC->begin();
+ i != mC->end(); ++i) {
+ (*i)->setInstrument(instrumentId);
+ }
+ } else {
+ for (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 (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());
+ }
+
+ 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(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(MappedComposition *mC,
+ MidiFilter filter,
+ bool filterControlDevice)
+{
+ for (MappedComposition::iterator i = mC->begin();
+ i != mC->end(); ) { // increment in loop
+ MappedComposition::iterator j = i;
+ ++j;
+ if (((*i)->getType() & filter) ||
+ (filterControlDevice && ((*i)->getRecordedDevice() ==
+ Device::CONTROL_DEVICE))) {
+ mC->erase(i);
+ }
+ i = j;
+ }
+}
+
+
+int
+RosegardenSequencerApp::record(const RealTime &time,
+ const RealTime &readAhead,
+ const RealTime &audioMix,
+ const RealTime &audioRead,
+ const 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<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<InstrumentId>") {
+ reply >> armedInstruments;
+ } else {
+ SEQUENCER_DEBUG << "RosegardenSequencer::record() - "
+ << "unrecognised type returned for getArmedInstruments" << endl;
+ }
+ }
+
+ QValueVector<InstrumentId> audioInstruments;
+
+ for (unsigned int i = 0; i < armedInstruments.size(); ++i) {
+ if (armedInstruments[i] >= AudioInstrumentBase &&
+ armedInstruments[i] < 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<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<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(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 RealTime &time,
+ const RealTime &readAhead,
+ const RealTime &audioMix,
+ const RealTime &audioRead,
+ const 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 == 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);
+ // MappedComposition testCompo;
+ // m_metaIterator->fillCompositionWithEventsUntil(&testCompo,
+ // 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 (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;
+ std::cout << "RosegardenSequencerApp::remapTracks" << std::endl;
+
+ rationalisePlayingAudio();
+}
+
+// DCOP Wrapper for play(RealTime,
+// RealTime,
+// 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(RealTime(timeSec, timeNSec),
+ RealTime(readAheadSec, readAheadNSec),
+ RealTime(audioMixSec, audioMixNsec),
+ RealTime(audioReadSec, audioReadNsec),
+ RealTime(audioWriteSec, audioWriteNsec),
+ smallFileSize);
+}
+
+
+
+// Wrapper for record(RealTime,
+// RealTime,
+// 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(RealTime(timeSec, timeNSec),
+ RealTime(readAheadSec, readAheadNSec),
+ RealTime(audioMixSec, audioMixNsec),
+ RealTime(audioReadSec, audioReadNsec),
+ RealTime(audioWriteSec, audioWriteNsec),
+ smallFileSize,
+ recordMode);
+}
+
+
+void
+RosegardenSequencerApp::setLoop(const RealTime &loopStart,
+ const 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(RealTime(loopStartSec, loopStartNSec),
+ 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 |= 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;
+ Instrument::InstrumentType mType =
+ (Instrument::InstrumentType)type;
+ MidiByte mChannel = (MidiByte)channel;
+
+ m_driver->setMappedInstrument(
+ new MappedInstrument (mType, mChannel, mID));
+
+}
+
+// Process a MappedComposition sent from Sequencer with
+// immediate effect
+//
+void
+RosegardenSequencerApp::processSequencerSlice(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,
+ (MidiByte)pitch,
+ (MidiByte)velocity,
+ RealTime(absTimeSec, absTimeNsec),
+ RealTime(durationSec, durationNsec),
+ RealTime(audioStartMarkerSec, audioStartMarkerNSec));
+
+ 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)
+{
+ 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)
+//
+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((Device::DeviceType)type);
+}
+
+unsigned int
+RosegardenSequencerApp::addDevice(int type, unsigned int direction)
+{
+ return m_driver->addDevice((Device::DeviceType)type,
+ (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((Device::DeviceType)type,
+ (MidiDevice::DeviceDirection)direction);
+}
+
+QString
+RosegardenSequencerApp::getConnection(int type, unsigned int direction,
+ unsigned int connectionNo)
+{
+ return m_driver->getConnection((Device::DeviceType)type,
+ (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;
+}
+
+MappedRealTime
+RosegardenSequencerApp::getAudioPlayLatency()
+{
+ return MappedRealTime(m_driver->getAudioPlayLatency());
+}
+
+MappedRealTime
+RosegardenSequencerApp::getAudioRecordLatency()
+{
+ return 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;
+
+
+ MappedObject *object = m_studio->getObjectById(id);
+
+ if (object)
+ object->setProperty(property, value);
+}
+
+void
+RosegardenSequencerApp::setMappedProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values)
+{
+ MappedObject *object = 0;
+ 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;
+
+
+ MappedObject *object = m_studio->getObjectById(id);
+
+ if (object)
+ object->setProperty(property, value);
+}
+
+void
+RosegardenSequencerApp::setMappedPropertyList(int id, const QString &property,
+ const MappedObjectPropertyList &values)
+{
+ SEQUENCER_DEBUG << "setPropertyList: id = " << id
+ << " : property list size = \"" << values.size()
+ << "\"" << endl;
+
+ 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;
+
+ MappedObject *object =
+ m_studio->getObjectOfType(
+ 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;
+
+ 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;
+
+ PluginFactory::enumerateAllPlugins(list);
+
+ return list;
+}
+
+QString
+RosegardenSequencerApp::getPluginProgram(int id, int bank, int program)
+{
+ MappedObject *object = m_studio->getObjectById(id);
+
+ if (object) {
+ MappedPluginSlot *slot =
+ dynamic_cast<MappedPluginSlot *>(object);
+ if (slot) {
+ return slot->getProgram(bank, program);
+ }
+ }
+
+ return QString();
+}
+
+unsigned long
+RosegardenSequencerApp::getPluginProgram(int id, const QString &name)
+{
+ MappedObject *object = m_studio->getObjectById(id);
+
+ if (object) {
+ MappedPluginSlot *slot =
+ dynamic_cast<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)
+{
+ MappedObject *object =
+ m_studio->createObject(
+ 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(MappedObjectId(id))));
+}
+
+// Connect two objects
+//
+void
+RosegardenSequencerApp::connectMappedObjects(int id1, int id2)
+{
+ m_studio->connectObjects(MappedObjectId(id1),
+ 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) {
+ RealTime seqTime = m_driver->getSequencerTime();
+ jumpTo(seqTime.sec, seqTime.nsec);
+ }
+}
+
+// Disconnect two objects
+//
+void
+RosegardenSequencerApp::disconnectMappedObjects(int id1, int id2)
+{
+ m_studio->disconnectObjects(MappedObjectId(id1),
+ MappedObjectId(id2));
+}
+
+// Disconnect an object from everything
+//
+void
+RosegardenSequencerApp::disconnectMappedObject(int id)
+{
+ m_studio->disconnectObject(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)
+{
+ MappedObject *object =
+ m_studio->getObjectById(pluginId);
+
+ MappedPluginSlot *slot =
+ dynamic_cast<MappedPluginSlot *>(object);
+
+ if (slot) {
+ slot->setPort(portId, value);
+ } else {
+ SEQUENCER_DEBUG << "no such slot" << endl;
+ }
+}
+
+float
+RosegardenSequencerApp::getMappedPort(int pluginId,
+ unsigned long portId)
+{
+ MappedObject *object =
+ m_studio->getObjectById(pluginId);
+
+ MappedPluginSlot *slot =
+ dynamic_cast<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"
+ << RealTime(timeSec, timeNSec) << endl;
+
+ m_driver->setMIDIClockInterval(
+ 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<MappedEvent> audioEvents;
+ m_metaIterator->getAudioEvents(audioEvents);
+ m_driver->initialiseAudioQueue(audioEvents);
+}
+
+
+ExternalTransport::TransportToken
+RosegardenSequencerApp::transportChange(TransportRequest request)
+{
+ TransportPair pair(request, 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;
+}
+
+ExternalTransport::TransportToken
+RosegardenSequencerApp::transportJump(TransportRequest request,
+ 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 "RosegardenSequencerApp.moc"
diff --git a/src/sequencer/RosegardenSequencerApp.h b/src/sequencer/RosegardenSequencerApp.h
new file mode 100644
index 0000000..bb72547
--- /dev/null
+++ b/src/sequencer/RosegardenSequencerApp.h
@@ -0,0 +1,531 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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.
+*/
+
+#ifndef _ROSEGARDEN_SEQUENCER_APP_H_
+#define _ROSEGARDEN_SEQUENCER_APP_H_
+
+// RosegardenSequencerApp is the sequencer application for Rosegarden.
+// It owns a Sequencer object which wraps the ALSA
+// and JACK funtionality. At this level we deal with comms with
+// the Rosegarden GUI application, the high level marshalling of data
+// and main event loop of the sequencer. [rwb]
+//
+
+
+// include files for Qt
+#include <qstrlist.h>
+
+// include files for KDE
+#include <kapp.h>
+#include <kmainwindow.h>
+#include <kaccel.h>
+
+#include <qtimer.h>
+
+#include "base/Composition.h"
+#include "gui/application/RosegardenDCOP.h"
+
+#include "RosegardenSequencerIface.h"
+
+#include "sound/MappedComposition.h"
+#include "base/Event.h"
+#include "sound/MappedStudio.h"
+#include "sound/ExternalTransport.h"
+
+#include "MmappedSegment.h"
+#include "SequencerMmapper.h"
+
+#include <deque>
+
+class KURL;
+class KRecentFilesAction;
+
+namespace Rosegarden {
+
+// forward declaration of the RosegardenGUI classes
+class RosegardenGUIDoc;
+class RosegardenGUIView;
+class ControlBlockMmapper;
+
+class MappedInstrument;
+class SoundDriver;
+
+/**
+ * The sequencer application
+ */
+class RosegardenSequencerApp : public KMainWindow,
+ virtual public RosegardenSequencerIface,
+ public ExternalTransport
+{
+ Q_OBJECT
+
+public:
+ RosegardenSequencerApp();
+ ~RosegardenSequencerApp();
+
+ // -------- START OF DCOP INTERFACE METHODS --------
+ //
+ //
+
+
+ // Quit
+ virtual void quit();
+
+ // Based on RealTime timestamps
+ //
+ int play(const RealTime &position,
+ const RealTime &readAhead,
+ const RealTime &audioMix,
+ const RealTime &audioRead,
+ const RealTime &audioWrite,
+ long smallFileSize);
+
+ // recording
+ int record(const RealTime &position,
+ const RealTime &readAhead,
+ const RealTime &audioMix,
+ const RealTime &audioRead,
+ const RealTime &audioWrite,
+ long smallFileSize,
+ long recordMode);
+
+ virtual int punchOut();
+
+ // looping
+ void setLoop(const RealTime &loopStart,
+ const RealTime &loopEnd);
+
+
+ // Play wrapper for DCOP
+ //
+ virtual int play(long timeSec,
+ long timeNsec,
+ long readAheadSec,
+ long readAheadNsec,
+ long audioMixSec,
+ long audioMixNsec,
+ long audioReadSec,
+ long audioReadNsec,
+ long audioWriteSec,
+ long audioWriteNsec,
+ long smallFileSize);
+
+ // Record wrapper for DCOP
+ //
+ virtual int 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);
+
+
+ // Jump to a pointer in the playback (uses longs instead
+ // of RealTime for DCOP)
+ //
+ //
+ virtual void jumpTo(long posSec, long posNsec);
+
+ // Set a loop on the Sequencer
+ //
+ virtual void setLoop(long loopStartSec, long loopStartNsec,
+ long loopEndSec, long loopEndNsec);
+
+ // Return the Sound system status (audio/MIDI)
+ //
+ virtual unsigned int getSoundDriverStatus(const QString &guiVersion);
+
+ // Add and remove Audio files on the sequencer
+ //
+ virtual int addAudioFile(const QString &fileName, int id);
+ virtual int removeAudioFile(int id);
+
+ // Deletes all the audio files and clears down any flapping i/o handles
+ //
+ virtual void clearAllAudioFiles();
+
+ // stops the sequencer
+ //
+ virtual void stop();
+
+ // Set a MappedInstrument at the Sequencer
+ //
+ virtual void setMappedInstrument(int type, unsigned char channel,
+ unsigned int id);
+
+ // The sequencer will process the MappedComposition as soon as it
+ // gets the chance.
+ //
+ virtual void processSequencerSlice(MappedComposition mC);
+
+ // Yeuch!
+ //
+ virtual void processMappedEvent(unsigned int id,
+ int type,
+ unsigned char pitch,
+ unsigned char velocity,
+ long absTimeSec,
+ long absTimeNsec,
+ long durationSec,
+ long durationNsec,
+ long audioStartMarkerSec,
+ long audioStartMarkerNsec);
+
+ // And now do it properly
+ //
+ virtual void processMappedEvent(MappedEvent mE);
+
+ virtual unsigned int getDevices();
+ virtual MappedDevice getMappedDevice(unsigned int id);
+
+ virtual int canReconnect(int deviceType);
+ virtual unsigned int addDevice(int type, unsigned int direction);
+ virtual void removeDevice(unsigned int id);
+ virtual void renameDevice(unsigned int id, QString name);
+ virtual unsigned int getConnections(int type, unsigned int direction);
+ virtual QString getConnection(int type, unsigned int direction,
+ unsigned int connectionNo);
+ virtual void setConnection(unsigned int deviceId, QString connection);
+ virtual void setPlausibleConnection(unsigned int deviceId,
+ QString idealConnection);
+
+ virtual unsigned int getTimers();
+ virtual QString getTimer(unsigned int n);
+ virtual QString getCurrentTimer();
+ virtual void setCurrentTimer(QString timer);
+
+ virtual void setLowLatencyMode(bool);
+
+ // Audio latencies
+ //
+ virtual MappedRealTime getAudioPlayLatency();
+ virtual MappedRealTime getAudioRecordLatency();
+
+ // Set a MappedObject
+ //
+ virtual void setMappedProperty(int id,
+ const QString &property,
+ float value);
+
+ // Set many properties on many MappedObjects
+ //
+ virtual void setMappedProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values);
+
+ // Set a MappedObject to a string
+ //
+ virtual void setMappedProperty(int id,
+ const QString &property,
+ const QString &value);
+
+ // Set a MappedObject to a property list
+ //
+ virtual void setMappedPropertyList(int id,
+ const QString &property,
+ const MappedObjectPropertyList &values);
+
+ // Get a MappedObject for a type
+ //
+ virtual int getMappedObjectId(int type);
+
+ // Get a Property list from an Object
+ //
+ virtual std::vector<QString> getPropertyList(int id,
+ const QString &property);
+
+ virtual std::vector<QString> getPluginInformation();
+
+ virtual QString getPluginProgram(int id, int bank, int program);
+
+ virtual unsigned long getPluginProgram(int id, const QString &name);
+
+ // Set a plugin port
+ //
+ virtual void setMappedPort(int pluginId,
+ unsigned long portId,
+ float value);
+
+ virtual float getMappedPort(int pluginId,
+ unsigned long portId);
+
+ // Create a MappedObject
+ virtual int createMappedObject(int type);
+
+ // Destroy an object
+ //
+ virtual int destroyMappedObject(int id);
+
+ // Connect two objects
+ //
+ virtual void connectMappedObjects(int id1, int id2);
+
+ // Disconnect two objects
+ //
+ virtual void disconnectMappedObjects(int id1, int id2);
+
+ // Disconnect an object from everything
+ //
+ virtual void disconnectMappedObject(int id);
+
+ // Sample rate
+ //
+ virtual unsigned int getSampleRate() const;
+
+ // Clear the studio
+ //
+ virtual void clearStudio();
+
+ // Debug stuff, to check MmappedSegment::iterator
+ virtual void dumpFirstSegment();
+
+ virtual void remapSegment(const QString& filename, size_t newSize);
+ virtual void addSegment(const QString& filename);
+ virtual void deleteSegment(const QString& filename);
+ virtual void closeAllSegments();
+ virtual void remapTracks();
+
+ // Set Quarter note length
+ //
+ virtual void setQuarterNoteLength(long timeSec, long timeNsec);
+
+ // Get a status report
+ //
+ virtual QString getStatusLog();
+
+ //
+ //
+ //
+ // -------- END OF DCOP INTERFACE --------
+
+
+
+
+ void setStatus(TransportStatus status)
+ { m_transportStatus = status; }
+ TransportStatus getStatus() { return m_transportStatus; }
+
+ // Process the first chunk of Sequencer events
+ bool startPlaying();
+
+ // Process all subsequent events
+ bool keepPlaying();
+
+ // Update internal clock and send GUI position pointer movement
+ void updateClocks();
+
+ bool checkExternalTransport();
+
+ // Sends status changes up to GUI
+ void notifySequencerStatus();
+
+ // Send latest slice information back to GUI for display
+ void notifyVisuals(MappedComposition *mC);
+
+ // These two methods process any pending MIDI or audio
+ // and send them up to the gui for storage and display
+ //
+ void processRecordedMidi();
+ void processRecordedAudio();
+
+ // Called during stopped or playing operation to process
+ // any pending incoming MIDI events that aren't being
+ // recorded (i.e. for display in Transport or on Mixer)
+ //
+ void processAsynchronousEvents();
+
+ // Sleep for the given time, approximately. Called from the main
+ // loop in order to lighten CPU load (i.e. the timing quality of
+ // the sequencer does not depend on this being accurate). A good
+ // implementation of this call would return right away when an
+ // incoming MIDI event needed to be handled.
+ //
+ void sleep(const RealTime &rt);
+
+ // Removes from a MappedComposition the events not matching
+ // the supplied filer.
+ //
+ void applyFiltering(MappedComposition *mC,
+ MidiFilter filter,
+ bool filterControlDevice);
+
+ // This method assigns an Instrument to each MappedEvent
+ // belongin to the MappedComposition, and sends the
+ // transformed events to the driver to be played.
+ //
+ void routeEvents(MappedComposition *mC, bool useSelectedTrack);
+
+ // Are we looping?
+ //
+ bool isLooping() const { return !(m_loopStart == m_loopEnd); }
+
+ // the call itself
+ void sequencerAlive();
+
+ /*
+ // Audio latencies
+ //
+ RealTime getAudioPlaybackLatency()
+ { return m_audioPlayLatency; }
+ void setAudioPlaybackLatency(const RealTime &latency)
+ { m_audioPlayLatency = latency; }
+
+ RealTime getAudioRecordLatency()
+ { return m_audioRecordLatency; }
+ void setAudioRecordLatency(const RealTime &latency)
+ { m_audioRecordLatency = latency; }
+ */
+
+ // Initialise the virtual studio at this end of the link
+ //
+ void initialiseStudio();
+
+
+ // --------- EXTERNAL TRANSPORT INTERFACE METHODS --------
+ //
+ // Whereas the DCOP interface (above) is for the GUI to call to
+ // make the sequencer follow its wishes, this interface is for
+ // external clients to call (via some low-level audio callback)
+ // and requires sychronising with the GUI.
+
+ TransportToken transportChange(TransportRequest);
+ TransportToken transportJump(TransportRequest, RealTime);
+ bool isTransportSyncComplete(TransportToken token);
+ TransportToken getInvalidTransportToken() const { return 0; }
+
+public slots:
+
+ // Check for new clients - on timeout
+ //
+ void slotCheckForNewClients();
+
+protected:
+
+ // get events whilst handling loop
+ //
+ void fetchEvents(MappedComposition &,
+ const RealTime &start,
+ const RealTime &end,
+ bool firstFetch);
+
+ // just get a slice of events between markers
+ //
+ void getSlice(MappedComposition &,
+ const RealTime &start,
+ const RealTime &end,
+ bool firstFetch);
+
+ // adjust event times according to relative instrument latencies
+ //
+ void applyLatencyCompensation(MappedComposition &);
+
+ // mmap-related stuff
+ MmappedSegment* mmapSegment(const QString&);
+ void cleanupMmapData();
+ void initMetaIterator();
+
+ void rationalisePlayingAudio();
+ void setEndOfCompReached(bool e) { m_isEndOfCompReached = e; }
+ bool isEndOfCompReached() { return m_isEndOfCompReached; }
+ void incrementTransportToken();
+
+ //--------------- Data members ---------------------------------
+
+ SoundDriver *m_driver;
+ TransportStatus m_transportStatus;
+
+ // Position pointer
+ RealTime m_songPosition;
+ RealTime m_lastFetchSongPosition;
+
+ RealTime m_readAhead;
+ RealTime m_audioMix;
+ RealTime m_audioRead;
+ RealTime m_audioWrite;
+ int m_smallFileSize;
+
+ /*
+
+ // Not required at the sequencer
+
+ // Two more latencies for audio play and record - when we
+ // use an unsynchronised audio and MIDI system such as
+ // ALSA and JACK we need to use these additional values
+ // to help time-keeping.
+ //
+ RealTime m_audioPlayLatency;
+ RealTime m_audioRecordLatency;
+
+ */
+
+
+ RealTime m_loopStart;
+ RealTime m_loopEnd;
+
+ std::vector<MappedInstrument*> m_instruments;
+
+ // MappedStudio holds all of our session-persistent information -
+ // sliders and what have you. It's also streamable over DCOP
+ // so you can reconstruct it at either end of the link for
+ // presentation, storage etc.
+ //
+ MappedStudio *m_studio;
+
+ // Slice revert storage
+ //
+ RealTime m_oldSliceSize;
+ QTimer *m_sliceTimer;
+
+ // Timer to check for new clients
+ //
+ QTimer *m_newClientTimer;
+
+ // mmap segments
+ //
+ QString m_segmentFilesPath;
+ MmappedSegmentsMetaIterator::mmappedsegments m_mmappedSegments;
+ MmappedSegmentsMetaIterator* m_metaIterator;
+ RealTime m_lastStartTime;
+
+ MappedComposition m_mC;
+ ControlBlockMmapper *m_controlBlockMmapper;
+ SequencerMmapper m_sequencerMapper;
+
+ typedef std::pair<TransportRequest, RealTime> TransportPair;
+ std::deque<TransportPair> m_transportRequests;
+ TransportToken m_transportToken;
+
+ bool m_isEndOfCompReached;
+};
+
+}
+
+#endif // _ROSEGARDEN_SEQUENCER_APP_H_
diff --git a/src/sequencer/RosegardenSequencerIface.h b/src/sequencer/RosegardenSequencerIface.h
new file mode 100644
index 0000000..47c0215
--- /dev/null
+++ b/src/sequencer/RosegardenSequencerIface.h
@@ -0,0 +1,364 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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.
+*/
+
+#ifndef _ROSEGARDENSEQUENCERIFACE_H_
+#define _ROSEGARDENSEQUENCERIFACE_H_
+
+#include <dcopobject.h>
+// #include <qvaluevector.h>
+// #include <qpair.h>
+
+#include "gui/application/RosegardenDCOP.h"
+
+#include "base/Event.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "base/Instrument.h"
+#include "sound/MappedDevice.h"
+#include "sound/MappedRealTime.h"
+#include "sound/MappedStudio.h"
+#include "sound/MappedCommon.h"
+
+namespace Rosegarden {
+
+class RosegardenSequencerIface : virtual public DCOPObject
+{
+ K_DCOP
+public:
+ k_dcop:
+
+ // close the sequencer
+ //
+ virtual void quit() = 0;
+
+
+
+ // play from a given time with given parameters
+ //
+ virtual int play(long timeSec,
+ long timeNsec,
+ long readAheadSec,
+ long readAheadNsec,
+ long audioMixSec,
+ long audioMixNsec,
+ long audioReadSec,
+ long audioReadNsec,
+ long audioWriteSec,
+ long audioWriteNsec,
+ long smallFileSize) = 0;
+
+ // record from a given time with given parameters
+ //
+ virtual int 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) = 0;
+
+ // stop the sequencer
+ //
+ virtual ASYNC stop() = 0;
+
+ // punch out from recording to playback
+ //
+ virtual int punchOut() = 0;
+
+ // Set the sequencer to a given time
+ //
+ virtual void jumpTo(long posSec, long posNsec) = 0;
+
+ // Set a loop on the sequencer
+ //
+ virtual void setLoop(long loopStartSec,
+ long loopStartNsec,
+ long loopEndSec,
+ long loopEndNsec) = 0;
+
+ // Get the status of the Sequencer
+ //
+ virtual unsigned int getSoundDriverStatus(const QString &guiVersion) = 0;
+
+ // Add and delete audio files on the Sequencer
+ //
+ virtual int addAudioFile(const QString &fileName, int id) = 0;
+ virtual int removeAudioFile(int id) = 0;
+ virtual void clearAllAudioFiles() = 0;
+
+ // Single set function as the MappedInstrument is so lightweight.
+ // Any mods on the GUI are sent only through this method.
+ //
+ virtual void setMappedInstrument(int type,
+ unsigned char channel,
+ unsigned int id) = 0;
+
+ // The GUI can use this method to process an immediate selection
+ // of MappedEvents (Program Changes, SysExs, async Events etc).
+ //
+ virtual void processSequencerSlice(MappedComposition mC) = 0;
+
+
+ // Horrible ugly ugly ugly interface for single MappedEvents
+ // just until we implement the proper MappedEvent interface
+ //
+ virtual void processMappedEvent(unsigned int id,
+ int type,
+ unsigned char pitch,
+ unsigned char velocity,
+ long absTimeSec,
+ long absTimeNsec,
+ long durationSec,
+ long durationNsec,
+ long audioStartMarkerSec,
+ long audioStartMarkerNsec) = 0;
+
+ // The proper implementation
+ //
+ virtual void processMappedEvent(MappedEvent mE) = 0;
+
+ // Return device id following last existing one -- you can treat
+ // this as "number of devices" but there might be some holes if
+ // devices were deleted, which you will recognise because
+ // getMappedDevice(id) will return a device with id NO_DEVICE
+ //
+ virtual unsigned int getDevices() = 0;
+
+ // Return device by number
+ //
+ virtual MappedDevice getMappedDevice(unsigned int id) = 0;
+
+ // Query whether the driver implements device reconnection.
+ // Returns a non-zero value if the addDevice, removeDevice,
+ // getConnections, getConnection and setConnection methods
+ // may be used with devices of the given type.
+ //
+ virtual int canReconnect(int deviceType) = 0;
+
+ // Create a device of the given type and direction (corresponding
+ // to MidiDevice::DeviceDirection enum) and return its id.
+ // The device will have no connection by default. Direction is
+ // currently ignored for non-MIDI devices.
+ // Do not use this unless canReconnect(type) returned true.
+ //
+ virtual unsigned int addDevice(int type, unsigned int direction) = 0;
+
+ // Remove the device of the given id.
+ // Ignored if driver does not permit changing the number of devices
+ // (i.e. if canReconnect(type) would return false when given the
+ // type of the supplied device).
+ //
+ virtual void removeDevice(unsigned int id) = 0;
+
+ // Rename the given device.
+ // Ignored if the driver does not permit this operation.
+ //
+ virtual void renameDevice(unsigned int id, QString name) = 0;
+
+ // Return the number of permissible connections for a device of
+ // the given type and direction (corresponding to MidiDevice::
+ // DeviceDirection enum). Direction is ignored for non-MIDI devices.
+ // Returns zero if devices of this type are non-reconnectable
+ // (i.e. if canReconnect(type) would return false).
+ //
+ virtual unsigned int getConnections(int type, unsigned int direction) = 0;
+
+ // Return one of the set of permissible connections for a device of
+ // the given type and direction (corresponding to MidiDevice::
+ // DeviceDirection enum). Direction is ignored for non-MIDI devices.
+ // Returns the empty string for invalid parameters.
+ //
+ virtual QString getConnection(int type,
+ unsigned int direction,
+ unsigned int connectionNo) = 0;
+
+ // Reconnect a particular device.
+ // Ignored if driver does not permit reconnections or the connection
+ // is not one of the permissible set for that device.
+ //
+ virtual void setConnection(unsigned int deviceId, QString connection) = 0;
+
+ // Reconnect a device to a particular connection or to the closest
+ // thing to that connection currently available (using some heuristic).
+ // Ignored if driver does not permit reconnections.
+ //
+ virtual void setPlausibleConnection(unsigned int deviceId,
+ QString idealConnection) = 0;
+
+ // Return the number of different timers we are capable of
+ // sychronising against. This may return 0 if the driver has no
+ // ability to change the current timer.
+ //
+ virtual unsigned int getTimers() = 0;
+
+ // Return the name of a timer from the available set (where
+ // n is between 0 and the return value from getTimers() - 1).
+ //
+ virtual QString getTimer(unsigned int n) = 0;
+
+ // Return the name of the timer we are currently synchronising
+ // against.
+ //
+ virtual QString getCurrentTimer() = 0;
+
+ // Set the timer we are currently synchronising against.
+ // Invalid arguments are simply ignored.
+ //
+ virtual void setCurrentTimer(QString timer) = 0;
+
+ virtual void setLowLatencyMode(bool lowLatMode) = 0;
+
+ // Fetch audio play latencies
+ //
+ virtual MappedRealTime getAudioPlayLatency() = 0;
+ virtual MappedRealTime getAudioRecordLatency() = 0;
+
+ // Set a property on a MappedObject
+ //
+ virtual void setMappedProperty(int id,
+ const QString &property,
+ float value) = 0;
+
+ // Set many properties on many MappedObjects
+ //
+ virtual void setMappedProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values) = 0;
+
+ // Set a string property on a MappedObject
+ //
+ virtual void setMappedProperty(int id,
+ const QString &property,
+ const QString &value) = 0;
+
+ // Set a MappedObject to a property list
+ //
+ virtual void setMappedPropertyList(
+ int id,
+ const QString &property,
+ const MappedObjectPropertyList &values) = 0;
+
+ // Get a mapped object id for a object type
+ //
+ virtual int getMappedObjectId(int type) = 0;
+
+ // Get a list of properties of a certain type from an object
+ //
+ virtual std::vector<QString> getPropertyList(int id,
+ const QString &property) = 0;
+
+ // Get a list of available plugins
+ //
+ virtual std::vector<QString> getPluginInformation() = 0;
+
+ // Nasty hack: program name/number mappings are one thing that
+ // mapped object properties can't cope with
+ //
+ virtual QString getPluginProgram(int mappedId, int bank, int program) = 0;
+
+ // Nastier hack: return value is bank << 16 + program
+ //
+ virtual unsigned long getPluginProgram(int mappedId, const QString &name) = 0;
+
+ // Cheat - we can't use a call (getPropertyList) during playback
+ // so we use this method to set port N on plugin X.
+ //
+ virtual void setMappedPort(int pluginIn,
+ unsigned long id,
+ float value) = 0;
+
+ virtual float getMappedPort(int pluginIn,
+ unsigned long id) = 0;
+
+ // Create a (transient, writeable) object
+ //
+ virtual int createMappedObject(int type) = 0;
+
+ // Destroy an object (returns a bool but for KDE2 DCOP compat we
+ // use an int of course).
+ //
+ virtual int destroyMappedObject(int id) = 0;
+
+ // Connect two objects
+ //
+ virtual void connectMappedObjects(int id1, int id2) = 0;
+
+ // Disconnect two objects
+ //
+ virtual void disconnectMappedObjects(int id1, int id2) = 0;
+
+ // Disconnect an object from everything
+ //
+ virtual void disconnectMappedObject(int id) = 0;
+
+ // Driver sample rate
+ //
+ virtual unsigned int getSampleRate() const = 0;
+
+ // Initialise/Reinitialise the studio back down to read only objects
+ // and set to defaults.
+ //
+ virtual void clearStudio() = 0;
+
+ // Allow the GUI to tell the sequence the duration of a quarter
+ // note when the TEMPO changes - this is to allow the sequencer
+ // to generate MIDI clock (at 24 PPQN).
+ //
+ virtual void setQuarterNoteLength(long timeSec, long timeNsec) = 0;
+
+ // Return a (potentially lengthy) human-readable status log
+ //
+ virtual QString getStatusLog() = 0;
+
+ // Debug stuff, to check MmappedSegment::iterator
+ virtual void dumpFirstSegment() = 0;
+
+ /// Remap a segment while playing
+ virtual void remapSegment(const QString& filename, size_t newSize) = 0;
+
+ /// Add a segment while playing
+ virtual void addSegment(const QString& filename) = 0;
+
+ /// Delete a segment while playing
+ virtual void deleteSegment(const QString& filename) = 0;
+
+ /// Close all mmapped segments
+ virtual void closeAllSegments() = 0;
+
+ /** Update mute (etc) statuses while playing. The sequencer handles
+ this automatically (with no need for this call) for MIDI events,
+ but it needs to be prodded when an already-playing audio segment
+ drops in or out.
+ */
+ virtual void remapTracks() = 0;
+};
+
+}
+
+#endif // _ROSEGARDENSEQUENCERIFACE_H_
diff --git a/src/sequencer/SequencerMmapper.cpp b/src/sequencer/SequencerMmapper.cpp
new file mode 100644
index 0000000..3a634ba
--- /dev/null
+++ b/src/sequencer/SequencerMmapper.cpp
@@ -0,0 +1,146 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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 <kstddirs.h>
+
+#include <qfile.h>
+
+#include "SequencerMmapper.h"
+#include "misc/Debug.h"
+
+#include "base/RealTime.h"
+#include "base/Exception.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedComposition.h"
+
+namespace Rosegarden
+{
+
+// Seems not to be properly defined under some gcc 2.95 setups
+#ifndef MREMAP_MAYMOVE
+#define MREMAP_MAYMOVE 1
+#endif
+
+SequencerMmapper::SequencerMmapper():
+ m_fileName(createFileName()),
+ m_fd( -1),
+ m_mmappedBuffer(0),
+ m_mmappedSize(sizeof(SequencerDataBlock))
+{
+ // just in case
+ QFile::remove
+ (m_fileName);
+
+ m_fd = ::open(m_fileName.latin1(),
+ O_RDWR | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+
+ if (m_fd < 0) {
+ SEQUENCER_DEBUG << "SequencerMmapper : Couldn't open " << m_fileName
+ << endl;
+ throw Exception("Couldn't open " + std::string(m_fileName.data()));
+ }
+
+ setFileSize(m_mmappedSize);
+
+ //
+ // mmap() file for writing
+ //
+ m_mmappedBuffer = ::mmap(0, m_mmappedSize,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, m_fd, 0);
+
+ if (m_mmappedBuffer == (void*) - 1) {
+ SEQUENCER_DEBUG <<
+ QString("mmap failed : (%1) %2\n").arg(errno).arg(strerror(errno));
+ throw Exception("mmap failed");
+ }
+
+ SEQUENCER_DEBUG << "SequencerMmapper : mmap size : " << m_mmappedSize
+ << " at " << (void*)m_mmappedBuffer << endl;
+
+ // initialise
+ init();
+}
+
+SequencerMmapper::~SequencerMmapper()
+{
+ ::munmap(m_mmappedBuffer, m_mmappedSize);
+ ::close(m_fd);
+ QFile::remove
+ (m_fileName);
+}
+
+void
+SequencerMmapper::init()
+{
+ SEQUENCER_DEBUG << "SequencerMmapper::init()\n";
+
+ m_sequencerDataBlock = new (m_mmappedBuffer)
+ SequencerDataBlock(true);
+
+ ::msync(m_mmappedBuffer, m_mmappedSize, MS_ASYNC);
+}
+
+void
+SequencerMmapper::setFileSize(size_t size)
+{
+ SEQUENCER_DEBUG << "SequencerMmapper : setting size of "
+ << m_fileName << " to " << size << endl;
+ // rewind
+ ::lseek(m_fd, 0, SEEK_SET);
+
+ // enlarge the file
+ // (seek() to wanted size, then write a byte)
+ //
+ if (::lseek(m_fd, size - 1, SEEK_SET) == -1) {
+ std::cerr << "WARNING: SequencerMmapper : Couldn't lseek in " << m_fileName
+ << " to " << size << std::endl;
+ throw Exception("lseek failed");
+ }
+
+ if (::write(m_fd, "\0", 1) != 1) {
+ std::cerr << "WARNING: SequencerMmapper : Couldn't write byte in "
+ << m_fileName << std::endl;
+ throw Exception("write failed");
+ }
+}
+
+QString
+SequencerMmapper::createFileName()
+{
+ return KGlobal::dirs()->resourceDirs("tmp").last() +
+ "/rosegarden_sequencer_timing_block";
+}
+
+}
+
diff --git a/src/sequencer/SequencerMmapper.h b/src/sequencer/SequencerMmapper.h
new file mode 100644
index 0000000..f35c2a7
--- /dev/null
+++ b/src/sequencer/SequencerMmapper.h
@@ -0,0 +1,103 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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.
+*/
+
+#ifndef _SEQUENCERMAPPER_H_
+#define _SEQUENCERMAPPER_H_
+
+#include "sound/SequencerDataBlock.h"
+#include "base/RealTime.h"
+
+namespace Rosegarden
+{
+
+class MappedEvent;
+class MappedComposition;
+
+
+class SequencerMmapper
+{
+public:
+ SequencerMmapper();
+ ~SequencerMmapper();
+
+ void updatePositionPointer(RealTime time) {
+ m_sequencerDataBlock->setPositionPointer(time);
+ }
+
+ void updateVisual(MappedEvent *ev) {
+ m_sequencerDataBlock->setVisual(ev);
+ }
+
+ void updateRecordingBuffer(MappedComposition *mC) {
+ m_sequencerDataBlock->addRecordedEvents(mC);
+ }
+
+ void setTrackLevel(TrackId track, const LevelInfo &info) {
+ m_sequencerDataBlock->setTrackLevel(track, info);
+ }
+
+ void setInstrumentLevel(InstrumentId id,
+ const LevelInfo &info) {
+ m_sequencerDataBlock->setInstrumentLevel(id, info);
+ }
+
+ void setInstrumentRecordLevel(InstrumentId id,
+ const LevelInfo &info) {
+ m_sequencerDataBlock->setInstrumentRecordLevel(id, info);
+ }
+
+ void setSubmasterLevel(int submaster,
+ const LevelInfo &info) {
+ m_sequencerDataBlock->setSubmasterLevel(submaster, info);
+ }
+
+ void setMasterLevel(const LevelInfo &info) {
+ m_sequencerDataBlock->setMasterLevel(info);
+ }
+
+ SequencerDataBlock *getSequencerDataBlock() {
+ return m_sequencerDataBlock;
+ }
+ void setControlBlock(ControlBlock *cb) {
+ m_sequencerDataBlock->setControlBlock(cb);
+ }
+
+protected:
+ void init();
+ void setFileSize(size_t);
+ QString createFileName();
+
+ //--------------- Data members ---------------------------------
+ //
+ QString m_fileName;
+ int m_fd;
+ void* m_mmappedBuffer;
+ size_t m_mmappedSize;
+ SequencerDataBlock *m_sequencerDataBlock;
+};
+
+}
+
+#endif // _SEQUENCERMAPPER_H_
diff --git a/src/sequencer/main.cpp b/src/sequencer/main.cpp
new file mode 100644
index 0000000..aee5bc5
--- /dev/null
+++ b/src/sequencer/main.cpp
@@ -0,0 +1,246 @@
+// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ 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 "RosegardenSequencerApp.h"
+
+#include <signal.h>
+#include <iostream>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <dcopclient.h>
+
+#include "base/Profiler.h"
+#include "sound/MappedComposition.h"
+
+#include "gui/application/RosegardenDCOP.h"
+#include "misc/Debug.h"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+
+using namespace Rosegarden;
+
+static const char *description = I18N_NOOP("RosegardenSequencer");
+static RosegardenSequencerApp *roseSeq = 0;
+
+static KCmdLineOptions options[] =
+ {
+ // { "+[File]", I18N_NOOP("file to open"), 0 },
+ // INSERT YOUR COMMANDLINE OPTIONS HERE
+ { "+[playback_1 playback_2 capture_1 capture_2]",
+ I18N_NOOP("JACK playback and capture ports"), 0 },
+ { 0, 0, 0 }
+ };
+
+static bool _exiting = false;
+static sigset_t _signals;
+
+static void
+signalHandler(int /*sig*/)
+{
+ _exiting = true;
+ std::cerr << "Is that the time!?" << endl;
+}
+
+int main(int argc, char *argv[])
+{
+ srandom((unsigned int)time(0) * (unsigned int)getpid());
+
+ // Block signals during startup, so that child threads (inheriting
+ // this mask) ignore them; then after startup we can unblock them
+ // for this thread only. This trick picked up from the jackd code.
+ sigemptyset (&_signals);
+ sigaddset(&_signals, SIGHUP);
+ sigaddset(&_signals, SIGINT);
+ sigaddset(&_signals, SIGQUIT);
+ sigaddset(&_signals, SIGPIPE);
+ sigaddset(&_signals, SIGTERM);
+ sigaddset(&_signals, SIGUSR1);
+ sigaddset(&_signals, SIGUSR2);
+ pthread_sigmask(SIG_BLOCK, &_signals, 0);
+
+ KAboutData aboutData( "rosegardensequencer",
+ I18N_NOOP("RosegardenSequencer"),
+ VERSION, description, KAboutData::License_GPL,
+ "(c) 2000-2008, Guillaume Laurent, Chris Cannam, Richard Bown");
+ aboutData.addAuthor("Guillaume Laurent, Chris Cannam, Richard Bown", 0, "[email protected], [email protected], [email protected]");
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+
+ // Parse cmd line args
+ //
+ /*KCmdLineArgs *args =*/
+ KCmdLineArgs::parsedArgs();
+ KApplication app;
+
+ if (app.isRestored()) {
+ SEQUENCER_DEBUG << "RosegardenSequencer - we're being session-restored - that's not supposed to happen\n";
+ app.quit(); // don't do session restore -- GUI will start a sequencer
+ } else {
+ roseSeq = new RosegardenSequencerApp;
+ }
+
+ QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
+
+ app.disableSessionManagement(); // we don't want to be
+ // saved/restored by session
+ // management, only run by the GUI
+
+ // Started OK
+ //
+ SEQUENCER_DEBUG << "RosegardenSequencer - started OK" << endl;
+
+ // Register signal handlers and unblock signals
+ //
+ signal(SIGINT, signalHandler);
+ signal(SIGTERM, signalHandler);
+ signal(SIGHUP, signalHandler);
+ signal(SIGQUIT, signalHandler);
+ pthread_sigmask(SIG_UNBLOCK, &_signals, 0);
+
+ // Now we can enter our specialised event loop.
+ // For each pass through we wait for some pending
+ // events. We check status on the way through and
+ // act accordingly. DCOP events fire back and
+ // forth processed in the event loop changing
+ // state and hopefully controlling and providing
+ // feedback. We also put in some sleep time to
+ // make sure the loop doesn't eat up all the
+ // processor - we're not in that much of a rush!
+ //
+ TransportStatus lastSeqStatus = roseSeq->getStatus();
+
+ RealTime sleepTime = RealTime(0, 10000000);
+
+ while (!_exiting && roseSeq && roseSeq->getStatus() != QUIT) {
+
+ bool atLeisure = true;
+
+ switch (roseSeq->getStatus()) {
+ case STARTING_TO_PLAY:
+ if (!roseSeq->startPlaying()) {
+ // send result failed and stop Sequencer
+ roseSeq->setStatus(STOPPING);
+ } else {
+ roseSeq->setStatus(PLAYING);
+ }
+ break;
+
+ case PLAYING:
+ if (!roseSeq->keepPlaying()) {
+ // there's a problem or the piece has
+ // finished - so stop playing
+ roseSeq->setStatus(STOPPING);
+ } else {
+ // process any async events
+ //
+ roseSeq->processAsynchronousEvents();
+ }
+ break;
+
+ case STARTING_TO_RECORD:
+ if (!roseSeq->startPlaying()) {
+ roseSeq->setStatus(STOPPING);
+ } else {
+ roseSeq->setStatus(RECORDING);
+ }
+ break;
+
+ case RECORDING:
+ if (!roseSeq->keepPlaying()) {
+ // there's a problem or the piece has
+ // finished - so stop playing
+ roseSeq->setStatus(STOPPING);
+ } else {
+ // Now process any incoming MIDI events
+ // and return them to the gui
+ //
+ roseSeq->processRecordedMidi();
+
+ // Now process any incoming audio
+ // and return it to the gui
+ //
+ roseSeq->processRecordedAudio();
+
+ // Still process these so we can send up
+ // audio levels as MappedEvents
+ //
+ roseSeq->processAsynchronousEvents();
+ }
+ break;
+
+ case STOPPING:
+ // There's no call to roseSeq to actually process the
+ // stop, because this arises from a DCOP call from the GUI
+ // direct to roseSeq to start with
+ roseSeq->setStatus(STOPPED);
+ SEQUENCER_DEBUG << "RosegardenSequencer - Stopped" << endl;
+ break;
+
+ case RECORDING_ARMED:
+ SEQUENCER_DEBUG << "RosegardenSequencer - "
+ << "Sequencer can't enter \""
+ << "RECORDING_ARMED\" state - "
+ << "internal error"
+ << endl;
+ break;
+
+ case STOPPED:
+ default:
+ roseSeq->processAsynchronousEvents();
+ break;
+ }
+
+ // Update internal clock and send pointer position
+ // change event to GUI - this is the heartbeat of
+ // the Sequencer - it doesn't tick over without
+ // this call.
+ //
+ // Also attempt to send the MIDI clock at this point.
+ //
+ roseSeq->updateClocks();
+
+ // we want to process transport changes immediately
+ if (roseSeq->checkExternalTransport()) {
+ atLeisure = false;
+ } else if (lastSeqStatus != roseSeq->getStatus()) {
+ SEQUENCER_DEBUG << "Sequencer status changed from " << lastSeqStatus << " to " << roseSeq->getStatus() << endl;
+ roseSeq->notifySequencerStatus();
+ lastSeqStatus = roseSeq->getStatus();
+ atLeisure = false;
+ }
+
+ app.processEvents();
+ if (atLeisure)
+ roseSeq->sleep(sleepTime);
+ }
+
+ delete roseSeq;
+
+ std::cerr << "Toodle-pip." << endl;
+ return 0;
+}
+