You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
14 KiB
476 lines
14 KiB
/* -*- 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 <tqlayout.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 <tqbuttongroup.h>
|
|
#include <tqcheckbox.h>
|
|
#include <tqframe.h>
|
|
#include <tqgroupbox.h>
|
|
#include <tqhbox.h>
|
|
#include <tqlabel.h>
|
|
#include <tqradiobutton.h>
|
|
#include <tqstring.h>
|
|
#include <tqvbox.h>
|
|
#include <tqwidget.h>
|
|
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
TempoDialog::TempoDialog(TQWidget *parent, RosegardenGUIDoc *doc,
|
|
bool timeEditable):
|
|
KDialogBase(parent, 0, true, i18n("Insert Tempo Change"), Ok | Cancel | Help),
|
|
m_doc(doc),
|
|
m_tempoTime(0)
|
|
{
|
|
setHelp("tempo");
|
|
|
|
TQVBox *vbox = makeVBoxMainWidget();
|
|
TQGroupBox *groupBox = new TQGroupBox(1, Qt::Horizontal, i18n("Tempo"), vbox);
|
|
|
|
TQFrame *frame = new TQFrame(groupBox);
|
|
TQGridLayout *tqlayout = new TQGridLayout(frame, 4, 3, 5, 5);
|
|
|
|
// Set tempo
|
|
tqlayout->addWidget(new TQLabel(i18n("New tempo:"), frame), 0, 1);
|
|
m_tempoValueSpinBox = new HSpinBox(frame, 0, 100000, 0.0, 1000.0, 5);
|
|
tqlayout->addWidget(m_tempoValueSpinBox, 0, 2);
|
|
|
|
connect(m_tempoValueSpinBox, TQT_SIGNAL(valueChanged(const TQString &)),
|
|
TQT_SLOT(slotTempoChanged(const TQString &)));
|
|
|
|
m_tempoTap= new TQPushButton(i18n("Tap"), frame);
|
|
tqlayout->addWidget(m_tempoTap, 0, 3);
|
|
connect(m_tempoTap, TQT_SIGNAL(clicked()), TQT_SLOT(slotTapClicked()));
|
|
|
|
|
|
m_tempoConstant = new TQRadioButton(i18n("Tempo is fixed until the following tempo change"), frame);
|
|
m_tempoRampToNext = new TQRadioButton(i18n("Tempo ramps to the following tempo"), frame);
|
|
m_tempoRampToTarget = new TQRadioButton(i18n("Tempo ramps to:"), frame);
|
|
|
|
// m_tempoTargetCheckBox = new TQCheckBox(i18n("Ramping to:"), frame);
|
|
m_tempoTargetSpinBox = new HSpinBox(frame, 0, 100000, 0.0, 1000.0, 5);
|
|
|
|
// tqlayout->addMultiCellWidget(m_tempoTargetCheckBox, 1, 1, 0, 1, AlignRight);
|
|
// tqlayout->addWidget(m_tempoTargetSpinBox, 1, 2);
|
|
|
|
tqlayout->addMultiCellWidget(m_tempoConstant, 1, 1, 1, 2);
|
|
tqlayout->addMultiCellWidget(m_tempoRampToNext, 2, 2, 1, 2);
|
|
tqlayout->addWidget(m_tempoRampToTarget, 3, 1);
|
|
tqlayout->addWidget(m_tempoTargetSpinBox, 3, 2);
|
|
|
|
// connect(m_tempoTargetCheckBox, TQT_SIGNAL(clicked()),
|
|
// TQT_SLOT(slotTargetCheckBoxClicked()));
|
|
connect(m_tempoConstant, TQT_SIGNAL(clicked()),
|
|
TQT_SLOT(slotTempoConstantClicked()));
|
|
connect(m_tempoRampToNext, TQT_SIGNAL(clicked()),
|
|
TQT_SLOT(slotTempoRampToNextClicked()));
|
|
connect(m_tempoRampToTarget, TQT_SIGNAL(clicked()),
|
|
TQT_SLOT(slotTempoRampToTargetClicked()));
|
|
connect(m_tempoTargetSpinBox, TQT_SIGNAL(valueChanged(const TQString &)),
|
|
TQT_SLOT(slotTargetChanged(const TQString &)));
|
|
|
|
m_tempoBeatLabel = new TQLabel(frame);
|
|
tqlayout->addWidget(m_tempoBeatLabel, 0, 4);
|
|
|
|
m_tempoBeat = new TQLabel(frame);
|
|
tqlayout->addWidget(m_tempoBeat, 0, 5);
|
|
|
|
m_tempoBeatsPerMinute = new TQLabel(frame);
|
|
tqlayout->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
|
|
TQButtonGroup *scopeGroup = new TQButtonGroup(1, Qt::Horizontal,
|
|
i18n("Scope"), vbox);
|
|
|
|
// new TQLabel(scopeBox);
|
|
|
|
TQVBox *scopeBox = new TQVBox(scopeGroup);
|
|
|
|
scopeBox->setSpacing(5);
|
|
scopeBox->setMargin(5);
|
|
|
|
TQHBox *currentBox = new TQHBox(scopeBox);
|
|
new TQLabel(i18n("The pointer is currently at "), currentBox);
|
|
m_tempoTimeLabel = new TQLabel(currentBox);
|
|
m_tempoBarLabel = new TQLabel(currentBox);
|
|
TQLabel *spare = new TQLabel(currentBox);
|
|
currentBox->setStretchFactor(spare, 20);
|
|
|
|
m_tempoStatusLabel = new TQLabel(scopeBox);
|
|
|
|
// new TQLabel(scopeBox);
|
|
|
|
TQHBox *changeWhereBox = new TQHBox(scopeBox);
|
|
spare = new TQLabel(" ", changeWhereBox);
|
|
TQVBox *changeWhereVBox = new TQVBox(changeWhereBox);
|
|
changeWhereBox->setStretchFactor(changeWhereVBox, 20);
|
|
|
|
m_tempoChangeHere = new TQRadioButton
|
|
(i18n("Apply this tempo from here onwards"),
|
|
changeWhereVBox);
|
|
|
|
m_tempoChangeBefore = new TQRadioButton
|
|
(i18n("Replace the last tempo change"),
|
|
changeWhereVBox);
|
|
m_tempoChangeBeforeAt = new TQLabel(changeWhereVBox);
|
|
m_tempoChangeBeforeAt->hide();
|
|
|
|
m_tempoChangeStartOfBar = new TQRadioButton
|
|
(i18n("Apply this tempo from the start of this bar"), changeWhereVBox);
|
|
|
|
m_tempoChangeGlobal = new TQRadioButton
|
|
(i18n("Apply this tempo to the whole composition"), changeWhereVBox);
|
|
|
|
TQHBox *optionHBox = new TQHBox(changeWhereVBox);
|
|
new TQLabel(" ", optionHBox);
|
|
m_defaultBox = new TQCheckBox
|
|
(i18n("Also make this the default tempo"), optionHBox);
|
|
spare = new TQLabel(optionHBox);
|
|
optionHBox->setStretchFactor(spare, 20);
|
|
|
|
// new TQLabel(scopeBox);
|
|
|
|
connect(m_tempoChangeHere, TQT_SIGNAL(clicked()),
|
|
TQT_SLOT(slotActionChanged()));
|
|
connect(m_tempoChangeBefore, TQT_SIGNAL(clicked()),
|
|
TQT_SLOT(slotActionChanged()));
|
|
connect(m_tempoChangeStartOfBar, TQT_SIGNAL(clicked()),
|
|
TQT_SLOT(slotActionChanged()));
|
|
connect(m_tempoChangeGlobal, TQT_SIGNAL(clicked()),
|
|
TQT_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);
|
|
TQString milliSeconds;
|
|
milliSeconds.sprintf("%03d", tempoTime.msec());
|
|
m_tempoTimeLabel->setText(i18n("%1.%2 s,").tqarg(tempoTime.sec)
|
|
.tqarg(milliSeconds));
|
|
|
|
int barNo = comp.getBarNumber(m_tempoTime);
|
|
if (comp.getBarStart(barNo) == m_tempoTime) {
|
|
m_tempoBarLabel->setText
|
|
(i18n("at the start of measure %1.").tqarg(barNo + 1));
|
|
m_tempoChangeStartOfBar->setEnabled(false);
|
|
} else {
|
|
m_tempoBarLabel->setText(
|
|
i18n("in the middle of measure %1.").tqarg(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);
|
|
TQString lastms;
|
|
lastms.sprintf("%03d", lastRT.msec());
|
|
int lastBar = comp.getBarNumber(lastTempoTime);
|
|
m_tempoChangeBeforeAt->setText
|
|
(i18n(" (at %1.%2 s, in measure %3)").tqarg(lastRT.sec)
|
|
.tqarg(lastms).tqarg(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::toTQPixmap
|
|
(NotePixmapFactory::makeNoteMenuPixmap(beat, error)));
|
|
m_tempoBeat->setMaximumWidth(25);
|
|
if (error)
|
|
m_tempoBeat->setPixmap(NotePixmapFactory::toTQPixmap
|
|
(NotePixmapFactory::makeToolbarPixmap
|
|
("menu-no-note")));
|
|
|
|
m_tempoBeatsPerMinute->setText
|
|
// (TQString("= %1 )").arg
|
|
(TQString("= %1 ").arg
|
|
(int(qpm * Note(Note::Crotchet).getDuration() / beat)));
|
|
m_tempoBeatLabel->show();
|
|
m_tempoBeat->show();
|
|
m_tempoBeatsPerMinute->show();
|
|
}
|
|
}
|
|
|
|
void
|
|
TempoDialog::slotTempoChanged(const TQString &)
|
|
{
|
|
updateBeatLabels(double(m_tempoValueSpinBox->valuef()));
|
|
}
|
|
|
|
void
|
|
TempoDialog::slotTargetChanged(const TQString &)
|
|
{
|
|
//...
|
|
}
|
|
|
|
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()
|
|
{
|
|
TQTime now = TQTime::currentTime();
|
|
|
|
if (m_tapMinusOne != TQTime()) {
|
|
|
|
int ms1 = m_tapMinusOne.msecsTo(now);
|
|
|
|
if (ms1 < 10000) {
|
|
|
|
int msec = ms1;
|
|
|
|
if (m_tapMinusTwo != TQTime()) {
|
|
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"
|