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 "[AudioMixerWindow2]"
19
20 #include "AudioMixerWindow2.h"
21
22 #include "AudioStrip.h"
23
24 #include "gui/dialogs/AboutDialog.h"
25 #include "base/AudioLevel.h"
26 #include "misc/Debug.h"
27 #include "gui/general/IconLoader.h"
28 #include "base/Instrument.h"
29 #include "base/InstrumentStaticSignals.h"
30 #include "sound/MappedEvent.h"
31 #include "sound/Midi.h"
32 #include "document/RosegardenDocument.h"
33 #include "gui/application/RosegardenMainViewWidget.h"
34 #include "gui/application/RosegardenMainWindow.h"
35 #include "base/Studio.h"
36
37 #include <QDesktopServices>
38 #include <QHBoxLayout>
39
40 namespace Rosegarden
41 {
42
43
AudioMixerWindow2(QWidget * parent)44 AudioMixerWindow2::AudioMixerWindow2(QWidget *parent) :
45 QMainWindow(parent),
46 m_centralWidget(new QWidget),
47 m_layout(new QHBoxLayout(m_centralWidget)),
48 m_masterStrip(new AudioStrip(this, 0))
49 {
50 setObjectName("AudioMixerWindow2");
51
52 setWindowTitle(tr("Audio Mixer"));
53 setWindowIcon(IconLoader::loadPixmap("window-audiomixer"));
54
55 // This avoids using CPU when we're "closed". Normally with Qt,
56 // "closed" really just means "hidden".
57 setAttribute(Qt::WA_DeleteOnClose);
58
59 // Connect for RosegardenDocument changes.
60 // ??? If the doc changes, this will get disconnected. Who do we have
61 // to connect to to get wind of document changes?
62 // RMW::documentAboutToChange()?
63 connect(RosegardenMainWindow::self()->getDocument(),
64 &RosegardenDocument::documentModified,
65 this, &AudioMixerWindow2::slotDocumentModified);
66 // Connect for "external controller" events.
67 connect(&ExternalController::self(),
68 &ExternalController::externalControllerAMW2,
69 this, &AudioMixerWindow2::slotExternalController);
70
71 // Connect for high-frequency control change notifications.
72 connect(Instrument::getStaticSignals().data(),
73 &InstrumentStaticSignals::controlChange,
74 this, &AudioMixerWindow2::slotControlChange);
75
76 // File > Close
77 createAction("file_close", SLOT(slotClose()));
78
79 // Transport Menu
80 createAction("play", RosegardenMainWindow::self(), SLOT(slotPlay()));
81 createAction("stop", RosegardenMainWindow::self(), SLOT(slotStop()));
82 createAction("playback_pointer_back_bar", RosegardenMainWindow::self(), SLOT(slotRewind()));
83 createAction("playback_pointer_forward_bar", RosegardenMainWindow::self(), SLOT(slotFastforward()));
84 createAction("playback_pointer_start", RosegardenMainWindow::self(), SLOT(slotRewindToBeginning()));
85 createAction("playback_pointer_end", RosegardenMainWindow::self(), SLOT(slotFastForwardToEnd()));
86 createAction("record", RosegardenMainWindow::self(), SLOT(slotRecord()));
87 createAction("panic", RosegardenMainWindow::self(), SLOT(slotPanic()));
88
89 // "Settings > Number of Stereo Inputs" Actions
90 // For i in {1,2,4,8,16}
91 for (int i = 1; i <= 16; i *= 2) {
92 createAction(QString("inputs_%1").arg(i),
93 SLOT(slotNumberOfStereoInputs()));
94 }
95
96 // "Settings > Number of Submasters" Actions
97 createAction("submasters_0", SLOT(slotNumberOfSubmasters()));
98 createAction("submasters_2", SLOT(slotNumberOfSubmasters()));
99 createAction("submasters_4", SLOT(slotNumberOfSubmasters()));
100 createAction("submasters_8", SLOT(slotNumberOfSubmasters()));
101
102 // "Settings > Panning Law" Actions
103 // ??? Can we do something easier to understand than panlaw_0,
104 // panlaw_1, etc...? E.g. panlaw_0dB, panlaw_-3dB, panlaw_-6dB,
105 // panlaw_alt-3dB.
106 createAction("panlaw_0", SLOT(slotPanningLaw()));
107 createAction("panlaw_1", SLOT(slotPanningLaw()));
108 createAction("panlaw_2", SLOT(slotPanningLaw()));
109 createAction("panlaw_3", SLOT(slotPanningLaw()));
110
111 // Settings > Show Audio Faders
112 createAction("show_audio_faders", SLOT(slotShowAudioFaders()));
113
114 // Settings > Show Synth Faders
115 createAction("show_synth_faders", SLOT(slotShowSynthFaders()));
116
117 // Settings > Show Audio Submasters
118 createAction("show_audio_submasters", SLOT(slotShowAudioSubmasters()));
119
120 // Settings > Show Unassigned Faders
121 createAction("show_unassigned_faders", SLOT(slotShowUnassignedFaders()));
122
123 // Help > Help
124 createAction("mixer_help", SLOT(slotHelp()));
125
126 // Help > About Rosegarden
127 createAction("help_about_app", SLOT(slotAboutRosegarden()));
128
129 createMenusAndToolbars("mixer.rc");
130
131 // Set the rewind and fast-forward buttons for auto-repeat.
132 enableAutoRepeat("Transport Toolbar", "playback_pointer_back_bar");
133 enableAutoRepeat("Transport Toolbar", "playback_pointer_forward_bar");
134
135 // Since we can't set the layout for QMainWindow, we have to use
136 // a central widget and set the layout there.
137 setCentralWidget(m_centralWidget);
138
139 // Space between strips.
140 m_layout->setSpacing(7);
141
142 // Force an initial update to make sure we're in sync.
143 updateWidgets();
144
145 show();
146 }
147
~AudioMixerWindow2()148 AudioMixerWindow2::~AudioMixerWindow2()
149 {
150 }
151
152 void
updateStripCounts()153 AudioMixerWindow2::updateStripCounts()
154 {
155 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
156 Studio &studio = doc->getStudio();
157
158 // Verify Input Strips
159
160 InstrumentList instruments = studio.getPresentationInstruments();
161 std::vector<InstrumentId> instrumentIds;
162
163 // For each instrument
164 for (InstrumentList::iterator i = instruments.begin();
165 i != instruments.end();
166 ++i) {
167 Instrument *instrument = *i;
168
169 // Not audio or softsynth, try the next.
170 if (instrument->getType() != Instrument::Audio &&
171 instrument->getType() != Instrument::SoftSynth)
172 continue;
173
174 // If this is an audio instrument, and audio strips are hidden,
175 // try the next.
176 if (instrument->getType() == Instrument::Audio &&
177 !studio.amwShowAudioFaders)
178 continue;
179
180 // If this is a softsynth, and softsynth strips are hidden,
181 // try the next.
182 if (instrument->getType() == Instrument::SoftSynth &&
183 !studio.amwShowSynthFaders)
184 continue;
185
186 // If we shouldn't show unassigned faders and this Instrument
187 // is unassigned, try the next.
188 if (!studio.amwShowUnassignedFaders &&
189 !doc->getComposition().hasTrack(instrument->getId()))
190 continue;
191
192 // Add to list
193 instrumentIds.push_back(instrument->getId());
194 }
195
196 // First, make sure the sizes match.
197 bool inputStripsMatch = (instrumentIds.size() == m_inputStrips.size());
198
199 // If the sizes match, make sure the ids match.
200 if (inputStripsMatch) {
201 for (unsigned i = 0; i < instrumentIds.size(); ++i) {
202 if (instrumentIds[i] != static_cast<InstrumentId>(m_inputStrips[i]->getId())) {
203 inputStripsMatch = false;
204 break;
205 }
206 }
207 }
208
209 unsigned submasterStripCount = 0;
210
211 if (studio.amwShowAudioSubmasters)
212 submasterStripCount = studio.getBusses().size()-1;
213
214 bool submasterStripsMatch =
215 (m_submasterStrips.size() == submasterStripCount);
216
217 // If everything is in order, bail.
218 if (inputStripsMatch && submasterStripsMatch)
219 return;
220
221 // Changes to the strip counts and positions are very rare, so there is
222 // no need to be very efficient from here on out. Go for readable
223 // instead.
224
225 bool recreateLayout = false;
226
227 // If the input Strip count is wrong, fix it.
228 if (m_inputStrips.size() != instrumentIds.size()) {
229 // If we don't have enough strips
230 if (m_inputStrips.size() < instrumentIds.size()) {
231 unsigned count = instrumentIds.size() - m_inputStrips.size();
232 // Add the appropriate number.
233 for (unsigned i = 0; i < count; ++i) {
234 // Add a new one. We'll fix the ID later.
235 m_inputStrips.push_back(new AudioStrip(this));
236 }
237 } else { // We have too many input strips
238 unsigned count = m_inputStrips.size() - instrumentIds.size();
239 // Remove the appropriate number.
240 for (unsigned i = 0; i < count; ++i) {
241 // Delete and remove.
242 delete m_inputStrips.back();
243 m_inputStrips.pop_back();
244 }
245 }
246
247 recreateLayout = true;
248 }
249
250 // If the submaster Strip count is wrong, fix it.
251 if (!submasterStripsMatch) {
252 // If we don't have enough strips
253 if (m_submasterStrips.size() < submasterStripCount) {
254 unsigned count = submasterStripCount - m_submasterStrips.size();
255 // Add the appropriate number
256 for (unsigned i = 0; i < count; ++i) {
257 int id = static_cast<int>(m_submasterStrips.size()) + 1;
258 m_submasterStrips.push_back(new AudioStrip(this, id));
259 }
260 } else { // We have too many submaster strips
261 unsigned count = m_submasterStrips.size() - submasterStripCount;
262 // Remove the appropriate number
263 for (unsigned i = 0; i < count; ++i) {
264 // Delete and remove.
265 delete m_submasterStrips.back();
266 m_submasterStrips.pop_back();
267 }
268 }
269
270 recreateLayout = true;
271 }
272
273 // If either Strip count was wrong, recreate the layout.
274 if (recreateLayout) {
275 // Remove the widgets from the layout.
276 while (m_layout->takeAt(0) != nullptr) {
277 // Do nothing. Normally, we might delete the child widgets.
278 // But we're about to re-add them below.
279 }
280
281 // Add the input strips
282 for (unsigned i = 0; i < m_inputStrips.size(); ++i) {
283 m_layout->addWidget(m_inputStrips[i]);
284 }
285
286 // Add the submaster strips
287 for (unsigned i = 0; i < m_submasterStrips.size(); ++i) {
288 m_layout->addWidget(m_submasterStrips[i]);
289 }
290
291 // Add the master strip
292 m_layout->addWidget(m_masterStrip);
293 }
294
295 // Make sure the InstrumentIds are correct in each input Strip.
296 for (unsigned i = 0; i < m_inputStrips.size(); ++i) {
297 m_inputStrips[i]->setId(instrumentIds[i]);
298 }
299 }
300
updateWidgets()301 void AudioMixerWindow2::updateWidgets()
302 {
303 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
304 Studio &studio = doc->getStudio();
305
306 // Menu items.
307
308 // Update "Settings > Number of Stereo Inputs"
309 findAction(QString("inputs_%1").arg(studio.getRecordIns().size()))->
310 setChecked(true);
311
312 // Update "Settings > Number of Submasters"
313 findAction(QString("submasters_%1").arg(studio.getBusses().size()-1))->
314 setChecked(true);
315
316 // Update "Settings > Panning Law"
317 findAction(QString("panlaw_%1").arg(AudioLevel::getPanLaw()))->
318 setChecked(true);
319
320 bool visible = studio.amwShowAudioFaders;
321 QAction *action = findAction("show_audio_faders");
322 if (action)
323 action->setChecked(visible);
324
325 visible = studio.amwShowSynthFaders;
326 action = findAction("show_synth_faders");
327 if (action)
328 action->setChecked(visible);
329
330 visible = studio.amwShowAudioSubmasters;
331 action = findAction("show_audio_submasters");
332 if (action)
333 action->setChecked(visible);
334
335 visible = studio.amwShowUnassignedFaders;
336 action = findAction("show_unassigned_faders");
337 if (action)
338 action->setChecked(visible);
339
340 updateStripCounts();
341
342 // At this point, the strips match the document. We can just update them.
343
344 // Update the input strips.
345 for (unsigned i = 0; i < m_inputStrips.size(); ++i) {
346 m_inputStrips[i]->updateWidgets();
347
348 if (i < 16)
349 m_inputStrips[i]->setExternalControllerChannel(i);
350 }
351
352 // Update the submaster strips.
353 for (unsigned i = 0; i < m_submasterStrips.size(); ++i) {
354 m_submasterStrips[i]->updateWidgets();
355 }
356
357 // Update the master strip.
358 m_masterStrip->updateWidgets();
359 }
360
slotDocumentModified(bool)361 void AudioMixerWindow2::slotDocumentModified(bool /*modified*/)
362 {
363 updateWidgets();
364 }
365
366 void
slotClose()367 AudioMixerWindow2::slotClose()
368 {
369 close();
370 }
371
372 void
slotNumberOfStereoInputs()373 AudioMixerWindow2::slotNumberOfStereoInputs()
374 {
375 const QAction *action = dynamic_cast<const QAction *>(sender());
376
377 if (!action)
378 return;
379
380 QString name = action->objectName();
381
382 // Not the expected action name? Bail.
383 if (name.left(7) != "inputs_")
384 return;
385
386 // Extract the number of inputs from the action name.
387 unsigned count = name.mid(7).toUInt();
388
389 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
390 Studio &studio = doc->getStudio();
391
392 studio.setRecordInCount(count);
393
394 // Set the mapped IDs for the RecordIns.
395 // ??? Overkill? Can we do better?
396 doc->initialiseStudio();
397
398 doc->slotDocumentModified();
399 }
400
401 void
slotNumberOfSubmasters()402 AudioMixerWindow2::slotNumberOfSubmasters()
403 {
404 const QAction *action = dynamic_cast<const QAction *>(sender());
405
406 if (!action)
407 return;
408
409 QString name = action->objectName();
410
411 // Not the expected action name? Bail.
412 if (name.left(11) != "submasters_")
413 return;
414
415 // Extract the count from the name.
416 int count = name.mid(11).toInt();
417
418 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
419 Studio &studio = doc->getStudio();
420
421 // Add one for the master buss.
422 studio.setBussCount(count + 1);
423
424 doc->initialiseStudio();
425
426 doc->slotDocumentModified();
427 }
428
429 void
slotPanningLaw()430 AudioMixerWindow2::slotPanningLaw()
431 {
432 const QAction *action = dynamic_cast<const QAction *>(sender());
433
434 if (!action)
435 return;
436
437 QString name = action->objectName();
438
439 // Not the expected action name? Bail.
440 if (name.left(7) != "panlaw_")
441 return;
442
443 int panLaw = name.mid(7).toInt();
444
445 AudioLevel::setPanLaw(panLaw);
446
447 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
448
449 doc->slotDocumentModified();
450 }
451
452 void
slotShowAudioFaders()453 AudioMixerWindow2::slotShowAudioFaders()
454 {
455 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
456 Studio &studio = doc->getStudio();
457
458 studio.amwShowAudioFaders = !studio.amwShowAudioFaders;
459
460 doc->slotDocumentModified();
461 }
462
463 void
slotShowSynthFaders()464 AudioMixerWindow2::slotShowSynthFaders()
465 {
466 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
467 Studio &studio = doc->getStudio();
468
469 studio.amwShowSynthFaders = !studio.amwShowSynthFaders;
470
471 doc->slotDocumentModified();
472 }
473
474 void
slotShowAudioSubmasters()475 AudioMixerWindow2::slotShowAudioSubmasters()
476 {
477 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
478 Studio &studio = doc->getStudio();
479
480 studio.amwShowAudioSubmasters = !studio.amwShowAudioSubmasters;
481
482 doc->slotDocumentModified();
483 }
484
485 void
slotShowUnassignedFaders()486 AudioMixerWindow2::slotShowUnassignedFaders()
487 {
488 RosegardenDocument *doc = RosegardenMainWindow::self()->getDocument();
489 Studio &studio = doc->getStudio();
490
491 studio.amwShowUnassignedFaders = !studio.amwShowUnassignedFaders;
492
493 doc->slotDocumentModified();
494 }
495
496 void
slotHelp()497 AudioMixerWindow2::slotHelp()
498 {
499 // TRANSLATORS: if the manual is translated into your language, you can
500 // change the two-letter language code in this URL to point to your language
501 // version, eg. "http://rosegardenmusic.com/wiki/doc:audioMixerWindow-es" for the
502 // Spanish version. If your language doesn't yet have a translation, feel
503 // free to create one.
504 QString helpURL = tr("http://rosegardenmusic.com/wiki/doc:audioMixerWindow-en");
505 QDesktopServices::openUrl(QUrl(helpURL));
506 }
507
508 void
slotAboutRosegarden()509 AudioMixerWindow2::slotAboutRosegarden()
510 {
511 new AboutDialog(this);
512 }
513
514 void
slotExternalController(const MappedEvent * event)515 AudioMixerWindow2::slotExternalController(const MappedEvent *event)
516 {
517 //RG_DEBUG << "slotExternalController()...";
518
519 // Some window managers (e.g. GNOME) do not allow the application to
520 // change focus on the user. So, this might not work.
521 activateWindow();
522 raise();
523
524 // If this isn't a MIDI controller, bail.
525 if (event->getType() != MappedEvent::MidiController)
526 return;
527
528 unsigned channel = event->getRecordedChannel();
529 if (channel >= m_inputStrips.size())
530 return;
531
532 MidiByte controller = event->getData1();
533 MidiByte value = event->getData2();
534
535 switch (controller) {
536
537 case MIDI_CONTROLLER_VOLUME:
538 {
539 float level = AudioLevel::fader_to_dB(
540 value, 127, AudioLevel::LongFader);
541
542 m_inputStrips[channel]->faderLevelChanged(level);
543
544 break;
545 }
546
547 case MIDI_CONTROLLER_PAN:
548 {
549 m_inputStrips[channel]->panChanged(
550 static_cast<float>(AudioLevel::AudioPanD(value) - 100.0));
551
552 break;
553 }
554
555 default:
556 break;
557 }
558 }
559
560 void
slotControlChange(Instrument * instrument,int cc)561 AudioMixerWindow2::slotControlChange(Instrument *instrument, int cc)
562 {
563 InstrumentId instrumentId = instrument->getId();
564
565 // for each input strip
566 // ??? Performance: LINEAR SEARCH. std::map<id, strip> might be better.
567 for (unsigned i = 0; i < m_inputStrips.size(); ++i) {
568 // If this is the one
569 if (m_inputStrips[i]->getId() == instrumentId) {
570 m_inputStrips[i]->controlChange(cc);
571 break;
572 }
573 }
574 }
575
576 void
changeEvent(QEvent * event)577 AudioMixerWindow2::changeEvent(QEvent *event)
578 {
579 // Let baseclass handle first.
580 QWidget::changeEvent(event);
581
582 // Only handle this for Rosegarden native mode.
583 if (!ExternalController::self().isNative())
584 return;
585
586 // ??? Double updates seem to go out so we might want to be a little
587 // more picky about the event we react to.
588
589 if (event->type() != QEvent::ActivationChange)
590 return;
591
592 if (!isActiveWindow())
593 return;
594
595 ExternalController::self().activeWindow =
596 ExternalController::AudioMixer;
597
598 size_t count = m_inputStrips.size();
599 if (count > 16)
600 count = 16;
601
602 // For each strip, send vol/pan to the external controller port.
603 for (unsigned i = 0; i < count; i++)
604 m_inputStrips[i]->updateExternalController();
605 }
606
607
608 }
609