comparison widgets/UnitConverter.cpp @ 898:2be9753651c8 cxx11

Merge from default branch
author Chris Cannam
date Mon, 09 Feb 2015 10:31:07 +0000
parents 78ae34f388f6
children 8e4b90aeefaa
comparison
equal deleted inserted replaced
879:899c4d7e7e63 898:2be9753651c8
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
225 updatePitchesFromFreq();
226 updatePitchPrefsLabel();
227 updateTempiFromSamples();
228 }
229
230 UnitConverter::~UnitConverter()
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 {
267 updatePitchesFromFreq();
268 updatePitchPrefsLabel();
269 }
270
271 void
272 UnitConverter::updatePitchPrefsLabel()
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
283 UnitConverter::freqChanged()
284 {
285 updatePitchesFromFreq();
286 }
287
288 void
289 UnitConverter::midiChanged()
290 {
291 double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value());
292 m_freq->setValue(freq);
293 }
294
295 void
296 UnitConverter::noteChanged()
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
305 UnitConverter::octaveChanged()
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
314 UnitConverter::centsChanged()
315 {
316 double freq = Pitch::getFrequencyForPitch(m_midi->value(), m_cents->value());
317 m_freq->setValue(freq);
318 }
319
320 void
321 UnitConverter::updatePitchesFromFreq()
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
340 UnitConverter::samplesChanged()
341 {
342 updateTempiFromSamples();
343 }
344
345 void
346 UnitConverter::periodChanged()
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
355 UnitConverter::bpmChanged()
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
364 UnitConverter::tempofreqChanged()
365 {
366 double rate = getSampleRate();
367 double samples = rate / m_tempofreq->value();
368 m_samples->setValue(samples);
369 }
370
371 void
372 UnitConverter::samplerateChanged()
373 {
374 // Preserve the beat period in seconds, here, not in samples
375 periodChanged();
376 }
377
378 double
379 UnitConverter::getSampleRate()
380 {
381 return double(atoi(m_samplerate->currentText().toLocal8Bit().data()));
382 }
383
384 void
385 UnitConverter::updateTempiFromSamples()
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