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