1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_MODULE_STRING "[RosegardenSequencer]"
19 
20 #include "RosegardenSequencer.h"
21 
22 //#include <sys/types.h>
23 //#include <sys/stat.h>
24 //#include <fcntl.h>
25 //#include <sys/mman.h>
26 //#include <unistd.h>
27 //#include <errno.h>
28 
29 #include <QVector>
30 
31 #include "misc/Debug.h"
32 #include "misc/Strings.h"
33 #include "sound/ControlBlock.h"
34 #include "sound/SoundDriver.h"
35 #include "sound/SoundDriverFactory.h"
36 #include "sound/MappedInstrument.h"
37 #include "sound/MappedEventInserter.h"
38 #include "sound/SequencerDataBlock.h"
39 #include "gui/seqmanager/MEBIterator.h"
40 #include "base/Profiler.h"
41 #include "sound/PluginFactory.h"
42 #include "base/Instrument.h"
43 #include "base/InstrumentStaticSignals.h"
44 #include "gui/studio/StudioControl.h"
45 
46 #include "gui/application/RosegardenApplication.h"
47 #include "gui/application/RosegardenMainWindow.h"
48 
49 #include "rosegarden-version.h"
50 
51 //#define DEBUG_ROSEGARDEN_SEQUENCER
52 
53 //#define LOCKED QMutexLocker rgseq_locker(&m_mutex); SEQUENCER_DEBUG << "Locked in " << __PRETTY_FUNCTION__ << " at " << __LINE__
54 #define LOCKED QMutexLocker rgseq_locker(&m_mutex)
55 
56 
57 namespace Rosegarden
58 {
59 
60 
RosegardenSequencer()61 RosegardenSequencer::RosegardenSequencer() :
62     m_driver(nullptr),
63     m_transportStatus(STOPPED),
64     m_songPosition(0, 0),
65     m_lastFetchSongPosition(0, 0),
66     // 160 msecs for low latency mode.  Historically, we used
67     // 500 msecs for "high" latency mode.
68     m_readAhead(0, 160000000),
69     // 60 msecs for low latency mode.  Historically, we used
70     // 400 msecs for "high" latency mode.
71     m_audioMix(0, 60000000),
72     m_audioRead(2, 500000000),  // 2.5 secs
73     m_audioWrite(4, 0),  // 4.0 secs
74     m_smallFileSize(256),  // 256 kbytes
75     m_loopStart(0, 0),
76     m_loopEnd(0, 0),
77     m_studio(new MappedStudio()),
78     m_transportToken(1),
79 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
80     m_isEndOfCompReached(false)
81 #else
82     m_isEndOfCompReached(false),
83     m_mutex(QMutex::Recursive) // recursive
84 #endif
85 {
86     // Initialise the MappedStudio
87     //
88     initialiseStudio();
89 
90     // Creating this object also initialises the Rosegarden ALSA/JACK
91     // interface for both playback and recording. MappedStudio
92     // audio faders are also created.
93     //
94     m_driver = SoundDriverFactory::createDriver(m_studio);
95     m_studio->setSoundDriver(m_driver);
96 
97     if (!m_driver) {
98         SEQUENCER_DEBUG << "RosegardenSequencer object could not be allocated";
99         m_transportStatus = QUIT;
100         return;
101     }
102 
103     m_driver->setAudioBufferSizes(m_audioMix, m_audioRead, m_audioWrite,
104                                   m_smallFileSize);
105 
106     // Connect for high-frequency control change notifications.
107     // Note that we must use a DirectConnection or else the signals may
108     // get lost.  I assume this is because the sequencer thread doesn't
109     // handle queued signals.  Perhaps it doesn't have a Qt event loop?
110     // Regardless, we really don't want something as high-frequency as
111     // this going through a message queue anyway.  We should probably
112     // request a DirectConnection for every controlChange() connection.
113     connect(Instrument::getStaticSignals().data(),
114                 &InstrumentStaticSignals::controlChange,
115             this, &RosegardenSequencer::slotControlChange,
116             Qt::DirectConnection);
117 }
118 
~RosegardenSequencer()119 RosegardenSequencer::~RosegardenSequencer()
120 {
121     RG_DEBUG << "dtor...";
122 
123     // MappedStudio holds a SoundDriver pointer, so it must be destroyed
124     // first.
125     delete m_studio;
126     m_studio = nullptr;
127 
128     if (m_driver) {
129         m_driver->shutdown();
130         delete m_driver;
131         m_driver = nullptr;
132     }
133 }
134 
135 RosegardenSequencer *
getInstance()136 RosegardenSequencer::getInstance()
137 {
138     // Guaranteed in C++11 to be lazy initialized and thread-safe.
139     // See ISO/IEC 14882:2011 6.7(4).
140     static RosegardenSequencer instance;
141 
142     // ??? To avoid the static destruction order fiasco, we might want to
143     //     switch to keeping and returning a shared_ptr.  That would
144     //     allow callers to hold on to a shared_ptr until they are done.
145     //     For now, hopefully no one else with static lifetime tries to
146     //     access this after it is destroyed.
147     //
148     //     Note that std::shared_ptr is thread-safe, unlike QSharedPointer.
149     //     This means that we can safely return a shared_ptr to any thread
150     //     that asks for one.
151 
152     return &instance;
153 }
154 
155 void
lock()156 RosegardenSequencer::lock()
157 {
158     m_mutex.lock();
159 }
160 
161 void
unlock()162 RosegardenSequencer::unlock()
163 {
164     m_mutex.unlock();
165 }
166 
167 // "Public" (ex-DCOP, locks required) functions first
168 
169 void
quit()170 RosegardenSequencer::quit()
171 {
172     LOCKED;
173 
174 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
175     SEQUENCER_DEBUG << "RosegardenSequencer::quit()";
176 #endif
177     // and break out of the loop next time around
178     m_transportStatus = QUIT;
179 }
180 
181 
182 // We receive a starting time from the GUI which we use as the
183 // basis of our first fetch of events from the GUI core.  Assuming
184 // this works we set our internal state to PLAYING and go ahead
185 // and play the piece until we get a signal to stop.
186 //
187 bool
play(const RealTime & time)188 RosegardenSequencer::play(const RealTime &time)
189 {
190     LOCKED;
191 
192     // ??? Precondition: readAhead should be larger than the JACK
193     //     (m_driver) period size.
194 
195     if (m_transportStatus == PLAYING ||
196         m_transportStatus == STARTING_TO_PLAY)
197         return true;
198 
199     // Check for record toggle (punch out)
200     //
201     if (m_transportStatus == RECORDING) {
202         m_transportStatus = PLAYING;
203         return punchOut();
204     }
205 
206     // To play from the given song position sets up the internal
207     // play state to "STARTING_TO_PLAY" which is then caught in
208     // the main event loop
209     //
210     m_songPosition = time;
211 
212     SequencerDataBlock::getInstance()->setPositionPointer(m_songPosition);
213 
214     if (m_transportStatus != RECORDING &&
215         m_transportStatus != STARTING_TO_RECORD) {
216         m_transportStatus = STARTING_TO_PLAY;
217     }
218 
219     m_driver->stopClocks();
220 
221     m_driver->setAudioBufferSizes(m_audioMix, m_audioRead, m_audioWrite,
222                                   m_smallFileSize);
223 
224     // report
225     //
226 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
227     SEQUENCER_DEBUG << "RosegardenSequencer::play() - starting to play\n";
228 #endif
229 //!!!
230 //    dumpFirstSegment();
231 
232     // keep it simple
233     return true;
234 }
235 
236 bool
record(const RealTime & time,long recordMode)237 RosegardenSequencer::record(const RealTime &time,
238                             long recordMode)
239 {
240     LOCKED;
241 
242     // ??? Precondition: readAhead should be larger than the JACK
243     //     (m_driver) period size.
244 
245     TransportStatus localRecordMode = (TransportStatus) recordMode;
246 
247 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
248     SEQUENCER_DEBUG << "RosegardenSequencer::record - recordMode is " << recordMode << ", transport status is " << m_transportStatus;
249 #endif
250     // punch in recording
251     if (m_transportStatus == PLAYING) {
252         if (localRecordMode == STARTING_TO_RECORD) {
253 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
254             SEQUENCER_DEBUG << "RosegardenSequencer::record: punching in";
255 #endif
256             localRecordMode = RECORDING; // no need to start playback
257         }
258     }
259 
260     // For audio recording we need to retrieve audio
261     // file names from the GUI
262     //
263     if (localRecordMode == STARTING_TO_RECORD ||
264         localRecordMode == RECORDING) {
265 
266 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
267         SEQUENCER_DEBUG << "RosegardenSequencer::record()"
268                         << " - starting to record" << endl;
269 #endif
270         // This function is (now) called synchronously from the GUI
271         // thread, which is why we needed to obtain the sequencer lock
272         // above.  This means we can safely call back into GUI
273         // functions, so long as we don't call anything that will need
274         // to call any other locking sequencer functions.
275 
276         QVector<InstrumentId> armedInstruments =
277             RosegardenMainWindow::self()->getArmedInstruments();
278 
279         // Compute the list of armed audio instruments.
280         // ??? rename: armedAudioIntruments
281         QVector<InstrumentId> audioInstruments;
282         for (int i = 0; i < armedInstruments.size(); ++i) {
283             // Audio Instrument?  Add to audioInstruments.
284             if (armedInstruments[i] >= AudioInstrumentBase  &&
285                 armedInstruments[i] < MidiInstrumentBase)
286                 audioInstruments.push_back(armedInstruments[i]);
287         }
288 
289         QVector<QString> audioFileNames;
290 
291         // If there are armed audio Instruments
292         if (audioInstruments.size() > 0) {
293 
294             // Create record audio files for each armed audio Instrument.
295             audioFileNames =
296                 RosegardenMainWindow::self()->createRecordAudioFiles
297                 (audioInstruments);
298 
299             if (audioFileNames.size() != audioInstruments.size()) {
300 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
301                 SEQUENCER_DEBUG << "ERROR: RosegardenSequencer::record(): Failed to create correct number of audio files (wanted " << audioInstruments.size() << ", got " << audioFileNames.size() << ")";
302 #endif
303                 stop();
304                 return false;
305             }
306         }
307 
308         // Convert from QVector to std::vector.
309         // ??? We can probably convert the above functions to use std::vector
310         //     and avoid this conversion.
311         std::vector<InstrumentId> armedInstrumentsVec;
312         for (int i = 0; i < armedInstruments.size(); ++i) {
313             armedInstrumentsVec.push_back(armedInstruments[i]);
314         }
315 
316         // Convert from QVector to std::vector.
317         // ??? We can probably convert the above functions to use std::vector
318         //     and avoid this conversion.
319         std::vector<QString> audioFileNamesVec;
320         for (int i = 0; i < audioFileNames.size(); ++i) {
321             audioFileNamesVec.push_back(audioFileNames[i]);
322         }
323 
324         // Get the Sequencer to prepare itself for recording - if
325         // this fails we stop.
326         //
327         if (m_driver->record(RECORD_ON,
328                              armedInstrumentsVec,
329                              audioFileNamesVec) == false) {
330             stop();
331             return false;
332         }
333     } else {
334         // unrecognised type - return a problem
335         return false;
336     }
337 
338     // Now set the local transport status to the record mode
339     //
340     //
341     m_transportStatus = localRecordMode;
342 
343     if (localRecordMode == RECORDING) { // punch in
344         return true;
345     } else {
346 
347         // Ensure that playback is initialised
348         //
349         m_driver->initialisePlayback(m_songPosition);
350 
351         return play(time);
352     }
353 }
354 
355 void
stop()356 RosegardenSequencer::stop()
357 {
358     LOCKED;
359 
360     // set our state at this level to STOPPING (pending any
361     // unfinished NOTES)
362     m_transportStatus = STOPPING;
363 
364     // report
365     //
366 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
367     SEQUENCER_DEBUG << "RosegardenSequencer::stop() - stopping";
368 #endif
369     // process pending NOTE OFFs and stop the Sequencer
370     m_driver->stopPlayback();
371 
372     // the Sequencer doesn't need to know these once
373     // we've stopped.
374     //
375     m_songPosition.sec = 0;
376     m_songPosition.nsec = 0;
377     m_lastFetchSongPosition.sec = 0;
378     m_lastFetchSongPosition.nsec = 0;
379 
380 //    cleanupMmapData();
381 
382     Profiles::getInstance()->dump();
383 
384     incrementTransportToken();
385 }
386 
387 bool
punchOut()388 RosegardenSequencer::punchOut()
389 {
390     LOCKED;
391 
392     // Check for record toggle (punch out)
393     //
394     if (m_transportStatus == RECORDING) {
395         m_driver->punchOut();
396         m_transportStatus = PLAYING;
397         return true;
398     }
399     return false;
400 }
401 
402 // Sets the Sequencer object and this object to the new time
403 // from where playback can continue.
404 //
405 void
jumpTo(const RealTime & pos)406 RosegardenSequencer::jumpTo(const RealTime &pos)
407 {
408     LOCKED;
409 
410 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
411     SEQUENCER_DEBUG << "RosegardenSequencer::jumpTo(" << pos << ")\n";
412 #endif
413     if (pos < RealTime::zeroTime) return;
414 
415     m_driver->stopClocks();
416 
417     RealTime oldPosition = m_songPosition;
418 
419     m_songPosition = m_lastFetchSongPosition = pos;
420 
421     SequencerDataBlock::getInstance()->setPositionPointer(m_songPosition);
422 
423     m_driver->resetPlayback(oldPosition, m_songPosition);
424 
425     if (m_driver->isPlaying()) {
426 
427         // Now prebuffer as in startPlaying:
428 
429         MappedEventList c;
430         fetchEvents(c, m_songPosition, m_songPosition + m_readAhead, true);
431 
432         // process whether we need to or not as this also processes
433         // the audio queue for us
434         //
435         m_driver->processEventsOut(c, m_songPosition, m_songPosition + m_readAhead);
436     }
437 
438     incrementTransportToken();
439 
440     //    SEQUENCER_DEBUG << "RosegardenSequencer::jumpTo: pausing to simulate high-load environment";
441     //    ::sleep(1);
442 
443     m_driver->startClocks();
444 
445     return ;
446 }
447 
448 void
setLoop(const RealTime & loopStart,const RealTime & loopEnd)449 RosegardenSequencer::setLoop(const RealTime &loopStart,
450                              const RealTime &loopEnd)
451 {
452     LOCKED;
453 
454     m_loopStart = loopStart;
455     m_loopEnd = loopEnd;
456 
457     m_driver->setLoop(loopStart, loopEnd);
458 }
459 
460 
461 
462 unsigned
getSoundDriverStatus()463 RosegardenSequencer::getSoundDriverStatus()
464 {
465     LOCKED;
466 
467     return m_driver->getStatus();
468 }
469 
470 
471 // Add an audio file to the sequencer
472 bool
addAudioFile(const QString & fileName,int id)473 RosegardenSequencer::addAudioFile(const QString &fileName, int id)
474 {
475     LOCKED;
476 
477     //call SoundDriver->addAudioFile()
478     return m_driver->addAudioFile(fileName.toUtf8().data(), id);
479 }
480 
481 bool
removeAudioFile(int id)482 RosegardenSequencer::removeAudioFile(int id)
483 {
484     LOCKED;
485 
486     return m_driver->removeAudioFile(id);
487 }
488 
489 void
clearAllAudioFiles()490 RosegardenSequencer::clearAllAudioFiles()
491 {
492     LOCKED;
493 
494     m_driver->clearAudioFiles();
495 }
496 
497 void
setMappedInstrument(int type,unsigned int id)498 RosegardenSequencer::setMappedInstrument(int type, unsigned int id)
499 {
500     LOCKED;
501 
502     InstrumentId mID = (InstrumentId)id;
503     Instrument::InstrumentType mType =
504         (Instrument::InstrumentType)type;
505 
506     m_driver->setMappedInstrument(
507         new MappedInstrument (mType, 0, mID));
508 
509 }
510 
511 void
processMappedEvent(MappedEvent mE)512 RosegardenSequencer::processMappedEvent(MappedEvent mE)
513 {
514     QMutexLocker locker(&m_asyncQueueMutex);
515     m_asyncOutQueue.push_back(new MappedEvent(mE));
516 //    SEQUENCER_DEBUG << "processMappedEvent: Have " << m_asyncOutQueue.size()
517 //                    << " events in async out queue" << endl;
518 }
519 
520 bool
addDevice(Device::DeviceType type,DeviceId id,InstrumentId baseInstrumentId,MidiDevice::DeviceDirection direction)521 RosegardenSequencer::addDevice(Device::DeviceType type,
522                                DeviceId id,
523                                InstrumentId baseInstrumentId,
524                                MidiDevice::DeviceDirection direction)
525 {
526     LOCKED;
527 
528     return m_driver->addDevice(type, id, baseInstrumentId, direction);
529 }
530 
531 void
removeDevice(unsigned int deviceId)532 RosegardenSequencer::removeDevice(unsigned int deviceId)
533 {
534     LOCKED;
535 
536     m_driver->removeDevice(deviceId);
537 }
538 
539 void
removeAllDevices()540 RosegardenSequencer::removeAllDevices()
541 {
542     LOCKED;
543 
544     m_driver->removeAllDevices();
545 }
546 
547 void
renameDevice(unsigned int deviceId,QString name)548 RosegardenSequencer::renameDevice(unsigned int deviceId, QString name)
549 {
550     LOCKED;
551 
552     m_driver->renameDevice(deviceId, name);
553 }
554 
555 unsigned int
getConnections(Device::DeviceType type,MidiDevice::DeviceDirection direction)556 RosegardenSequencer::getConnections(Device::DeviceType type,
557                                     MidiDevice::DeviceDirection direction)
558 {
559     LOCKED;
560 
561     return m_driver->getConnections(type, direction);
562 }
563 
564 QString
getConnection(Device::DeviceType type,MidiDevice::DeviceDirection direction,unsigned int connectionNo)565 RosegardenSequencer::getConnection(Device::DeviceType type,
566                                    MidiDevice::DeviceDirection direction,
567                                    unsigned int connectionNo)
568 {
569     LOCKED;
570 
571     return m_driver->getConnection(type, direction, connectionNo);
572 }
573 
574 QString
getConnection(DeviceId id)575 RosegardenSequencer::getConnection(DeviceId id)
576 {
577     LOCKED;
578 
579     return m_driver->getConnection(id);
580 }
581 
582 void
setConnection(unsigned int deviceId,QString connection)583 RosegardenSequencer::setConnection(unsigned int deviceId,
584                                    QString connection)
585 {
586     LOCKED;
587 
588     m_driver->setConnection(deviceId, connection);
589 }
590 
591 void
setPlausibleConnection(unsigned int deviceId,QString idealConnection)592 RosegardenSequencer::setPlausibleConnection(unsigned int deviceId,
593                                             QString idealConnection)
594 {
595     LOCKED;
596 
597     m_driver->setPlausibleConnection(
598             deviceId,
599             idealConnection,
600             false);  // recordDevice
601 }
602 
603 void
connectSomething()604 RosegardenSequencer::connectSomething()
605 {
606     LOCKED;
607 
608     m_driver->connectSomething();
609 }
610 
611 unsigned int
getTimers()612 RosegardenSequencer::getTimers()
613 {
614     LOCKED;
615 
616     return m_driver->getTimers();
617 }
618 
619 QString
getTimer(unsigned int n)620 RosegardenSequencer::getTimer(unsigned int n)
621 {
622     LOCKED;
623 
624     return m_driver->getTimer(n);
625 }
626 
627 QString
getCurrentTimer()628 RosegardenSequencer::getCurrentTimer()
629 {
630     LOCKED;
631 
632     return m_driver->getCurrentTimer();
633 }
634 
635 void
setCurrentTimer(QString timer)636 RosegardenSequencer::setCurrentTimer(QString timer)
637 {
638     LOCKED;
639 
640     m_driver->setCurrentTimer(timer);
641 }
642 
643 RealTime
getAudioPlayLatency()644 RosegardenSequencer::getAudioPlayLatency()
645 {
646     LOCKED;
647 
648     return m_driver->getAudioPlayLatency();
649 }
650 
651 RealTime
getAudioRecordLatency()652 RosegardenSequencer::getAudioRecordLatency()
653 {
654     LOCKED;
655 
656     return m_driver->getAudioRecordLatency();
657 }
658 
659 
660 void
setMappedProperty(int id,const QString & property,float value)661 RosegardenSequencer::setMappedProperty(int id,
662         const QString &property,
663         float value)
664 {
665     LOCKED;
666 
667     //RG_DEBUG << "setMappedProperty(int, QString, float): id = " << id << "; property = \"" << property << "\"" << "; value = " << value;
668 
669     MappedObject *object = m_studio->getObjectById(id);
670 
671     if (object)
672         object->setProperty(property, value);
673 }
674 
675 void
setMappedProperties(const MappedObjectIdList & ids,const MappedObjectPropertyList & properties,const MappedObjectValueList & values)676 RosegardenSequencer::setMappedProperties(const MappedObjectIdList &ids,
677         const MappedObjectPropertyList &properties,
678         const MappedObjectValueList &values)
679 {
680     LOCKED;
681 
682     MappedObject *object = nullptr;
683     MappedObjectId prevId = 0;
684 
685     for (size_t i = 0;
686             i < ids.size() && i < properties.size() && i < values.size();
687             ++i) {
688 
689         if (i == 0 || ids[i] != prevId) {
690             object = m_studio->getObjectById(ids[i]);
691             prevId = ids[i];
692         }
693 
694         if (object) {
695             object->setProperty(properties[i], values[i]);
696         }
697     }
698 }
699 
700 void
setMappedProperty(int id,const QString & property,const QString & value)701 RosegardenSequencer::setMappedProperty(int id,
702                                        const QString &property,
703                                        const QString &value)
704 {
705     LOCKED;
706 
707 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
708     SEQUENCER_DEBUG << "setProperty: id = " << id
709                     << " : property = \"" << property << "\""
710                     << ", value = " << value << endl;
711 #endif
712     MappedObject *object = m_studio->getObjectById(id);
713 
714     if (object) object->setStringProperty(property, value);
715 }
716 
717 QString
setMappedPropertyList(int id,const QString & property,const MappedObjectPropertyList & values)718 RosegardenSequencer::setMappedPropertyList(int id, const QString &property,
719         const MappedObjectPropertyList &values)
720 {
721     LOCKED;
722 
723 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
724     SEQUENCER_DEBUG << "setPropertyList: id = " << id
725                     << " : property list size = \"" << values.size()
726                     << "\"" << endl;
727 #endif
728     MappedObject *object = m_studio->getObjectById(id);
729 
730     if (object) {
731         try {
732             object->setPropertyList(property, values);
733         } catch (QString err) {
734             return err;
735         }
736         return "";
737     }
738 
739 //    return "(object not found)";
740 
741     //!!! This is where the "object not found" error is coming from when changing
742     // the category combo.  I suspect something isn't wired quite right in here
743     // somewhere in the chain, and that's what's causing this error to come up,
744     // but testing with this simply disabled, everything seems to be working as
745     // expected if we ignore the error and move right along.  I have to admit I
746     // have only a very tenuous grasp on any of this, however.
747     return "";
748 }
749 
750 int
getMappedObjectId(int type)751 RosegardenSequencer::getMappedObjectId(int type)
752 {
753     LOCKED;
754 
755     int value = -1;
756 
757     MappedObject *object =
758         m_studio->getObjectOfType(
759             MappedObject::MappedObjectType(type));
760 
761     if (object) {
762         value = int(object->getId());
763     }
764 
765     return value;
766 }
767 
768 
769 std::vector<QString>
getPropertyList(int id,const QString & property)770 RosegardenSequencer::getPropertyList(int id,
771                                      const QString &property)
772 {
773     LOCKED;
774 
775     std::vector<QString> list;
776 
777     MappedObject *object =
778         m_studio->getObjectById(id);
779 
780     if (object) {
781         list = object->getPropertyList(property);
782     }
783 
784 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
785     SEQUENCER_DEBUG << "getPropertyList - return " << list.size()
786                     << " items" << endl;
787 #endif
788     return list;
789 }
790 
791 std::vector<QString>
getPluginInformation()792 RosegardenSequencer::getPluginInformation()
793 {
794     LOCKED;
795 
796     std::vector<QString> list;
797 
798     PluginFactory::enumerateAllPlugins(list);
799 
800     return list;
801 }
802 
803 QString
getPluginProgram(int id,int bank,int program)804 RosegardenSequencer::getPluginProgram(int id, int bank, int program)
805 {
806     LOCKED;
807 
808     MappedObject *object = m_studio->getObjectById(id);
809 
810     if (object) {
811         MappedPluginSlot *slot =
812             dynamic_cast<MappedPluginSlot *>(object);
813         if (slot) {
814             return slot->getProgram(bank, program);
815         }
816     }
817 
818     return QString();
819 }
820 
821 unsigned long
getPluginProgram(int id,const QString & name)822 RosegardenSequencer::getPluginProgram(int id, const QString &name)
823 {
824     LOCKED;
825 
826     MappedObject *object = m_studio->getObjectById(id);
827 
828     if (object) {
829         MappedPluginSlot *slot =
830             dynamic_cast<MappedPluginSlot *>(object);
831         if (slot) {
832             return slot->getProgram(name);
833         }
834     }
835 
836     return 0;
837 }
838 
839 void
setMappedPort(int pluginId,unsigned long portId,float value)840 RosegardenSequencer::setMappedPort(int pluginId,
841                                       unsigned long portId,
842                                       float value)
843 {
844     LOCKED;
845 
846     MappedObject *object =
847         m_studio->getObjectById(pluginId);
848 
849     MappedPluginSlot *slot =
850         dynamic_cast<MappedPluginSlot *>(object);
851 
852     if (slot) {
853         slot->setPort(portId, value);
854     } else {
855 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
856         SEQUENCER_DEBUG << "no such slot";
857 #endif
858     }
859 }
860 
861 float
getMappedPort(int pluginId,unsigned long portId)862 RosegardenSequencer::getMappedPort(int pluginId,
863                                       unsigned long portId)
864 {
865     LOCKED;
866 
867     MappedObject *object =
868         m_studio->getObjectById(pluginId);
869 
870     MappedPluginSlot *slot =
871         dynamic_cast<MappedPluginSlot *>(object);
872 
873     if (slot) {
874         return slot->getPort(portId);
875     } else {
876 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
877         SEQUENCER_DEBUG << "no such slot";
878 #endif
879     }
880 
881     return 0;
882 }
883 
884 // Creates an object of a type
885 //
886 int
createMappedObject(int type)887 RosegardenSequencer::createMappedObject(int type)
888 {
889     LOCKED;
890 
891     MappedObject *object =
892         m_studio->createObject(MappedObject::MappedObjectType(type));
893 
894     if (object) {
895 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
896         SEQUENCER_DEBUG << "createMappedObject - type = "
897                         << type << ", object id = "
898                         << object->getId() << endl;
899 #endif
900         return object->getId();
901     }
902 
903     return 0;
904 }
905 
906 // Destroy an object
907 //
908 bool
destroyMappedObject(int id)909 RosegardenSequencer::destroyMappedObject(int id)
910 {
911     LOCKED;
912 
913     return m_studio->destroyObject(MappedObjectId(id));
914 }
915 
916 // Connect two objects
917 //
918 void
connectMappedObjects(int id1,int id2)919 RosegardenSequencer::connectMappedObjects(int id1, int id2)
920 {
921     LOCKED;
922 
923     m_studio->connectObjects(MappedObjectId(id1),
924                              MappedObjectId(id2));
925 
926     // When this happens we need to resynchronise our audio processing,
927     // and this is the easiest (and most brutal) way to do it.
928     if (m_transportStatus == PLAYING ||
929             m_transportStatus == RECORDING) {
930         RealTime seqTime = m_driver->getSequencerTime();
931         jumpTo(seqTime);
932     }
933 }
934 
935 // Disconnect two objects
936 //
937 void
disconnectMappedObjects(int id1,int id2)938 RosegardenSequencer::disconnectMappedObjects(int id1, int id2)
939 {
940     LOCKED;
941 
942     m_studio->disconnectObjects(MappedObjectId(id1),
943                                 MappedObjectId(id2));
944 }
945 
946 // Disconnect an object from everything
947 //
948 void
disconnectMappedObject(int id)949 RosegardenSequencer::disconnectMappedObject(int id)
950 {
951     LOCKED;
952 
953     m_studio->disconnectObject(MappedObjectId(id));
954 }
955 
956 unsigned int
getSampleRate() const957 RosegardenSequencer::getSampleRate() const
958 {
959 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
960     QMutexLocker locker(const_cast<QRecursiveMutex *>(&m_mutex));
961 #else
962     QMutexLocker locker(const_cast<QMutex *>(&m_mutex));
963 #endif
964 
965     if (m_driver) return m_driver->getSampleRate();
966 
967     return 0;
968 }
969 
970 void
clearStudio()971 RosegardenSequencer::clearStudio()
972 {
973     LOCKED;
974 
975 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
976     SEQUENCER_DEBUG << "clearStudio()";
977 #endif
978     m_studio->clear();
979     SequencerDataBlock::getInstance()->clearTemporaries();
980 
981 }
982 
983 // Set the MIDI Clock period in microseconds
984 //
985 void
setQuarterNoteLength(RealTime rt)986 RosegardenSequencer::setQuarterNoteLength(RealTime rt)
987 {
988     LOCKED;
989 
990 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
991     SEQUENCER_DEBUG << "RosegardenSequencer::setQuarterNoteLength"
992                     << rt << endl;
993 #endif
994     m_driver->setMIDIClockInterval(rt / 24);
995 }
996 
dumpFirstSegment()997 void RosegardenSequencer::dumpFirstSegment()
998 {
999     LOCKED;
1000 
1001     SEQUENCER_DEBUG << "Dumping 1st segment data :";
1002 
1003     unsigned int i = 0;
1004 
1005     std::set<QSharedPointer<MappedEventBuffer> > segs = m_metaIterator.getBuffers();
1006     if (segs.empty()) {
1007         SEQUENCER_DEBUG << "(no segments)";
1008         return;
1009     }
1010 
1011     QSharedPointer<MappedEventBuffer> firstMappedEventBuffer = *segs.begin();
1012 
1013     MEBIterator it(firstMappedEventBuffer);
1014 
1015     for (; !it.atEnd(); ++it) {
1016 
1017         MappedEvent evt = (*it);
1018         SEQUENCER_DEBUG << i << " : inst = " << evt.getInstrument()
1019                         << " - type = " << evt.getType()
1020                         << " - data1 = " << (unsigned int)evt.getData1()
1021                         << " - data2 = " << (unsigned int)evt.getData2()
1022                         << " - time = " << evt.getEventTime()
1023                         << " - duration = " << evt.getDuration()
1024                         << " - audio mark = " << evt.getAudioStartMarker();
1025 
1026         ++i;
1027     }
1028 
1029     SEQUENCER_DEBUG << "Dumping 1st segment data - done\n";
1030 
1031 }
1032 
1033 void
segmentModified(QSharedPointer<MappedEventBuffer> mapper)1034 RosegardenSequencer::segmentModified(QSharedPointer<MappedEventBuffer> mapper)
1035 {
1036     if (!mapper) return;
1037 
1038  #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1039    SEQUENCER_DEBUG << "RosegardenSequencer::segmentModified(" << mapper << ")\n";
1040 #endif
1041    LOCKED;
1042    /* We don't force an immediate rewind while recording.  It would be
1043       "the right thing" soundwise, but historically we haven't,
1044       there's been no demand and nobody knows what subtle problems
1045       might be introduced. */
1046    bool immediate = (m_transportStatus == PLAYING);
1047    m_metaIterator.resetIteratorForBuffer(mapper, immediate);
1048 }
1049 
1050 void
segmentAdded(QSharedPointer<MappedEventBuffer> mapper)1051 RosegardenSequencer::segmentAdded(QSharedPointer<MappedEventBuffer> mapper)
1052 {
1053     if (!mapper) return;
1054 
1055     LOCKED;
1056 
1057 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1058     SEQUENCER_DEBUG << "RosegardenSequencer::segmentAdded(" << mapper << ")\n";
1059 #endif
1060     // m_metaIterator takes ownership of the mapper, shared with other
1061     // MappedBufMetaIterators
1062     m_metaIterator.addBuffer(mapper);
1063 }
1064 
1065 void
segmentAboutToBeDeleted(QSharedPointer<MappedEventBuffer> mapper)1066 RosegardenSequencer::segmentAboutToBeDeleted(
1067         QSharedPointer<MappedEventBuffer> mapper)
1068 {
1069     if (!mapper)
1070         return;
1071 
1072     LOCKED;
1073 
1074 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1075     SEQUENCER_DEBUG << "RosegardenSequencer::segmentAboutToBeDeleted(" << mapper << ")\n";
1076 #endif
1077 
1078     // This deletes mapper just if no other metaiterator owns it.
1079     m_metaIterator.removeBuffer(mapper);
1080 }
1081 
1082 void
compositionAboutToBeDeleted()1083 RosegardenSequencer::compositionAboutToBeDeleted()
1084 {
1085     LOCKED;
1086 
1087 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1088     SEQUENCER_DEBUG << "RosegardenSequencer::compositionAboutToBeDeleted()\n";
1089 #endif
1090     m_metaIterator.clear();
1091 }
1092 
1093 void
remapTracks()1094 RosegardenSequencer::remapTracks()
1095 {
1096     LOCKED;
1097 
1098 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1099     SEQUENCER_DEBUG << "RosegardenSequencer::remapTracks";
1100 #endif
1101     rationalisePlayingAudio();
1102 }
1103 
1104 bool
getNextTransportRequest(TransportRequest & request,RealTime & time)1105 RosegardenSequencer::getNextTransportRequest(TransportRequest &request,
1106                                              RealTime &time)
1107 {
1108     QMutexLocker locker(&m_transportRequestMutex);
1109 
1110     if (m_transportRequests.empty()) return false;
1111     TransportPair pair = *m_transportRequests.begin();
1112     m_transportRequests.pop_front();
1113     request = pair.first;
1114     time = pair.second;
1115 
1116     //!!! review transport token management -- jumpToTime has an
1117     // extra incrementTransportToken() below
1118 
1119     return true;  // fix "control reaches end of non-void function warning"
1120 }
1121 
1122 MappedEventList
pullAsynchronousMidiQueue()1123 RosegardenSequencer::pullAsynchronousMidiQueue()
1124 {
1125     QMutexLocker locker(&m_asyncQueueMutex);
1126     MappedEventList mq = m_asyncInQueue;
1127     m_asyncInQueue = MappedEventList();
1128     return mq;
1129 }
1130 
1131 // END of public API
1132 
1133 
1134 
1135 // Get a slice of events from the composition into a MappedEventList.
1136 void
fetchEvents(MappedEventList & mappedEventList,const RealTime & start,const RealTime & end,bool firstFetch)1137 RosegardenSequencer::fetchEvents(MappedEventList &mappedEventList,
1138                                     const RealTime &start,
1139                                     const RealTime &end,
1140                                     bool firstFetch)
1141 {
1142     // Always return nothing if we're stopped
1143     //
1144     if ( m_transportStatus == STOPPED || m_transportStatus == STOPPING )
1145         return ;
1146 
1147     getSlice(mappedEventList, start, end, firstFetch);
1148     applyLatencyCompensation(mappedEventList);
1149 }
1150 
1151 
1152 void
getSlice(MappedEventList & mappedEventList,const RealTime & start,const RealTime & end,bool firstFetch)1153 RosegardenSequencer::getSlice(MappedEventList &mappedEventList,
1154                                  const RealTime &start,
1155                                  const RealTime &end,
1156                                  bool firstFetch)
1157 {
1158     //    SEQUENCER_DEBUG << "RosegardenSequencer::getSlice (" << start << " -> " << end << ", " << firstFetch << ")";
1159 
1160     if (firstFetch || (start < m_lastStartTime)) {
1161 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1162         SEQUENCER_DEBUG << "[calling jumpToTime on start]";
1163 #endif
1164         m_metaIterator.jumpToTime(start);
1165     }
1166 
1167     MappedEventInserter inserter(mappedEventList);
1168 
1169     m_metaIterator.fetchEvents(inserter, start, end);
1170 
1171     // don't do this, it breaks recording because
1172     // playing stops right after it starts.
1173 //  m_isEndOfCompReached = eventsRemaining;
1174 
1175     m_lastStartTime = start;
1176 }
1177 
1178 
1179 void
applyLatencyCompensation(MappedEventList & mappedEventList)1180 RosegardenSequencer::applyLatencyCompensation(MappedEventList &mappedEventList)
1181 {
1182     RealTime maxLatency = m_driver->getMaximumPlayLatency();
1183     if (maxLatency == RealTime::zeroTime)
1184         return ;
1185 
1186     for (MappedEventList::iterator i = mappedEventList.begin();
1187             i != mappedEventList.end(); ++i) {
1188 
1189         RealTime instrumentLatency =
1190             m_driver->getInstrumentPlayLatency((*i)->getInstrument());
1191 
1192         //	SEQUENCER_DEBUG << "RosegardenSequencer::applyLatencyCompensation: maxLatency " << maxLatency << ", instrumentLatency " << instrumentLatency << ", moving " << (*i)->getEventTime() << " to " << (*i)->getEventTime() + maxLatency - instrumentLatency;
1193 
1194         (*i)->setEventTime((*i)->getEventTime() +
1195                            maxLatency - instrumentLatency);
1196     }
1197 }
1198 
1199 
1200 // The first fetch of events from the core/ and initialisation for
1201 // this session of playback.  We fetch up to m_readAhead ahead at
1202 // first at then top up at each slice.
1203 //
1204 bool
startPlaying()1205 RosegardenSequencer::startPlaying()
1206 {
1207     // Fetch up to m_readAhead microseconds worth of events
1208     m_lastFetchSongPosition = m_songPosition + m_readAhead;
1209 
1210     // This will reset the Sequencer's internal clock
1211     // ready for new playback
1212     m_driver->initialisePlayback(m_songPosition);
1213 
1214     MappedEventList c;
1215     fetchEvents(c, m_songPosition, m_songPosition + m_readAhead, true);
1216 
1217     // process whether we need to or not as this also processes
1218     // the audio queue for us
1219     m_driver->processEventsOut(c, m_songPosition, m_songPosition + m_readAhead);
1220 
1221     std::vector<MappedEvent> audioEvents;
1222     m_metaIterator.getAudioEvents(audioEvents);
1223     m_driver->initialiseAudioQueue(audioEvents);
1224 
1225     //SEQUENCER_DEBUG << "RosegardenSequencer::startPlaying: pausing to simulate high-load environment";
1226     //::sleep(2);
1227 
1228     // and only now do we signal to start the clock
1229     m_driver->startClocks();
1230 
1231     incrementTransportToken();
1232 
1233     return true; // !m_isEndOfCompReached;
1234 }
1235 
1236 bool
keepPlaying()1237 RosegardenSequencer::keepPlaying()
1238 {
1239     Profiler profiler("RosegardenSequencer::keepPlaying");
1240 
1241     MappedEventList c;
1242 
1243     RealTime fetchEnd = m_songPosition + m_readAhead;
1244     if (isLooping() && fetchEnd >= m_loopEnd) {
1245         fetchEnd = m_loopEnd - RealTime(0, 1);
1246     }
1247     if (fetchEnd > m_lastFetchSongPosition) {
1248         fetchEvents(c, m_lastFetchSongPosition, fetchEnd, false);
1249     }
1250 
1251     // Again, process whether we need to or not to keep
1252     // the Sequencer up-to-date with audio events
1253     //
1254     m_driver->processEventsOut(c, m_lastFetchSongPosition, fetchEnd);
1255 
1256     if (fetchEnd > m_lastFetchSongPosition) {
1257         m_lastFetchSongPosition = fetchEnd;
1258     }
1259 
1260     return true; // !m_isEndOfCompReached; - until we sort this out, we don't stop at end of comp.
1261 }
1262 
1263 // Return current Sequencer time in GUI compatible terms
1264 //
1265 void
updateClocks()1266 RosegardenSequencer::updateClocks()
1267 {
1268     Profiler profiler("RosegardenSequencer::updateClocks");
1269 
1270     m_driver->runTasks();
1271 
1272     //SEQUENCER_DEBUG << "RosegardenSequencer::updateClocks";
1273 
1274     // If we're not playing etc. then that's all we need to do
1275     //
1276     if (m_transportStatus != PLAYING &&
1277             m_transportStatus != RECORDING)
1278         return ;
1279 
1280     RealTime newPosition = m_driver->getSequencerTime();
1281 
1282     // Go around the loop if we've reached the end
1283     //
1284     if (isLooping() && newPosition >= m_loopEnd) {
1285 
1286         RealTime oldPosition = m_songPosition;
1287 
1288         // Remove the loop width from the song position and send
1289         // this position to the GUI
1290         //
1291         newPosition = m_songPosition = m_lastFetchSongPosition = m_loopStart;
1292 
1293         m_driver->stopClocks();
1294 
1295         // Reset playback using this jump
1296         //
1297         m_driver->resetPlayback(oldPosition, m_songPosition);
1298 
1299         MappedEventList c;
1300         fetchEvents(c, m_songPosition, m_songPosition + m_readAhead, true);
1301 
1302         m_driver->processEventsOut(c, m_songPosition, m_songPosition + m_readAhead);
1303 
1304         m_driver->startClocks();
1305     } else {
1306         m_songPosition = newPosition;
1307 
1308         if (m_songPosition <= m_driver->getStartPosition())
1309             newPosition = m_driver->getStartPosition();
1310     }
1311 
1312     RealTime maxLatency = m_driver->getMaximumPlayLatency();
1313     if (maxLatency != RealTime::zeroTime) {
1314         //	SEQUENCER_DEBUG << "RosegardenSequencer::updateClocks: latency compensation moving " << newPosition << " to " << newPosition - maxLatency;
1315         newPosition = newPosition - maxLatency;
1316     }
1317 
1318     // Remap the position pointer
1319     //
1320     SequencerDataBlock::getInstance()->setPositionPointer(newPosition);
1321 }
1322 
1323 void
sleep(const RealTime & rt)1324 RosegardenSequencer::sleep(const RealTime &rt)
1325 {
1326     m_driver->sleep(rt);
1327 }
1328 
1329 void
processRecordedMidi()1330 RosegardenSequencer::processRecordedMidi()
1331 {
1332 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1333     SEQUENCER_DEBUG << "RosegardenSequencer::processRecordedMidi";
1334 #endif
1335 
1336     MappedEventList recordList;
1337 
1338     // Get the MIDI events from the ALSA driver
1339     m_driver->getMappedEventList(recordList);
1340 
1341     if (recordList.empty()) return;
1342 
1343     // Handle "thru" first to reduce latency.
1344 
1345     // Make a copy so we don't mess up the list for recording.
1346     MappedEventList thruList = recordList;
1347 
1348     // Remove events that match the thru filter
1349     applyFiltering(&thruList, ControlBlock::getInstance()->getThruFilter(), true);
1350 
1351     // Route the MIDI thru events to MIDI out.  Use the instrument and
1352     // track information from each event.
1353     routeEvents(&thruList, true);
1354 
1355 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1356     SEQUENCER_DEBUG << "RosegardenSequencer::processRecordedMidi: have " << mC.size() << " events";
1357 #endif
1358 
1359     // Remove events that match the record filter
1360     applyFiltering(&recordList, ControlBlock::getInstance()->getRecordFilter(), false);
1361 
1362     // Store the events
1363     SequencerDataBlock::getInstance()->addRecordedEvents(&recordList);
1364 }
1365 
1366 void
routeEvents(MappedEventList * mappedEventList,bool recording)1367 RosegardenSequencer::routeEvents(
1368         MappedEventList *mappedEventList, bool recording)
1369 {
1370     // For each event
1371     for (MappedEventList::iterator i = mappedEventList->begin();
1372          i != mappedEventList->end();
1373          ++i) {
1374         MappedEvent *event = (*i);
1375 
1376         // Transform the output instrument and channel as needed.
1377 
1378         InstrumentAndChannel info =
1379                 ControlBlock::getInstance()->getInstAndChanForEvent(
1380                         recording,
1381                         event->getRecordedDevice(),
1382                         event->getRecordedChannel());
1383 
1384         event->setInstrument(info.id);
1385         event->setRecordedChannel(info.channel);
1386     }
1387 
1388     // Send the transformed events out...
1389     m_driver->processEventsOut(*mappedEventList);
1390 }
1391 
1392 // Send an update
1393 //
1394 void
processRecordedAudio()1395 RosegardenSequencer::processRecordedAudio()
1396 {
1397     // Nothing to do here: the recording time is sent back to the GUI
1398     // in the sequencer mapper as a normal case.
1399 }
1400 
1401 void
processAsynchronousEvents()1402 RosegardenSequencer::processAsynchronousEvents()
1403 {
1404     // *** Outgoing ad-hoc async events
1405 
1406     std::deque<MappedEvent *> outQueue;
1407 
1408     m_asyncQueueMutex.lock();
1409 
1410     // If there's something to send out
1411     if (!m_asyncOutQueue.empty()) {
1412         // MOVE to local queue.
1413         // ??? std::move() should be faster.
1414         outQueue = m_asyncOutQueue;
1415         m_asyncOutQueue.clear();
1416 
1417         //RG_DEBUG << "processAsynchronousEvents(): Have " << outQueue.size() << " events in async out queue";
1418     }
1419 
1420     m_asyncQueueMutex.unlock();
1421 
1422     MappedEventList mappedEventList;
1423 
1424     // For each event, send to AlsaDriver
1425     while (!outQueue.empty()) {
1426         // ??? Why one at a time?  This is a lot of processing.
1427         mappedEventList.insert(outQueue.front());
1428         m_driver->processEventsOut(mappedEventList);
1429         outQueue.pop_front();
1430         mappedEventList.clear();
1431     }
1432 
1433     // *** Incoming ad-hoc async events
1434 
1435     m_driver->getMappedEventList(mappedEventList);
1436 
1437     if (!mappedEventList.empty()) {
1438         m_asyncQueueMutex.lock();
1439         m_asyncInQueue.merge(mappedEventList);
1440         m_asyncQueueMutex.unlock();
1441 
1442         // MIDI THRU handling
1443 
1444         applyFiltering(
1445                 &mappedEventList,
1446                 ControlBlock::getInstance()->getThruFilter(),  // filter
1447                 true);  // filterControlDevice
1448 
1449         // Send the incoming events back out using the instrument and
1450         // track for the selected track.
1451         routeEvents(&mappedEventList,
1452                     false);  // recording
1453     }
1454 
1455     // Process any pending events (Note Offs or Audio).
1456     m_driver->processPending();
1457 }
1458 
1459 void
applyFiltering(MappedEventList * mC,MidiFilter filter,bool filterControlDevice)1460 RosegardenSequencer::applyFiltering(MappedEventList *mC,
1461                                        MidiFilter filter,
1462                                        bool filterControlDevice)
1463 {
1464     // For each event in the list
1465     for (MappedEventList::iterator i = mC->begin();
1466          i != mC->end();
1467          /* increment in loop */) {
1468 
1469         // Hold on to the current event for processing.
1470         MappedEventList::iterator j = i;
1471         // Move to the next in case the current is erased.
1472         ++i;
1473 
1474         // If this event matches the filter, erase it from the list
1475         if (((*j)->getType() & filter) ||
1476                 (filterControlDevice && ((*j)->getRecordedDevice() ==
1477                                          Device::EXTERNAL_CONTROLLER))) {
1478             mC->erase(j);
1479         }
1480     }
1481 }
1482 
1483 // Initialise the virtual studio with a few audio faders and
1484 // create a plugin manager.  For the moment this is pretty
1485 // arbitrary but eventually we'll drive this from the gui
1486 // and rg file "Studio" entries.
1487 //
1488 void
initialiseStudio()1489 RosegardenSequencer::initialiseStudio()
1490 {
1491     // clear down the studio before we start adding anything
1492     //
1493     m_studio->clear();
1494 }
1495 
1496 void
checkForNewClients()1497 RosegardenSequencer::checkForNewClients()
1498 {
1499     // Don't do this check if any of these conditions hold
1500     //
1501     if (m_transportStatus == PLAYING ||
1502         m_transportStatus == RECORDING)
1503         return ;
1504 
1505     m_driver->checkForNewClients();
1506 }
1507 
1508 
1509 void
rationalisePlayingAudio()1510 RosegardenSequencer::rationalisePlayingAudio()
1511 {
1512     std::vector<MappedEvent> audioEvents;
1513     m_metaIterator.getAudioEvents(audioEvents);
1514     m_driver->initialiseAudioQueue(audioEvents);
1515 }
1516 
1517 
1518 RosegardenSequencer::TransportToken
transportChange(TransportRequest request)1519 RosegardenSequencer::transportChange(TransportRequest request)
1520 {
1521     QMutexLocker locker(&m_transportRequestMutex);
1522 
1523     TransportPair pair(request, RealTime::zeroTime);
1524     m_transportRequests.push_back(pair);
1525 
1526 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1527     SEQUENCER_DEBUG << "RosegardenSequencer::transportChange: " << request;
1528 #endif
1529     if (request == TransportNoChange)
1530         return m_transportToken;
1531     else
1532         return m_transportToken + 1;
1533 }
1534 
1535 RosegardenSequencer::TransportToken
transportJump(TransportRequest request,RealTime rt)1536 RosegardenSequencer::transportJump(TransportRequest request,
1537                                       RealTime rt)
1538 {
1539     QMutexLocker locker(&m_transportRequestMutex);
1540 
1541     TransportPair pair(request, rt);
1542     m_transportRequests.push_back(pair);
1543 
1544 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1545     SEQUENCER_DEBUG << "RosegardenSequencer::transportJump: " << request << ", " << rt;
1546 #endif
1547     if (request == TransportNoChange)
1548         return m_transportToken + 1;
1549     else
1550         return m_transportToken + 2;
1551 }
1552 
1553 bool
isTransportSyncComplete(TransportToken token)1554 RosegardenSequencer::isTransportSyncComplete(TransportToken token)
1555 {
1556     QMutexLocker locker(&m_transportRequestMutex);
1557 
1558 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1559     SEQUENCER_DEBUG << "RosegardenSequencer::isTransportSyncComplete: token " << token << ", current token " << m_transportToken;
1560 #endif
1561     return m_transportToken >= token;
1562 }
1563 
1564 void
incrementTransportToken()1565 RosegardenSequencer::incrementTransportToken()
1566 {
1567     ++m_transportToken;
1568 #ifdef DEBUG_ROSEGARDEN_SEQUENCER
1569     SEQUENCER_DEBUG << "RosegardenSequencer::incrementTransportToken: incrementing to " << m_transportToken;
1570 #endif
1571 }
1572 
1573 void
slotControlChange(Instrument * instrument,int cc)1574 RosegardenSequencer::slotControlChange(Instrument *instrument, int cc)
1575 {
1576     if (!instrument)
1577         return;
1578 
1579     // MIDI
1580     if (instrument->getType() == Instrument::Midi)
1581     {
1582         //RG_DEBUG << "slotControlChange(): cc = " << cc << " value = " << instrument->getControllerValue(cc);
1583 
1584         instrument->sendController(cc, instrument->getControllerValue(cc));
1585 
1586         return;
1587     }
1588 
1589     // Audio or SoftSynth
1590     if (instrument->getType() == Instrument::Audio  ||
1591         instrument->getType() == Instrument::SoftSynth)
1592     {
1593         if (cc == MIDI_CONTROLLER_VOLUME) {
1594 
1595             setMappedProperty(
1596                     instrument->getMappedId(),
1597                     MappedAudioFader::FaderLevel,
1598                     instrument->getLevel());
1599 
1600         } else if (cc == MIDI_CONTROLLER_PAN) {
1601 
1602             setMappedProperty(
1603                     instrument->getMappedId(),
1604                     MappedAudioFader::Pan,
1605                     static_cast<float>(instrument->getPan()) - 100);
1606 
1607         }
1608 
1609         return;
1610     }
1611 }
1612 
1613 
1614 }
1615