summaryrefslogtreecommitdiffstats
path: root/src/gui/dialogs/TempoDialog.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/dialogs/TempoDialog.cpp')
-rw-r--r--src/gui/dialogs/TempoDialog.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/src/gui/dialogs/TempoDialog.cpp b/src/gui/dialogs/TempoDialog.cpp
new file mode 100644
index 0000000..3896fde
--- /dev/null
+++ b/src/gui/dialogs/TempoDialog.cpp
@@ -0,0 +1,475 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "TempoDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/widgets/TimeWidget.h"
+#include "gui/widgets/HSpinBox.h"
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TempoDialog::TempoDialog(QWidget *parent, RosegardenGUIDoc *doc,
+ bool timeEditable):
+ KDialogBase(parent, 0, true, i18n("Insert Tempo Change"), Ok | Cancel | Help),
+ m_doc(doc),
+ m_tempoTime(0)
+{
+ setHelp("tempo");
+
+ QVBox *vbox = makeVBoxMainWidget();
+ QGroupBox *groupBox = new QGroupBox(1, Horizontal, i18n("Tempo"), vbox);
+
+ QFrame *frame = new QFrame(groupBox);
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 5, 5);
+
+ // Set tempo
+ layout->addWidget(new QLabel(i18n("New tempo:"), frame), 0, 1);
+ m_tempoValueSpinBox = new HSpinBox(frame, 0, 100000, 0.0, 1000.0, 5);
+ layout->addWidget(m_tempoValueSpinBox, 0, 2);
+
+ connect(m_tempoValueSpinBox, SIGNAL(valueChanged(const QString &)),
+ SLOT(slotTempoChanged(const QString &)));
+
+ m_tempoTap= new QPushButton(i18n("Tap"), frame);
+ layout->addWidget(m_tempoTap, 0, 3);
+ connect(m_tempoTap, SIGNAL(clicked()), SLOT(slotTapClicked()));
+
+
+ m_tempoConstant = new QRadioButton(i18n("Tempo is fixed until the following tempo change"), frame);
+ m_tempoRampToNext = new QRadioButton(i18n("Tempo ramps to the following tempo"), frame);
+ m_tempoRampToTarget = new QRadioButton(i18n("Tempo ramps to:"), frame);
+
+ // m_tempoTargetCheckBox = new QCheckBox(i18n("Ramping to:"), frame);
+ m_tempoTargetSpinBox = new HSpinBox(frame, 0, 100000, 0.0, 1000.0, 5);
+
+ // layout->addMultiCellWidget(m_tempoTargetCheckBox, 1, 1, 0, 1, AlignRight);
+ // layout->addWidget(m_tempoTargetSpinBox, 1, 2);
+
+ layout->addMultiCellWidget(m_tempoConstant, 1, 1, 1, 2);
+ layout->addMultiCellWidget(m_tempoRampToNext, 2, 2, 1, 2);
+ layout->addWidget(m_tempoRampToTarget, 3, 1);
+ layout->addWidget(m_tempoTargetSpinBox, 3, 2);
+
+ // connect(m_tempoTargetCheckBox, SIGNAL(clicked()),
+ // SLOT(slotTargetCheckBoxClicked()));
+ connect(m_tempoConstant, SIGNAL(clicked()),
+ SLOT(slotTempoConstantClicked()));
+ connect(m_tempoRampToNext, SIGNAL(clicked()),
+ SLOT(slotTempoRampToNextClicked()));
+ connect(m_tempoRampToTarget, SIGNAL(clicked()),
+ SLOT(slotTempoRampToTargetClicked()));
+ connect(m_tempoTargetSpinBox, SIGNAL(valueChanged(const QString &)),
+ SLOT(slotTargetChanged(const QString &)));
+
+ m_tempoBeatLabel = new QLabel(frame);
+ layout->addWidget(m_tempoBeatLabel, 0, 4);
+
+ m_tempoBeat = new QLabel(frame);
+ layout->addWidget(m_tempoBeat, 0, 5);
+
+ m_tempoBeatsPerMinute = new QLabel(frame);
+ layout->addWidget(m_tempoBeatsPerMinute, 0, 6);
+
+ m_timeEditor = 0;
+
+ if (timeEditable) {
+ m_timeEditor = new TimeWidget
+ (i18n("Time of tempo change"),
+ vbox, &m_doc->getComposition(), 0, true);
+ populateTempo();
+ return ;
+ }
+
+ // Scope Box
+ QButtonGroup *scopeGroup = new QButtonGroup(1, Horizontal,
+ i18n("Scope"), vbox);
+
+// new QLabel(scopeBox);
+
+ QVBox *scopeBox = new QVBox(scopeGroup);
+
+ scopeBox->setSpacing(5);
+ scopeBox->setMargin(5);
+
+ QHBox *currentBox = new QHBox(scopeBox);
+ new QLabel(i18n("The pointer is currently at "), currentBox);
+ m_tempoTimeLabel = new QLabel(currentBox);
+ m_tempoBarLabel = new QLabel(currentBox);
+ QLabel *spare = new QLabel(currentBox);
+ currentBox->setStretchFactor(spare, 20);
+
+ m_tempoStatusLabel = new QLabel(scopeBox);
+
+// new QLabel(scopeBox);
+
+ QHBox *changeWhereBox = new QHBox(scopeBox);
+ spare = new QLabel(" ", changeWhereBox);
+ QVBox *changeWhereVBox = new QVBox(changeWhereBox);
+ changeWhereBox->setStretchFactor(changeWhereVBox, 20);
+
+ m_tempoChangeHere = new QRadioButton
+ (i18n("Apply this tempo from here onwards"),
+ changeWhereVBox);
+
+ m_tempoChangeBefore = new QRadioButton
+ (i18n("Replace the last tempo change"),
+ changeWhereVBox);
+ m_tempoChangeBeforeAt = new QLabel(changeWhereVBox);
+ m_tempoChangeBeforeAt->hide();
+
+ m_tempoChangeStartOfBar = new QRadioButton
+ (i18n("Apply this tempo from the start of this bar"), changeWhereVBox);
+
+ m_tempoChangeGlobal = new QRadioButton
+ (i18n("Apply this tempo to the whole composition"), changeWhereVBox);
+
+ QHBox *optionHBox = new QHBox(changeWhereVBox);
+ new QLabel(" ", optionHBox);
+ m_defaultBox = new QCheckBox
+ (i18n("Also make this the default tempo"), optionHBox);
+ spare = new QLabel(optionHBox);
+ optionHBox->setStretchFactor(spare, 20);
+
+// new QLabel(scopeBox);
+
+ connect(m_tempoChangeHere, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+ connect(m_tempoChangeBefore, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+ connect(m_tempoChangeStartOfBar, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+ connect(m_tempoChangeGlobal, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+
+ m_tempoChangeHere->setChecked(true);
+
+ // disable initially
+ m_defaultBox->setEnabled(false);
+
+ populateTempo();
+}
+
+TempoDialog::~TempoDialog()
+{}
+
+void
+TempoDialog::setTempoPosition(timeT time)
+{
+ m_tempoTime = time;
+ populateTempo();
+}
+
+void
+TempoDialog::populateTempo()
+{
+ Composition &comp = m_doc->getComposition();
+ tempoT tempo = comp.getTempoAtTime(m_tempoTime);
+ std::pair<bool, tempoT> ramping(false, tempo);
+
+ int tempoChangeNo = comp.getTempoChangeNumberAt(m_tempoTime);
+ if (tempoChangeNo >= 0) {
+ tempo = comp.getTempoChange(tempoChangeNo).second;
+ ramping = comp.getTempoRamping(tempoChangeNo, false);
+ }
+
+ m_tempoValueSpinBox->setValue(tempo);
+
+ if (ramping.first) {
+ if (ramping.second) {
+ m_tempoTargetSpinBox->setEnabled(true);
+ m_tempoTargetSpinBox->setValue(ramping.second);
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToNext->setChecked(false);
+ m_tempoRampToTarget->setChecked(true);
+ } else {
+ ramping = comp.getTempoRamping(tempoChangeNo, true);
+ m_tempoTargetSpinBox->setEnabled(false);
+ m_tempoTargetSpinBox->setValue(ramping.second);
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToNext->setChecked(true);
+ m_tempoRampToTarget->setChecked(false);
+ }
+ } else {
+ m_tempoTargetSpinBox->setEnabled(false);
+ m_tempoTargetSpinBox->setValue(tempo);
+ m_tempoConstant->setChecked(true);
+ m_tempoRampToNext->setChecked(false);
+ m_tempoRampToTarget->setChecked(false);
+ }
+
+ // m_tempoTargetCheckBox->setChecked(ramping.first);
+ m_tempoTargetSpinBox->setEnabled(ramping.first);
+
+ updateBeatLabels(comp.getTempoQpm(tempo));
+
+ if (m_timeEditor) {
+ m_timeEditor->slotSetTime(m_tempoTime);
+ return ;
+ }
+
+ RealTime tempoTime = comp.getElapsedRealTime(m_tempoTime);
+ QString milliSeconds;
+ milliSeconds.sprintf("%03d", tempoTime.msec());
+ m_tempoTimeLabel->setText(i18n("%1.%2 s,").arg(tempoTime.sec)
+ .arg(milliSeconds));
+
+ int barNo = comp.getBarNumber(m_tempoTime);
+ if (comp.getBarStart(barNo) == m_tempoTime) {
+ m_tempoBarLabel->setText
+ (i18n("at the start of measure %1.").arg(barNo + 1));
+ m_tempoChangeStartOfBar->setEnabled(false);
+ } else {
+ m_tempoBarLabel->setText(
+ i18n("in the middle of measure %1.").arg(barNo + 1));
+ m_tempoChangeStartOfBar->setEnabled(true);
+ }
+
+ m_tempoChangeBefore->setEnabled(false);
+ m_tempoChangeBeforeAt->setEnabled(false);
+
+ bool havePrecedingTempo = false;
+
+ if (tempoChangeNo >= 0) {
+
+ timeT lastTempoTime = comp.getTempoChange(tempoChangeNo).first;
+ if (lastTempoTime < m_tempoTime) {
+
+ RealTime lastRT = comp.getElapsedRealTime(lastTempoTime);
+ QString lastms;
+ lastms.sprintf("%03d", lastRT.msec());
+ int lastBar = comp.getBarNumber(lastTempoTime);
+ m_tempoChangeBeforeAt->setText
+ (i18n(" (at %1.%2 s, in measure %3)").arg(lastRT.sec)
+ .arg(lastms).arg(lastBar + 1));
+ m_tempoChangeBeforeAt->show();
+
+ m_tempoChangeBefore->setEnabled(true);
+ m_tempoChangeBeforeAt->setEnabled(true);
+
+ havePrecedingTempo = true;
+ }
+ }
+
+ if (comp.getTempoChangeCount() > 0) {
+
+ if (havePrecedingTempo) {
+ m_tempoStatusLabel->hide();
+ } else {
+ m_tempoStatusLabel->setText
+ (i18n("There are no preceding tempo changes."));
+ }
+
+ m_tempoChangeGlobal->setEnabled(true);
+
+ } else {
+
+ m_tempoStatusLabel->setText
+ (i18n("There are no other tempo changes."));
+
+ m_tempoChangeGlobal->setEnabled(false);
+ }
+
+ m_defaultBox->setEnabled(false);
+}
+
+void
+TempoDialog::updateBeatLabels(double qpm)
+{
+ Composition &comp = m_doc->getComposition();
+
+ // If the time signature's beat is not a crotchet, need to show
+ // bpm separately
+
+ timeT beat = comp.getTimeSignatureAt(m_tempoTime).getBeatDuration();
+ if (beat == Note(Note::Crotchet).getDuration()) {
+ m_tempoBeatLabel->setText(i18n(" bpm"));
+ m_tempoBeatLabel->show();
+ m_tempoBeat->hide();
+ m_tempoBeatsPerMinute->hide();
+ } else {
+ // m_tempoBeatLabel->setText(" (");
+ m_tempoBeatLabel->setText(" ");
+
+ timeT error = 0;
+ m_tempoBeat->setPixmap(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(beat, error)));
+ m_tempoBeat->setMaximumWidth(25);
+ if (error)
+ m_tempoBeat->setPixmap(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap
+ ("menu-no-note")));
+
+ m_tempoBeatsPerMinute->setText
+ // (QString("= %1 )").arg
+ (QString("= %1 ").arg
+ (int(qpm * Note(Note::Crotchet).getDuration() / beat)));
+ m_tempoBeatLabel->show();
+ m_tempoBeat->show();
+ m_tempoBeatsPerMinute->show();
+ }
+}
+
+void
+TempoDialog::slotTempoChanged(const QString &)
+{
+ updateBeatLabels(double(m_tempoValueSpinBox->valuef()));
+}
+
+void
+TempoDialog::slotTargetChanged(const QString &)
+{
+ //...
+}
+
+void
+TempoDialog::slotTempoConstantClicked()
+{
+ m_tempoRampToNext->setChecked(false);
+ m_tempoRampToTarget->setChecked(false);
+ m_tempoTargetSpinBox->setEnabled(false);
+}
+
+void
+TempoDialog::slotTempoRampToNextClicked()
+{
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToTarget->setChecked(false);
+ m_tempoTargetSpinBox->setEnabled(false);
+}
+
+void
+TempoDialog::slotTempoRampToTargetClicked()
+{
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToNext->setChecked(false);
+ m_tempoTargetSpinBox->setEnabled(true);
+}
+
+void
+TempoDialog::slotActionChanged()
+{
+ m_defaultBox->setEnabled(m_tempoChangeGlobal->isChecked());
+}
+
+void
+TempoDialog::slotOk()
+{
+ tempoT tempo = m_tempoValueSpinBox->value();
+ RG_DEBUG << "Tempo is " << tempo << endl;
+
+ tempoT target = -1;
+ if (m_tempoRampToNext->isChecked()) {
+ target = 0;
+ } else if (m_tempoRampToTarget->isChecked()) {
+ target = m_tempoTargetSpinBox->value();
+ }
+
+ RG_DEBUG << "Target is " << target << endl;
+
+ if (m_timeEditor) {
+
+ emit changeTempo(m_timeEditor->getTime(),
+ tempo,
+ target,
+ AddTempo);
+
+ } else {
+
+ TempoDialogAction action = AddTempo;
+
+ if (m_tempoChangeBefore->isChecked()) {
+ action = ReplaceTempo;
+ } else if (m_tempoChangeStartOfBar->isChecked()) {
+ action = AddTempoAtBarStart;
+ } else if (m_tempoChangeGlobal->isChecked()) {
+ action = GlobalTempo;
+ if (m_defaultBox->isChecked()) {
+ action = GlobalTempoWithDefault;
+ }
+ }
+
+ emit changeTempo(m_tempoTime,
+ tempo,
+ target,
+ action);
+ }
+
+ KDialogBase::slotOk();
+}
+
+void
+TempoDialog::slotTapClicked()
+{
+ QTime now = QTime::currentTime();
+
+ if (m_tapMinusOne != QTime()) {
+
+ int ms1 = m_tapMinusOne.msecsTo(now);
+
+ if (ms1 < 10000) {
+
+ int msec = ms1;
+
+ if (m_tapMinusTwo != QTime()) {
+ int ms2 = m_tapMinusTwo.msecsTo(m_tapMinusOne);
+ if (ms2 < 10000) {
+ msec = (ms1 + ms2) / 2;
+ }
+ }
+
+ int bpm = 60000 / msec;
+ m_tempoValueSpinBox->setValue(bpm * 100000);
+ }
+ }
+
+ m_tapMinusTwo = m_tapMinusOne;
+ m_tapMinusOne = now;
+}
+
+
+}
+
+#include "TempoDialog.moc"