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 <qtextedit.h>
47 #include <qpushbutton.h>
49 #include <Music/Convolution.h>
50 using namespace Music;
51 #include "modules/View.h"
53 CustomInstrumentTunerForm::CustomInstrumentTunerForm()
54 : m_capture_thread("fmit")
55 , m_timer_refresh(this, "m_timer_refresh")
56 , m_algo_combedfft(NULL)
57 , m_range_filter(&m_dummy_range_filter)
58 , m_quantizer(&m_latency_quantizer)
59 , m_settings("gillesdegottex.ch", "fmit", "009700") // not necessarily equal to the soft version
61 View::s_settings = &m_settings;
62 m_settings.add(m_config_form.ui_chkFullScreen);
63 m_settings.add(m_config_form.ui_chkAutoSaveOnExit);
64 m_settings.add(m_config_form.ui_chkShowA4Offset);
66 m_settings.add(m_config_form.ui_cbTuning);
67 m_settings.add(m_config_form.ui_cbTonality);
68 m_settings.add(m_config_form.ui_cbNotesName);
69 m_settings.add(ui_spinAFreq);
70 m_settings.add(ui_spinA3Offset);
72 m_settings.add(m_config_form.ui_chkAutoDetect);
74 m_settings.add(m_config_form.ui_chkJACKAutoConnect);
75 m_settings.add(m_config_form.ui_txtJACKSourcePort);
78 m_settings.add(m_config_form.ui_chkALSASamplingRateMax);
79 m_settings.add(m_config_form.ui_spinALSASamplingRate);
80 m_settings.add(m_config_form.ui_chkALSAMixMultipleChannels);
81 m_settings.add(m_config_form.ui_txtALSAPCMName);
84 m_settings.add(m_config_form.ui_chkOSSSamplingRateMax);
85 m_settings.add(m_config_form.ui_spinOSSSamplingRate);
86 m_settings.add(m_config_form.ui_chkOSSMixMultipleChannels);
87 m_settings.add(m_config_form.ui_txtOSSPCMName);
89 #ifdef CAPTURE_PORTAUDIO
90 m_settings.add(m_config_form.ui_chkPortAudioSamplingRateMax);
91 m_settings.add(m_config_form.ui_spinPortAudioSamplingRate);
92 m_settings.add(m_config_form.ui_chkPortAudioMixMultipleChannels);
95 m_settings.add(m_config_form.ui_spinRefreshTime);
96 m_settings.add(m_config_form.ui_spinMinHT);
97 m_settings.add(m_config_form.ui_spinMaxHT);
99 m_settings.add(m_config_form.ui_grpRangeFiltering);
100 m_settings.add(m_config_form.ui_rdRangeFilteringRectangular);
101 m_settings.add(m_config_form.ui_rdRangeFilteringFIR);
103 m_settings.add(m_config_form.ui_spinVolumeTreshold);
104 m_settings.add(m_config_form.ui_spinWindowSizeFactor);
105 m_settings.add(m_config_form.ui_chkAlgoUseSubHarmTresh);
106 m_settings.add(m_config_form.ui_spinCombedFFTAudibilityRatio);
108 m_settings.add(m_config_form.up_grpFreqRefinement);
109 m_settings.add(m_config_form.ui_rdUseFreqRefinement);
110 m_settings.add(m_config_form.ui_spinFreqRefinMaxHarm);
111 m_settings.add(m_config_form.ui_rdUseTimeRefinement);
112 m_settings.add(m_config_form.ui_spinTimeRefinMaxPeriod);
114 m_settings.add(m_config_form.ui_grpQuantizer);
115 m_settings.add(m_config_form.ui_spinErrorLatency);
117 m_algo_combedfft = new CombedFT();
119 for(int i=0; i<m_capture_thread.getTransports().size(); i++)
120 m_config_form.ui_cbTransports->insertItem(m_capture_thread.getTransports()[i]->getName());
122 if(m_capture_thread.getTransports().empty())
123 QMessageBox::critical(this, "Error", "Please compile me with a capture system ...");
124 if(m_capture_thread.getTransports().size()==1)
126 m_config_form.ui_lblSelectedCaptureSystem->hide();
127 m_config_form.ui_btnAutoDetect->hide();
128 m_config_form.ui_chkAutoDetect->hide();
129 m_config_form.ui_cbTransports->hide();
131 m_config_form.ui_grpALSA->hide();
132 m_config_form.ui_grpJACK->hide();
133 m_config_form.ui_grpPortAudio->hide();
134 m_config_form.ui_grpOSS->hide();
136 ui_lblA3Offset->hide();
137 ui_spinA3Offset->hide();
139 connect(&m_capture_thread, SIGNAL(samplingRateChanged(int)), this, SLOT(samplingRateChanged(int)));
140 connect(&m_capture_thread, SIGNAL(errorRaised(const QString&)), this, SLOT(errorRaised(const QString&)));
141 connect(&m_capture_thread, SIGNAL(transportChanged(const QString&)), this, SLOT(transportChanged(const QString&)));
143 connect(&m_latency_quantizer, SIGNAL(noteStarted(double,double)), this, SLOT(noteStarted(double,double)));
144 connect(&m_latency_quantizer, SIGNAL(noteFinished(double,double)), this, SLOT(noteFinished(double,double)));
145 connect(&m_dummy_quantizer, SIGNAL(noteStarted(double,double)), this, SLOT(noteStarted(double,double)));
146 connect(&m_dummy_quantizer, SIGNAL(noteFinished(double,double)), this, SLOT(noteFinished(double,double)));
148 m_dialTune = new DialView(centralWidget());
149 ui_dialTuneLayout->addWidget(m_dialTune);
151 m_glGraph = new GLGraph("Graph", centralWidget());
152 connect(m_glGraph->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
153 connect(m_glGraph->setting_spinMaxHeight, SIGNAL(valueChanged(int)), this, SLOT(update_views()));
154 m_glGraph->setting_show->addTo(ui_tbViews);
155 ui_graphLayout->addWidget(m_glGraph);
157 m_glErrorHistory = new GLErrorHistory(centralWidget());
158 connect(m_glErrorHistory->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
159 m_glErrorHistory->setting_show->addTo(ui_tbViews);
160 ui_errorLayout->addWidget(m_glErrorHistory);
163 connect(m_dialTune->setting_spinScale, SIGNAL(valueChanged(int)), m_glErrorHistory->setting_spinScale, SLOT(setValue(int)));
164 connect(m_glErrorHistory->setting_spinScale, SIGNAL(valueChanged(int)), m_dialTune->setting_spinScale, SLOT(setValue(int)));
165 connect(m_dialTune->setting_useCents, SIGNAL(toggled(bool)), m_glErrorHistory->setting_useCents, SLOT(setOn(bool)));
166 connect(m_glErrorHistory->setting_useCents, SIGNAL(toggled(bool)), m_dialTune->setting_useCents, SLOT(setOn(bool)));
168 m_glVolumeHistory = new GLVolumeHistory(centralWidget());
169 connect(m_config_form.ui_spinVolumeTreshold, SIGNAL(valueChanged(int)), m_glVolumeHistory, SLOT(setVolumeTreshold(int)));
170 connect(m_glVolumeHistory->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
171 m_glVolumeHistory->setting_show->addTo(ui_tbViews);
172 ui_volumeLayout->addWidget(m_glVolumeHistory);
174 // link keep settings
175 connect(ui_btnKeepErrorHistory, SIGNAL(toggled(bool)), m_glErrorHistory->setting_keep, SLOT(setOn(bool)));
176 connect(m_glErrorHistory->setting_keep, SIGNAL(toggled(bool)), m_glVolumeHistory->setting_keep, SLOT(setOn(bool)));
177 connect(m_glErrorHistory->setting_keep, SIGNAL(toggled(bool)), ui_btnKeepErrorHistory, SLOT(setOn(bool)));
179 m_glSample = new GLSample(centralWidget());
180 connect(m_glSample->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
181 m_glSample->setting_show->addTo(ui_tbViews);
182 ui_sampleLayout->addWidget(m_glSample);
184 m_glFreqStruct = new GLFreqStruct(centralWidget());
185 connect(m_glFreqStruct->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
186 m_glFreqStruct->setting_show->addTo(ui_tbViews);
187 ui_formantsLayout->addWidget(m_glFreqStruct);
189 m_glFT = new GLFT(centralWidget());
190 connect(m_glFT->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
191 m_glFT->setting_show->addTo(ui_tbViews);
192 ui_FT->addWidget(m_glFT);
194 m_microtonalView = new MicrotonalView(centralWidget());
195 connect(m_microtonalView->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
196 connect(m_microtonalView, SIGNAL(tuningFreqChanged(float)), this, SLOT(tuningFreqChanged(float)));
197 m_microtonalView->setting_show->addTo(ui_tbViews);
198 ui_microtonalLayout->addWidget(m_microtonalView);
200 m_glStatistics = new GLStatistics(centralWidget());
201 connect(m_glStatistics->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
202 m_glStatistics->setting_show->addTo(ui_tbViews);
203 ui_microtonalLayout->addWidget(m_glStatistics);
205 connect(m_dialTune->setting_spinScale, SIGNAL(valueChanged(int)), m_glStatistics->setting_spinScale, SLOT(setValue(int)));
206 connect(m_glStatistics->setting_spinScale, SIGNAL(valueChanged(int)), m_dialTune->setting_spinScale, SLOT(setValue(int)));
207 connect(m_dialTune->setting_useCents, SIGNAL(toggled(bool)), m_glStatistics->setting_useCents, SLOT(setOn(bool)));
208 connect(m_glStatistics->setting_useCents, SIGNAL(toggled(bool)), m_dialTune->setting_useCents, SLOT(setOn(bool)));
210 connect(m_dialTune->setting_showTolerance, SIGNAL(toggled(bool)), m_glStatistics->setting_showTolerance, SLOT(setOn(bool)));
211 connect(m_glStatistics->setting_showTolerance, SIGNAL(toggled(bool)), m_dialTune->setting_showTolerance, SLOT(setOn(bool)));
213 connect(m_config_form.buttonOk, SIGNAL(clicked()), this, SLOT(configure_ok()));
214 connect(m_config_form.ui_btnRestoreFactorySettings, SIGNAL(clicked()), this, SLOT(restoreFactorySettings()));
215 connect(m_config_form.ui_spinMinHT, SIGNAL(valueChanged(int)), this, SLOT(noteRangeChanged()));
216 connect(m_config_form.ui_spinMaxHT, SIGNAL(valueChanged(int)), this, SLOT(noteRangeChanged()));
217 connect(m_config_form.ui_cbTuning, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
218 connect(m_config_form.ui_cbTonality, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
219 connect(m_config_form.ui_cbNotesName, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
220 connect(m_config_form.ui_btnAutoDetect, SIGNAL(clicked()), this, SLOT(autoDetectTransport()));
221 connect(m_config_form.ui_cbTransports, SIGNAL(activated(const QString&)), this, SLOT(selectTransport(const QString&)));
222 connect(m_config_form.ui_chkALSAMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
223 connect(m_config_form.ui_chkOSSMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
224 connect(m_config_form.ui_chkPortAudioMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
228 if(m_config_form.ui_chkAutoDetect->isChecked())
229 m_capture_thread.autoDetectTransport();
233 if(m_config_form.ui_chkFullScreen->isChecked())
236 m_time_refresh_views.start();
237 m_time_refresh.start();
239 connect((QObject*)&m_timer_refresh, SIGNAL(timeout()), this, SLOT(refresh()));
240 m_timer_refresh.start(m_config_form.ui_spinRefreshTime->value());
243 void CustomInstrumentTunerForm::transportChanged(const QString& name)
245 selectTransport(name);
247 if(m_capture_thread.getCurrentTransportIndex()!=m_config_form.ui_cbTransports->currentItem())
248 m_config_form.ui_cbTransports->setCurrentItem(m_capture_thread.getCurrentTransportIndex());
250 void CustomInstrumentTunerForm::selectTransport(const QString& name)
252 m_config_form.ui_grpALSA->hide();
253 m_config_form.ui_grpJACK->hide();
254 m_config_form.ui_grpPortAudio->hide();
255 m_config_form.ui_grpOSS->hide();
257 if(name=="ALSA") m_config_form.ui_grpALSA->show();
258 else if(name=="JACK") m_config_form.ui_grpJACK->show();
259 else if(name=="PortAudio") m_config_form.ui_grpPortAudio->show();
260 else if(name=="OSS") m_config_form.ui_grpOSS->show();
262 void CustomInstrumentTunerForm::autoDetectTransport()
264 m_capture_thread.autoDetectTransport();
266 // here transportChanged will be called
269 void CustomInstrumentTunerForm::toggleFullScreen()
271 static bool fs = true;
274 m_config_form.ui_chkFullScreen->setChecked(true);
279 m_config_form.ui_chkFullScreen->setChecked(false);
285 void CustomInstrumentTunerForm::noteRangeChanged()
287 // cerr << "CustomInstrumentTunerForm::noteRangeChanged" << endl;
289 m_config_form.ui_txtMinHT->setText(h2n(m_config_form.ui_spinMinHT->value())+" = "+QString::number(h2f(m_config_form.ui_spinMinHT->value()))+" hz");
290 m_config_form.ui_txtMaxHT->setText(h2n(m_config_form.ui_spinMaxHT->value())+" = "+QString::number(h2f(m_config_form.ui_spinMaxHT->value()))+" hz");
293 void CustomInstrumentTunerForm::errorRaised(const QString& error)
295 // cerr << "CustomInstrumentTunerForm::errorRaised " << error << endl;
297 statusBar()->message(QString("ERROR: ")+error);
299 ui_lblSoundStability->setBackgroundColor(QColor(180,74,74));
302 void CustomInstrumentTunerForm::samplingRateChanged(int sampling_rate)
304 // cerr << "CustomInstrumentTunerForm::samplingRateChanged " << sampling_rate << endl;
306 Music::SetSamplingRate(sampling_rate);
308 m_rect_range_filter.reset(int(GetSamplingRate()/h2f(GetSemitoneMin())));
310 if(m_config_form.ui_cbTransports->currentText()=="JACK")
311 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(sampling_rate));
314 void CustomInstrumentTunerForm::ui_spinAFreq_valueChanged(int AFreq)
317 if(m_config_form.ui_chkShowA4Offset->isOn())
318 A = h2f(ui_spinA3Offset->value()*1/100.0f, A);
320 // cerr << A << endl;
322 void CustomInstrumentTunerForm::ui_spinAOffset_valueChanged(int offset)
324 double A = ui_spinAFreq->value();
325 if(m_config_form.ui_chkShowA4Offset->isOn())
326 A = h2f(offset*1/100.0f, ui_spinAFreq->value());
328 // cerr << A << endl;
331 void CustomInstrumentTunerForm::tuningFreqChanged(float freq)
333 // cerr << "CustomInstrumentTunerForm::tuningFreqChanged " << freq << endl;
337 if(m_compared_freq!=0.0f)
339 ui_txtNoteFreq->display(m_compared_freq);
340 ui_txtNote->setText(h2n(f2h(m_compared_freq)));
345 m_compared_freq = freq;
346 ui_txtNoteFreq->display(int(freq*100)/100.0f);
347 ui_txtNote->setText(m_microtonalView->getTuningNoteName());
350 m_quantizer->reset();
352 // m_dialTune->setError(-10.0f);
355 void CustomInstrumentTunerForm::pause(bool on)
357 m_capture_thread.togglePause(on);
359 if(on) m_timer_refresh.stop();
360 else m_timer_refresh.start(m_config_form.ui_spinRefreshTime->value());
363 void CustomInstrumentTunerForm::refresh()
365 double elapsed_time = m_time_refresh.elapsed();
366 m_time_refresh.start();
368 QColor capture_failed_color(180,74,74);
369 QColor prb_color(208,146,0);
370 QColor empty_color(128,128,128);
371 QColor ok_color(83,165,105);
373 // 1/{time between refresh} = {nb refresh by seconds}
374 // limit the nb new data by fs/{nb refresh by seconds}
375 // add 1 to {nb refresh by second} to eventualy recover lags
376 int limit = int( m_capture_thread.getSamplingRate() /
377 (1.0/(m_config_form.ui_spinRefreshTime->value()/1000.0) - 1));
379 // cerr << "REFRESH ";
381 m_capture_thread.lock();
382 double max_amplitude = 0.0;
384 while(!m_capture_thread.m_values.empty() &&
385 (m_capture_thread.m_values.size()>m_capture_thread.getPacketSizeSinceLastLock() || nb_new_data<limit))
387 // cerr << m_capture_thread.m_values.back() << " ";
388 double value = (*m_range_filter)(m_capture_thread.m_values.back());
389 // cerr << value << " ";
390 m_capture_thread.m_values.pop_back();
392 max_amplitude = max(max_amplitude, fabs(value));
394 m_queue.push_front(value);
395 if(m_glGraph) m_glGraph->addValue(value);
396 if(m_glFT) m_glFT->buff.push_front(value);
400 m_capture_thread.unlock();
404 int max_size = max(m_range_filter->getLength(), max(m_glGraph->getLength(), m_algo_combedfft->getMinSize()));
405 while(!m_queue.empty() && int(m_queue.size())>max_size)
408 // refresh graph data
409 m_glGraph->refreshGraph(); // TODO refresh the view each time ??
410 m_glFT->refreshGraph();
412 // ------- Analysis stage -------
414 // if something goes wrong in the capture system
415 if(nb_new_data==0 || m_algo_combedfft==NULL
416 || elapsed_time>8*m_config_form.ui_spinRefreshTime->value())
417 ui_lblSoundStability->setBackgroundColor(capture_failed_color);
420 m_algo_combedfft->apply(m_queue);
422 double max_component = 20*log10(m_algo_combedfft->getComponentsMax());
423 ui_pgbVolume->setProgress(100+int(max_component));
426 if(m_algo_combedfft->hasNoteRecognized())
427 freq = m_algo_combedfft->getFondamentalFreq();
429 double freq_rel = freq*m_algo_combedfft->m_plan.in.size()/double(GetSamplingRate());
430 if(freq_rel<1 || freq_rel>(m_algo_combedfft->m_plan.out.size()/2))
433 // frequency refinement
434 if(freq>0.0 && m_config_form.up_grpFreqRefinement->isChecked())
436 if(m_config_form.ui_rdUseFreqRefinement->isChecked())
438 freq = FundFreqRefinementOfHarmonicStruct(m_algo_combedfft->m_plan.out, freq, m_config_form.ui_spinFreqRefinMaxHarm->value(), m_algo_combedfft->getZeroPaddingFactor());
441 else if(m_config_form.ui_rdUseTimeRefinement->isChecked())
443 double period = GetAveragePeriodFromApprox(m_queue, int(GetSamplingRate()/freq), m_config_form.ui_spinTimeRefinMaxPeriod->value());
445 freq = GetSamplingRate()/period;
449 // cerr << "2) test freq=" << m_test_freq <<endl;
451 m_quantizer->quantize(freq);
453 if(!m_quantizer->isPlaying())
454 ui_lblSoundStability->setBackgroundColor(empty_color);
457 if(max_amplitude>=1.0)
458 ui_lblSoundStability->setBackgroundColor(prb_color);
460 ui_lblSoundStability->setBackgroundColor(ok_color);
462 m_freq = m_quantizer->getAverageFrequency();
463 m_error = f2hf(m_freq, m_compared_freq);
466 m_glErrorHistory->addError(m_error);
467 m_dialTune->setError(m_error);
468 m_dialTune->m_avg_error = m_glErrorHistory->m_notes.back().avg_err;
469 m_dialTune->m_min_error = m_glErrorHistory->m_notes.back().min_err;
470 m_dialTune->m_max_error = m_glErrorHistory->m_notes.back().max_err;
471 ui_txtFreq->display(m_freq);
473 // refresh intonal tuning cursor
474 m_microtonalView->setAFreq(Music::GetAFreq());
475 m_microtonalView->updateCursor(m_freq);
478 m_glVolumeHistory->addVolume(max_component);
480 // refresh sample data
481 refresh_data_sample();
483 // refresh formants data
484 refresh_data_harmonics();
486 m_glStatistics->addNote(f2h(m_compared_freq), m_error);
490 if(m_time_refresh_views.elapsed()>50) // 20 images/second max
494 void CustomInstrumentTunerForm::noteStarted(double freq, double dt)
496 // cerr << "CustomInstrumentTunerForm::noteStarted " << freq << "," << dt << endl;
498 // set the compared freq
499 if(m_microtonalView->setting_show->isOn() && m_microtonalView->hasTuningFreqSelected())
500 m_compared_freq = m_microtonalView->getTuningFreq();
502 m_compared_freq = m_quantizer->getCenterFrequency(); // h2f(f2h(freq));
504 if(m_microtonalView->setting_show->isOn() && m_microtonalView->hasTuningFreqSelected())
506 ui_txtNoteFreq->display(int(m_microtonalView->getTuningFreq()*100)/100.0);
507 ui_txtNote->setText(m_microtonalView->getTuningNoteName());
508 if(m_microtonalView->m_selected_jivalue->is_ratio)
510 m_glErrorHistory->addNote(GLErrorHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->num, m_microtonalView->m_selected_jivalue->den));
511 m_glVolumeHistory->addNote(GLVolumeHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->num, m_microtonalView->m_selected_jivalue->den));
515 m_glErrorHistory->addNote(GLErrorHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->cents));
516 m_glVolumeHistory->addNote(GLVolumeHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->cents));
521 ui_txtNoteFreq->display(m_compared_freq);
522 ui_txtNote->setText(h2n(f2h(m_compared_freq)));
523 m_glErrorHistory->addNote(GLErrorHistory::Note(f2h(m_compared_freq)));
524 m_glVolumeHistory->addNote(GLVolumeHistory::Note(f2h(m_compared_freq)));
527 void CustomInstrumentTunerForm::noteFinished(double freq, double dt)
529 m_compared_freq = 0.0;
530 // cerr << "CustomInstrumentTunerForm::noteFinished " << freq << "," << dt << endl;
533 void CustomInstrumentTunerForm::refresh_data_sample()
535 if(m_freq==0.0f || !m_glSample->setting_show->isOn())
541 deque<double> sample;
542 GetWaveSample(m_queue, size_t(m_capture_thread.getSamplingRate()/m_freq), sample);
543 m_glSample->add(m_time.elapsed(), sample);
546 void CustomInstrumentTunerForm::refresh_data_harmonics()
548 if(!(m_algo_combedfft!=NULL &&
550 m_glFreqStruct->setting_show->isOn()))
553 vector<Harmonic> harms = GetHarmonicStruct(m_algo_combedfft->m_plan.out, m_freq, m_glFreqStruct->m_components.size(), m_algo_combedfft->getZeroPaddingFactor());
555 m_glFreqStruct->m_components_max = 0.0;
556 for(int i=0; i<harms.size(); i++)
558 if(harms[i].harm_number<m_glFreqStruct->m_components.size())
560 m_glFreqStruct->m_components[harms[i].harm_number-1] = 20*log10(harms[i].mod/0.001);
562 m_glFreqStruct->m_components_max = max(m_glFreqStruct->m_components_max, m_glFreqStruct->m_components[i]);
567 void CustomInstrumentTunerForm::refresh_views()
569 // cerr << "CustomInstrumentTunerForm::refresh_views " << endl;
571 // m_dialTune->repaint();
573 if(m_glGraph->setting_show->isOn())
574 m_glGraph->updateGL();
576 if(m_glErrorHistory->setting_show->isOn())
577 m_glErrorHistory->updateGL();
579 if(m_glVolumeHistory->setting_show->isOn())
580 m_glVolumeHistory->updateGL();
582 if(m_microtonalView->setting_show->isOn())
583 m_microtonalView->update();
585 if(m_glSample->setting_show->isOn())
586 m_glSample->updateGL();
588 if(m_glFreqStruct->setting_show->isOn())
589 m_glFreqStruct->updateGL();
591 if(m_glFT->setting_show->isOn())
594 if(m_glStatistics->setting_show->isOn())
595 m_glStatistics->updateGL();
597 m_time_refresh_views.start();
600 void CustomInstrumentTunerForm::keyPressEvent(QKeyEvent * e)
606 void CustomInstrumentTunerForm::resizeEvent(QResizeEvent* e)
610 InstrumentTunerForm::resizeEvent(e);
613 void CustomInstrumentTunerForm::update_views()
615 if( !m_glGraph->setting_show->isOn() &&
616 !m_glErrorHistory->setting_show->isOn() &&
617 !m_glVolumeHistory->setting_show->isOn() &&
618 !m_glSample->setting_show->isOn() &&
619 !m_glFreqStruct->setting_show->isOn() &&
620 !m_glFT->setting_show->isOn())
621 m_dialTune->setMaximumWidth(size().width());
623 m_dialTune->setMaximumWidth(ui_rightLayout->minimumSize().width());
625 if(m_glGraph->setting_show->isOn() &&
626 !m_glErrorHistory->setting_show->isOn() &&
627 !m_glVolumeHistory->setting_show->isOn() &&
628 !m_glSample->setting_show->isOn() &&
629 !m_glFreqStruct->setting_show->isOn() &&
630 !m_glFT->setting_show->isOn())
631 m_glGraph->setMaximumHeight(size().height());
633 m_glGraph->setMaximumHeight(m_glGraph->setting_spinMaxHeight->value());
635 if(!m_glErrorHistory->setting_show->isOn() && !m_glVolumeHistory->setting_show->isOn())
636 ui_btnKeepErrorHistory->hide();
638 ui_btnKeepErrorHistory->show();
641 void CustomInstrumentTunerForm::configure()
645 if(m_capture_thread.getCurrentTransportIndex()<m_config_form.ui_cbTransports->count());
646 m_config_form.ui_cbTransports->setCurrentItem(m_capture_thread.getCurrentTransportIndex());
650 m_config_form.ui_grpJACK->setTitle(m_capture_thread.getTransport("JACK")->getDescription());
651 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(m_capture_thread.getSamplingRate()));
653 #ifdef CAPTURE_PORTAUDIO
654 m_config_form.ui_grpPortAudio->setTitle(m_capture_thread.getTransport("PortAudio")->getDescription());
655 m_config_form.ui_spinPortAudioSamplingRate->setValue(m_capture_thread.getSamplingRate());
656 if(m_capture_thread.getTransport("PortAudio"))
661 err = Pa_Initialize();
663 throw QString("PortAudio: CustomInstrumentTunerForm::configure:Pa_Initialize ")+Pa_GetErrorText(err);
664 int numDevices = Pa_GetDeviceCount();
666 int current_index = -1;
667 m_config_form.ui_cbPortAudioDeviceName->clear();
668 m_config_form.ui_cbPortAudioDeviceName->insertItem("default");
669 const PaDeviceInfo* deviceInfo;
670 for(int i=0; i<numDevices; i++)
672 deviceInfo = Pa_GetDeviceInfo(i);
673 m_config_form.ui_cbPortAudioDeviceName->insertItem(QString(deviceInfo->name));
674 if(QString(deviceInfo->name)==m_capture_thread.getTransport("PortAudio")->getSource())
677 m_config_form.ui_cbPortAudioDeviceName->setCurrentItem(current_index);
681 cerr << "CustomInstrumentTunerForm: ERROR: " << error << endl;
687 m_config_form.ui_grpALSA->setTitle(m_capture_thread.getTransport("ALSA")->getDescription());
688 m_config_form.ui_spinALSASamplingRate->setValue(m_capture_thread.getSamplingRate());
691 m_config_form.ui_grpOSS->setTitle(m_capture_thread.getTransport("OSS")->getDescription());
692 m_config_form.ui_spinOSSSamplingRate->setValue(m_capture_thread.getSamplingRate());
695 m_config_form.adjustSize();
696 m_config_form.show();
698 void CustomInstrumentTunerForm::configure_ok()
700 switch(m_config_form.ui_cbTuning->currentItem())
703 SetTuning(CHROMATIC);
706 SetTuning(WERCKMEISTER3);
709 SetTuning(KIRNBERGER3);
718 SetTuning(CHROMATIC);
722 if(m_config_form.ui_cbTonality->currentItem()==0) SetTonality(0);
723 else if(m_config_form.ui_cbTonality->currentItem()==1) SetTonality(+2);
724 else SetTonality(-3);
726 if(m_config_form.ui_cbNotesName->currentItem()==0) SetNotesName(LOCAL_ANGLO);
727 else SetNotesName(LOCAL_LATIN);
728 m_microtonalView->notesNameChanged();
729 m_microtonalView->setAFreq(Music::GetAFreq());
731 SetSemitoneBounds(m_config_form.ui_spinMinHT->value(), m_config_form.ui_spinMaxHT->value());
733 ui_spinA3Offset->setShown(m_config_form.ui_chkShowA4Offset->isOn());
734 ui_lblA3Offset->setShown(m_config_form.ui_chkShowA4Offset->isOn());
737 // ui_txtNote->setText(h2n(m_note));
739 // m_dialTune->setError(-10.0f);
741 // cerr << "b" << endl;
745 if(m_config_form.ui_cbTransports->currentText()=="ALSA")
747 m_capture_thread.selectTransport("ALSA");
748 m_capture_thread.setSource(m_config_form.ui_txtALSAPCMName->text());
749 if(m_config_form.ui_chkALSASamplingRateMax->isChecked())
750 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
752 m_capture_thread.setSamplingRate(m_config_form.ui_spinALSASamplingRate->value());
756 if(m_config_form.ui_cbTransports->currentText()=="JACK")
758 m_capture_thread.selectTransport("JACK");
759 if(m_config_form.ui_chkJACKAutoConnect->isChecked())
760 m_capture_thread.setSource(m_config_form.ui_txtJACKSourcePort->text());
762 m_capture_thread.setSource("");
763 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(m_capture_thread.getSamplingRate()));
766 #ifdef CAPTURE_PORTAUDIO
767 if(m_config_form.ui_cbTransports->currentText()=="PortAudio")
769 m_capture_thread.selectTransport("PortAudio");
770 m_capture_thread.setSource(m_config_form.ui_cbPortAudioDeviceName->currentText());
771 if(m_config_form.ui_chkPortAudioSamplingRateMax->isChecked())
772 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
774 m_capture_thread.setSamplingRate(m_config_form.ui_spinPortAudioSamplingRate->value());
778 if(m_config_form.ui_cbTransports->currentText()=="OSS")
780 m_capture_thread.selectTransport("OSS");
781 m_capture_thread.setSource(m_config_form.ui_txtOSSPCMName->text());
782 if(m_config_form.ui_chkOSSSamplingRateMax->isChecked())
783 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
785 m_capture_thread.setSamplingRate(m_config_form.ui_spinOSSSamplingRate->value());
788 m_timer_refresh.changeInterval(m_config_form.ui_spinRefreshTime->value());
791 m_glGraph->m_treshold = pow(10, m_config_form.ui_spinVolumeTreshold->value()/20.0);
792 m_glGraph->clearValues();
794 // cerr << "c" << endl;
796 if(m_config_form.ui_grpRangeFiltering->isChecked())
798 m_rect_range_filter.reset(int(GetSamplingRate()/h2f(GetSemitoneMin())));
799 // m_fir_range_filter.setImpulseResponse(fir1_highpass(int(GetSamplingRate()/h2f(GetSemitoneMin())), ));
801 if(m_config_form.ui_rdRangeFilteringRectangular->isChecked())
802 m_range_filter = &m_rect_range_filter;
803 else if(m_config_form.ui_rdRangeFilteringFIR->isChecked())
804 m_range_filter = &m_fir_range_filter;
807 m_range_filter = &m_dummy_range_filter;
809 m_algo_combedfft->setWindowFactor(m_config_form.ui_spinWindowSizeFactor->value());
810 // m_glFT->m_zp_factor = m_config_form.ui_spinWindowSizeFactor->value();
811 m_algo_combedfft->useAudibilityRatio(m_config_form.ui_chkAlgoUseSubHarmTresh->isChecked());
812 m_algo_combedfft->setAudibilityRatio(pow(10, -m_config_form.ui_spinCombedFFTAudibilityRatio->value()/20.0));
813 m_algo_combedfft->setAmplitudeTreshold(pow(10, m_config_form.ui_spinVolumeTreshold->value()/20.0));
814 m_algo_combedfft->setComponentTreshold(pow(10, m_config_form.ui_spinVolumeTreshold->value()/20.0));
816 // cerr << "d" << endl;
819 m_quantizer->reset();
820 if(m_config_form.ui_grpQuantizer->isChecked())
822 m_latency_quantizer.setLatency(m_config_form.ui_spinErrorLatency->value());
823 m_quantizer = &m_latency_quantizer;
826 m_quantizer = &m_dummy_quantizer;
828 // cerr << pow(10, -m_config_form.ui_spinCombedFFTAudibilityRatio->value()/20.0) << endl;
830 if(!pauseAction->isOn() && !m_capture_thread.isCapturing())
831 m_capture_thread.startCapture();
834 void CustomInstrumentTunerForm::saveSettings()
840 m_settings.writeEntry("width", width());
841 m_settings.writeEntry("height", height());
842 m_settings.writeEntry("ui_tbViews", ui_tbViews->isShown());
843 m_settings.writeEntry("ui_tbButtons", ui_tbButtons->isShown());
846 m_settings.writeEntry(m_config_form.ui_cbTransports->name(), m_config_form.ui_cbTransports->currentText());
847 #ifdef CAPTURE_PORTAUDIO
848 m_settings.writeEntry(m_config_form.ui_cbPortAudioDeviceName->name(), m_config_form.ui_cbPortAudioDeviceName->currentText());
851 void CustomInstrumentTunerForm::loadSettings()
857 resize(m_settings.readNumEntry("width", 800), m_settings.readNumEntry("height", 600));
858 ui_tbViews->setShown(m_settings.readBoolEntry("ui_tbViews", ui_tbViews->isShown()));
859 ui_tbButtons->setShown(m_settings.readBoolEntry("ui_tbButtons", ui_tbButtons->isShown()));
862 QString saved_transport = m_settings.readEntry(m_config_form.ui_cbTransports->name(), "");
863 if(saved_transport!="")
864 for(int i=0; i<m_config_form.ui_cbTransports->count(); i++)
865 if(m_config_form.ui_cbTransports->text(i)==saved_transport)
866 m_config_form.ui_cbTransports->setCurrentItem(i);
867 #ifdef CAPTURE_PORTAUDIO
868 // cerr << "read: " << m_settings.readEntry("ui_cbPortAudioDeviceName", "default") << endl;
869 QString saved_device = m_settings.readEntry(m_config_form.ui_cbPortAudioDeviceName->name(), "default");
873 err = Pa_Initialize();
875 throw QString("PortAudio: CustomInstrumentTunerForm::loadSettings:Pa_Initialize ")+Pa_GetErrorText(err);
876 int numDevices = Pa_GetDeviceCount();
878 int current_index = 0;
879 m_config_form.ui_cbPortAudioDeviceName->clear();
880 m_config_form.ui_cbPortAudioDeviceName->insertItem("default");
881 const PaDeviceInfo* deviceInfo;
882 for(int i=0; i<numDevices; i++)
884 deviceInfo = Pa_GetDeviceInfo(i);
885 m_config_form.ui_cbPortAudioDeviceName->insertItem(QString(deviceInfo->name));
886 if(QString(deviceInfo->name)==saved_device)
889 if(current_index<m_config_form.ui_cbPortAudioDeviceName->count())
890 m_config_form.ui_cbPortAudioDeviceName->setCurrentItem(current_index);
894 // cerr << "CustomInstrumentTunerForm: ERROR: " << error << endl;
900 void CustomInstrumentTunerForm::restoreFactorySettings()
902 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)
905 View::clearAllSettings();
907 m_settings.removeEntry("width");
908 m_settings.removeEntry("height");
909 m_settings.removeEntry("ui_tbViews");
910 m_settings.removeEntry("ui_tbButtons");
912 m_settings.removeEntry(m_config_form.ui_cbPortAudioDeviceName->name());
914 QMessageBox::information(this, tr("Restore Factory Settings"), tr("You can now restart FMIT to get back factory settings"), QMessageBox::Ok, QMessageBox::NoButton);
918 void CustomInstrumentTunerForm::helpAbout()
921 text = "<h2>Free Music Instrument Tuner</h2>";
922 text += tr("<h3>Version ")+PACKAGE_VERSION;
923 text += tr("</h3><p><h3>Website:</h3><p>homepage: <a href=\"http://home.gna.org/fmit\">http://home.gna.org/fmit</a>");
924 text += tr("<p>development site: <a href=\"http://gna.org/projects/fmit\">http://gna.org/projects/fmit</a>");
925 text += tr("<p>donation link: <a href=\"http://home.gna.org/fmit/donation.html\">http://home.gna.org/fmit/donation.html</a>");
926 text += tr("<p><h3>Author:</h3><p>Gilles Degottex [gilles.degottex@net2000.ch]");
927 #ifdef PACKAGER_STRING
928 if(PACKAGER_STRING!="")
929 text += tr("<p><h3>Packager:</h3><p>")+QString(PACKAGER_STRING).replace(QChar('<'),"[").replace(QChar('>'),"]");
932 QDialog about_dlg(this);
934 QTextEdit* textEdit1;
935 QPushButton* pushButton1;
936 QVBoxLayout* Form2Layout;
937 QHBoxLayout* layout1;
938 QSpacerItem* spacer1;
939 QSpacerItem* spacer2;
941 about_dlg.setName( tr("about_box") );
942 about_dlg.setCaption( tr("About Free Music Instrument Tuner") );
943 Form2Layout = new QVBoxLayout( &about_dlg, 11, 6, "Form2Layout");
945 textEdit1 = new QTextEdit( &about_dlg, "textEdit1" );
946 textEdit1->setText(text);
947 textEdit1->setReadOnly(true);
948 Form2Layout->addWidget( textEdit1 );
950 layout1 = new QHBoxLayout( 0, 0, 6, "layout1");
951 spacer1 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
952 layout1->addItem( spacer1 );
954 pushButton1 = new QPushButton( &about_dlg, "pushButton1" );
955 pushButton1->setText( tr( "OK" ) );
956 layout1->addWidget( pushButton1 );
957 connect(pushButton1, SIGNAL(clicked()), &about_dlg, SLOT(close()));
958 spacer2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
959 layout1->addItem( spacer2 );
960 Form2Layout->addLayout( layout1 );
961 about_dlg.resize( QSize(400, textEdit1->heightForWidth(400)+100) );
962 // about_dlg.clearWState( WState_Polished );
967 CustomInstrumentTunerForm::~CustomInstrumentTunerForm()
969 if(m_config_form.ui_chkAutoSaveOnExit->isChecked())
973 m_settings.beginGroup("Auto/");
974 m_settings.save(m_config_form.ui_chkAutoSaveOnExit);
975 m_settings.endGroup();
980 bool displayInBrowser(const QString& theURL)
983 //TODO replace with less buggy ShellExecuteEx?
985 int)::ShellExecute(qApp->mainWidget()->winId(),NULL,theURL,NULL,NULL,SW_SHOW)
988 OFMessageBox::criticalMessageOK(QMessageBox::tr("Unable to display a
989 web browser. Ensure that you have a web browser installed."));
992 //TODO warn if netscape not installed
993 QString aCommand("netscape ");
996 if (system(aCommand) != 0)
998 OFMessageBox::criticalMessageOK(QMessageBox::tr("Unable to display a
999 netscape browser. You need to have netscape installed and in the