1 // Copyright 2004 "Gilles Degottex"
3 // This file is part of "fmit"
5 // "fmit" is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // "fmit" is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "CustomInstrumentTunerForm.h"
29 #include <qlcdnumber.h>
32 #include <qcombobox.h>
33 #include <qsplitter.h>
34 #include <qprogressbar.h>
35 #include <qmessagebox.h>
37 #include <qpushbutton.h>
38 #include <qcheckbox.h>
39 #include <qgroupbox.h>
40 #include <qbuttongroup.h>
41 #include <qlineedit.h>
42 #include <qstatusbar.h>
43 #include <qdatetime.h>
44 #include <qradiobutton.h>
46 #include <qtextbrowser.h>
47 #include <qpushbutton.h>
48 #include <qwidgetaction.h>
50 #include <qabstracttextdocumentlayout.h>
51 #include <Music/Convolution.h>
52 using namespace Music;
53 #include "modules/View.h"
55 CustomInstrumentTunerForm::CustomInstrumentTunerForm()
57 , m_capture_thread("fmit")
58 , m_timer_refresh(this)
59 , m_algo_combedfft(NULL)
60 , m_range_filter(&m_dummy_range_filter)
61 , m_quantizer(&m_latency_quantizer)
62 , m_settings("fmit", "fmit", "009801") // not necessarily equal to the soft version
66 /*QWidgetAction* viewsCaption = new QWidgetAction(ui_tbViews);
67 viewsCaption->setDefaultWidget(new QLabel(tr("Views"), ui_tbViews));
68 ui_tbViews->addAction(viewsCaption);*/
70 View::s_settings = &m_settings;
71 m_settings.add(m_config_form.ui_chkFullScreen);
72 m_settings.add(m_config_form.ui_chkAutoSaveOnExit);
73 m_settings.add(m_config_form.ui_chkShowA4Offset);
75 m_settings.add(m_config_form.ui_cbTuning);
76 m_settings.add(m_config_form.ui_cbTonality);
77 m_settings.add(m_config_form.ui_cbNotesName);
78 m_settings.add(ui_spinAFreq);
79 m_settings.add(ui_spinA3Offset);
81 m_settings.add(m_config_form.ui_chkAutoDetect);
83 m_settings.add(m_config_form.ui_chkJACKAutoConnect);
84 m_settings.add(m_config_form.ui_txtJACKSourcePort);
87 m_settings.add(m_config_form.ui_chkALSASamplingRateMax);
88 m_settings.add(m_config_form.ui_spinALSASamplingRate);
89 m_settings.add(m_config_form.ui_chkALSAMixMultipleChannels);
90 m_settings.add(m_config_form.ui_txtALSAPCMName);
93 m_settings.add(m_config_form.ui_chkOSSSamplingRateMax);
94 m_settings.add(m_config_form.ui_spinOSSSamplingRate);
95 m_settings.add(m_config_form.ui_chkOSSMixMultipleChannels);
96 m_settings.add(m_config_form.ui_txtOSSPCMName);
98 #ifdef CAPTURE_PORTAUDIO
99 m_settings.add(m_config_form.ui_chkPortAudioSamplingRateMax);
100 m_settings.add(m_config_form.ui_spinPortAudioSamplingRate);
101 m_settings.add(m_config_form.ui_chkPortAudioMixMultipleChannels);
104 m_settings.add(m_config_form.ui_spinRefreshTime);
105 m_settings.add(m_config_form.ui_spinMinHT);
106 m_settings.add(m_config_form.ui_spinMaxHT);
108 m_settings.add(m_config_form.ui_grpRangeFiltering);
109 m_settings.add(m_config_form.ui_rdRangeFilteringRectangular);
110 m_settings.add(m_config_form.ui_rdRangeFilteringFIR);
112 m_settings.add(m_config_form.ui_spinVolumeTreshold);
113 m_settings.add(m_config_form.ui_spinWindowSizeFactor);
114 m_settings.add(m_config_form.ui_chkAlgoUseSubHarmTresh);
115 m_settings.add(m_config_form.ui_spinCombedFFTAudibilityRatio);
117 m_settings.add(m_config_form.up_grpFreqRefinement);
118 m_settings.add(m_config_form.ui_rdUseFreqRefinement);
119 m_settings.add(m_config_form.ui_spinFreqRefinMaxHarm);
120 m_settings.add(m_config_form.ui_rdUseTimeRefinement);
121 m_settings.add(m_config_form.ui_spinTimeRefinMaxPeriod);
123 m_settings.add(m_config_form.ui_grpQuantizer);
124 m_settings.add(m_config_form.ui_spinErrorLatency);
126 m_algo_combedfft = new CombedFT();
128 for(int i=0; i<m_capture_thread.getTransports().size(); i++)
129 m_config_form.ui_cbTransports->addItem(m_capture_thread.getTransports()[i]->getName());
131 if(m_capture_thread.getTransports().empty())
132 QMessageBox::critical(this, "Error", "Please compile me with a capture system ...");
133 if(m_capture_thread.getTransports().size()==1)
135 m_config_form.ui_lblSelectedCaptureSystem->hide();
136 m_config_form.ui_btnAutoDetect->hide();
137 m_config_form.ui_chkAutoDetect->hide();
138 m_config_form.ui_cbTransports->hide();
140 m_config_form.ui_grpALSA->hide();
141 m_config_form.ui_grpJACK->hide();
142 m_config_form.ui_grpPortAudio->hide();
143 m_config_form.ui_grpOSS->hide();
145 ui_lblA3Offset->hide();
146 ui_spinA3Offset->hide();
148 connect(&m_capture_thread, SIGNAL(samplingRateChanged(int)), this, SLOT(samplingRateChanged(int)));
149 connect(&m_capture_thread, SIGNAL(errorRaised(const QString&)), this, SLOT(errorRaised(const QString&)));
150 connect(&m_capture_thread, SIGNAL(transportChanged(const QString&)), this, SLOT(transportChanged(const QString&)));
152 connect(&m_latency_quantizer, SIGNAL(noteStarted(double,double)), this, SLOT(noteStarted(double,double)));
153 connect(&m_latency_quantizer, SIGNAL(noteFinished(double,double)), this, SLOT(noteFinished(double,double)));
154 connect(&m_dummy_quantizer, SIGNAL(noteStarted(double,double)), this, SLOT(noteStarted(double,double)));
155 connect(&m_dummy_quantizer, SIGNAL(noteFinished(double,double)), this, SLOT(noteFinished(double,double)));
157 m_dialTune = new DialView(centralWidget());
158 ui_dialTuneLayout->addWidget(m_dialTune);
160 m_glGraph = new GLGraph(centralWidget());
161 connect(m_glGraph->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
162 connect(m_glGraph->setting_spinMaxHeight, SIGNAL(valueChanged(int)), this, SLOT(update_views()));
163 ui_tbViews->addAction(m_glGraph->setting_show);
164 ui_graphLayout->addWidget(m_glGraph);
166 m_glErrorHistory = new GLErrorHistory(centralWidget());
167 connect(m_glErrorHistory->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
168 ui_tbViews->addAction(m_glErrorHistory->setting_show);
169 ui_errorLayout->addWidget(m_glErrorHistory);
172 connect(m_dialTune->setting_spinScale, SIGNAL(valueChanged(int)), m_glErrorHistory->setting_spinScale, SLOT(setValue(int)));
173 connect(m_glErrorHistory->setting_spinScale, SIGNAL(valueChanged(int)), m_dialTune->setting_spinScale, SLOT(setValue(int)));
174 connect(m_dialTune->setting_useCents, SIGNAL(toggled(bool)), m_glErrorHistory->setting_useCents, SLOT(setOn(bool)));
175 connect(m_glErrorHistory->setting_useCents, SIGNAL(toggled(bool)), m_dialTune->setting_useCents, SLOT(setOn(bool)));
177 m_glVolumeHistory = new GLVolumeHistory(centralWidget());
178 connect(m_config_form.ui_spinVolumeTreshold, SIGNAL(valueChanged(int)), m_glVolumeHistory, SLOT(setVolumeTreshold(int)));
179 connect(m_glVolumeHistory->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
180 ui_tbViews->addAction(m_glVolumeHistory->setting_show);
181 ui_volumeLayout->addWidget(m_glVolumeHistory);
183 // link keep settings
184 connect(ui_btnKeepErrorHistory, SIGNAL(toggled(bool)), m_glErrorHistory->setting_keep, SLOT(setOn(bool)));
185 connect(m_glErrorHistory->setting_keep, SIGNAL(toggled(bool)), m_glVolumeHistory->setting_keep, SLOT(setOn(bool)));
186 connect(m_glErrorHistory->setting_keep, SIGNAL(toggled(bool)), ui_btnKeepErrorHistory, SLOT(setOn(bool)));
188 m_glSample = new GLSample(centralWidget());
189 connect(m_glSample->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
190 ui_tbViews->addAction(m_glSample->setting_show);
191 ui_sampleLayout->addWidget(m_glSample);
193 m_glFreqStruct = new GLFreqStruct(centralWidget());
194 connect(m_glFreqStruct->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
195 ui_tbViews->addAction(m_glFreqStruct->setting_show);
196 ui_formantsLayout->addWidget(m_glFreqStruct);
198 m_glFT = new GLFT(centralWidget());
199 connect(m_glFT->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
200 ui_tbViews->addAction(m_glFT->setting_show);
201 ui_FT->addWidget(m_glFT);
203 m_microtonalView = new MicrotonalView(centralWidget());
204 connect(m_microtonalView->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
205 connect(m_microtonalView, SIGNAL(tuningFreqChanged(float)), this, SLOT(tuningFreqChanged(float)));
206 ui_tbViews->addAction(m_microtonalView->setting_show);
207 ui_microtonalLayout->addWidget(m_microtonalView);
209 m_glStatistics = new GLStatistics(centralWidget());
210 connect(m_glStatistics->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
211 ui_tbViews->addAction(m_glStatistics->setting_show);
212 ui_microtonalLayout->addWidget(m_glStatistics);
214 connect(m_dialTune->setting_spinScale, SIGNAL(valueChanged(int)), m_glStatistics->setting_spinScale, SLOT(setValue(int)));
215 connect(m_glStatistics->setting_spinScale, SIGNAL(valueChanged(int)), m_dialTune->setting_spinScale, SLOT(setValue(int)));
216 connect(m_dialTune->setting_useCents, SIGNAL(toggled(bool)), m_glStatistics->setting_useCents, SLOT(setOn(bool)));
217 connect(m_glStatistics->setting_useCents, SIGNAL(toggled(bool)), m_dialTune->setting_useCents, SLOT(setOn(bool)));
219 connect(m_dialTune->setting_showTolerance, SIGNAL(toggled(bool)), m_glStatistics->setting_showTolerance, SLOT(setOn(bool)));
220 connect(m_glStatistics->setting_showTolerance, SIGNAL(toggled(bool)), m_dialTune->setting_showTolerance, SLOT(setOn(bool)));
222 connect(&m_config_form, SIGNAL(accepted()), this, SLOT(configure_ok()));
223 connect(&m_config_form, SIGNAL(rejected()), this, SLOT(configure_cancel()));
224 connect(m_config_form.ui_btnRestoreFactorySettings, SIGNAL(clicked()), this, SLOT(restoreFactorySettings()));
225 connect(m_config_form.ui_spinMinHT, SIGNAL(valueChanged(int)), this, SLOT(noteRangeChanged()));
226 connect(m_config_form.ui_spinMaxHT, SIGNAL(valueChanged(int)), this, SLOT(noteRangeChanged()));
227 connect(m_config_form.ui_cbTuning, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
228 connect(m_config_form.ui_cbTonality, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
229 connect(m_config_form.ui_cbNotesName, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
230 connect(m_config_form.ui_btnAutoDetect, SIGNAL(clicked()), this, SLOT(autoDetectTransport()));
231 connect(m_config_form.ui_cbTransports, SIGNAL(activated(const QString&)), this, SLOT(selectTransport(const QString&)));
232 connect(m_config_form.ui_chkALSAMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
233 connect(m_config_form.ui_chkOSSMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
234 connect(m_config_form.ui_chkPortAudioMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
238 if(m_config_form.ui_chkAutoDetect->isChecked())
239 m_capture_thread.autoDetectTransport();
243 if(m_config_form.ui_chkFullScreen->isChecked())
246 m_time_refresh_views.start();
247 m_time_refresh.start();
249 connect((QObject*)&m_timer_refresh, SIGNAL(timeout()), this, SLOT(refresh()));
250 m_timer_refresh.start(m_config_form.ui_spinRefreshTime->value());
252 // cerr << __FILE__ << " " << __LINE__ << endl;
255 void CustomInstrumentTunerForm::transportChanged(const QString& name)
257 selectTransport(name);
259 if(m_capture_thread.getCurrentTransportIndex()!=m_config_form.ui_cbTransports->currentIndex())
260 m_config_form.ui_cbTransports->setCurrentIndex(m_capture_thread.getCurrentTransportIndex());
262 void CustomInstrumentTunerForm::selectTransport(const QString& name)
264 m_config_form.ui_grpALSA->hide();
265 m_config_form.ui_grpJACK->hide();
266 m_config_form.ui_grpPortAudio->hide();
267 m_config_form.ui_grpOSS->hide();
269 if(name=="ALSA") m_config_form.ui_grpALSA->show();
270 else if(name=="JACK") m_config_form.ui_grpJACK->show();
271 else if(name=="PortAudio") m_config_form.ui_grpPortAudio->show();
272 else if(name=="OSS") m_config_form.ui_grpOSS->show();
274 void CustomInstrumentTunerForm::autoDetectTransport()
276 m_capture_thread.autoDetectTransport();
278 // here transportChanged will be called
281 void CustomInstrumentTunerForm::toggleFullScreen()
283 static bool fs = true;
286 m_config_form.ui_chkFullScreen->setChecked(true);
291 m_config_form.ui_chkFullScreen->setChecked(false);
297 void CustomInstrumentTunerForm::noteRangeChanged()
299 // cerr << "CustomInstrumentTunerForm::noteRangeChanged" << endl;
301 m_config_form.ui_txtMinHT->setText(QString::fromStdString(h2n(m_config_form.ui_spinMinHT->value())) + " = " + QString::number(h2f(m_config_form.ui_spinMinHT->value())) + " hz");
302 m_config_form.ui_txtMaxHT->setText(QString::fromStdString(h2n(m_config_form.ui_spinMaxHT->value())) + " = " + QString::number(h2f(m_config_form.ui_spinMaxHT->value())) + " hz");
305 void CustomInstrumentTunerForm::errorRaised(const QString& error)
307 // cerr << "CustomInstrumentTunerForm::errorRaised " << error << endl;
309 statusBar()->showMessage(QString("ERROR: ")+error);
312 palette.setColor(ui_lblSoundStability->backgroundRole(), QColor(180,74,74));
313 ui_lblSoundStability->setPalette(palette);
316 void CustomInstrumentTunerForm::samplingRateChanged(int sampling_rate)
318 cerr << "CustomInstrumentTunerForm::samplingRateChanged " << sampling_rate << endl;
320 Music::SetSamplingRate(sampling_rate);
322 m_rect_range_filter.reset(int(GetSamplingRate()/2000.0));
323 // m_fir_range_filter.setImpulseResponse(fir1_lowpass(400, 2/400));
324 // m_rect_range_filter.reset(int(GetSamplingRate()/h2f(GetSemitoneMin())));
325 m_glFT->spinWinLengthChanged(m_glFT->setting_winlen->value());
326 m_glFT->setSamplingRate(sampling_rate);
328 if(m_config_form.ui_cbTransports->currentText()=="JACK")
329 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(sampling_rate));
332 void CustomInstrumentTunerForm::ui_spinAFreq_valueChanged(int AFreq)
335 if(m_config_form.ui_chkShowA4Offset->isChecked())
336 A = h2f(ui_spinA3Offset->value()*1/100.0f, A);
338 // cerr << A << endl;
340 void CustomInstrumentTunerForm::ui_spinAOffset_valueChanged(int offset)
342 double A = ui_spinAFreq->value();
343 if(m_config_form.ui_chkShowA4Offset->isChecked())
344 A = h2f(offset*1/100.0f, ui_spinAFreq->value());
346 // cerr << A << endl;
349 void CustomInstrumentTunerForm::tuningFreqChanged(float freq)
351 // cerr << "CustomInstrumentTunerForm::tuningFreqChanged " << freq << endl;
355 if(m_compared_freq!=0.0f)
357 ui_txtNoteFreq->display(m_compared_freq);
358 ui_txtNote->setText(QString::fromStdString(h2n(f2h(m_compared_freq))));
363 m_compared_freq = freq;
364 ui_txtNoteFreq->display(int(freq*100)/100.0f);
365 ui_txtNote->setText(m_microtonalView->getTuningNoteName());
368 m_quantizer->reset();
370 // m_dialTune->setError(-10.0f);
373 void CustomInstrumentTunerForm::pause(bool on)
375 m_capture_thread.togglePause(on);
377 if(on) m_timer_refresh.stop();
378 else m_timer_refresh.start(m_config_form.ui_spinRefreshTime->value());
381 void CustomInstrumentTunerForm::refresh()
383 double elapsed_time = m_time_refresh.elapsed();
384 m_time_refresh.start();
386 QColor capture_failed_color(180,74,74);
387 QColor prb_color(208,146,0);
388 QColor empty_color(128,128,128);
389 QColor ok_color(83,165,105);
391 // 1/{time between refresh} = {nb refresh by seconds}
392 // limit the nb of new data by fs/{nb refresh by seconds}
393 // add 1 to {nb refresh by second} to eventualy recover small lags
394 // (a big lag is managed by m_values.size()>getPacketSizeSinceLastLock())
395 int limit = int( m_capture_thread.getSamplingRate() /
396 (1.0/(m_config_form.ui_spinRefreshTime->value()/1000.0) - 1));
398 // cerr << "REFRESH ";
400 m_capture_thread.lock();
401 // cerr << "CustomInstrumentTunerForm::refresh locked, values to read=" << m_capture_thread.m_values.size() << endl;
403 while(!m_capture_thread.m_values.empty() &&
404 (m_capture_thread.m_values.size()>m_capture_thread.getPacketSizeSinceLastLock() || nb_new_data<limit))
406 // cerr << m_capture_thread.m_values.back() << " ";
407 double value = (*m_range_filter)(m_capture_thread.m_values.back());
408 // cerr << value << " ";
409 m_capture_thread.m_values.pop_back();
411 m_queue.push_front(value);
412 if(m_glGraph) m_glGraph->addValue(value);
413 if(m_glFT) m_glFT->buff.push_front(value);
417 m_capture_thread.unlock();
418 // cerr << "CustomInstrumentTunerForm::refresh unlocked" << endl;
422 int max_size = max(m_range_filter->getLength(), max(m_glGraph->getLength(), m_algo_combedfft->getMinSize()));
423 while(!m_queue.empty() && int(m_queue.size())>max_size)
426 // refresh graph data
427 m_glGraph->refreshGraph(); // TODO refresh the view each time ??
428 m_glFT->refreshGraph();
430 // ------- Analysis stage -------
432 // if something goes wrong in the capture system
433 if(nb_new_data==0 || m_algo_combedfft==NULL || elapsed_time>8*m_config_form.ui_spinRefreshTime->value())
436 palette.setColor(ui_lblSoundStability->backgroundRole(), capture_failed_color);
437 ui_lblSoundStability->setPalette(palette);
441 m_algo_combedfft->apply(m_queue);
443 // TODO hem ... use energy
444 double max_component = 20*log10(m_algo_combedfft->getComponentsMax());
445 ui_pgbVolume->setValue(100+int(max_component));
448 if(m_algo_combedfft->hasNoteRecognized())
449 freq = m_algo_combedfft->getFondamentalFreq();
451 double freq_rel = freq*m_algo_combedfft->m_plan.in.size()/double(GetSamplingRate());
452 if(freq_rel<1 || freq_rel>(m_algo_combedfft->m_plan.out.size()/2))
455 // frequency refinement
456 if(freq>0.0 && m_config_form.up_grpFreqRefinement->isChecked())
458 if(m_config_form.ui_rdUseFreqRefinement->isChecked())
460 freq = FundFreqRefinementOfHarmonicStruct(m_algo_combedfft->m_plan.out, freq, m_config_form.ui_spinFreqRefinMaxHarm->value(), m_algo_combedfft->getZeroPaddingFactor());
463 else if(m_config_form.ui_rdUseTimeRefinement->isChecked())
465 double period = GetAveragePeriodFromApprox(m_queue, int(GetSamplingRate()/freq), m_config_form.ui_spinTimeRefinMaxPeriod->value());
467 freq = GetSamplingRate()/period;
471 // cerr << "2) test freq=" << m_test_freq <<endl;
473 m_quantizer->quantize(freq);
475 if(!m_quantizer->isPlaying())
478 palette.setColor(ui_lblSoundStability->backgroundRole(), empty_color);
479 ui_lblSoundStability->setPalette(palette);
483 // Use 100ms rect window
484 int max_amplitude_limit = min(m_queue.size(),size_t(m_capture_thread.getSamplingRate()*100.0/1000));
485 double max_amplitude = 0.0;
486 for(int i=0;i<max_amplitude_limit; i++)
487 max_amplitude = max(max_amplitude, fabs(m_queue[i]));
489 if(max_amplitude>=1.0)
492 palette.setColor(ui_lblSoundStability->backgroundRole(), prb_color);
493 ui_lblSoundStability->setPalette(palette);
498 palette.setColor(ui_lblSoundStability->backgroundRole(), ok_color);
499 ui_lblSoundStability->setPalette(palette);
502 m_freq = m_quantizer->getAverageFrequency();
503 m_error = f2hf(m_freq, m_compared_freq);
506 m_glErrorHistory->addError(m_error);
507 m_dialTune->setError(m_error);
508 m_dialTune->m_avg_error = m_glErrorHistory->m_notes.back().avg_err;
509 m_dialTune->m_min_error = m_glErrorHistory->m_notes.back().min_err;
510 m_dialTune->m_max_error = m_glErrorHistory->m_notes.back().max_err;
511 ui_txtFreq->display(m_freq);
513 // refresh intonal tuning cursor
514 m_microtonalView->setAFreq(Music::GetAFreq());
515 m_microtonalView->updateCursor(m_freq);
518 m_glVolumeHistory->addVolume(max_component);
520 // refresh sample data
521 refresh_data_sample();
523 // refresh formants data
524 refresh_data_harmonics();
526 m_glStatistics->addNote(f2h(m_compared_freq), m_error);
530 if(m_time_refresh_views.elapsed()>50) // 20 images/second max
534 void CustomInstrumentTunerForm::noteStarted(double freq, double dt)
536 // cerr << "CustomInstrumentTunerForm::noteStarted " << freq << "," << dt << endl;
538 // set the compared freq
539 if(m_microtonalView->setting_show->isChecked() && m_microtonalView->hasTuningFreqSelected())
540 m_compared_freq = m_microtonalView->getTuningFreq();
542 m_compared_freq = m_quantizer->getCenterFrequency(); // h2f(f2h(freq));
544 if(m_microtonalView->setting_show->isChecked() && m_microtonalView->hasTuningFreqSelected())
546 ui_txtNoteFreq->display(int(m_microtonalView->getTuningFreq()*100)/100.0);
547 ui_txtNote->setText(m_microtonalView->getTuningNoteName());
548 if(m_microtonalView->m_selected_jivalue->is_ratio)
550 m_glErrorHistory->addNote(GLErrorHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->num, m_microtonalView->m_selected_jivalue->den));
551 m_glVolumeHistory->addNote(GLVolumeHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->num, m_microtonalView->m_selected_jivalue->den));
555 m_glErrorHistory->addNote(GLErrorHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->cents));
556 m_glVolumeHistory->addNote(GLVolumeHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->cents));
561 ui_txtNoteFreq->display(m_compared_freq);
562 ui_txtNote->setText(QString::fromStdString(h2n(f2h(m_compared_freq))));
563 m_glErrorHistory->addNote(GLErrorHistory::Note(f2h(m_compared_freq)));
564 m_glVolumeHistory->addNote(GLVolumeHistory::Note(f2h(m_compared_freq)));
567 void CustomInstrumentTunerForm::noteFinished(double freq, double dt)
569 m_compared_freq = 0.0;
570 // cerr << "CustomInstrumentTunerForm::noteFinished " << freq << "," << dt << endl;
573 void CustomInstrumentTunerForm::refresh_data_sample()
575 if(m_freq==0.0f || !m_glSample->setting_show->isChecked())
581 deque<double> sample;
582 GetWaveSample(m_queue, size_t(m_capture_thread.getSamplingRate()/m_freq), sample);
583 m_glSample->add(m_time.elapsed(), sample);
586 void CustomInstrumentTunerForm::refresh_data_harmonics()
588 if(!(m_algo_combedfft!=NULL &&
590 m_glFreqStruct->setting_show->isChecked()))
593 vector<Harmonic> harms = GetHarmonicStruct(m_algo_combedfft->m_plan.out, m_freq, m_glFreqStruct->m_components.size(), m_algo_combedfft->getZeroPaddingFactor());
595 m_glFreqStruct->m_components_max = 0.0;
596 for(int i=0; i<harms.size(); i++)
598 if(harms[i].harm_number<m_glFreqStruct->m_components.size())
600 m_glFreqStruct->m_components[harms[i].harm_number-1] = 20*log10(harms[i].mod/0.001);
602 m_glFreqStruct->m_components_max = max(m_glFreqStruct->m_components_max, m_glFreqStruct->m_components[i]);
607 void CustomInstrumentTunerForm::refresh_views()
609 // cerr << "CustomInstrumentTunerForm::refresh_views " << endl;
611 // m_dialTune->repaint();
613 if(m_glGraph->setting_show->isChecked())
614 m_glGraph->updateGL();
616 if(m_glErrorHistory->setting_show->isChecked())
617 m_glErrorHistory->updateGL();
619 if(m_glVolumeHistory->setting_show->isChecked())
620 m_glVolumeHistory->updateGL();
622 if(m_microtonalView->setting_show->isChecked())
623 m_microtonalView->update();
625 if(m_glSample->setting_show->isChecked())
626 m_glSample->updateGL();
628 if(m_glFreqStruct->setting_show->isChecked())
629 m_glFreqStruct->updateGL();
631 if(m_glFT->setting_show->isChecked())
634 if(m_glStatistics->setting_show->isChecked())
635 m_glStatistics->updateGL();
637 m_time_refresh_views.start();
640 void CustomInstrumentTunerForm::keyPressEvent(QKeyEvent * e)
642 if(e->key()==Qt::Key_F)
646 void CustomInstrumentTunerForm::resizeEvent(QResizeEvent* e)
651 void CustomInstrumentTunerForm::update_views()
653 if( !m_glGraph->setting_show->isChecked() &&
654 !m_glErrorHistory->setting_show->isChecked() &&
655 !m_glVolumeHistory->setting_show->isChecked() &&
656 !m_glSample->setting_show->isChecked() &&
657 !m_glFreqStruct->setting_show->isChecked() &&
658 !m_glFT->setting_show->isChecked())
659 m_dialTune->setMaximumWidth(size().width());
661 m_dialTune->setMaximumWidth(ui_rightLayout->minimumSize().width());
663 if(m_glGraph->setting_show->isChecked() &&
664 !m_glErrorHistory->setting_show->isChecked() &&
665 !m_glVolumeHistory->setting_show->isChecked() &&
666 !m_glSample->setting_show->isChecked() &&
667 !m_glFreqStruct->setting_show->isChecked() &&
668 !m_glFT->setting_show->isChecked())
669 m_glGraph->setMaximumHeight(size().height());
671 m_glGraph->setMaximumHeight(m_glGraph->setting_spinMaxHeight->value());
673 if(!m_glErrorHistory->setting_show->isChecked() && !m_glVolumeHistory->setting_show->isChecked())
674 ui_btnKeepErrorHistory->hide();
676 ui_btnKeepErrorHistory->show();
679 void CustomInstrumentTunerForm::configure()
681 // Checking PortAudio devices while capturing using ALSA seems to crash ALSA
682 // thus, stop everything while configuring and capture again when exiting the configuration panel
683 m_capture_thread.stopCapture();
687 if(m_capture_thread.getCurrentTransportIndex()<m_config_form.ui_cbTransports->count());
688 m_config_form.ui_cbTransports->setCurrentIndex(m_capture_thread.getCurrentTransportIndex());
692 m_config_form.ui_grpJACK->setTitle(m_capture_thread.getTransport("JACK")->getDescription());
693 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(m_capture_thread.getSamplingRate()));
696 #ifdef CAPTURE_PORTAUDIO
697 m_config_form.ui_grpPortAudio->setTitle(m_capture_thread.getTransport("PortAudio")->getDescription());
698 m_config_form.ui_spinPortAudioSamplingRate->setValue(m_capture_thread.getSamplingRate());
699 if(m_capture_thread.getTransport("PortAudio"))
704 err = Pa_Initialize();
706 throw QString("PortAudio: CustomInstrumentTunerForm::configure:Pa_Initialize ")+Pa_GetErrorText(err);
707 int numDevices = Pa_GetDeviceCount();
708 int current_index = -1;
709 m_config_form.ui_cbPortAudioDeviceName->clear();
710 const PaDeviceInfo* deviceInfo;
711 for(int i=0; i<numDevices; i++)
713 deviceInfo = Pa_GetDeviceInfo(i);
714 m_config_form.ui_cbPortAudioDeviceName->addItem(QString(deviceInfo->name));
715 if(QString(deviceInfo->name)==m_capture_thread.getTransport("PortAudio")->getSource())
718 if(current_index!=-1)
719 m_config_form.ui_cbPortAudioDeviceName->setCurrentIndex(current_index);
723 cerr << "CustomInstrumentTunerForm: ERROR: " << error.toStdString() << endl;
730 m_config_form.ui_grpALSA->setTitle(m_capture_thread.getTransport("ALSA")->getDescription());
731 m_config_form.ui_spinALSASamplingRate->setValue(m_capture_thread.getSamplingRate());
734 m_config_form.ui_grpOSS->setTitle(m_capture_thread.getTransport("OSS")->getDescription());
735 m_config_form.ui_spinOSSSamplingRate->setValue(m_capture_thread.getSamplingRate());
738 m_config_form.adjustSize();
739 m_config_form.show();
741 void CustomInstrumentTunerForm::configure_cancel()
743 if(!pauseAction->isChecked() && !m_capture_thread.isCapturing())
744 m_capture_thread.startCapture();
746 void CustomInstrumentTunerForm::configure_ok()
748 switch(m_config_form.ui_cbTuning->currentIndex())
751 SetTuning(CHROMATIC);
754 SetTuning(WERCKMEISTER3);
757 SetTuning(KIRNBERGER3);
766 SetTuning(CHROMATIC);
770 if(m_config_form.ui_cbTonality->currentIndex()==0) SetTonality(0);
771 else if(m_config_form.ui_cbTonality->currentIndex()==1) SetTonality(+2);
772 else SetTonality(-3);
774 if(m_config_form.ui_cbNotesName->currentIndex()==0) SetNotesName(LOCAL_ANGLO);
775 else SetNotesName(LOCAL_LATIN);
776 m_microtonalView->notesNameChanged();
777 m_microtonalView->setAFreq(Music::GetAFreq());
779 SetSemitoneBounds(m_config_form.ui_spinMinHT->value(), m_config_form.ui_spinMaxHT->value());
781 ui_spinA3Offset->setShown(m_config_form.ui_chkShowA4Offset->isChecked());
782 ui_lblA3Offset->setShown(m_config_form.ui_chkShowA4Offset->isChecked());
785 // ui_txtNote->setText(h2n(m_note));
787 // m_dialTune->setError(-10.0f);
789 // cerr << "b" << endl;
793 if(m_config_form.ui_cbTransports->currentText()=="ALSA")
795 m_capture_thread.selectTransport("ALSA");
796 m_capture_thread.setSource(m_config_form.ui_txtALSAPCMName->text());
797 if(m_config_form.ui_chkALSASamplingRateMax->isChecked())
798 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
800 m_capture_thread.setSamplingRate(m_config_form.ui_spinALSASamplingRate->value());
804 if(m_config_form.ui_cbTransports->currentText()=="JACK")
806 m_capture_thread.selectTransport("JACK");
807 if(m_config_form.ui_chkJACKAutoConnect->isChecked())
808 m_capture_thread.setSource(m_config_form.ui_txtJACKSourcePort->text());
810 m_capture_thread.setSource("");
811 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(m_capture_thread.getSamplingRate()));
814 #ifdef CAPTURE_PORTAUDIO
815 if(m_config_form.ui_cbTransports->currentText()=="PortAudio")
817 m_capture_thread.selectTransport("PortAudio");
818 m_capture_thread.setSource(m_config_form.ui_cbPortAudioDeviceName->currentText());
819 if(m_config_form.ui_chkPortAudioSamplingRateMax->isChecked())
820 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
822 m_capture_thread.setSamplingRate(m_config_form.ui_spinPortAudioSamplingRate->value());
826 if(m_config_form.ui_cbTransports->currentText()=="OSS")
828 m_capture_thread.selectTransport("OSS");
829 m_capture_thread.setSource(m_config_form.ui_txtOSSPCMName->text());
830 if(m_config_form.ui_chkOSSSamplingRateMax->isChecked())
831 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
833 m_capture_thread.setSamplingRate(m_config_form.ui_spinOSSSamplingRate->value());
836 m_timer_refresh.setInterval(m_config_form.ui_spinRefreshTime->value());
839 m_glGraph->m_treshold = invlp(m_config_form.ui_spinVolumeTreshold->value());
840 m_glGraph->clearValues();
842 // cerr << "c" << endl;
844 if(m_config_form.ui_grpRangeFiltering->isChecked())
846 // cout << "set filters " << GetSamplingRate() << endl;
847 // m_rect_range_filter.reset(int(GetSamplingRate()/6000.0));
848 m_rect_range_filter.reset(int(GetSamplingRate()/h2f(GetSemitoneMin())));
849 // m_fir_range_filter.setImpulseResponse(fir1_bandpass(104, 2*1000.0/GetSamplingRate(), 2*2000.0/GetSamplingRate()));
850 // m_fir_range_filter.setImpulseResponse(fir1_highpass(int(GetSamplingRate()/h2f(GetSemitoneMin())), 0.5));
852 if(m_config_form.ui_rdRangeFilteringRectangular->isChecked())
853 m_range_filter = &m_rect_range_filter;
854 else if(m_config_form.ui_rdRangeFilteringFIR->isChecked())
855 m_range_filter = &m_fir_range_filter;
858 m_range_filter = &m_dummy_range_filter;
860 m_algo_combedfft->setWindowFactor(m_config_form.ui_spinWindowSizeFactor->value());
861 // m_glFT->m_zp_factor = m_config_form.ui_spinWindowSizeFactor->value();
862 m_algo_combedfft->useAudibilityRatio(m_config_form.ui_chkAlgoUseSubHarmTresh->isChecked());
863 m_algo_combedfft->setAudibilityRatio(invlp(-m_config_form.ui_spinCombedFFTAudibilityRatio->value()));
864 m_algo_combedfft->setAmplitudeTreshold(invlp(double(m_config_form.ui_spinVolumeTreshold->value())));
865 m_algo_combedfft->setComponentTreshold(invlp(double(m_config_form.ui_spinVolumeTreshold->value())));
867 // cerr << "d" << endl;
870 m_quantizer->reset();
871 if(m_config_form.ui_grpQuantizer->isChecked())
873 m_latency_quantizer.setLatency(m_config_form.ui_spinErrorLatency->value());
874 m_quantizer = &m_latency_quantizer;
877 m_quantizer = &m_dummy_quantizer;
879 // cerr << invlp(-m_config_form.ui_spinCombedFFTAudibilityRatio->value()) << endl;
881 if(!pauseAction->isChecked() && !m_capture_thread.isCapturing())
882 m_capture_thread.startCapture();
885 void CustomInstrumentTunerForm::saveSettings()
891 m_settings.setValue("width", width());
892 m_settings.setValue("height", height());
893 //m_settings.setValue("ui_tbViews", QVariant(ui_tbViews->isVisible()));
894 //m_settings.setValue("ui_tbButtons", ui_tbButtons->isVisible());
897 m_settings.setValue(m_config_form.ui_cbTransports->objectName(), m_config_form.ui_cbTransports->currentText());
898 #ifdef CAPTURE_PORTAUDIO
899 m_settings.setValue(m_config_form.ui_cbPortAudioDeviceName->objectName(), m_config_form.ui_cbPortAudioDeviceName->currentText());
902 void CustomInstrumentTunerForm::loadSettings()
904 // cerr << __FILE__ << ":" << __LINE__ << endl;
910 resize(m_settings.value("width", 800).toInt(), m_settings.value("height", 600).toInt());
911 //ui_tbViews->setVisible(m_settings.value("ui_tbViews", ui_tbViews->isVisible()).toBool());
912 //ui_tbButtons->setVisible(m_settings.value("ui_tbButtons", ui_tbButtons->isVisible()).toBool());
915 QString saved_transport = m_settings.value(m_config_form.ui_cbTransports->objectName(), "").toString();
916 if(saved_transport!="")
917 for(int i=0; i<m_config_form.ui_cbTransports->count(); i++)
918 if(m_config_form.ui_cbTransports->itemText(i)==saved_transport)
919 m_config_form.ui_cbTransports->setCurrentIndex(i);
921 #ifdef CAPTURE_PORTAUDIO
922 QString saved_device = m_settings.value(m_config_form.ui_cbPortAudioDeviceName->objectName(), "default").toString();
926 err = Pa_Initialize();
928 throw QString("PortAudio: CustomInstrumentTunerForm::loadSettings:Pa_Initialize ")+Pa_GetErrorText(err);
929 int numDevices = Pa_GetDeviceCount();
930 // cerr << "PortAudio devices:"<< endl;
931 int saved_index = -1;
932 m_config_form.ui_cbPortAudioDeviceName->clear();
933 const PaDeviceInfo* deviceInfo;
934 for(int i=0; i<numDevices; i++)
936 deviceInfo = Pa_GetDeviceInfo(i);
937 // cerr << " " << QString(deviceInfo->name).toStdString() << endl;
938 m_config_form.ui_cbPortAudioDeviceName->addItem(QString(deviceInfo->name));
939 if(QString(deviceInfo->name)==saved_device)
943 m_config_form.ui_cbPortAudioDeviceName->setCurrentIndex(saved_index);
947 // cerr << "CustomInstrumentTunerForm: ERROR: " << error << endl;
953 void CustomInstrumentTunerForm::restoreFactorySettings()
955 if(QMessageBox::question(this, tr("Restore Factory Settings"), tr("This operation is NOT reversible.\nAre you sure you want to lose all your current settings ?"), QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes)
958 View::clearAllSettings();
960 m_settings.remove("width");
961 m_settings.remove("height");
962 m_settings.remove("ui_cbTransports");
963 //m_settings.remove("ui_tbViews");
964 //m_settings.remove("ui_tbButtons");
966 m_settings.remove(m_config_form.ui_cbPortAudioDeviceName->objectName());
968 QMessageBox::information(this, tr("Restore Factory Settings"), tr("You can now restart FMIT to get back factory settings"), QMessageBox::Ok, QMessageBox::NoButton);
972 void CustomInstrumentTunerForm::helpAbout()
975 text = "<h2>Free Music Instrument Tuner</h2>";
976 text += tr("<h3>Version ")+PACKAGE_VERSION;
977 text += tr("</h3><p><h3>Website:</h3><p>homepage: <a href=\"http://home.gna.org/fmit\">http://home.gna.org/fmit</a>");
978 text += tr("<p>development site: <a href=\"http://gna.org/projects/fmit\">http://gna.org/projects/fmit</a>");
979 text += tr("<p>donation link: <a href=\"http://home.gna.org/fmit/donation.html\">http://home.gna.org/fmit/donation.html</a>");
980 text += tr("<p><h3>Author:</h3><p>Gilles Degottex <a href=\"mailto:gilles.degottex@gmail.com\">gilles.degottex@gmail.com</a>");
981 #ifdef PACKAGER_STRING
982 if(PACKAGER_STRING!="")
983 text += tr("<p><h3>Packager:</h3><p>")+QString(PACKAGER_STRING).replace(QChar('<'),"[").replace(QChar('>'),"]");
986 QDialog about_dlg(this);
988 QTextBrowser* textBrowser1;
989 QPushButton* pushButton1;
990 QVBoxLayout* Form2Layout;
991 QHBoxLayout* layout1;
992 QSpacerItem* spacer1;
993 QSpacerItem* spacer2;
995 about_dlg.setObjectName( tr("about_box") );
996 about_dlg.setWindowTitle( tr("About Free Music Instrument Tuner") );
998 Form2Layout = new QVBoxLayout( &about_dlg );
999 Form2Layout->setMargin(11);
1000 Form2Layout->setSpacing(6);
1001 about_dlg.setLayout( Form2Layout );
1003 textBrowser1 = new QTextBrowser( &about_dlg );
1004 textBrowser1->setHtml(text);
1005 textBrowser1->setOpenExternalLinks(true);
1006 textBrowser1->setWordWrapMode(QTextOption::NoWrap);
1007 textBrowser1->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1008 textBrowser1->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1009 textBrowser1->document()->adjustSize();
1010 textBrowser1->setMinimumSize(textBrowser1->document()->size().toSize() + QSize(10, 10));
1011 Form2Layout->addWidget( textBrowser1 );
1013 layout1 = new QHBoxLayout();
1014 layout1->setMargin(0);
1015 layout1->setSpacing(6);
1017 spacer1 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
1018 layout1->addItem( spacer1 );
1020 pushButton1 = new QPushButton(&about_dlg);
1021 pushButton1->setText( tr( "&OK" ) );
1022 layout1->addWidget( pushButton1 );
1023 connect(pushButton1, SIGNAL(clicked()), &about_dlg, SLOT(close()));
1025 spacer2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
1026 layout1->addItem( spacer2 );
1028 Form2Layout->addLayout( layout1 );
1033 CustomInstrumentTunerForm::~CustomInstrumentTunerForm()
1035 if(m_config_form.ui_chkAutoSaveOnExit->isChecked())
1039 m_settings.beginGroup("Auto/");
1040 m_settings.save(m_config_form.ui_chkAutoSaveOnExit);
1041 m_settings.endGroup();
1046 bool displayInBrowser(const QString& theURL)
1049 //TODO replace with less buggy ShellExecuteEx?
1051 int)::ShellExecute(qApp->mainWidget()->winId(),NULL,theURL,NULL,NULL,SW_SHOW)
1054 OFMessageBox::criticalMessageOK(QMessageBox::tr("Unable to display a
1055 web browser. Ensure that you have a web browser installed."));
1058 //TODO warn if netscape not installed
1059 QString aCommand("netscape ");
1062 if (system(aCommand) != 0)
1064 OFMessageBox::criticalMessageOK(QMessageBox::tr("Unable to display a
1065 netscape browser. You need to have netscape installed and in the