annotate widgets/UnitConverter.cpp @ 1510:872873aa6463

Merge
author Chris Cannam
date Tue, 17 Sep 2019 12:50:58 +0100
parents a34a2a25907c
children
rev   line source
Chris@885 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@885 2
Chris@885 3 /*
Chris@885 4 Sonic Visualiser
Chris@885 5 An audio file viewer and annotation editor.
Chris@885 6 Centre for Digital Music, Queen Mary, University of London.
Chris@885 7
Chris@885 8 This program is free software; you can redistribute it and/or
Chris@885 9 modify it under the terms of the GNU General Public License as
Chris@885 10 published by the Free Software Foundation; either version 2 of the
Chris@885 11 License, or (at your option) any later version. See the file
Chris@885 12 COPYING included with this distribution for more information.
Chris@885 13 */
Chris@885 14
Chris@885 15 #include "UnitConverter.h"
Chris@885 16
Chris@885 17 #include <QSpinBox>
Chris@885 18 #include <QComboBox>
Chris@885 19 #include <QDoubleSpinBox>
Chris@885 20 #include <QLabel>
Chris@885 21 #include <QDialogButtonBox>
Chris@885 22 #include <QGridLayout>
Chris@889 23 #include <QTabWidget>
Chris@885 24
Chris@885 25 #include "base/Debug.h"
Chris@885 26 #include "base/Pitch.h"
Chris@888 27 #include "base/Preferences.h"
Chris@885 28
Chris@885 29 using namespace std;
Chris@885 30
Chris@886 31 static QString pianoNotes[] = {
Chris@886 32 "C", "C# / Db", "D", "D# / Eb", "E",
Chris@886 33 "F", "F# / Gb", "G", "G# / Ab", "A", "A# / Bb", "B"
Chris@886 34 };
Chris@886 35
Chris@885 36 UnitConverter::UnitConverter(QWidget *parent) :
Chris@885 37 QDialog(parent)
Chris@885 38 {
Chris@889 39 QGridLayout *maingrid = new QGridLayout;
Chris@889 40 setLayout(maingrid);
Chris@889 41
Chris@889 42 QTabWidget *tabs = new QTabWidget;
Chris@889 43 maingrid->addWidget(tabs, 0, 0);
Chris@889 44
Chris@889 45 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close);
Chris@889 46 maingrid->addWidget(bb, 1, 0);
Chris@889 47 connect(bb, SIGNAL(rejected()), this, SLOT(close()));
Chris@889 48
Chris@889 49 QFrame *frame = new QFrame;
Chris@889 50 tabs->addTab(frame, tr("Pitch"));
Chris@889 51
Chris@885 52 QGridLayout *grid = new QGridLayout;
Chris@889 53 frame->setLayout(grid);
Chris@885 54
Chris@889 55 m_freq = new QDoubleSpinBox;
Chris@889 56 m_freq->setSuffix(QString(" Hz"));
Chris@889 57 m_freq->setDecimals(6);
Chris@889 58 m_freq->setMinimum(1e-3);
Chris@889 59 m_freq->setMaximum(1e6);
Chris@889 60 m_freq->setValue(440);
Chris@889 61 connect(m_freq, SIGNAL(valueChanged(double)),
Chris@1266 62 this, SLOT(freqChanged()));
Chris@885 63
Chris@888 64 // The min and max range values for all the remaining controls are
Chris@888 65 // determined by the min and max Hz above
Chris@888 66
Chris@885 67 m_midi = new QSpinBox;
Chris@888 68 m_midi->setMinimum(-156);
Chris@888 69 m_midi->setMaximum(203);
Chris@885 70 connect(m_midi, SIGNAL(valueChanged(int)),
Chris@1266 71 this, SLOT(midiChanged()));
Chris@885 72
Chris@885 73 m_note = new QComboBox;
Chris@886 74 for (int i = 0; i < 12; ++i) {
Chris@1266 75 m_note->addItem(pianoNotes[i]);
Chris@886 76 }
Chris@886 77 connect(m_note, SIGNAL(currentIndexChanged(int)),
Chris@1266 78 this, SLOT(noteChanged()));
Chris@885 79
Chris@885 80 m_octave = new QSpinBox;
Chris@888 81 m_octave->setMinimum(-14);
Chris@888 82 m_octave->setMaximum(15);
Chris@886 83 connect(m_octave, SIGNAL(valueChanged(int)),
Chris@1266 84 this, SLOT(octaveChanged()));
Chris@885 85
Chris@885 86 m_cents = new QDoubleSpinBox;
Chris@885 87 m_cents->setSuffix(tr(" cents"));
Chris@885 88 m_cents->setDecimals(4);
Chris@885 89 m_cents->setMinimum(-50);
Chris@885 90 m_cents->setMaximum(50);
Chris@885 91 connect(m_cents, SIGNAL(valueChanged(double)),
Chris@1266 92 this, SLOT(centsChanged()));
Chris@885 93
Chris@893 94 int row = 0;
Chris@893 95
Chris@893 96 grid->addWidget(new QLabel(tr("In 12-tone Equal Temperament:")), row, 0, 1, 9);
Chris@893 97
Chris@893 98 ++row;
Chris@893 99
Chris@893 100 grid->setRowMinimumHeight(row, 8);
Chris@893 101
Chris@893 102 ++row;
Chris@886 103
Chris@892 104 grid->addWidget(m_freq, row, 0, 2, 1, Qt::AlignRight | Qt::AlignVCenter);
Chris@892 105 grid->addWidget(new QLabel(tr("=")), row, 1, 2, 1, Qt::AlignHCenter | Qt::AlignVCenter);
Chris@885 106
Chris@892 107 grid->addWidget(new QLabel(tr("+")), row, 7, 2, 1, Qt::AlignHCenter | Qt::AlignVCenter);
Chris@892 108 grid->addWidget(m_cents, row, 8, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
Chris@885 109
Chris@886 110 grid->addWidget(new QLabel(tr("Piano note")), row, 2, 1, 2);
Chris@886 111 grid->addWidget(m_note, row, 4);
Chris@886 112 grid->addWidget(new QLabel(tr("in octave")), row, 5);
Chris@886 113 grid->addWidget(m_octave, row, 6);
Chris@886 114
Chris@886 115 ++row;
Chris@886 116
Chris@888 117 grid->addWidget(new QLabel(tr("MIDI pitch")), row, 2, 1, 2);
Chris@886 118 grid->addWidget(m_midi, row, 4);
Chris@886 119
Chris@886 120 ++row;
Chris@885 121
Chris@893 122 grid->setRowStretch(row, 20);
Chris@893 123 grid->setRowMinimumHeight(row, 8);
Chris@893 124
Chris@893 125 ++row;
Chris@893 126
Chris@891 127 m_pitchPrefsLabel = new QLabel;
Chris@891 128 grid->addWidget(m_pitchPrefsLabel, row, 0, 1, 9);
Chris@888 129
Chris@888 130 ++row;
Chris@888 131
Chris@888 132 grid->addWidget
Chris@1266 133 (new QLabel(tr("Note that only pitches in the range 0 to 127 are valid "
Chris@1266 134 "in the MIDI protocol.")),
Chris@1266 135 row, 0, 1, 9);
Chris@888 136
Chris@888 137 ++row;
Chris@891 138
Chris@891 139 frame = new QFrame;
Chris@891 140 tabs->addTab(frame, tr("Tempo"));
Chris@891 141
Chris@891 142 grid = new QGridLayout;
Chris@891 143 frame->setLayout(grid);
Chris@885 144
Chris@892 145 m_samples = new QDoubleSpinBox;
Chris@892 146 m_samples->setSuffix(QString(" samples"));
Chris@893 147 m_samples->setDecimals(2);
Chris@893 148 m_samples->setMinimum(1);
Chris@893 149 m_samples->setMaximum(1e8);
Chris@892 150 m_samples->setValue(22050);
Chris@892 151 connect(m_samples, SIGNAL(valueChanged(double)),
Chris@1266 152 this, SLOT(samplesChanged()));
Chris@892 153
Chris@892 154 m_period = new QDoubleSpinBox;
Chris@892 155 m_period->setSuffix(QString(" ms"));
Chris@892 156 m_period->setDecimals(4);
Chris@893 157 m_period->setMinimum(1e-3);
Chris@893 158 m_period->setMaximum(100000);
Chris@892 159 m_period->setValue(500);
Chris@892 160 connect(m_period, SIGNAL(valueChanged(double)),
Chris@1266 161 this, SLOT(periodChanged()));
Chris@892 162
Chris@892 163 m_bpm = new QDoubleSpinBox;
Chris@892 164 m_bpm->setSuffix(QString(" bpm"));
Chris@892 165 m_bpm->setDecimals(4);
Chris@892 166 m_bpm->setMinimum(0.1);
Chris@892 167 m_bpm->setMaximum(1e6);
Chris@892 168 m_bpm->setValue(120);
Chris@892 169 connect(m_bpm, SIGNAL(valueChanged(double)),
Chris@1266 170 this, SLOT(bpmChanged()));
Chris@892 171
Chris@892 172 m_tempofreq = new QDoubleSpinBox;
Chris@892 173 m_tempofreq->setSuffix(QString(" beats/sec"));
Chris@892 174 m_tempofreq->setDecimals(4);
Chris@893 175 m_tempofreq->setMinimum(1e-3);
Chris@893 176 m_tempofreq->setMaximum(1e5);
Chris@892 177 m_tempofreq->setValue(0.5);
Chris@893 178
Chris@892 179 connect(m_tempofreq, SIGNAL(valueChanged(double)),
Chris@1266 180 this, SLOT(tempofreqChanged()));
Chris@1266 181
Chris@892 182 m_samplerate = new QComboBox;
Chris@892 183 QList<int> rates;
Chris@892 184 rates << 8000;
Chris@892 185 for (int i = 1; i <= 16; i *= 2) {
Chris@1266 186 rates << 11025 * i << 12000 * i;
Chris@892 187 }
Chris@892 188 foreach (int r, rates) {
Chris@1266 189 m_samplerate->addItem(QString("%1 Hz").arg(r));
Chris@892 190 }
Chris@893 191 connect(m_samplerate, SIGNAL(currentIndexChanged(int)),
Chris@1266 192 this, SLOT(samplerateChanged()));
Chris@892 193 m_samplerate->setCurrentText("44100 Hz");
Chris@892 194
Chris@891 195 connect(Preferences::getInstance(),
Chris@1266 196 SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
Chris@1266 197 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
Chris@892 198
Chris@893 199 row = 0;
Chris@892 200
Chris@893 201 grid->setRowStretch(row, 20);
Chris@893 202 grid->setRowMinimumHeight(row, 8);
Chris@893 203
Chris@893 204 ++row;
Chris@893 205
Chris@893 206 grid->addWidget(new QLabel(tr("Beat period")), row, 0, 2, 1, Qt::AlignVCenter);
Chris@893 207 grid->addWidget(m_period, row, 1);
Chris@893 208 grid->addWidget(new QLabel(tr("=")), row, 2, 2, 1, Qt::AlignVCenter);
Chris@893 209
Chris@893 210 grid->addWidget(m_tempofreq, row, 3);
Chris@893 211
Chris@893 212 grid->addWidget(new QLabel(tr("at")), row, 4, 2, 1, Qt::AlignVCenter);
Chris@893 213 grid->addWidget(m_samplerate, row, 5, 2, 1, Qt::AlignVCenter);
Chris@893 214
Chris@893 215 ++row;
Chris@893 216
Chris@892 217 grid->addWidget(m_samples, row, 1);
Chris@892 218 grid->addWidget(m_bpm, row, 3);
Chris@892 219
Chris@892 220 ++row;
Chris@891 221
Chris@893 222 grid->setRowStretch(row, 20);
Chris@893 223 grid->setRowMinimumHeight(row, 8);
Chris@892 224
Chris@889 225 updatePitchesFromFreq();
Chris@891 226 updatePitchPrefsLabel();
Chris@891 227 updateTempiFromSamples();
Chris@885 228 }
Chris@885 229
Chris@885 230 UnitConverter::~UnitConverter()
Chris@885 231 {
Chris@885 232 }
Chris@885 233
Chris@885 234 void
Chris@893 235 UnitConverter::setTo(QSpinBox *box, int value)
Chris@893 236 {
Chris@893 237 box->blockSignals(true);
Chris@893 238 if (value < box->minimum() || value > box->maximum()) {
Chris@1266 239 QPalette p;
Chris@1266 240 p.setColor(QPalette::Text, Qt::red);
Chris@1266 241 box->setPalette(p);
Chris@893 242 } else {
Chris@1266 243 box->setPalette(QPalette());
Chris@893 244 }
Chris@893 245 box->setValue(value);
Chris@893 246 box->blockSignals(false);
Chris@893 247 }
Chris@893 248
Chris@893 249 void
Chris@893 250 UnitConverter::setTo(QDoubleSpinBox *box, double value)
Chris@893 251 {
Chris@893 252 box->blockSignals(true);
Chris@893 253 if (value < box->minimum() || value > box->maximum()) {
Chris@1266 254 QPalette p;
Chris@1266 255 p.setColor(QPalette::Text, Qt::red);
Chris@1266 256 box->setPalette(p);
Chris@893 257 } else {
Chris@1266 258 box->setPalette(QPalette());
Chris@893 259 }
Chris@893 260 box->setValue(value);
Chris@893 261 box->blockSignals(false);
Chris@893 262 }
Chris@893 263
Chris@893 264 void
Chris@891 265 UnitConverter::preferenceChanged(PropertyContainer::PropertyName)
Chris@891 266 {
Chris@891 267 updatePitchesFromFreq();
Chris@891 268 updatePitchPrefsLabel();
Chris@891 269 }
Chris@891 270
Chris@891 271 void
Chris@891 272 UnitConverter::updatePitchPrefsLabel()
Chris@891 273 {
Chris@891 274 m_pitchPrefsLabel->setText
Chris@1266 275 (tr("With concert-A tuning frequency at %1 Hz, and "
Chris@1266 276 "middle C residing in octave %2.\n"
Chris@1266 277 "(These can be changed in the application preferences.)")
Chris@1266 278 .arg(Preferences::getInstance()->getTuningFrequency())
Chris@1266 279 .arg(Preferences::getInstance()->getOctaveOfMiddleC()));
Chris@891 280 }
Chris@891 281
Chris@891 282 void
Chris@893 283 UnitConverter::freqChanged()
Chris@885 284 {
Chris@889 285 updatePitchesFromFreq();
Chris@885 286 }
Chris@885 287
Chris@885 288 void
Chris@893 289 UnitConverter::midiChanged()
Chris@885 290 {
Chris@889 291 double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value());
Chris@889 292 m_freq->setValue(freq);
Chris@885 293 }
Chris@885 294
Chris@885 295 void
Chris@893 296 UnitConverter::noteChanged()
Chris@885 297 {
Chris@887 298 int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(),
Chris@1266 299 m_octave->value());
Chris@889 300 double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value());
Chris@889 301 m_freq->setValue(freq);
Chris@885 302 }
Chris@885 303
Chris@885 304 void
Chris@893 305 UnitConverter::octaveChanged()
Chris@885 306 {
Chris@888 307 int pitch = Pitch::getPitchForNoteAndOctave(m_note->currentIndex(),
Chris@1266 308 m_octave->value());
Chris@889 309 double freq = Pitch::getFrequencyForPitch(pitch, m_cents->value());
Chris@889 310 m_freq->setValue(freq);
Chris@885 311 }
Chris@885 312
Chris@885 313 void
Chris@893 314 UnitConverter::centsChanged()
Chris@885 315 {
Chris@889 316 double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value());
Chris@889 317 m_freq->setValue(freq);
Chris@885 318 }
Chris@885 319
Chris@885 320 void
Chris@889 321 UnitConverter::updatePitchesFromFreq()
Chris@885 322 {
Chris@892 323 double cents = 0;
Chris@889 324 int pitch = Pitch::getPitchForFrequency(m_freq->value(), &cents);
Chris@887 325 int note, octave;
Chris@887 326 Pitch::getNoteAndOctaveForPitch(pitch, note, octave);
Chris@885 327
Chris@1158 328 // cerr << "pitch " << pitch << " note " << note << " octave " << octave << " cents " << cents << endl;
Chris@893 329
Chris@893 330 setTo(m_midi, pitch);
Chris@893 331 setTo(m_cents, cents);
Chris@893 332 setTo(m_octave, octave);
Chris@893 333
Chris@887 334 m_note->blockSignals(true);
Chris@887 335 m_note->setCurrentIndex(note);
Chris@887 336 m_note->blockSignals(false);
Chris@885 337 }
Chris@885 338
Chris@891 339 void
Chris@893 340 UnitConverter::samplesChanged()
Chris@892 341 {
Chris@892 342 updateTempiFromSamples();
Chris@892 343 }
Chris@892 344
Chris@892 345 void
Chris@893 346 UnitConverter::periodChanged()
Chris@892 347 {
Chris@892 348 double rate = getSampleRate();
Chris@892 349 double sec = m_period->value() / 1000.0;
Chris@892 350 double samples = rate * sec;
Chris@892 351 m_samples->setValue(samples);
Chris@892 352 }
Chris@892 353
Chris@892 354 void
Chris@893 355 UnitConverter::bpmChanged()
Chris@892 356 {
Chris@892 357 double rate = getSampleRate();
Chris@892 358 double sec = 60.0 / m_bpm->value();
Chris@892 359 double samples = rate * sec;
Chris@892 360 m_samples->setValue(samples);
Chris@892 361 }
Chris@892 362
Chris@892 363 void
Chris@893 364 UnitConverter::tempofreqChanged()
Chris@892 365 {
Chris@892 366 double rate = getSampleRate();
Chris@892 367 double samples = rate / m_tempofreq->value();
Chris@892 368 m_samples->setValue(samples);
Chris@892 369 }
Chris@892 370
Chris@892 371 void
Chris@893 372 UnitConverter::samplerateChanged()
Chris@892 373 {
Chris@893 374 // Preserve the beat period in seconds, here, not in samples
Chris@893 375 periodChanged();
Chris@892 376 }
Chris@892 377
Chris@892 378 double
Chris@892 379 UnitConverter::getSampleRate()
Chris@892 380 {
Chris@892 381 return double(atoi(m_samplerate->currentText().toLocal8Bit().data()));
Chris@892 382 }
Chris@892 383
Chris@892 384 void
Chris@891 385 UnitConverter::updateTempiFromSamples()
Chris@891 386 {
Chris@892 387 double samples = m_samples->value();
Chris@892 388 double rate = getSampleRate();
Chris@892 389
Chris@1158 390 // cerr << samples << " samples at rate " << rate << endl;
Chris@892 391
Chris@892 392 double sec = samples / rate;
Chris@892 393 double hz = rate / samples;
Chris@892 394 double bpm = 60.0 / sec;
Chris@892 395
Chris@893 396 setTo(m_bpm, bpm);
Chris@893 397 setTo(m_period, sec * 1000.0);
Chris@893 398 setTo(m_tempofreq, hz);
Chris@891 399 }
Chris@885 400
Chris@885 401