1 //=============================================================================
2 //  MuseScore
3 //  Linux Music Score Editor
4 //
5 //  Copyright (C) 2002-2016 Werner Schweer and others
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //=============================================================================
19 
20 #include "playpanel.h"
21 #include "libmscore/sig.h"
22 #include "libmscore/score.h"
23 #include "libmscore/repeatlist.h"
24 #include "seq.h"
25 #include "musescore.h"
26 #include "libmscore/measure.h"
27 #include "audio/midi/msynthesizer.h"
28 
29 
30 namespace Ms {
31 
32 //---------------------------------------------------------
33 //   PlayPanel
34 //---------------------------------------------------------
35 
PlayPanel(QWidget * parent)36 PlayPanel::PlayPanel(QWidget* parent)
37     : QDockWidget(qApp->translate("PlayPanelBase", "Play Panel"), parent)
38       {
39       cachedTickPosition = -1;
40       cachedTimePosition = -1;
41       cs                 = 0;
42       _isSpeedSliderPressed = false;
43       setupUi(this);
44       setWindowFlags(Qt::Tool);
45       setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
46       setAllowedAreas(Qt::DockWidgetAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea));
47       MuseScore::restoreGeometry(this);
48 
49       setScore(0);
50 
51       playButton->setDefaultAction(getAction("play"));
52       rewindButton->setDefaultAction(getAction("rewind"));
53       countInButton->setDefaultAction(getAction("countin"));
54       metronomeButton->setDefaultAction(getAction("metronome"));
55       loopButton->setDefaultAction(getAction("loop"));
56       loopInButton->setDefaultAction(getAction("loop-in"));
57       loopOutButton->setDefaultAction(getAction("loop-out"));
58       enablePlay = new EnablePlayForWidget(this);
59 
60       float minDecibels = synti->minGainAsDecibels;
61       float maxDecibels = synti->maxGainAsDecibels;
62       volSpinBox->setRange(minDecibels, maxDecibels);
63 
64       volumeSlider->setLog(false);
65       volumeSlider->setRange(minDecibels, maxDecibels);
66       volumeSlider->setDclickValue1(synti->defaultGainAsDecibels);
67 
68       speedSlider->setDclickValue1(100.0);
69       speedSlider->setDclickValue2(100.0);
70       speedSlider->setUseActualValue(true);
71       mgainSlider->setValue(seq->metronomeGain());
72       mgainSlider->setDclickValue1(seq->metronomeGain() - 10.75f);
73       mgainSlider->setDclickValue2(seq->metronomeGain() - 10.75f);
74 
75       volumeSlider->setDclickValue1(synti->defaultGainAsDecibels); // double click restores -40dB default
76       volumeSlider->setDclickValue2(synti->defaultGainAsDecibels);
77 
78       connect(volumeSlider, SIGNAL(valueChanged(double,int)), SLOT(volumeChanged(double,int)));
79       connect(mgainSlider,  SIGNAL(valueChanged(double,int)), SLOT(metronomeGainChanged(double,int)));
80       connect(posSlider,    SIGNAL(sliderMoved(int)),         SLOT(setPos(int)));
81       connect(speedSlider,  SIGNAL(valueChanged(double,int)), SLOT(speedChanged(double,int)));
82       connect(speedSlider,  SIGNAL(sliderPressed(int)),       SLOT(speedSliderPressed(int)));
83       connect(speedSlider,  SIGNAL(sliderReleased(int)),      SLOT(speedSliderReleased(int)));
84       connect(speedSpinBox, SIGNAL(valueChanged(int)),        SLOT(speedChanged()));
85       connect(volSpinBox,   SIGNAL(valueChanged(double)),     SLOT(volSpinBoxEdited()));
86       connect(seq,          SIGNAL(heartBeat(int,int,int)),   SLOT(heartBeat(int,int,int)));
87 
88       volLabel();
89       volSpinBoxEdited();     //update spinbox and, as a side effect, the slider with current gain value
90       }
91 
~PlayPanel()92 PlayPanel::~PlayPanel()
93       {
94       // if widget is visible, store geometry and pos into settings
95       // if widget is not visible/closed, pos is not reliable (and anyway
96       // has been stored into settings when the widget has been hidden)
97       if (isVisible()) {
98             MuseScore::saveGeometry(this);
99             }
100       }
101 
102 //---------------------------------------------------------
103 //   speedChanged
104 //---------------------------------------------------------
105 
speedChanged(double d,int)106 void PlayPanel::speedChanged(double d, int)
107       {
108       double speed = d * .01;
109       // Snap speed slider to 100% when it gets close
110       if (speed < 1.01 && speed > 0.99) {
111             speed = 1.00;
112             }
113       emit speedChanged(speed);
114       setTempo(seq->curTempo() * speed);
115       setSpeed(speed);
116       }
117 
118 //---------------------------------------------------------
119 //   speedChanged
120 //---------------------------------------------------------
121 
speedChanged()122 void PlayPanel::speedChanged()
123       {
124       double v = speedSpinBox->value();
125       speedSlider->setValue(v);
126       speedChanged(v, 0);
127       }
128 
129 //---------------------------------------------------------
130 //   closeEvent
131 //
132 //    Called when the PlayPanel is closed with its own button
133 //    but not when it is hidden with the main menu command
134 //---------------------------------------------------------
135 
closeEvent(QCloseEvent * ev)136 void PlayPanel::closeEvent(QCloseEvent* ev)
137       {
138       emit closed(false);
139       QDockWidget::closeEvent(ev);
140       }
141 
142 //---------------------------------------------------------
143 //   hideEvent
144 //
145 //    Called both when the PlayPanel is closed with its own button and
146 //    when it is hidden via the main menu command
147 //
148 //    Stores widget geometry and position into settings.
149 //---------------------------------------------------------
150 
hideEvent(QHideEvent * ev)151 void PlayPanel::hideEvent(QHideEvent* ev)
152       {
153       MuseScore::saveGeometry(this);
154       QDockWidget::hideEvent(ev);
155       }
156 
157 //---------------------------------------------------------
158 //   showEvent
159 //---------------------------------------------------------
160 
showEvent(QShowEvent * e)161 void PlayPanel::showEvent(QShowEvent* e)
162       {
163       if (e->spontaneous() && !isFloating()) {
164             QDockWidget::showEvent(e);
165             }
166       else {
167             enablePlay->showEvent(e);
168             QDockWidget::showEvent(e);
169             activateWindow();
170             setFocus();
171             }
172       }
173 
174 //---------------------------------------------------------
175 //   eventFilter
176 //---------------------------------------------------------
177 
eventFilter(QObject * obj,QEvent * e)178 bool PlayPanel::eventFilter(QObject* obj, QEvent* e)
179       {
180       if (enablePlay->eventFilter(obj, e))
181             return true;
182       return QDockWidget::eventFilter(obj, e);
183       }
184 
keyPressEvent(QKeyEvent * ev)185 void PlayPanel::keyPressEvent(QKeyEvent* ev) {
186       if (ev->key() == Qt::Key_Escape && ev->modifiers() == Qt::NoModifier) {
187             close();
188             return;
189             }
190       QDockWidget::keyPressEvent(ev);
191       }
192 
193 //---------------------------------------------------------
194 //   setScore
195 //---------------------------------------------------------
196 
setScore(Score * s)197 void PlayPanel::setScore(Score* s)
198       {
199       if (cs != 0 && cs == s)
200             return;
201       cs = s;
202       bool enable = cs != 0;
203       volumeSlider->setEnabled(enable);
204       posSlider->setEnabled(enable);
205       speedSlider->setEnabled(enable);
206       if (cs && seq && seq->canStart()) {
207             setTempo(cs->tempomap()->tempo(0));
208             setSpeed(cs->tempomap()->relTempo());
209             setEndpos(cs->repeatList().ticks());
210             Fraction tick = cs->pos(POS::CURRENT);
211             heartBeat(tick.ticks(), tick.ticks(), 0);
212             }
213       else {
214             setTempo(2.0);
215             setSpeed(1.0);
216             setEndpos(0);
217             heartBeat(0, 0, 0);
218             updatePosLabel(0);
219             }
220       update();
221       }
222 
223 //---------------------------------------------------------
224 //   setEndpos
225 //---------------------------------------------------------
226 
setEndpos(int val)227 void PlayPanel::setEndpos(int val)
228       {
229       posSlider->setRange(0, val);
230       }
231 
232 //---------------------------------------------------------
233 //   speed
234 //---------------------------------------------------------
235 
speed() const236 double PlayPanel::speed() const
237       {
238       return speedSpinBox->value() / 100.0;
239       }
240 
241 //---------------------------------------------------------
242 //   setTempo
243 //---------------------------------------------------------
244 
setTempo(double val)245 void PlayPanel::setTempo(double val)
246       {
247       tempoLabel->setText(tr("Tempo\n%1 BPM").arg(val * 60, 6, 'f', 2, QLatin1Char(' ')));
248       }
249 
250 //---------------------------------------------------------
251 //   setSpeed
252 //---------------------------------------------------------
253 
setSpeed(double speed)254 void PlayPanel::setSpeed(double speed)
255       {
256       const auto clampedRoundedVal = qBound(10, static_cast<int>((100.0 * speed) - std::remainder(100.0 * speed, 1.0)), 300);
257       speedSlider->setValue(clampedRoundedVal);
258       speedSpinBox->setValue(speedSlider->value());
259       }
260 
261 //---------------------------------------------------------
262 //   increaseSpeed
263 //---------------------------------------------------------
264 
increaseSpeed()265 void PlayPanel::increaseSpeed()
266       {
267       const auto speed = speedSpinBox->value();
268       const auto step = speedSpinBox->singleStep();
269 
270       // Increase to the next higher increment (relative to 100%).
271       setSpeed((100 + step * (((speed + ((speed >= 100) ? 0 : 1 - step) -100) / step) + 1)) / 100.0);
272       }
273 
274 //---------------------------------------------------------
275 //   decreaseSpeed
276 //---------------------------------------------------------
277 
decreaseSpeed()278 void PlayPanel::decreaseSpeed()
279       {
280       const auto speed = speedSpinBox->value();
281       const auto step = speedSpinBox->singleStep();
282 
283       // Decrease to the next lower increment (relative to 100%).
284       setSpeed((100 + step * (((speed + ((speed >= 100) ? 0 : 1 - step) - 100) / step) - 1)) / 100.0);
285       }
286 
287 //---------------------------------------------------------
288 //   resetSpeed
289 //---------------------------------------------------------
290 
resetSpeed()291 void PlayPanel::resetSpeed()
292       {
293       setSpeed(1.0);
294       }
295 
296 //---------------------------------------------------------
297 //   setGain
298 //---------------------------------------------------------
299 
setGain(float gain)300 void PlayPanel::setGain(float gain)  // respond to gainChanged() SIGNAL from MasterSynthesizer
301       {
302       Q_UNUSED(gain);
303       const QSignalBlocker blockVolumeSpinBoxSignals(volSpinBox);
304       volumeSlider->setValue(synti->gainAsDecibels());
305       volLabel();
306       }
307 
308 
309 //---------------------------------------------------------
310 //   volumeChanged
311 //---------------------------------------------------------
312 
volumeChanged(double decibels,int)313 void PlayPanel::volumeChanged(double decibels, int)
314       {
315       synti->setGainAsDecibels(decibels);
316       }
317 
318 //---------------------------------------------------------
319 //   metronomeGainChanged
320 //---------------------------------------------------------
321 
metronomeGainChanged(double val,int)322 void PlayPanel::metronomeGainChanged(double val, int)
323       {
324       emit metronomeGainChanged(val);
325       }
326 
327 //---------------------------------------------------------
328 //    setPos
329 //---------------------------------------------------------
330 
setPos(int utick)331 void PlayPanel::setPos(int utick)
332       {
333       if (!cs)
334             return;
335       if (cachedTickPosition != utick)
336             emit posChange(utick);
337       updatePosLabel(utick);
338       updateTimeLabel(cs->utick2utime(utick));
339       }
340 
341 //---------------------------------------------------------
342 //   heartBeat
343 //---------------------------------------------------------
344 
heartBeat(int,int utick,int samples)345 void PlayPanel::heartBeat(int /*tick*/, int utick, int samples)
346       {
347       if (cachedTickPosition != utick) {
348             updatePosLabel(utick);
349             posSlider->setValue(utick);
350             }
351       int sec = samples/MScore::sampleRate;
352       if (sec == cachedTimePosition)
353             return;
354       updateTimeLabel(sec);
355       }
356 
357 //---------------------------------------------------------
358 //   updateTime
359 //---------------------------------------------------------
360 
updateTimeLabel(int sec)361 void PlayPanel::updateTimeLabel(int sec)
362       {
363       cachedTimePosition = sec;
364       int m              = sec / 60;
365       sec                = sec % 60;
366       int h              = m / 60;
367       m                  = m % 60;
368 
369       // time is displayed in three separate labels
370       // this prevents jitter as width of time grows and shrinks
371       // alternative would be to use a monospaced font and a
372       // single label
373       char hourBuffer[8];
374       sprintf(hourBuffer, "%d", h);
375       hourLabel->setText(QString(hourBuffer));
376 
377       char minBuffer[8];
378       sprintf(minBuffer, "%02d", m);
379       minuteLabel->setText(QString(minBuffer));
380 
381       char secondBuffer[8];
382       sprintf(secondBuffer, "%02d", sec);
383       secondLabel->setText(QString(secondBuffer));
384 
385       }
386 
387 //---------------------------------------------------------
388 //   updatePos
389 //---------------------------------------------------------
390 
updatePosLabel(int utick)391 void PlayPanel::updatePosLabel(int utick)
392       {
393       cachedTickPosition = utick;
394       int bar = 0;
395       int beat = 0;
396       int t = 0;
397       int tick = 0;
398       if (cs) {
399             tick = cs->repeatList().utick2tick(utick);
400             cs->sigmap()->tickValues(tick, &bar, &beat, &t);
401             double tpo = cs->tempomap()->tempo(tick) * cs->tempomap()->relTempo();
402             setTempo(tpo);
403             }
404 
405       // position is displayed in two separate labels
406       // this prevents jitter as width of time grows and shrinks
407       // alternative would be to use a monospaced font and a
408       // single label
409 
410       char barBuffer[12];
411       sprintf(barBuffer, "%d", bar+1);// sprintf(barBuffer, "%03d", bar+1);
412       measureLabel->setText(QString(barBuffer));
413 
414       char beatBuffer[12];
415       sprintf(beatBuffer, "%02d", beat+1);
416       beatLabel->setText(QString(beatBuffer));
417       }
418 
419 //---------------------------------------------------------
420 //   speedSliderPressed
421 //---------------------------------------------------------
422 
speedSliderPressed(int)423 void PlayPanel::speedSliderPressed(int)
424       {
425       _isSpeedSliderPressed = true;
426       }
427 //---------------------------------------------------------
428 //   setVolume
429 //---------------------------------------------------------
430 
volLabel()431 void PlayPanel::volLabel()
432       {
433       volSpinBox->setValue(synti->gainAsDecibels());
434       volSpinBox->setSuffix(" dB");
435       }
436 
437 
volSpinBoxEdited()438 void PlayPanel::volSpinBoxEdited()
439       {
440       synti->setGainAsDecibels(volSpinBox->value());
441       volLabel();
442       }
443 
444 
445 //---------------------------------------------------------
446 //   speedSliderReleased
447 //---------------------------------------------------------
448 
speedSliderReleased(int)449 void PlayPanel::speedSliderReleased(int)
450       {
451       _isSpeedSliderPressed = false;
452       }
453 
454 //---------------------------------------------------------
455 //   changeEvent
456 //---------------------------------------------------------
457 
changeEvent(QEvent * event)458 void PlayPanel::changeEvent(QEvent *event)
459       {
460       QDockWidget::changeEvent(event);
461       if (event->type() == QEvent::LanguageChange)
462             retranslate();
463       }
464 
465 //---------------------------------------------------------
466 //   setSpeedIncrement
467 //---------------------------------------------------------
468 
setSpeedIncrement(const int speedIncrement)469 void PlayPanel::setSpeedIncrement(const int speedIncrement)
470       {
471       speedSpinBox->setSingleStep(speedIncrement);
472       }
473 }
474