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 "[TransportDialog]"
19
20 #include "TransportDialog.h"
21 #include "base/Composition.h"
22 #include "base/NotationTypes.h"
23 #include "base/RealTime.h"
24 #include "base/Profiler.h"
25 #include "misc/Debug.h"
26 #include "misc/Strings.h"
27 #include "gui/general/ThornStyle.h"
28 #include "sequencer/RosegardenSequencer.h"
29 #include "gui/application/TransportStatus.h"
30 #include "gui/application/RosegardenApplication.h"
31 #include "gui/general/MidiPitchLabel.h"
32 #include "gui/general/IconLoader.h"
33 #include "gui/studio/StudioControl.h"
34 #include "gui/widgets/Label.h"
35 #include "sound/MappedEvent.h"
36 #include "misc/ConfigGroups.h"
37
38 #include <QSettings>
39 #include <QShortcut>
40 #include <QColor>
41 #include <QByteArray>
42 #include <QDataStream>
43 #include <QFont>
44 #include <QLabel>
45 #include <QPalette>
46 #include <QPixmap>
47 #include <QPushButton>
48 #include <QString>
49 #include <QTimer>
50 #include <QWidget>
51 #include <QHBoxLayout>
52 #include <QPainter>
53 #include <QtGlobal>
54 #include <QScreen>
55
56
57 namespace // anonymous
58 {
59 QColor ledBlue(192, 216, 255);
60 }
61
62 namespace Rosegarden
63 {
64
TransportDialog(QWidget * parent)65 TransportDialog::TransportDialog(QWidget *parent):
66 QDialog(parent),
67 ui(new Ui_RosegardenTransport()),
68 //m_lcdList(),
69 //m_lcdListDefault(),
70 //m_lcdNegative(),
71 m_lastTenHours(0),
72 m_lastUnitHours(0),
73 m_lastTenMinutes(0),
74 m_lastUnitMinutes(0),
75 m_lastTenSeconds(0),
76 m_lastUnitSeconds(0),
77 m_lastTenths(0),
78 m_lastHundreths(0),
79 m_lastThousandths(0),
80 m_lastTenThousandths(0),
81 m_lastNegative(false),
82 m_lastMode(RealMode),
83 m_currentMode(RealMode),
84 m_tenHours(0),
85 m_unitHours(0),
86 m_tenMinutes(0),
87 m_unitMinutes(0),
88 m_tenSeconds(0),
89 m_unitSeconds(0),
90 m_tenths(0),
91 m_hundreths(0),
92 m_thousandths(0),
93 m_tenThousandths(0),
94 m_numerator(0),
95 m_denominator(0),
96 m_framesPerSecond(24),
97 m_bitsPerFrame(80),
98 m_midiInTimer(nullptr),
99 m_midiOutTimer(nullptr),
100 m_clearMetronomeTimer(nullptr),
101 m_enableMIDILabels(true),
102 //m_panelOpen(),
103 //m_panelClosed(),
104 m_shortcuts(nullptr),
105 m_isExpanded(true),
106 m_isBackgroundSet(false),
107 m_sampleRate(0)
108 //m_modeMap()
109 {
110 // So we can identify it in RosegardenMainWindow::awaitDialogClearance().
111 // Do not change this string:
112 setObjectName("Rosegarden Transport");
113 setWindowTitle(tr("Rosegarden Transport"));
114 setWindowIcon(IconLoader::loadPixmap("window-transport"));
115
116 // The child application area (client area in Windows-speak).
117 QFrame *frame = new QFrame(this);
118
119 ui->setupUi(frame);
120
121 resetFonts();
122
123 initModeMap();
124
125 // set the LCD frame background to black
126 //
127 QPalette lcdPalette = ui->LCDBoxFrame->palette();
128 lcdPalette.setColor(ui->LCDBoxFrame->backgroundRole(), QColor(Qt::black));
129 ui->LCDBoxFrame->setPalette(lcdPalette); // this propagates to children, but they don't autofill their background anyway.
130 ui->LCDBoxFrame->setAutoFillBackground(true);
131
132 // unset the negative sign to begin with
133 ui->NegativePixmap->clear();
134
135 // Set our toggle buttons
136 //
137 ui->PlayButton->setCheckable(true);
138 ui->RecordButton->setCheckable(true);
139
140 // Disable the loop button if JACK transport enabled, because this
141 // causes a nasty race condition, and it just seems our loops are not JACK compatible
142 // #1240039 - DMM
143 // QSettings settings ; // was: mainWindow->config()
144 // settings.beginGroup(SequencerOptionsConfigGroup);
145 // if ( qStrToBool( settings.value("jacktransport", "false" ) ) )
146 // {
147 // ui->LoopButton->setEnabled(false);
148 // }
149 // settings.endGroup();
150
151 // fix and hold the size of the dialog
152 //
153 //!!! this probably won't work -- need sizeHint() after widget is realised
154 // setMinimumSize(width(), height());
155 // setMaximumSize(width(), height());
156
157 loadPixmaps();
158
159 // Create Midi label timers
160 m_midiInTimer = new QTimer(this);
161 m_midiOutTimer = new QTimer(this);
162 m_clearMetronomeTimer = new QTimer(this);
163
164 connect(m_midiInTimer, &QTimer::timeout,
165 this, &TransportDialog::slotClearMidiInLabel);
166
167 connect(m_midiOutTimer, &QTimer::timeout,
168 this, &TransportDialog::slotClearMidiOutLabel);
169
170 connect(m_clearMetronomeTimer, &QTimer::timeout,
171 this, &TransportDialog::slotResetBackground);
172
173 ui->TimeDisplayLabel->hide();
174 ui->ToEndLabel->hide();
175
176 connect(ui->TimeDisplayButton, &QAbstractButton::clicked,
177 this, &TransportDialog::slotChangeTimeDisplay);
178
179 connect(ui->ToEndButton, &QAbstractButton::clicked,
180 this, &TransportDialog::slotChangeToEnd);
181
182 connect(ui->LoopButton, &QAbstractButton::clicked,
183 this, &TransportDialog::slotLoopButtonClicked);
184
185 connect(ui->PanelOpenButton, &QAbstractButton::clicked,
186 this, &TransportDialog::slotPanelOpenButtonClicked);
187
188 connect(ui->PanelCloseButton, &QAbstractButton::clicked,
189 this, &TransportDialog::slotPanelCloseButtonClicked);
190
191 connect(ui->PanicButton, &QAbstractButton::clicked, this, &TransportDialog::panic);
192 /*
193 const QPixmap *p = ui->PanelOpenButton->pixmap();
194 if (p) m_panelOpen = *p;
195 p = ui->PanelCloseButton->pixmap();
196 if (p) m_panelClosed = *p;
197 */
198 connect(ui->SetStartLPButton, &QAbstractButton::clicked, this, &TransportDialog::slotSetStartLoopingPointAtMarkerPos);
199 connect(ui->SetStopLPButton, &QAbstractButton::clicked, this, &TransportDialog::slotSetStopLoopingPointAtMarkerPos);
200
201 // clear labels
202 //
203 slotClearMidiInLabel();
204 slotClearMidiOutLabel();
205
206 // and by default we close the lower panel
207 //
208 // int rfh = ui->RecordingFrame->height();
209 ui->RecordingFrame->hide();
210 // setFixedSize(width(), height() - rfh);
211 // ui->PanelOpenButton->setPixmap(m_panelClosed);
212
213 // and since by default we show real time (not SMPTE), by default
214 // we hide the small colon pixmaps
215 //
216 ui->SecondColonPixmap->hide();
217 ui->HundredthColonPixmap->hide();
218
219 // We have to specify these settings in this class (copied
220 // from rosegardentransport.cpp) as we're using a specialised
221 // widgets for TempoDisplay. Ugly but works - does mean that
222 // if the rest of the Transport ever changes then this code
223 // will have to as well.
224 //
225 QPalette tempoPalette = ui->TempoDisplay->palette();
226 tempoPalette.setColor(
227 ui->TempoDisplay->foregroundRole(),
228 ledBlue);
229 ui->TempoDisplay->setPalette(tempoPalette);
230 ui->TempoDisplay->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
231
232 QPalette timeSigPalette = ui->TimeSigDisplay->palette();
233 timeSigPalette.setColor(
234 ui->TimeSigDisplay->foregroundRole(),
235 ledBlue);
236 ui->TimeSigDisplay->setPalette(timeSigPalette);
237 ui->TimeSigDisplay->setAlignment( Qt::AlignVCenter | Qt::AlignRight );
238
239 QFont localFont(ui->OutDisplay->font() );
240 localFont.setFamily( "lucida" );
241 localFont.setBold( true );
242
243 ui->TempoDisplay->setFont( localFont );
244 ui->TimeSigDisplay->setFont( localFont );
245
246 connect(ui->TempoDisplay, &Label::doubleClicked,
247 this, &TransportDialog::slotEditTempo);
248
249 //connect(ui->TempoDisplay, &Label::scrollWheel,
250 // this, &TransportDialog::scrollTempo);
251
252 connect(ui->TimeSigDisplay, &Label::doubleClicked,
253 this, &TransportDialog::slotEditTimeSignature);
254
255 // toil through the individual pixmaps
256 connect(ui->NegativePixmap, &Label::doubleClicked,
257 this, &TransportDialog::slotEditTime);
258 connect(ui->TenHoursPixmap, &Label::doubleClicked,
259 this, &TransportDialog::slotEditTime);
260 connect(ui->UnitHoursPixmap, &Label::doubleClicked,
261 this, &TransportDialog::slotEditTime);
262 connect(ui->HourColonPixmap, &Label::doubleClicked,
263 this, &TransportDialog::slotEditTime);
264 connect(ui->TenMinutesPixmap, &Label::doubleClicked,
265 this, &TransportDialog::slotEditTime);
266 connect(ui->UnitMinutesPixmap, &Label::doubleClicked,
267 this, &TransportDialog::slotEditTime);
268 connect(ui->MinuteColonPixmap, &Label::doubleClicked,
269 this, &TransportDialog::slotEditTime);
270 connect(ui->TenSecondsPixmap, &Label::doubleClicked,
271 this, &TransportDialog::slotEditTime);
272 connect(ui->UnitSecondsPixmap, &Label::doubleClicked,
273 this, &TransportDialog::slotEditTime);
274 connect(ui->SecondColonPixmap, &Label::doubleClicked,
275 this, &TransportDialog::slotEditTime);
276 connect(ui->TenthsPixmap, &Label::doubleClicked,
277 this, &TransportDialog::slotEditTime);
278 connect(ui->HundredthsPixmap, &Label::doubleClicked,
279 this, &TransportDialog::slotEditTime);
280 connect(ui->HundredthColonPixmap, &Label::doubleClicked,
281 this, &TransportDialog::slotEditTime);
282 connect(ui->TenThousandthsPixmap, &Label::doubleClicked,
283 this, &TransportDialog::slotEditTime);
284 connect(ui->ThousandthsPixmap, &Label::doubleClicked,
285 this, &TransportDialog::slotEditTime);
286
287 // shortcut object
288 //
289 m_shortcuts = new QShortcut(this);
290
291 // Note: For Thorn style, ThornStyle sets the transport's background
292 // to dark gray. See AppEventFilter::polishWidget() in
293 // ThornStyle.cpp.
294
295 loadGeo();
296
297 // Performance Testing
298
299 QSettings settings;
300 settings.beginGroup("Performance_Testing");
301
302 m_enableMIDILabels =
303 (settings.value("TransportDialog_MIDI_Labels", 1).toInt() != 0);
304
305 // Write it to the file to make it easier to find.
306 settings.setValue("TransportDialog_MIDI_Labels",
307 m_enableMIDILabels ? 1 : 0);
308
309 settings.endGroup();
310 }
311
~TransportDialog()312 TransportDialog::~TransportDialog()
313 {
314 // Only save if visible. RMW::slotUpdateTransportVisibility() has already
315 // saved if we are hidden.
316 if (isVisible())
317 saveGeo();
318 }
319
320 std::string
getCurrentModeAsString()321 TransportDialog::getCurrentModeAsString()
322 {
323 bool found = false;
324 for (std::map<std::string, TimeDisplayMode>::iterator iter = m_modeMap.begin();
325 iter != m_modeMap.end() && !found;
326 ++iter)
327 {
328 if (iter->second == m_currentMode) {
329 return iter->first;
330 }
331 }
332
333 // we shouldn't get here unless the map is not well-configured
334 RG_DEBUG << "TransportDialog::getCurrentModeAsString: could not map current mode "
335 << m_currentMode << " to string.";
336 throw Exception("could not map current mode to string.");
337 }
338
339 void
initModeMap()340 TransportDialog::initModeMap()
341 {
342 m_modeMap["RealMode"] = RealMode;
343 m_modeMap["SMPTEMode"] = SMPTEMode;
344 m_modeMap["BarMode"] = BarMode;
345 m_modeMap["BarMetronomeMode"] = BarMetronomeMode;
346 m_modeMap["FrameMode"] = FrameMode;
347 }
348
349 void
loadPixmaps()350 TransportDialog::loadPixmaps()
351 {
352 m_lcdList.clear();
353 m_lcdListDefault.clear();
354
355 for (int i = 0; i < 10; i++) {
356 m_lcdList[i] = IconLoader::loadPixmap(QString("led-%1").arg(i));
357 QImage im(m_lcdList[i].size(), QImage::Format_RGB32);
358 im.fill(0);
359 QPainter p(&im);
360 p.drawPixmap(0, 0, m_lcdList[i]);
361 m_lcdListDefault[i] = QPixmap::fromImage(im);
362 }
363
364 // Load the "negative" sign pixmap
365 //
366 m_lcdNegative = IconLoader::loadPixmap("led--");
367 }
368
369 void
resetFonts()370 TransportDialog::resetFonts()
371 {
372 resetFont(ui->TimeSigLabel);
373 resetFont(ui->TimeSigDisplay);
374 resetFont(ui->TempoLabel);
375 resetFont(ui->TempoDisplay);
376 resetFont(ui->DivisionLabel);
377 resetFont(ui->DivisionDisplay);
378 resetFont(ui->InLabel);
379 resetFont(ui->InDisplay);
380 resetFont(ui->OutLabel);
381 resetFont(ui->OutDisplay);
382 resetFont(ui->ToEndLabel);
383 resetFont(ui->TimeDisplayLabel);
384 }
385
386 void
resetFont(QWidget * w)387 TransportDialog::resetFont(QWidget *w)
388 {
389 QFont font = w->font();
390 font.setPixelSize(10);
391 w->setFont(font);
392 }
393
394 void
setSMPTEResolution(int framesPerSecond,int bitsPerFrame)395 TransportDialog::setSMPTEResolution(int framesPerSecond,
396 int bitsPerFrame)
397 {
398 m_framesPerSecond = framesPerSecond;
399 m_bitsPerFrame = bitsPerFrame;
400 }
401
402 void
getSMPTEResolution(int & framesPerSecond,int & bitsPerFrame)403 TransportDialog::getSMPTEResolution(int &framesPerSecond,
404 int &bitsPerFrame)
405 {
406 framesPerSecond = m_framesPerSecond;
407 bitsPerFrame = m_bitsPerFrame;
408 }
409
410 void
computeSampleRate()411 TransportDialog::computeSampleRate()
412 {
413 if (m_sampleRate == 0) {
414 m_sampleRate = RosegardenSequencer::getInstance()->getSampleRate();
415 }
416 }
417
418 void
cycleThroughModes()419 TransportDialog::cycleThroughModes()
420 {
421 switch (m_currentMode) {
422
423 case RealMode:
424 if (m_sampleRate > 0)
425 m_currentMode = FrameMode;
426 else
427 m_currentMode = BarMode;
428 break;
429
430 case FrameMode:
431 m_currentMode = BarMode;
432 break;
433
434 case SMPTEMode:
435 m_currentMode = BarMode;
436 break;
437
438 case BarMode:
439 m_currentMode = BarMetronomeMode;
440 break;
441
442 case BarMetronomeMode:
443 m_currentMode = RealMode;
444 break;
445 }
446 }
447
448 void
displayTime()449 TransportDialog::displayTime()
450 {
451 switch (m_currentMode) {
452 case RealMode:
453 m_clearMetronomeTimer->stop();
454 ui->TimeDisplayLabel->hide();
455 break;
456
457 case SMPTEMode:
458 m_clearMetronomeTimer->stop();
459 ui->TimeDisplayLabel->setText("SMPTE"); // DO NOT i18n
460 ui->TimeDisplayLabel->show();
461 break;
462
463 case BarMode:
464 m_clearMetronomeTimer->stop();
465 ui->TimeDisplayLabel->setText("BAR"); // DO NOT i18n
466 ui->TimeDisplayLabel->show();
467 break;
468
469 case BarMetronomeMode:
470 m_clearMetronomeTimer->setSingleShot(false);
471 m_clearMetronomeTimer->start(1700);
472 ui->TimeDisplayLabel->setText("MET"); // DO NOT i18n
473 ui->TimeDisplayLabel->show();
474 break;
475
476 case FrameMode:
477 m_clearMetronomeTimer->stop();
478 ui->TimeDisplayLabel->setText(QString("%1").arg(m_sampleRate));
479 ui->TimeDisplayLabel->show();
480 break;
481 }
482 }
483
484 void
setNewMode(const std::string & newModeAsString)485 TransportDialog::setNewMode(const std::string& newModeAsString)
486 {
487 TimeDisplayMode newMode = RealMode; // default value if not found
488
489 std::map<std::string, TimeDisplayMode>::iterator iter =
490 m_modeMap.find(newModeAsString);
491
492 if (iter != m_modeMap.end()) {
493 // value found
494 newMode = iter->second;
495 } else {
496 // don't fail: use default value set at declaration
497 }
498
499 setNewMode(newMode);
500 }
501
502 void
setNewMode(const TimeDisplayMode & newMode)503 TransportDialog::setNewMode(const TimeDisplayMode& newMode)
504 {
505 computeSampleRate();
506
507 m_currentMode = newMode;
508
509 displayTime();
510 }
511
512
513 void
slotChangeTimeDisplay()514 TransportDialog::slotChangeTimeDisplay()
515 {
516 computeSampleRate();
517
518 cycleThroughModes();
519
520 displayTime();
521 }
522
523 void
slotChangeToEnd()524 TransportDialog::slotChangeToEnd()
525 {
526 if (ui->ToEndButton->isChecked()) {
527 ui->ToEndLabel->show();
528 } else {
529 ui->ToEndLabel->hide();
530 }
531 }
532
533 bool
isShowingTimeToEnd()534 TransportDialog::isShowingTimeToEnd()
535 {
536 return ui->ToEndButton->isChecked();
537 }
538
539 void
displayRealTime(const RealTime & rt)540 TransportDialog::displayRealTime(const RealTime &rt)
541 {
542 RealTime st = rt;
543
544 slotResetBackground();
545
546 if (m_lastMode != RealMode) {
547 ui->HourColonPixmap->show();
548 ui->MinuteColonPixmap->show();
549 ui->SecondColonPixmap->hide();
550 ui->HundredthColonPixmap->hide();
551 m_lastMode = RealMode;
552 }
553
554 // If time is negative then reverse the time and set the minus flag
555 //
556 if (st < RealTime::zeroTime) {
557 st = RealTime::zeroTime - st;
558 if (!m_lastNegative) {
559 ui->NegativePixmap->setPixmap(m_lcdNegative);
560 m_lastNegative = true;
561 }
562 } else // don't show the flag
563 {
564 if (m_lastNegative) {
565 ui->NegativePixmap->clear();
566 m_lastNegative = false;
567 }
568 }
569
570 m_tenThousandths = ( st.usec() / 100 ) % 10;
571 m_thousandths = ( st.usec() / 1000 ) % 10;
572 m_hundreths = ( st.usec() / 10000 ) % 10;
573 m_tenths = ( st.usec() / 100000 ) % 10;
574
575 m_unitSeconds = ( st.sec ) % 10;
576 m_tenSeconds = ( st.sec / 10 ) % 6;
577
578 m_unitMinutes = ( st.sec / 60 ) % 10;
579 m_tenMinutes = ( st.sec / 600 ) % 6;
580
581 m_unitHours = ( st.sec / 3600 ) % 10;
582 m_tenHours = (st.sec / 36000 ) % 10;
583
584 updateTimeDisplay();
585 }
586
587 void
displayFrameTime(const RealTime & rt)588 TransportDialog::displayFrameTime(const RealTime &rt)
589 {
590 RealTime st = rt;
591
592 slotResetBackground();
593
594 if (m_lastMode != FrameMode) {
595 ui->HourColonPixmap->hide();
596 ui->MinuteColonPixmap->hide();
597 ui->SecondColonPixmap->hide();
598 ui->HundredthColonPixmap->hide();
599 m_lastMode = FrameMode;
600 }
601
602 // If time is negative then reverse the time and set the minus flag
603 //
604 if (st < RealTime::zeroTime) {
605 st = RealTime::zeroTime - st;
606 if (!m_lastNegative) {
607 ui->NegativePixmap->setPixmap(m_lcdNegative);
608 m_lastNegative = true;
609 }
610 } else // don't show the flag
611 {
612 if (m_lastNegative) {
613 ui->NegativePixmap->clear();
614 m_lastNegative = false;
615 }
616 }
617
618 long frame = RealTime::realTime2Frame(st, m_sampleRate);
619
620 m_tenThousandths = frame % 10;
621 frame /= 10;
622 m_thousandths = frame % 10;
623 frame /= 10;
624 m_hundreths = frame % 10;
625 frame /= 10;
626 m_tenths = frame % 10;
627 frame /= 10;
628 m_unitSeconds = frame % 10;
629 frame /= 10;
630 m_tenSeconds = frame % 10;
631 frame /= 10;
632 m_unitMinutes = frame % 10;
633 frame /= 10;
634 m_tenMinutes = frame % 10;
635 frame /= 10;
636 m_unitHours = frame % 10;
637 frame /= 10;
638 m_tenHours = frame % 10;
639 frame /= 10;
640
641 updateTimeDisplay();
642 }
643
644 void
displaySMPTETime(const RealTime & rt)645 TransportDialog::displaySMPTETime(const RealTime &rt)
646 {
647 RealTime st = rt;
648
649 slotResetBackground();
650
651 if (m_lastMode != SMPTEMode) {
652 ui->HourColonPixmap->show();
653 ui->MinuteColonPixmap->show();
654 ui->SecondColonPixmap->show();
655 ui->HundredthColonPixmap->show();
656 m_lastMode = SMPTEMode;
657 }
658
659 // If time is negative then reverse the time and set the minus flag
660 //
661 if (st < RealTime::zeroTime) {
662 st = RealTime::zeroTime - st;
663 if (!m_lastNegative) {
664 ui->NegativePixmap->setPixmap(m_lcdNegative);
665 m_lastNegative = true;
666 }
667 } else // don't show the flag
668 {
669 if (m_lastNegative) {
670 ui->NegativePixmap->clear();
671 m_lastNegative = false;
672 }
673 }
674
675 m_tenThousandths =
676 (( st.usec() * m_framesPerSecond * m_bitsPerFrame) / 1000000 ) % 10;
677 m_thousandths =
678 (( st.usec() * m_framesPerSecond * m_bitsPerFrame) / 10000000 ) %
679 (m_bitsPerFrame / 10);
680 m_hundreths =
681 (( st.usec() * m_framesPerSecond) / 1000000 ) % 10;
682 m_tenths =
683 (( st.usec() * m_framesPerSecond) / 10000000 ) % 10;
684
685 m_unitSeconds = ( st.sec ) % 10;
686 m_tenSeconds = ( st.sec / 10 ) % 6;
687
688 m_unitMinutes = ( st.sec / 60 ) % 10;
689 m_tenMinutes = ( st.sec / 600 ) % 6;
690
691 m_unitHours = ( st.sec / 3600 ) % 10;
692 m_tenHours = ( st.sec / 36000 ) % 10;
693
694 updateTimeDisplay();
695 }
696
697 void
displayBarTime(int bar,int beat,int unit)698 TransportDialog::displayBarTime(int bar, int beat, int unit)
699 {
700 if (m_lastMode != BarMode) {
701 ui->HourColonPixmap->hide();
702 ui->MinuteColonPixmap->show();
703 ui->SecondColonPixmap->hide();
704 ui->HundredthColonPixmap->hide();
705 m_lastMode = BarMode;
706 }
707
708 // If time is negative then reverse the time and set the minus flag
709 //
710 if (bar < 0) {
711 bar = -bar;
712 if (!m_lastNegative) {
713 ui->NegativePixmap->setPixmap(m_lcdNegative);
714 m_lastNegative = true;
715 }
716 } else // don't show the flag
717 {
718 if (m_lastNegative) {
719 ui->NegativePixmap->clear();
720 m_lastNegative = false;
721 }
722 }
723
724 if (m_currentMode == BarMetronomeMode && unit < 2) {
725 if (beat == 1) {
726 setBackgroundColor(Qt::red);
727 } else {
728 setBackgroundColor(Qt::cyan);
729 }
730 } else {
731 slotResetBackground();
732 }
733
734 m_tenThousandths = ( unit ) % 10;
735 m_thousandths = ( unit / 10 ) % 10;
736 m_hundreths = ( unit / 100 ) % 10;
737 m_tenths = ( unit / 1000 ) % 10;
738
739 if (m_tenths == 0) {
740 m_tenths = -1;
741 if (m_hundreths == 0) {
742 m_hundreths = -1;
743 if (m_thousandths == 0) {
744 m_thousandths = -1;
745 }
746 }
747 }
748
749 m_unitSeconds = ( beat ) % 10;
750 m_tenSeconds = ( beat / 10 ) % 6;
751
752 if (m_tenSeconds == 0) {
753 m_tenSeconds = -1;
754 }
755
756 m_unitMinutes = ( bar ) % 10;
757 m_tenMinutes = ( bar / 10 ) % 10;
758
759 m_unitHours = ( bar / 100 ) % 10;
760 m_tenHours = ( bar / 1000 ) % 10;
761
762 if (m_tenHours == 0) {
763 m_tenHours = -1;
764 if (m_unitHours == 0) {
765 m_unitHours = -1;
766 if (m_tenMinutes == 0) {
767 m_tenMinutes = -1;
768 }
769 }
770 }
771
772 updateTimeDisplay();
773 }
774
775 void
updateTimeDisplay()776 TransportDialog::updateTimeDisplay()
777 {
778 //Profiler profiler("TransportDialog::updateTimeDisplay");
779
780 #define UPDATE(NEW,OLD,WIDGET) \
781 if (NEW != OLD) { \
782 if (NEW < 0) { \
783 ui->WIDGET->clear(); \
784 } else if (!m_isBackgroundSet) { \
785 ui->WIDGET->setPixmap(m_lcdListDefault[NEW]); \
786 } else { \
787 ui->WIDGET->setPixmap(m_lcdList[NEW]); \
788 } \
789 OLD = NEW; \
790 }
791
792 UPDATE(m_tenThousandths, m_lastTenThousandths, TenThousandthsPixmap);
793 UPDATE(m_thousandths, m_lastThousandths, ThousandthsPixmap);
794 UPDATE(m_hundreths, m_lastHundreths, HundredthsPixmap);
795 UPDATE(m_tenths, m_lastTenths, TenthsPixmap);
796 UPDATE(m_unitSeconds, m_lastUnitSeconds, UnitSecondsPixmap);
797 UPDATE(m_tenSeconds, m_lastTenSeconds, TenSecondsPixmap);
798 UPDATE(m_unitMinutes, m_lastUnitMinutes, UnitMinutesPixmap);
799 UPDATE(m_tenMinutes, m_lastTenMinutes, TenMinutesPixmap);
800 UPDATE(m_unitHours, m_lastUnitHours, UnitHoursPixmap);
801 UPDATE(m_tenHours, m_lastTenHours, TenHoursPixmap);
802 }
803
804 void
setTimeSignature(const TimeSignature & timeSig)805 TransportDialog::setTimeSignature(const TimeSignature &timeSig)
806 {
807 int numerator = timeSig.getNumerator();
808 int denominator = timeSig.getDenominator();
809 if (m_numerator == numerator && m_denominator == denominator)
810 return ;
811 m_numerator = numerator;
812 m_denominator = denominator;
813
814 const QString timeSigString =
815 QString::asprintf("%d/%d", numerator, denominator);
816 ui->TimeSigDisplay->setText(timeSigString);
817 }
818
819 void
slotMidiInLabel(const MappedEvent * mE)820 TransportDialog::slotMidiInLabel(const MappedEvent *mE)
821 {
822 // If MIDI label updates have been turned off, bail.
823 if (!m_enableMIDILabels)
824 return;
825
826 Q_CHECK_PTR(mE);
827
828 switch (mE->getType()) {
829 case MappedEvent::MidiNote:
830 case MappedEvent::MidiNoteOneShot:
831 {
832 // don't do anything if we've got an effective NOTE OFF
833 //
834 if (mE->getVelocity() == 0)
835 return ;
836
837 MidiPitchLabel mPL(mE->getPitch());
838 ui->InDisplay->setText
839 (mPL.getQString() +
840 QString(" %1").arg(mE->getVelocity()));
841 }
842 break;
843
844 case MappedEvent::MidiPitchBend:
845 ui->InDisplay->setText(tr("PITCH WHEEL"));
846 break;
847
848 case MappedEvent::MidiController:
849 ui->InDisplay->setText(tr("CONTROLLER"));
850 break;
851
852 case MappedEvent::MidiProgramChange:
853 ui->InDisplay->setText(tr("PROG CHNGE"));
854 break;
855
856 case MappedEvent::MidiKeyPressure:
857 case MappedEvent::MidiChannelPressure:
858 ui->InDisplay->setText(tr("PRESSURE"));
859 break;
860
861 case MappedEvent::MidiSystemMessage:
862 ui->InDisplay->setText(tr("SYS MESSAGE"));
863 break;
864
865 // Pacify compiler warnings about missed cases.
866 case MappedEvent::InvalidMappedEvent:
867 case MappedEvent::Audio:
868 case MappedEvent::AudioCancel:
869 case MappedEvent::AudioLevel:
870 case MappedEvent::AudioStopped:
871 case MappedEvent::AudioGeneratePreview:
872 case MappedEvent::Marker:
873 case MappedEvent::SystemUpdateInstruments:
874 case MappedEvent::SystemJackTransport:
875 case MappedEvent::SystemMMCTransport:
876 case MappedEvent::SystemMIDIClock:
877 case MappedEvent::SystemMetronomeDevice:
878 case MappedEvent::SystemAudioPortCounts:
879 case MappedEvent::SystemAudioPorts:
880 case MappedEvent::SystemFailure:
881 case MappedEvent::Panic:
882 case MappedEvent::SystemMTCTransport:
883 case MappedEvent::SystemMIDISyncAuto:
884 case MappedEvent::SystemAudioFileFormat:
885 case MappedEvent::TimeSignature:
886 case MappedEvent::Tempo:
887 case MappedEvent::Text:
888 case MappedEvent::KeySignature:
889 default: // do nothing
890 return ;
891 }
892
893 // Reset the timer if it's already running
894 //
895 if (m_midiInTimer->isActive())
896 m_midiInTimer->stop();
897
898 // 1.5 second timeout for MIDI event
899 //
900 m_midiInTimer->setSingleShot(true);
901 m_midiInTimer->start(1500);
902 }
903
904 void
slotClearMidiInLabel()905 TransportDialog::slotClearMidiInLabel()
906 {
907 ui->InDisplay->setText(tr("NO EVENTS"));
908
909 // also, just to be sure:
910 slotResetBackground();
911 }
912
913 void
slotMidiOutLabel(const MappedEvent * mE)914 TransportDialog::slotMidiOutLabel(const MappedEvent *mE)
915 {
916 // If MIDI label updates have been turned off, bail.
917 if (!m_enableMIDILabels)
918 return;
919
920 Q_CHECK_PTR(mE);
921
922 switch (mE->getType()) {
923 case MappedEvent::MidiNote:
924 case MappedEvent::MidiNoteOneShot:
925 {
926 MidiPitchLabel mPL(mE->getPitch());
927 ui->OutDisplay->setText
928 (mPL.getQString() +
929 QString(" %1").arg(mE->getVelocity()));
930 }
931 break;
932
933 case MappedEvent::MidiPitchBend:
934 ui->OutDisplay->setText(tr("PITCH WHEEL"));
935 break;
936
937 case MappedEvent::MidiController:
938 ui->OutDisplay->setText(tr("CONTROLLER"));
939 break;
940
941 case MappedEvent::MidiProgramChange:
942 ui->OutDisplay->setText(tr("PROG CHNGE"));
943 break;
944
945 case MappedEvent::MidiKeyPressure:
946 case MappedEvent::MidiChannelPressure:
947 ui->OutDisplay->setText(tr("PRESSURE"));
948 break;
949
950 case MappedEvent::MidiSystemMessage:
951 ui->OutDisplay->setText(tr("SYS MESSAGE"));
952 break;
953
954 // Pacify compiler warnings about missed cases.
955 case MappedEvent::InvalidMappedEvent:
956 case MappedEvent::Audio:
957 case MappedEvent::AudioCancel:
958 case MappedEvent::AudioLevel:
959 case MappedEvent::AudioStopped:
960 case MappedEvent::AudioGeneratePreview:
961 case MappedEvent::Marker:
962 case MappedEvent::SystemUpdateInstruments:
963 case MappedEvent::SystemJackTransport:
964 case MappedEvent::SystemMMCTransport:
965 case MappedEvent::SystemMIDIClock:
966 case MappedEvent::SystemMetronomeDevice:
967 case MappedEvent::SystemAudioPortCounts:
968 case MappedEvent::SystemAudioPorts:
969 case MappedEvent::SystemFailure:
970 case MappedEvent::Panic:
971 case MappedEvent::SystemMTCTransport:
972 case MappedEvent::SystemMIDISyncAuto:
973 case MappedEvent::SystemAudioFileFormat:
974 case MappedEvent::TimeSignature:
975 case MappedEvent::Tempo:
976 case MappedEvent::Text:
977 case MappedEvent::KeySignature:
978 default: // do nothing
979 return ;
980 }
981
982 // Reset the timer if it's already running
983 //
984 if (m_midiOutTimer->isActive())
985 m_midiOutTimer->stop();
986
987 // 200 millisecond timeout
988 //
989 m_midiOutTimer->setSingleShot(true);
990 m_midiOutTimer->start(200);
991 }
992
993 void
slotClearMidiOutLabel()994 TransportDialog::slotClearMidiOutLabel()
995 {
996 ui->OutDisplay->setText(tr("NO EVENTS"));
997 }
998
999 void
closeEvent(QCloseEvent *)1000 TransportDialog::closeEvent(QCloseEvent * /*e*/)
1001 {
1002 emit closed();
1003
1004 // We don't actually close. Instead we hide.
1005 // RosegardenMainWindow::slotCloseTransport() handles that and keeping
1006 // the menu in sync.
1007 //e->accept();
1008 }
1009
1010 void
slotLoopButtonClicked()1011 TransportDialog::slotLoopButtonClicked()
1012 {
1013 // disable if JACK transport has been set #1240039 - DMM
1014 // QSettings settings;
1015 // settings.beginGroup( SequencerOptionsConfigGroup );
1016 //
1017 // if ( qStrToBool( settings.value("jacktransport", "false" ) ) )
1018 // {
1019 // //!!! - this will fail silently
1020 // ui->LoopButton->setEnabled(false);
1021 // ui->LoopButton->setOn(false);
1022 // return;
1023 // }
1024 // settings.endGroup();
1025
1026 if (ui->LoopButton->isChecked()) {
1027 emit setLoop();
1028 } else {
1029 emit unsetLoop();
1030 }
1031 }
1032
1033 void
slotSetStartLoopingPointAtMarkerPos()1034 TransportDialog::slotSetStartLoopingPointAtMarkerPos()
1035 {
1036 emit setLoopStartTime();
1037 }
1038
1039 void
slotSetStopLoopingPointAtMarkerPos()1040 TransportDialog::slotSetStopLoopingPointAtMarkerPos()
1041 {
1042 emit setLoopStopTime();
1043 }
1044
slotTempoChanged(tempoT tempo)1045 void TransportDialog::slotTempoChanged(tempoT tempo)
1046 {
1047 const QString tempoString =
1048 QString::asprintf("%4.3f", Composition::getTempoQpm(tempo));
1049 ui->TempoDisplay->setText(tempoString);
1050 }
1051
slotPlaying(bool checked)1052 void TransportDialog::slotPlaying(bool checked)
1053 {
1054 ui->PlayButton->setChecked(checked);
1055 }
1056
slotRecording(bool checked)1057 void TransportDialog::slotRecording(bool checked)
1058 {
1059 ui->RecordButton->setChecked(checked);
1060 }
1061
slotMetronomeActivated(bool checked)1062 void TransportDialog::slotMetronomeActivated(bool checked)
1063 {
1064 ui->MetronomeButton->setChecked(checked);
1065 }
1066
1067 void
slotPanelOpenButtonClicked()1068 TransportDialog::slotPanelOpenButtonClicked()
1069 {
1070 // int rfh = ui->RecordingFrame->height();
1071
1072 if (ui->RecordingFrame->isVisible()) {
1073 ui->RecordingFrame->hide();
1074 // setFixedSize(width(), height() - rfh);
1075 // ui->PanelOpenButton->setPixmap(m_panelClosed);
1076 // adjustSize();
1077 setFixedSize(416, 87);
1078 // cerr << "size hint: " << sizeHint().width() << "x" << sizeHint().height() << endl;
1079 // setFixedSize(sizeHint());
1080 // setMinimumSize(sizeHint());
1081 m_isExpanded = false;
1082 } else {
1083 // setFixedSize(width(), height() + rfh);
1084 setFixedSize(416, 132);
1085 ui->RecordingFrame->show();
1086 // ui->PanelOpenButton->setPixmap(m_panelOpen);
1087 // adjustSize();
1088 // cerr << "size hint: " << sizeHint().width() << "x" << sizeHint().height() << endl;
1089 // setFixedSize(sizeHint());
1090 // setMinimumSize(sizeHint());
1091 m_isExpanded = true;
1092 }
1093 }
1094
1095 void
slotPanelCloseButtonClicked()1096 TransportDialog::slotPanelCloseButtonClicked()
1097 {
1098 // int rfh = ui->RecordingFrame->height();
1099
1100 if (ui->RecordingFrame->isVisible()) {
1101 ui->RecordingFrame->hide();
1102 // setFixedSize(width(), height() - rfh);
1103 // ui->PanelOpenButton->setPixmap(m_panelClosed);
1104 setFixedSize(416, 87);
1105 // adjustSize();
1106 // cerr << "size hint: " << sizeHint().width() << "x" << sizeHint().height() << endl;
1107 // setFixedSize(sizeHint());
1108 // setMinimumSize(sizeHint());
1109 m_isExpanded = false;
1110 }
1111 }
1112
1113 bool
isExpanded()1114 TransportDialog::isExpanded()
1115 {
1116 return m_isExpanded;
1117 }
1118
1119 void
slotEditTempo()1120 TransportDialog::slotEditTempo()
1121 {
1122 emit editTempo(this);
1123 }
1124
1125 void
slotEditTimeSignature()1126 TransportDialog::slotEditTimeSignature()
1127 {
1128 emit editTimeSignature(this);
1129 }
1130
1131 void
slotEditTime()1132 TransportDialog::slotEditTime()
1133 {
1134 emit editTransportTime(this);
1135 }
1136
1137 void
setBackgroundColor(QColor color)1138 TransportDialog::setBackgroundColor(QColor color)
1139 {
1140 QPalette palette = ui->LCDBoxFrame->palette();
1141 palette.setColor(ui->LCDBoxFrame->backgroundRole(), color);
1142 ui->LCDBoxFrame->setPalette(palette);
1143
1144 m_isBackgroundSet = true;
1145 }
1146
1147 void
slotResetBackground()1148 TransportDialog::slotResetBackground()
1149 {
1150 if (m_isBackgroundSet) {
1151 setBackgroundColor(Qt::black);
1152 }
1153 m_isBackgroundSet = false;
1154 }
1155
saveGeo()1156 void TransportDialog::saveGeo()
1157 {
1158 QSettings settings;
1159 settings.beginGroup(WindowGeometryConfigGroup);
1160 settings.setValue("Transport_Geometry", saveGeometry());
1161 }
1162
loadGeo()1163 void TransportDialog::loadGeo()
1164 {
1165 QSettings settings;
1166 settings.beginGroup(WindowGeometryConfigGroup);
1167 restoreGeometry(settings.value("Transport_Geometry").toByteArray());
1168 }
1169
1170
1171 }
1172