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