summaryrefslogtreecommitdiffstats
path: root/src/sequencer/MmappedSegment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sequencer/MmappedSegment.cpp')
-rw-r--r--src/sequencer/MmappedSegment.cpp702
1 files changed, 702 insertions, 0 deletions
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;
+}
+
+}
+