Mercurial > hg > svgui
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(), ¢s); | |
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 |