1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Rosegarden
5 A sequencer and musical notation editor.
6 Copyright 2000-2021 the Rosegarden development team.
7 See the AUTHORS file for more details.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #define RG_MODULE_STRING "[Studio]"
17
18 #include <iostream>
19
20 #include "base/Studio.h"
21 #include "MidiDevice.h"
22 #include "AudioDevice.h"
23 #include "SoftSynthDevice.h"
24 #include "Instrument.h"
25
26 #include "base/RecordIn.h"
27 #include "base/Segment.h"
28 #include "misc/Strings.h"
29 #include "Track.h"
30 #include "Composition.h"
31 #include "sequencer/RosegardenSequencer.h"
32
33 #include <sstream>
34 #include <string>
35
36 #include <QString>
37
38
39 using std::endl;
40
41
42 namespace Rosegarden
43 {
44
45 Studio::Studio() :
46 amwShowAudioFaders(true),
47 amwShowSynthFaders(true),
48 amwShowAudioSubmasters(true),
49 amwShowUnassignedFaders(false),
50 m_midiThruFilter(0),
51 m_midiRecordFilter(0),
52 m_metronomeDevice(0)
53 {
54 // We _always_ have a buss with id zero, for the master out
55 m_busses.push_back(new Buss(0));
56
57 // And we always create one audio record in
58 m_recordIns.push_back(new RecordIn());
59
60 // And we always have one audio and one soft-synth device, whose
61 // IDs match the base instrument numbers (for no good reason
62 // except easy identifiability)
63 addDevice(QObject::tr("Audio").toUtf8().data(),
64 AudioInstrumentBase, AudioInstrumentBase,
65 Device::Audio);
66 addDevice(QObject::tr("Synth plugin").toUtf8().data(),
67 SoftSynthInstrumentBase, SoftSynthInstrumentBase,
68 Device::SoftSynth);
69 }
70
71 Studio::~Studio()
72 {
73 DeviceListIterator dIt = m_devices.begin();
74
75 for (; dIt != m_devices.end(); ++dIt)
76 delete(*dIt);
77
78 m_devices.clear();
79
80 for (size_t i = 0; i < m_busses.size(); ++i) {
81 delete m_busses[i];
82 }
83
84 for (size_t i = 0; i < m_recordIns.size(); ++i) {
85 delete m_recordIns[i];
86 }
87 }
88
89 void
90 Studio::addDevice(const std::string &name,
91 DeviceId id,
92 InstrumentId baseInstrumentId,
93 Device::DeviceType type)
94 {
95 Device *d = nullptr;
96
97 switch (type) {
98
99 case Device::Midi:
100 d = new MidiDevice(id, baseInstrumentId, name, MidiDevice::Play);
101 break;
102
103 case Device::Audio:
104 d = new AudioDevice(id, name);
105 break;
106
107 case Device::SoftSynth:
108 d = new SoftSynthDevice(id, name);
109 break;
110
snapY(int y)111 default:
112 RG_WARNING << "addDevice(): WARNING: unrecognised device type " << type;
113 return;
114 }
115
116 m_devices.push_back(d);
117 }
118
119 void
120 Studio::removeDevice(DeviceId id)
121 {
122 DeviceListIterator it;
123 for (it = m_devices.begin(); it != m_devices.end(); it++) {
124 if ((*it)->getId() == id) {
125 delete *it;
126 m_devices.erase(it);
127 return;
128 }
129 }
130 }
131
132 void
setYSnap(int ysnap)133 Studio::resyncDeviceConnections()
134 {
135 // Sync all the MidiDevice connections to the current connections
136 // according to RosegardenSequencer.
137
138 DeviceList *devices = getDevices();
139
140 // For each Device
141 for (unsigned i = 0; i < devices->size(); ++i) {
142 // Only MidiDevice's have connections.
143 MidiDevice *midiDevice = dynamic_cast<MidiDevice *>((*devices)[i]);
144 if (!midiDevice)
145 continue;
146
147 DeviceId deviceId = midiDevice->getId();
148 QString connection =
149 RosegardenSequencer::getInstance()->getConnection(deviceId);
150
151 midiDevice->setCurrentConnection(qstrtostr(connection));
152
153 // If we are connected to something, but the user didn't ask for
154 // anything, we must have been connected up by
155 // AlsaDriver::connectSomething(). In that case, we'd better store
156 // the connection as the user selection in case the user actually
157 // likes it.
158 if (connection != "" && midiDevice->getUserConnection() == "")
159 midiDevice->setUserConnection(qstrtostr(connection));
160 }
161 }
162
163
164 DeviceId
165 Studio::getSpareDeviceId(InstrumentId &baseInstrumentId)
166 {
167 InstrumentId highestMidiInstrumentId = MidiInstrumentBase;
168 bool foundInstrument = false;
169
170 std::set<DeviceId> ids;
171 DeviceListIterator it;
172 for (it = m_devices.begin(); it != m_devices.end(); it++) {
173 ids.insert((*it)->getId());
174 if ((*it)->getType() == Device::Midi) {
175 InstrumentList il = (*it)->getAllInstruments();
176 for (size_t i = 0; i < il.size(); ++i) {
177 if (il[i]->getId() > highestMidiInstrumentId) {
178 highestMidiInstrumentId = il[i]->getId();
179 foundInstrument = true;
180 }
181 }
182 }
183 }
184
185 if (!foundInstrument) {
186 baseInstrumentId = MidiInstrumentBase;
187 } else {
188 baseInstrumentId = ((highestMidiInstrumentId / 128) + 1) * 128;
189 }
190
191 DeviceId id = 0;
192 while (ids.find(id) != ids.end()) ++id;
193 return id;
194 }
195
196 InstrumentList
197 Studio::getAllInstruments()
198 {
199 InstrumentList list, subList;
200
201 DeviceListIterator it;
202
203 // Append lists
204 //
205 for (it = m_devices.begin(); it != m_devices.end(); it++)
206 {
207 // get sub list
208 subList = (*it)->getAllInstruments();
209
210 // concetenate
211 list.insert(list.end(), subList.begin(), subList.end());
212 }
213
214 return list;
215
216 }
217
218 InstrumentList
219 Studio::getPresentationInstruments() const
220 {
221 InstrumentList list;
222
223 // For each device...
224 for (DeviceList::const_iterator it = m_devices.begin();
225 it != m_devices.end();
226 ++it) {
227 const MidiDevice *midiDevice = dynamic_cast<MidiDevice *>(*it);
228
229 if (midiDevice) {
230 // skip read-only devices
231 if (midiDevice->getDirection() == MidiDevice::Record)
232 continue;
233 }
234
235 // get sub list
236 InstrumentList subList = (*it)->getPresentationInstruments();
237
238 // concatenate
239 list.insert(list.end(), subList.begin(), subList.end());
240 }
241
242 return list;
243 }
244
245 Instrument *
246 Studio::getInstrumentById(InstrumentId id) const
247 {
248 // For each Device
249 for (std::vector<Device *>::const_iterator deviceIter = m_devices.begin();
250 deviceIter != m_devices.end();
251 ++deviceIter)
252 {
253 InstrumentList list = (*deviceIter)->getAllInstruments();
254
255 for (InstrumentList::const_iterator instrumentIter = list.begin();
256 instrumentIter != list.end();
257 ++instrumentIter) {
258 if ((*instrumentIter)->getId() == id)
259 return (*instrumentIter);
260 }
261 }
262
263 return nullptr;
264
265 }
266
267 // From a user selection (from a "Presentation" list) return
268 // the matching Instrument
269 //
270 Instrument*
271 Studio::getInstrumentFromList(int index)
272 {
273 std::vector<Device*>::iterator it;
274 InstrumentList list;
275 InstrumentList::iterator iit;
276 int count = 0;
277
278 for (it = m_devices.begin(); it != m_devices.end(); ++it)
279 {
280 MidiDevice *midiDevice = dynamic_cast<MidiDevice*>(*it);
281
282 if (midiDevice)
283 {
284 // skip read-only devices
285 if (midiDevice->getDirection() == MidiDevice::Record)
286 continue;
287 }
288
289 list = (*it)->getPresentationInstruments();
290
291 for (iit = list.begin(); iit != list.end(); ++iit)
292 {
293 if (count == index)
294 return (*iit);
295
296 count++;
297 }
298 }
299
300 return nullptr;
301
302 }
303
304 Instrument *
305 Studio::getInstrumentFor(const Segment *segment) const
306 {
307 if (!segment) return nullptr;
308 if (!segment->getComposition()) return nullptr;
309 TrackId tid = segment->getTrack();
310 Track *track = segment->getComposition()->getTrackById(tid);
311 if (!track) return nullptr;
312 return getInstrumentFor(track);
313 }
314
315 Instrument *
316 Studio::getInstrumentFor(const Track *track) const
317 {
318 if (!track) return nullptr;
319 InstrumentId iid = track->getInstrument();
320 return getInstrumentById(iid);
321 }
322
323 BussList
324 Studio::getBusses()
325 {
326 return m_busses;
327 }
328
329 Buss *
330 Studio::getBussById(BussId id)
331 {
332 for (BussList::iterator i = m_busses.begin(); i != m_busses.end(); ++i) {
333 if ((*i)->getId() == id) return *i;
334 }
335 return nullptr;
336 }
337
338 void
339 Studio::addBuss(Buss *buss)
340 {
341 if (buss->getId() != m_busses.size()) {
342 RG_WARNING << "addBuss() Precondition: Incoming buss has wrong ID.";
343 }
344
345 m_busses.push_back(buss);
346 }
347
348 #if 0
349 void
350 Studio::removeBuss(BussId id)
351 {
352 for (BussList::iterator i = m_busses.begin(); i != m_busses.end(); ++i) {
353 if ((*i)->getId() == id) {
354 delete *i;
355 m_busses.erase(i);
356 return;
357 }
358 }
359 }
360 #endif
361
362 void
363 Studio::setBussCount(unsigned newBussCount)
364 {
365 // We have to have at least one for the master.
366 if (newBussCount < 1)
367 return;
368 // Reasonable limit. Adjust if needed.
369 if (newBussCount > 16)
370 return;
371 // No change? Bail.
372 if (newBussCount == m_busses.size())
373 return;
374
375 // If we need to remove busses
376 if (newBussCount < m_busses.size()) {
377 int removeCount = m_busses.size() - newBussCount;
378
379 // For each one that needs removing.
380 for (int i = 0; i < removeCount; ++i) {
381 // Delete the last buss.
382 delete m_busses.back();
383 // Remove it from the list.
384 m_busses.pop_back();
385 }
386 } else { // We need to add busses
387 int addCount = newBussCount - m_busses.size();
388
389 for (int i = 0; i < addCount; ++i) {
390 unsigned bussId = m_busses.size();
391 m_busses.push_back(new Buss(bussId));
392 }
393 }
394
395 #if 0
396 Q_ASSERT_X(m_busses.size() == newBussCount,
397 "Studio::setBussCount()",
398 "Postcondition: Buss count is not as expected.");
399
400 for (BussId bussId = 0; bussId < m_busses.size(); ++bussId) {
401 Q_ASSERT_X(m_busses[bussId]->getId() == bussId,
402 "Studio::setBussCount()",
403 "Postcondition: Buss has wrong ID.");
404 }
405 #endif
406 }
407
408 PluginContainer *
409 Studio::getContainerById(InstrumentId id)
410 {
411 PluginContainer *pc = getInstrumentById(id);
412 if (pc) return pc;
413 else return getBussById(id);
414 }
415
416 RecordIn *
417 Studio::getRecordIn(int number)
418 {
419 if (number >= 0 && number < int(m_recordIns.size()))
420 return m_recordIns[number];
421 else
422 return nullptr;
423 }
424
425 void
426 Studio::setRecordInCount(unsigned newRecordInCount)
427 {
428 // Can't have zero.
429 if (newRecordInCount < 1)
430 return;
431 if (newRecordInCount > 32)
432 return;
433 // No change? Bail.
434 if (newRecordInCount == m_recordIns.size())
435 return;
436
437 // If we need to add some RecordIns.
438 if (newRecordInCount > m_recordIns.size()) {
439
440 unsigned addCount = newRecordInCount - m_recordIns.size();
441
442 for (unsigned i = 0; i < addCount; ++i) {
443 m_recordIns.push_back(new RecordIn());
444 }
445
446 } else { // We need to remove some.
447
448 unsigned removeCount = m_recordIns.size() - newRecordInCount;
449
450 // For each one that needs removing.
451 for (unsigned i = 0; i < removeCount; ++i) {
452 // Delete the last RecordIn.
453 delete m_recordIns.back();
454 // Remove it from the list.
455 m_recordIns.pop_back();
456 }
457 }
458
459 // The mapped IDs get set by RosegardenDocument::initialiseStudio().
460 }
461
462 // Clear down the devices - the devices will clear down their
463 // own Instruments.
464 //
465 void
466 Studio::clear()
467 {
468 InstrumentList list;
469 std::vector<Device*>::iterator it;
470
471 for (it = m_devices.begin(); it != m_devices.end(); ++it)
472 delete *it;
473
474 m_devices.erase(m_devices.begin(), m_devices.end());
475 }
476
477 std::string
478 Studio::toXmlString() const
479 {
480 return toXmlString(std::vector<DeviceId>());
481 }
482
483 std::string
484 Studio::toXmlString(const std::vector<DeviceId> &devices) const
485 {
486 // See RoseXmlHandler for the read side of this.
487
488 std::stringstream studio;
489
490 studio << "<studio thrufilter=\"" << m_midiThruFilter
491 << "\" recordfilter=\"" << m_midiRecordFilter
492 << "\" audioinputpairs=\"" << m_recordIns.size()
493 << "\" metronomedevice=\"" << m_metronomeDevice
494 << "\" amwshowaudiofaders=\"" << amwShowAudioFaders
495 << "\" amwshowsynthfaders=\"" << amwShowSynthFaders
496 << "\" amwshowaudiosubmasters=\"" << amwShowAudioSubmasters
497 << "\" amwshowunassignedfaders=\"" << amwShowUnassignedFaders
498 << "\">" << endl << endl;
499
500 studio << endl;
501
502 // Get XML version of devices
503 //
504 if (devices.empty()) { // export all devices and busses
505
506 for (DeviceListConstIterator it = m_devices.begin();
507 it != m_devices.end(); it++) {
508 studio << (*it)->toXmlString() << endl << endl;
509 }
510
511 for (BussList::const_iterator it = m_busses.begin();
512 it != m_busses.end(); ++it) {
513 studio << (*it)->toXmlString() << endl << endl;
514 }
515
516 } else {
517 for (std::vector<DeviceId>::const_iterator di(devices.begin());
518 di != devices.end(); ++di) {
519 Device *d = getDevice(*di);
520 if (!d) {
521 RG_WARNING << "toXmlString(): WARNING: Unknown device id " << (*di);
522 } else {
523 studio << d->toXmlString() << endl << endl;
524 }
525 }
526 }
527
528 studio << endl << endl;
529
530 studio << "</studio>" << endl;
531
532 return studio.str();
533 }
534
535 const MidiMetronome *
536 Studio::getMetronomeFromDevice(DeviceId id)
537 {
538 // For each Device
539 for (std::vector<Device *>::const_iterator deviceIter = m_devices.begin();
540 deviceIter != m_devices.end();
541 ++deviceIter) {
542
543 //RG_DEBUG << "getMetronomeFromDevice(): Having a look at device " << (*it)->getId();
544
545 // No ID match? Try the next.
546 if ((*deviceIter)->getId() != id)
547 continue;
548
549 MidiDevice *midiDevice = dynamic_cast<MidiDevice *>(*deviceIter);
550
551 // If it's a MidiDevice and it has a metronome, return it.
552 if (midiDevice &&
553 midiDevice->getMetronome()) {
554 //RG_DEBUG << "getMetronomeFromDevice(" << id << "): device is a MIDI device";
555 return midiDevice->getMetronome();
556 }
557
558 SoftSynthDevice *ssDevice = dynamic_cast<SoftSynthDevice *>(*deviceIter);
559
560 // If it's a SoftSynthDevice and it has a metronome, return it.
561 if (ssDevice &&
562 ssDevice->getMetronome()) {
563 //RG_DEBUG << "getMetronomeFromDevice(" << id << "): device is a soft synth device";
564 return ssDevice->getMetronome();
565 }
566 }
567
568 return nullptr;
569 }
570
571 // Scan all MIDI devices for available channels and map
572 // them to a current program
573
574 Instrument*
575 Studio::assignMidiProgramToInstrument(MidiByte program,
576 int msb, int lsb,
577 bool percussion)
578 {
579 MidiDevice *midiDevice;
580 std::vector<Device*>::iterator it;
581 Rosegarden::InstrumentList::iterator iit;
582 Rosegarden::InstrumentList instList;
583
584 // Instruments that we may return
585 //
586 Rosegarden::Instrument *newInstrument = nullptr;
587 Rosegarden::Instrument *firstInstrument = nullptr;
588
589 bool needBank = (msb >= 0 || lsb >= 0);
590 if (needBank) {
591 if (msb < 0) msb = 0;
592 if (lsb < 0) lsb = 0;
593 }
594
595 // Pass one - search through all MIDI instruments looking for
596 // a match that we can re-use. i.e. if we have a matching
597 // Program Change then we can use this Instrument.
598 //
599 for (it = m_devices.begin(); it != m_devices.end(); ++it)
600 {
601 midiDevice = dynamic_cast<MidiDevice*>(*it);
602
603 if (midiDevice && midiDevice->getDirection() == MidiDevice::Play)
604 {
605 instList = (*it)->getPresentationInstruments();
606
607 for (iit = instList.begin(); iit != instList.end(); ++iit)
608 {
609 if (firstInstrument == nullptr)
610 firstInstrument = *iit;
611
612 // If we find an Instrument sending the right program already.
613 //
614 if ((*iit)->sendsProgramChange() &&
615 (*iit)->getProgramChange() == program &&
616 (!needBank || ((*iit)->sendsBankSelect() &&
617 (*iit)->getMSB() == msb &&
618 (*iit)->getLSB() == lsb &&
619 (*iit)->isPercussion() == percussion)))
620 {
621 return (*iit);
622 }
623 else
624 {
625 // Ignore the program change and use the percussion
626 // flag.
627 //
628 if ((*iit)->isPercussion() && percussion)
629 {
630 return (*iit);
631 }
632
633 // Otherwise store the first Instrument for
634 // possible use later.
635 //
636 if (newInstrument == nullptr &&
637 (*iit)->sendsProgramChange() == false &&
638 (*iit)->sendsBankSelect() == false &&
639 (*iit)->isPercussion() == percussion)
640 newInstrument = *iit;
641 }
642 }
643 }
644 }
645
646
647 // Okay, if we've got this far and we have a new Instrument to use
648 // then use it.
649 //
650 if (newInstrument != nullptr)
651 {
652 newInstrument->setSendProgramChange(true);
653 newInstrument->setProgramChange(program);
654
655 if (needBank) {
656 newInstrument->setSendBankSelect(true);
657 newInstrument->setPercussion(percussion);
658 newInstrument->setMSB(msb);
659 newInstrument->setLSB(lsb);
660 }
661 }
662 else // Otherwise we just reuse the first Instrument we found
663 newInstrument = firstInstrument;
664
665
666 return newInstrument;
667 }
668
669 // Just make all of these Instruments available for automatic
670 // assignment in the assignMidiProgramToInstrument() method
671 // by invalidating the ProgramChange flag.
672 //
673 // This method sounds much more dramatic than it actually is -
674 // it could probably do with a rename.
675 //
676 //
677 void
678 Studio::unassignAllInstruments()
679 {
680 MidiDevice *midiDevice;
681 AudioDevice *audioDevice;
682 std::vector<Device*>::iterator it;
683 Rosegarden::InstrumentList::iterator iit;
684 Rosegarden::InstrumentList instList;
685 int channel = 0;
686
687 for (it = m_devices.begin(); it != m_devices.end(); ++it)
688 {
689 midiDevice = dynamic_cast<MidiDevice*>(*it);
690
691 if (midiDevice)
692 {
693 instList = (*it)->getPresentationInstruments();
694
695 for (iit = instList.begin(); iit != instList.end(); ++iit)
696 {
697 // Only for true MIDI Instruments - not System ones
698 //
699 if ((*iit)->getId() >= MidiInstrumentBase)
700 {
701 (*iit)->setSendBankSelect(false);
702 (*iit)->setSendProgramChange(false);
703 (*iit)->setNaturalChannel(channel);
704 channel = ( channel + 1 ) % 16;
705 (*iit)->setFixedChannel();
706 // ??? This is a "reset" of the instrument. It doesn't
707 // seem to make sense that we should send out the
708 // default values.
709 //(*iit)->sendChannelSetup();
710
711 (*iit)->setPan(MidiMidValue);
712 (*iit)->setVolume(100);
713
714 }
715 }
716 }
717 else
718 {
719 audioDevice = dynamic_cast<AudioDevice*>(*it);
720
721 if (audioDevice)
722 {
723 instList = (*it)->getPresentationInstruments();
724
725 for (iit = instList.begin(); iit != instList.end(); ++iit)
726 (*iit)->emptyPlugins();
727 }
728 }
729 }
730 }
731
732 void
733 Studio::clearMidiBanksAndPrograms()
734 {
735 MidiDevice *midiDevice;
736 std::vector<Device*>::iterator it;
737
738 for (it = m_devices.begin(); it != m_devices.end(); ++it)
739 {
740 midiDevice = dynamic_cast<MidiDevice*>(*it);
741
742 if (midiDevice)
743 {
744 midiDevice->clearProgramList();
745 midiDevice->clearBankList();
746 }
747 }
748 }
749
750 void
751 Studio::clearBusses()
752 {
753 for (size_t i = 0; i < m_busses.size(); ++i) {
754 delete m_busses[i];
755 }
756 m_busses.clear();
757 m_busses.push_back(new Buss(0));
758 }
759
760 void
761 Studio::clearRecordIns()
762 {
763 for (size_t i = 0; i < m_recordIns.size(); ++i) {
764 delete m_recordIns[i];
765 }
766 m_recordIns.clear();
767 m_recordIns.push_back(new RecordIn());
768 }
769
770 Device *
771 Studio::getDevice(DeviceId id) const
772 {
773 //RG_DEBUG << "Studio[" << this << "]::getDevice(" << id << ")... ";
774
775 std::vector<Device*>::const_iterator it;
776
777 for (it = m_devices.begin(); it != m_devices.end(); ++it) {
778
779 // possibly fix a following seg.fault :
780 if( ! (*it) ){
781 RG_WARNING << "getDevice(): WARNING: (*it) is nullptr";
782 continue;
783 }
784
785 //RG_DEBUG << (*it)->getId();
786
787 if ((*it)->getId() == id) {
788 //RG_DEBUG << "Found";
789 return (*it);
790 }
791 }
792
793 //RG_DEBUG << "NOT found";
794
795 return nullptr;
796 }
797
798 Device *
799 Studio::getAudioDevice()
800 {
801 std::vector<Device*>::iterator it;
802
803 for (it = m_devices.begin(); it != m_devices.end(); ++it) {
804 if ((*it)->getType() == Device::Audio) return *it;
805 }
806
807 return nullptr;
808 }
809
810 Device *
811 Studio::getSoftSynthDevice()
812 {
813 std::vector<Device*>::iterator it;
814
815 for (it = m_devices.begin(); it != m_devices.end(); ++it) {
816 if ((*it)->getType() == Device::SoftSynth) return *it;
817 }
818
819 return nullptr;
820 }
821
822 std::string
823 Studio::getSegmentName(InstrumentId id)
824 {
825 MidiDevice *midiDevice;
826 std::vector<Device*>::iterator it;
827 Rosegarden::InstrumentList::iterator iit;
828 Rosegarden::InstrumentList instList;
829
830 for (it = m_devices.begin(); it != m_devices.end(); ++it)
831 {
832 midiDevice = dynamic_cast<MidiDevice*>(*it);
833
834 if (midiDevice)
835 {
836 instList = (*it)->getAllInstruments();
837
838 for (iit = instList.begin(); iit != instList.end(); ++iit)
839 {
840 if ((*iit)->getId() == id)
841 {
842 if ((*iit)->sendsProgramChange())
843 {
844 return (*iit)->getProgramName();
845 }
846 else
847 {
848 return midiDevice->getName() + " " + (*iit)->getName();
849 }
850 }
851 }
852 }
853 }
854
855 return std::string("");
856 }
857
858 InstrumentId
859 Studio::getAudioPreviewInstrument()
860 {
861 AudioDevice *audioDevice;
862 std::vector<Device*>::iterator it;
863
864 for (it = m_devices.begin(); it != m_devices.end(); ++it)
865 {
866 audioDevice = dynamic_cast<AudioDevice*>(*it);
867
868 // Just the first one will do - we can make this more
869 // subtle if we need to later.
870 //
871 if (audioDevice)
872 return audioDevice->getPreviewInstrument();
873 }
874
875 // system instrument - won't accept audio
876 return 0;
877 }
878
879 bool
880 Studio::haveMidiDevices() const
881 {
882 Rosegarden::DeviceListConstIterator it = m_devices.begin();
883 for (; it != m_devices.end(); it++)
884 {
885 if ((*it)->getType() == Device::Midi) return true;
886 }
887 return false;
888 }
889
890
891 }
892
893