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