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