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_cbTonality);
67 m_settings.add(m_config_form.ui_cbNotesName);
68 m_settings.add(ui_spinAFreq);
69 m_settings.add(ui_spinA3Offset);
71 m_settings.add(m_config_form.ui_chkAutoDetect);
73 m_settings.add(m_config_form.ui_chkJACKAutoConnect);
74 m_settings.add(m_config_form.ui_txtJACKSourcePort);
77 m_settings.add(m_config_form.ui_chkALSASamplingRateMax);
78 m_settings.add(m_config_form.ui_spinALSASamplingRate);
79 m_settings.add(m_config_form.ui_chkALSAMixMultipleChannels);
80 m_settings.add(m_config_form.ui_txtALSAPCMName);
83 m_settings.add(m_config_form.ui_chkOSSSamplingRateMax);
84 m_settings.add(m_config_form.ui_spinOSSSamplingRate);
85 m_settings.add(m_config_form.ui_chkOSSMixMultipleChannels);
86 m_settings.add(m_config_form.ui_txtOSSPCMName);
88 #ifdef CAPTURE_PORTAUDIO
89 m_settings.add(m_config_form.ui_chkPortAudioSamplingRateMax);
90 m_settings.add(m_config_form.ui_spinPortAudioSamplingRate);
91 m_settings.add(m_config_form.ui_chkPortAudioMixMultipleChannels);
94 m_settings.add(m_config_form.ui_spinRefreshTime);
95 m_settings.add(m_config_form.ui_spinMinHT);
96 m_settings.add(m_config_form.ui_spinMaxHT);
98 m_settings.add(m_config_form.ui_grpRangeFiltering);
99 m_settings.add(m_config_form.ui_rdRangeFilteringRectangular);
100 m_settings.add(m_config_form.ui_rdRangeFilteringFIR);
102 m_settings.add(m_config_form.ui_spinVolumeTreshold);
103 m_settings.add(m_config_form.ui_spinWindowSizeFactor);
104 m_settings.add(m_config_form.ui_chkAlgoUseSubHarmTresh);
105 m_settings.add(m_config_form.ui_spinCombedFFTAudibilityRatio);
107 m_settings.add(m_config_form.up_grpFreqRefinement);
108 m_settings.add(m_config_form.ui_rdUseFreqRefinement);
109 m_settings.add(m_config_form.ui_spinFreqRefinMaxHarm);
110 m_settings.add(m_config_form.ui_rdUseTimeRefinement);
111 m_settings.add(m_config_form.ui_spinTimeRefinMaxPeriod);
113 m_settings.add(m_config_form.ui_grpQuantizer);
114 m_settings.add(m_config_form.ui_spinErrorLatency);
116 m_algo_combedfft = new CombedFT();
118 for(int i=0; i<m_capture_thread.getTransports().size(); i++)
119 m_config_form.ui_cbTransports->insertItem(m_capture_thread.getTransports()[i]->getName());
121 if(m_capture_thread.getTransports().empty())
122 QMessageBox::critical(this, "Error", "Please compile me with a capture system ...");
123 if(m_capture_thread.getTransports().size()==1)
125 m_config_form.ui_lblSelectedCaptureSystem->hide();
126 m_config_form.ui_btnAutoDetect->hide();
127 m_config_form.ui_chkAutoDetect->hide();
128 m_config_form.ui_cbTransports->hide();
130 m_config_form.ui_grpALSA->hide();
131 m_config_form.ui_grpJACK->hide();
132 m_config_form.ui_grpPortAudio->hide();
133 m_config_form.ui_grpOSS->hide();
135 ui_lblA3Offset->hide();
136 ui_spinA3Offset->hide();
138 connect(&m_capture_thread, SIGNAL(samplingRateChanged(int)), this, SLOT(samplingRateChanged(int)));
139 connect(&m_capture_thread, SIGNAL(errorRaised(const QString&)), this, SLOT(errorRaised(const QString&)));
140 connect(&m_capture_thread, SIGNAL(transportChanged(const QString&)), this, SLOT(transportChanged(const QString&)));
142 connect(&m_latency_quantizer, SIGNAL(noteStarted(double,double)), this, SLOT(noteStarted(double,double)));
143 connect(&m_latency_quantizer, SIGNAL(noteFinished(double,double)), this, SLOT(noteFinished(double,double)));
144 connect(&m_dummy_quantizer, SIGNAL(noteStarted(double,double)), this, SLOT(noteStarted(double,double)));
145 connect(&m_dummy_quantizer, SIGNAL(noteFinished(double,double)), this, SLOT(noteFinished(double,double)));
147 m_dialTune = new DialView(centralWidget());
148 ui_dialTuneLayout->addWidget(m_dialTune);
150 m_glGraph = new GLGraph("Graph", centralWidget());
151 connect(m_glGraph->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
152 connect(m_glGraph->setting_spinMaxHeight, SIGNAL(valueChanged(int)), this, SLOT(update_views()));
153 m_glGraph->setting_show->addTo(ui_tbViews);
154 ui_graphLayout->addWidget(m_glGraph);
156 m_glErrorHistory = new GLErrorHistory(centralWidget());
157 connect(m_glErrorHistory->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
158 m_glErrorHistory->setting_show->addTo(ui_tbViews);
159 ui_errorLayout->addWidget(m_glErrorHistory);
162 connect(m_dialTune->setting_spinScale, SIGNAL(valueChanged(int)), m_glErrorHistory->setting_spinScale, SLOT(setValue(int)));
163 connect(m_glErrorHistory->setting_spinScale, SIGNAL(valueChanged(int)), m_dialTune->setting_spinScale, SLOT(setValue(int)));
164 connect(m_dialTune->setting_useCents, SIGNAL(toggled(bool)), m_glErrorHistory->setting_useCents, SLOT(setOn(bool)));
165 connect(m_glErrorHistory->setting_useCents, SIGNAL(toggled(bool)), m_dialTune->setting_useCents, SLOT(setOn(bool)));
167 m_glVolumeHistory = new GLVolumeHistory(centralWidget());
168 connect(m_config_form.ui_spinVolumeTreshold, SIGNAL(valueChanged(int)), m_glVolumeHistory, SLOT(setVolumeTreshold(int)));
169 connect(m_glVolumeHistory->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
170 m_glVolumeHistory->setting_show->addTo(ui_tbViews);
171 ui_volumeLayout->addWidget(m_glVolumeHistory);
173 // link keep settings
174 connect(ui_btnKeepErrorHistory, SIGNAL(toggled(bool)), m_glErrorHistory->setting_keep, SLOT(setOn(bool)));
175 connect(m_glErrorHistory->setting_keep, SIGNAL(toggled(bool)), m_glVolumeHistory->setting_keep, SLOT(setOn(bool)));
176 connect(m_glErrorHistory->setting_keep, SIGNAL(toggled(bool)), ui_btnKeepErrorHistory, SLOT(setOn(bool)));
178 m_glSample = new GLSample(centralWidget());
179 connect(m_glSample->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
180 m_glSample->setting_show->addTo(ui_tbViews);
181 ui_sampleLayout->addWidget(m_glSample);
183 m_glFreqStruct = new GLFreqStruct(centralWidget());
184 connect(m_glFreqStruct->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
185 m_glFreqStruct->setting_show->addTo(ui_tbViews);
186 ui_formantsLayout->addWidget(m_glFreqStruct);
188 m_glFT = new GLFT(centralWidget());
189 connect(m_glFT->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
190 m_glFT->setting_show->addTo(ui_tbViews);
191 ui_FT->addWidget(m_glFT);
193 m_microtonalView = new MicrotonalView(centralWidget());
194 connect(m_microtonalView->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
195 connect(m_microtonalView, SIGNAL(tuningFreqChanged(float)), this, SLOT(tuningFreqChanged(float)));
196 m_microtonalView->setting_show->addTo(ui_tbViews);
197 ui_microtonalLayout->addWidget(m_microtonalView);
199 m_glStatistics = new GLStatistics(centralWidget());
200 connect(m_glStatistics->setting_show, SIGNAL(toggled(bool)), this, SLOT(update_views()));
201 m_glStatistics->setting_show->addTo(ui_tbViews);
202 ui_microtonalLayout->addWidget(m_glStatistics);
204 connect(m_dialTune->setting_spinScale, SIGNAL(valueChanged(int)), m_glStatistics->setting_spinScale, SLOT(setValue(int)));
205 connect(m_glStatistics->setting_spinScale, SIGNAL(valueChanged(int)), m_dialTune->setting_spinScale, SLOT(setValue(int)));
206 connect(m_dialTune->setting_useCents, SIGNAL(toggled(bool)), m_glStatistics->setting_useCents, SLOT(setOn(bool)));
207 connect(m_glStatistics->setting_useCents, SIGNAL(toggled(bool)), m_dialTune->setting_useCents, SLOT(setOn(bool)));
209 connect(m_dialTune->setting_showTolerance, SIGNAL(toggled(bool)), m_glStatistics->setting_showTolerance, SLOT(setOn(bool)));
210 connect(m_glStatistics->setting_showTolerance, SIGNAL(toggled(bool)), m_dialTune->setting_showTolerance, SLOT(setOn(bool)));
212 connect(m_config_form.buttonOk, SIGNAL(clicked()), this, SLOT(configure_ok()));
213 connect(m_config_form.ui_btnRestoreFactorySettings, SIGNAL(clicked()), this, SLOT(restoreFactorySettings()));
214 connect(m_config_form.ui_spinMinHT, SIGNAL(valueChanged(int)), this, SLOT(noteRangeChanged()));
215 connect(m_config_form.ui_spinMaxHT, SIGNAL(valueChanged(int)), this, SLOT(noteRangeChanged()));
216 connect(m_config_form.ui_cbTonality, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
217 connect(m_config_form.ui_cbNotesName, SIGNAL(highlighted(int)), this, SLOT(noteRangeChanged()));
218 connect(m_config_form.ui_btnAutoDetect, SIGNAL(clicked()), this, SLOT(autoDetectTransport()));
219 connect(m_config_form.ui_cbTransports, SIGNAL(activated(const QString&)), this, SLOT(selectTransport(const QString&)));
220 connect(m_config_form.ui_chkALSAMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
221 connect(m_config_form.ui_chkOSSMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
222 connect(m_config_form.ui_chkPortAudioMixMultipleChannels, SIGNAL(toggled(bool)), &m_capture_thread, SLOT(setMixMultipleChannels(bool)));
226 if(m_config_form.ui_chkAutoDetect->isChecked())
227 m_capture_thread.autoDetectTransport();
231 if(m_config_form.ui_chkFullScreen->isChecked())
234 m_time_refresh_views.start();
235 m_time_refresh.start();
237 connect((QObject*)&m_timer_refresh, SIGNAL(timeout()), this, SLOT(refresh()));
238 m_timer_refresh.start(m_config_form.ui_spinRefreshTime->value());
241 void CustomInstrumentTunerForm::transportChanged(const QString& name)
243 selectTransport(name);
245 if(m_capture_thread.getCurrentTransportIndex()!=m_config_form.ui_cbTransports->currentItem())
246 m_config_form.ui_cbTransports->setCurrentItem(m_capture_thread.getCurrentTransportIndex());
248 void CustomInstrumentTunerForm::selectTransport(const QString& name)
250 m_config_form.ui_grpALSA->hide();
251 m_config_form.ui_grpJACK->hide();
252 m_config_form.ui_grpPortAudio->hide();
253 m_config_form.ui_grpOSS->hide();
255 if(name=="ALSA") m_config_form.ui_grpALSA->show();
256 else if(name=="JACK") m_config_form.ui_grpJACK->show();
257 else if(name=="PortAudio") m_config_form.ui_grpPortAudio->show();
258 else if(name=="OSS") m_config_form.ui_grpOSS->show();
260 void CustomInstrumentTunerForm::autoDetectTransport()
262 m_capture_thread.autoDetectTransport();
264 // here transportChanged will be called
267 void CustomInstrumentTunerForm::toggleFullScreen()
269 static bool fs = true;
272 m_config_form.ui_chkFullScreen->setChecked(true);
277 m_config_form.ui_chkFullScreen->setChecked(false);
283 void CustomInstrumentTunerForm::noteRangeChanged()
285 // cerr << "CustomInstrumentTunerForm::noteRangeChanged" << endl;
287 m_config_form.ui_txtMinHT->setText(h2n(m_config_form.ui_spinMinHT->value())+" = "+QString::number(h2f(m_config_form.ui_spinMinHT->value()))+" hz");
288 m_config_form.ui_txtMaxHT->setText(h2n(m_config_form.ui_spinMaxHT->value())+" = "+QString::number(h2f(m_config_form.ui_spinMaxHT->value()))+" hz");
291 void CustomInstrumentTunerForm::errorRaised(const QString& error)
293 // cerr << "CustomInstrumentTunerForm::errorRaised " << error << endl;
295 statusBar()->message(QString("ERROR: ")+error);
297 ui_lblSoundStability->setBackgroundColor(QColor(180,74,74));
300 void CustomInstrumentTunerForm::samplingRateChanged(int sampling_rate)
302 // cerr << "CustomInstrumentTunerForm::samplingRateChanged " << sampling_rate << endl;
304 Music::SetSamplingRate(sampling_rate);
306 m_rect_range_filter.reset(int(GetSamplingRate()/h2f(GetSemitoneMin())));
308 if(m_config_form.ui_cbTransports->currentText()=="JACK")
309 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(sampling_rate));
312 void CustomInstrumentTunerForm::ui_spinAFreq_valueChanged(int AFreq)
315 if(m_config_form.ui_chkShowA4Offset->isOn())
316 A = h2f(ui_spinA3Offset->value()*1/100.0f, A);
318 // cerr << A << endl;
320 void CustomInstrumentTunerForm::ui_spinAOffset_valueChanged(int offset)
322 double A = ui_spinAFreq->value();
323 if(m_config_form.ui_chkShowA4Offset->isOn())
324 A = h2f(offset*1/100.0f, ui_spinAFreq->value());
326 // cerr << A << endl;
329 void CustomInstrumentTunerForm::tuningFreqChanged(float freq)
331 // cerr << "CustomInstrumentTunerForm::tuningFreqChanged " << freq << endl;
335 if(m_compared_freq!=0.0f)
337 ui_txtNoteFreq->display(m_compared_freq);
338 ui_txtNote->setText(h2n(f2h(m_compared_freq)));
343 m_compared_freq = freq;
344 ui_txtNoteFreq->display(int(freq*100)/100.0f);
345 ui_txtNote->setText(m_microtonalView->getTuningNoteName());
348 m_quantizer->reset();
350 // m_dialTune->setError(-10.0f);
353 void CustomInstrumentTunerForm::pause(bool on)
355 m_capture_thread.togglePause(on);
357 if(on) m_timer_refresh.stop();
358 else m_timer_refresh.start(m_config_form.ui_spinRefreshTime->value());
361 void CustomInstrumentTunerForm::refresh()
363 double elapsed_time = m_time_refresh.elapsed();
364 m_time_refresh.start();
366 QColor capture_failed_color(180,74,74);
367 QColor prb_color(208,146,0);
368 QColor empty_color(128,128,128);
369 QColor ok_color(83,165,105);
371 // 1/{time between refresh} = {nb refresh by seconds}
372 // limit the nb new data by fs/{nb refresh by seconds}
373 // add 1 to {nb refresh by second} to eventualy recover lags
374 int limit = int( m_capture_thread.getSamplingRate() /
375 (1.0/(m_config_form.ui_spinRefreshTime->value()/1000.0) - 1));
377 // cerr << "REFRESH ";
379 m_capture_thread.lock();
380 double max_amplitude = 0.0;
382 while(!m_capture_thread.m_values.empty() &&
383 (m_capture_thread.m_values.size()>m_capture_thread.getPacketSizeSinceLastLock() || nb_new_data<limit))
385 // cerr << m_capture_thread.m_values.back() << " ";
386 double value = (*m_range_filter)(m_capture_thread.m_values.back());
387 // cerr << value << " ";
388 m_capture_thread.m_values.pop_back();
390 max_amplitude = max(max_amplitude, fabs(value));
392 m_queue.push_front(value);
393 if(m_glGraph) m_glGraph->addValue(value);
394 if(m_glFT) m_glFT->buff.push_front(value);
398 m_capture_thread.unlock();
402 int max_size = max(m_range_filter->getLength(), max(m_glGraph->getLength(), m_algo_combedfft->getMinSize()));
403 while(!m_queue.empty() && int(m_queue.size())>max_size)
406 // refresh graph data
407 m_glGraph->refreshGraph(); // TODO refresh the view each time ??
408 m_glFT->refreshGraph();
410 // ------- Analysis stage -------
412 // if something goes wrong in the capture system
413 if(nb_new_data==0 || m_algo_combedfft==NULL
414 || elapsed_time>8*m_config_form.ui_spinRefreshTime->value())
415 ui_lblSoundStability->setBackgroundColor(capture_failed_color);
418 m_algo_combedfft->apply(m_queue);
420 double max_component = 20*log10(m_algo_combedfft->getComponentsMax());
421 ui_pgbVolume->setProgress(100+int(max_component));
424 if(m_algo_combedfft->hasNoteRecognized())
425 freq = m_algo_combedfft->getFondamentalFreq();
427 double freq_rel = freq*m_algo_combedfft->m_plan.in.size()/double(GetSamplingRate());
428 if(freq_rel<1 || freq_rel>(m_algo_combedfft->m_plan.out.size()/2))
431 // frequency refinement
432 if(freq>0.0 && m_config_form.up_grpFreqRefinement->isChecked())
434 if(m_config_form.ui_rdUseFreqRefinement->isChecked())
436 freq = FundFreqRefinementOfHarmonicStruct(m_algo_combedfft->m_plan.out, freq, m_config_form.ui_spinFreqRefinMaxHarm->value(), m_algo_combedfft->getZeroPaddingFactor());
439 else if(m_config_form.ui_rdUseTimeRefinement->isChecked())
441 double period = GetAveragePeriodFromApprox(m_queue, int(GetSamplingRate()/freq), m_config_form.ui_spinTimeRefinMaxPeriod->value());
443 freq = GetSamplingRate()/period;
447 // cerr << "2) test freq=" << m_test_freq <<endl;
449 m_quantizer->quantize(freq);
451 if(!m_quantizer->isPlaying())
452 ui_lblSoundStability->setBackgroundColor(empty_color);
455 if(max_amplitude>=1.0)
456 ui_lblSoundStability->setBackgroundColor(prb_color);
458 ui_lblSoundStability->setBackgroundColor(ok_color);
460 m_freq = m_quantizer->getAverageFrequency();
461 m_error = f2hf(m_freq, m_compared_freq);
464 m_glErrorHistory->addError(m_error);
465 m_dialTune->setError(m_error);
466 m_dialTune->m_avg_error = m_glErrorHistory->m_notes.back().avg_err;
467 m_dialTune->m_min_error = m_glErrorHistory->m_notes.back().min_err;
468 m_dialTune->m_max_error = m_glErrorHistory->m_notes.back().max_err;
469 ui_txtFreq->display(m_freq);
471 // refresh intonal tuning cursor
472 m_microtonalView->setAFreq(Music::GetAFreq());
473 m_microtonalView->updateCursor(m_freq);
476 m_glVolumeHistory->addVolume(max_component);
478 // refresh sample data
479 refresh_data_sample();
481 // refresh formants data
482 refresh_data_harmonics();
484 m_glStatistics->addNote(f2h(m_compared_freq), m_error);
488 if(m_time_refresh_views.elapsed()>50) // 20 images/second max
492 void CustomInstrumentTunerForm::noteStarted(double freq, double dt)
494 // cerr << "CustomInstrumentTunerForm::noteStarted " << freq << "," << dt << endl;
496 // set the compared freq
497 if(m_microtonalView->setting_show->isOn() && m_microtonalView->hasTuningFreqSelected())
498 m_compared_freq = m_microtonalView->getTuningFreq();
500 m_compared_freq = m_quantizer->getCenterFrequency(); // h2f(f2h(freq));
502 if(m_microtonalView->setting_show->isOn() && m_microtonalView->hasTuningFreqSelected())
504 ui_txtNoteFreq->display(int(m_microtonalView->getTuningFreq()*100)/100.0);
505 ui_txtNote->setText(m_microtonalView->getTuningNoteName());
506 if(m_microtonalView->m_selected_jivalue->is_ratio)
508 m_glErrorHistory->addNote(GLErrorHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->num, m_microtonalView->m_selected_jivalue->den));
509 m_glVolumeHistory->addNote(GLVolumeHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->num, m_microtonalView->m_selected_jivalue->den));
513 m_glErrorHistory->addNote(GLErrorHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->cents));
514 m_glVolumeHistory->addNote(GLVolumeHistory::Note(m_microtonalView->setting_selectedRoot, m_microtonalView->m_selected_jivalue->cents));
519 ui_txtNoteFreq->display(m_compared_freq);
520 ui_txtNote->setText(h2n(f2h(m_compared_freq)));
521 m_glErrorHistory->addNote(GLErrorHistory::Note(f2h(m_compared_freq)));
522 m_glVolumeHistory->addNote(GLVolumeHistory::Note(f2h(m_compared_freq)));
525 void CustomInstrumentTunerForm::noteFinished(double freq, double dt)
527 m_compared_freq = 0.0;
528 // cerr << "CustomInstrumentTunerForm::noteFinished " << freq << "," << dt << endl;
531 void CustomInstrumentTunerForm::refresh_data_sample()
533 if(m_freq==0.0f || !m_glSample->setting_show->isOn())
539 deque<double> sample;
540 GetWaveSample(m_queue, size_t(m_capture_thread.getSamplingRate()/m_freq), sample);
541 m_glSample->add(m_time.elapsed(), sample);
544 void CustomInstrumentTunerForm::refresh_data_harmonics()
546 if(!(m_algo_combedfft!=NULL &&
548 m_glFreqStruct->setting_show->isOn()))
551 vector<Harmonic> harms = GetHarmonicStruct(m_algo_combedfft->m_plan.out, m_freq, m_glFreqStruct->m_components.size(), m_algo_combedfft->getZeroPaddingFactor());
553 m_glFreqStruct->m_components_max = 0.0;
554 for(int i=0; i<harms.size(); i++)
556 if(harms[i].harm_number<m_glFreqStruct->m_components.size())
558 m_glFreqStruct->m_components[harms[i].harm_number-1] = 20*log10(harms[i].mod/0.001);
560 m_glFreqStruct->m_components_max = max(m_glFreqStruct->m_components_max, m_glFreqStruct->m_components[i]);
565 void CustomInstrumentTunerForm::refresh_views()
567 // cerr << "CustomInstrumentTunerForm::refresh_views " << endl;
569 // m_dialTune->repaint();
571 if(m_glGraph->setting_show->isOn())
572 m_glGraph->updateGL();
574 if(m_glErrorHistory->setting_show->isOn())
575 m_glErrorHistory->updateGL();
577 if(m_glVolumeHistory->setting_show->isOn())
578 m_glVolumeHistory->updateGL();
580 if(m_microtonalView->setting_show->isOn())
581 m_microtonalView->update();
583 if(m_glSample->setting_show->isOn())
584 m_glSample->updateGL();
586 if(m_glFreqStruct->setting_show->isOn())
587 m_glFreqStruct->updateGL();
589 if(m_glFT->setting_show->isOn())
592 if(m_glStatistics->setting_show->isOn())
593 m_glStatistics->updateGL();
595 m_time_refresh_views.start();
598 void CustomInstrumentTunerForm::keyPressEvent(QKeyEvent * e)
604 void CustomInstrumentTunerForm::resizeEvent(QResizeEvent* e)
608 InstrumentTunerForm::resizeEvent(e);
611 void CustomInstrumentTunerForm::update_views()
613 if( !m_glGraph->setting_show->isOn() &&
614 !m_glErrorHistory->setting_show->isOn() &&
615 !m_glVolumeHistory->setting_show->isOn() &&
616 !m_glSample->setting_show->isOn() &&
617 !m_glFreqStruct->setting_show->isOn() &&
618 !m_glFT->setting_show->isOn())
619 m_dialTune->setMaximumWidth(size().width());
621 m_dialTune->setMaximumWidth(ui_rightLayout->minimumSize().width());
623 if(m_glGraph->setting_show->isOn() &&
624 !m_glErrorHistory->setting_show->isOn() &&
625 !m_glVolumeHistory->setting_show->isOn() &&
626 !m_glSample->setting_show->isOn() &&
627 !m_glFreqStruct->setting_show->isOn() &&
628 !m_glFT->setting_show->isOn())
629 m_glGraph->setMaximumHeight(size().height());
631 m_glGraph->setMaximumHeight(m_glGraph->setting_spinMaxHeight->value());
633 if(!m_glErrorHistory->setting_show->isOn() && !m_glVolumeHistory->setting_show->isOn())
634 ui_btnKeepErrorHistory->hide();
636 ui_btnKeepErrorHistory->show();
639 void CustomInstrumentTunerForm::configure()
643 if(m_capture_thread.getCurrentTransportIndex()<m_config_form.ui_cbTransports->count());
644 m_config_form.ui_cbTransports->setCurrentItem(m_capture_thread.getCurrentTransportIndex());
648 m_config_form.ui_grpJACK->setTitle(m_capture_thread.getTransport("JACK")->getDescription());
649 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(m_capture_thread.getSamplingRate()));
651 #ifdef CAPTURE_PORTAUDIO
652 m_config_form.ui_grpPortAudio->setTitle(m_capture_thread.getTransport("PortAudio")->getDescription());
653 m_config_form.ui_spinPortAudioSamplingRate->setValue(m_capture_thread.getSamplingRate());
654 if(m_capture_thread.getTransport("PortAudio"))
659 err = Pa_Initialize();
661 throw QString("PortAudio: CustomInstrumentTunerForm::configure:Pa_Initialize ")+Pa_GetErrorText(err);
662 int numDevices = Pa_GetDeviceCount();
664 int current_index = -1;
665 m_config_form.ui_cbPortAudioDeviceName->clear();
666 m_config_form.ui_cbPortAudioDeviceName->insertItem("default");
667 const PaDeviceInfo* deviceInfo;
668 for(int i=0; i<numDevices; i++)
670 deviceInfo = Pa_GetDeviceInfo(i);
671 m_config_form.ui_cbPortAudioDeviceName->insertItem(QString(deviceInfo->name));
672 if(QString(deviceInfo->name)==m_capture_thread.getTransport("PortAudio")->getSource())
675 m_config_form.ui_cbPortAudioDeviceName->setCurrentItem(current_index);
679 cerr << "CustomInstrumentTunerForm: ERROR: " << error << endl;
685 m_config_form.ui_grpALSA->setTitle(m_capture_thread.getTransport("ALSA")->getDescription());
686 m_config_form.ui_spinALSASamplingRate->setValue(m_capture_thread.getSamplingRate());
689 m_config_form.ui_grpOSS->setTitle(m_capture_thread.getTransport("OSS")->getDescription());
690 m_config_form.ui_spinOSSSamplingRate->setValue(m_capture_thread.getSamplingRate());
693 m_config_form.adjustSize();
694 m_config_form.show();
696 void CustomInstrumentTunerForm::configure_ok()
698 if(m_config_form.ui_cbTonality->currentItem()==0) SetTonality(0);
699 else if(m_config_form.ui_cbTonality->currentItem()==1) SetTonality(+2);
700 else SetTonality(-3);
702 if(m_config_form.ui_cbNotesName->currentItem()==0) SetNotesName(LOCAL_ANGLO);
703 else SetNotesName(LOCAL_LATIN);
704 m_microtonalView->notesNameChanged();
705 m_microtonalView->setAFreq(Music::GetAFreq());
707 SetSemitoneBounds(m_config_form.ui_spinMinHT->value(), m_config_form.ui_spinMaxHT->value());
709 ui_spinA3Offset->setShown(m_config_form.ui_chkShowA4Offset->isOn());
710 ui_lblA3Offset->setShown(m_config_form.ui_chkShowA4Offset->isOn());
713 // ui_txtNote->setText(h2n(m_note));
715 // m_dialTune->setError(-10.0f);
717 // cerr << "b" << endl;
721 if(m_config_form.ui_cbTransports->currentText()=="ALSA")
723 m_capture_thread.selectTransport("ALSA");
724 m_capture_thread.setSource(m_config_form.ui_txtALSAPCMName->text());
725 if(m_config_form.ui_chkALSASamplingRateMax->isChecked())
726 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
728 m_capture_thread.setSamplingRate(m_config_form.ui_spinALSASamplingRate->value());
732 if(m_config_form.ui_cbTransports->currentText()=="JACK")
734 m_capture_thread.selectTransport("JACK");
735 if(m_config_form.ui_chkJACKAutoConnect->isChecked())
736 m_capture_thread.setSource(m_config_form.ui_txtJACKSourcePort->text());
738 m_capture_thread.setSource("");
739 m_config_form.ui_lblJACKSamplingRate->setText(QString::number(m_capture_thread.getSamplingRate()));
742 #ifdef CAPTURE_PORTAUDIO
743 if(m_config_form.ui_cbTransports->currentText()=="PortAudio")
745 m_capture_thread.selectTransport("PortAudio");
746 m_capture_thread.setSource(m_config_form.ui_cbPortAudioDeviceName->currentText());
747 if(m_config_form.ui_chkPortAudioSamplingRateMax->isChecked())
748 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
750 m_capture_thread.setSamplingRate(m_config_form.ui_spinPortAudioSamplingRate->value());
754 if(m_config_form.ui_cbTransports->currentText()=="OSS")
756 m_capture_thread.selectTransport("OSS");
757 m_capture_thread.setSource(m_config_form.ui_txtOSSPCMName->text());
758 if(m_config_form.ui_chkOSSSamplingRateMax->isChecked())
759 m_capture_thread.setSamplingRate(CaptureThread::SAMPLING_RATE_MAX);
761 m_capture_thread.setSamplingRate(m_config_form.ui_spinOSSSamplingRate->value());
764 m_timer_refresh.changeInterval(m_config_form.ui_spinRefreshTime->value());
767 m_glGraph->m_treshold = pow(10, m_config_form.ui_spinVolumeTreshold->value()/20.0);
768 m_glGraph->clearValues();
770 // cerr << "c" << endl;
772 if(m_config_form.ui_grpRangeFiltering->isChecked())
774 m_rect_range_filter.reset(int(GetSamplingRate()/h2f(GetSemitoneMin())));
775 // m_fir_range_filter.setImpulseResponse(fir1_highpass(int(GetSamplingRate()/h2f(GetSemitoneMin())), ));
777 if(m_config_form.ui_rdRangeFilteringRectangular->isChecked())
778 m_range_filter = &m_rect_range_filter;
779 else if(m_config_form.ui_rdRangeFilteringFIR->isChecked())
780 m_range_filter = &m_fir_range_filter;
783 m_range_filter = &m_dummy_range_filter;
785 m_algo_combedfft->setWindowFactor(m_config_form.ui_spinWindowSizeFactor->value());
786 // m_glFT->m_zp_factor = m_config_form.ui_spinWindowSizeFactor->value();
787 m_algo_combedfft->useAudibilityRatio(m_config_form.ui_chkAlgoUseSubHarmTresh->isChecked());
788 m_algo_combedfft->setAudibilityRatio(pow(10, -m_config_form.ui_spinCombedFFTAudibilityRatio->value()/20.0));
789 m_algo_combedfft->setAmplitudeTreshold(pow(10, m_config_form.ui_spinVolumeTreshold->value()/20.0));
790 m_algo_combedfft->setComponentTreshold(pow(10, m_config_form.ui_spinVolumeTreshold->value()/20.0));
792 // cerr << "d" << endl;
795 m_quantizer->reset();
796 if(m_config_form.ui_grpQuantizer->isChecked())
798 m_latency_quantizer.setLatency(m_config_form.ui_spinErrorLatency->value());
799 m_quantizer = &m_latency_quantizer;
802 m_quantizer = &m_dummy_quantizer;
804 // cerr << pow(10, -m_config_form.ui_spinCombedFFTAudibilityRatio->value()/20.0) << endl;
806 if(!pauseAction->isOn() && !m_capture_thread.isCapturing())
807 m_capture_thread.startCapture();
810 void CustomInstrumentTunerForm::saveSettings()
816 m_settings.writeEntry("width", width());
817 m_settings.writeEntry("height", height());
818 m_settings.writeEntry("ui_tbViews", ui_tbViews->isShown());
819 m_settings.writeEntry("ui_tbButtons", ui_tbButtons->isShown());
822 m_settings.writeEntry(m_config_form.ui_cbTransports->name(), m_config_form.ui_cbTransports->currentText());
823 #ifdef CAPTURE_PORTAUDIO
824 m_settings.writeEntry(m_config_form.ui_cbPortAudioDeviceName->name(), m_config_form.ui_cbPortAudioDeviceName->currentText());
827 void CustomInstrumentTunerForm::loadSettings()
833 resize(m_settings.readNumEntry("width", 800), m_settings.readNumEntry("height", 600));
834 ui_tbViews->setShown(m_settings.readBoolEntry("ui_tbViews", ui_tbViews->isShown()));
835 ui_tbButtons->setShown(m_settings.readBoolEntry("ui_tbButtons", ui_tbButtons->isShown()));
838 QString saved_transport = m_settings.readEntry(m_config_form.ui_cbTransports->name(), "");
839 if(saved_transport!="")
840 for(int i=0; i<m_config_form.ui_cbTransports->count(); i++)
841 if(m_config_form.ui_cbTransports->text(i)==saved_transport)
842 m_config_form.ui_cbTransports->setCurrentItem(i);
843 #ifdef CAPTURE_PORTAUDIO
844 // cerr << "read: " << m_settings.readEntry("ui_cbPortAudioDeviceName", "default") << endl;
845 QString saved_device = m_settings.readEntry(m_config_form.ui_cbPortAudioDeviceName->name(), "default");
849 err = Pa_Initialize();
851 throw QString("PortAudio: CustomInstrumentTunerForm::loadSettings:Pa_Initialize ")+Pa_GetErrorText(err);
852 int numDevices = Pa_GetDeviceCount();
854 int current_index = 0;
855 m_config_form.ui_cbPortAudioDeviceName->clear();
856 m_config_form.ui_cbPortAudioDeviceName->insertItem("default");
857 const PaDeviceInfo* deviceInfo;
858 for(int i=0; i<numDevices; i++)
860 deviceInfo = Pa_GetDeviceInfo(i);
861 m_config_form.ui_cbPortAudioDeviceName->insertItem(QString(deviceInfo->name));
862 if(QString(deviceInfo->name)==saved_device)
865 if(current_index<m_config_form.ui_cbPortAudioDeviceName->count())
866 m_config_form.ui_cbPortAudioDeviceName->setCurrentItem(current_index);
870 cerr << "CustomInstrumentTunerForm: ERROR: " << error << endl;
876 void CustomInstrumentTunerForm::restoreFactorySettings()
878 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)
881 View::clearAllSettings();
883 m_settings.removeEntry("width");
884 m_settings.removeEntry("height");
885 m_settings.removeEntry("ui_tbViews");
886 m_settings.removeEntry("ui_tbButtons");
888 m_settings.removeEntry(m_config_form.ui_cbPortAudioDeviceName->name());
890 QMessageBox::information(this, tr("Restore Factory Settings"), tr("You can now restart FMIT to get back factory settings"), QMessageBox::Ok, QMessageBox::NoButton);
894 void CustomInstrumentTunerForm::helpAbout()
897 text = "<h2>Free Music Instrument Tuner</h2>";
898 text += tr("<h3>Version ")+PACKAGE_VERSION;
899 text += tr("</h3><p><h3>Website:</h3><p>homepage: <a href=\"http://home.gna.org/fmit\">http://home.gna.org/fmit</a>");
900 text += tr("<p>development site: <a href=\"http://gna.org/projects/fmit\">http://gna.org/projects/fmit</a>");
901 text += tr("<p>donation link: <a href=\"http://home.gna.org/fmit/donation.html\">http://home.gna.org/fmit/donation.html</a>");
902 text += tr("<p><h3>Author:</h3><p>Gilles Degottex [gilles.degottex@net2000.ch]");
903 #ifdef PACKAGER_STRING
904 if(PACKAGER_STRING!="")
905 text += tr("<p><h3>Packager:</h3><p>")+QString(PACKAGER_STRING).replace(QChar('<'),"[").replace(QChar('>'),"]");
908 QDialog about_dlg(this);
910 QTextEdit* textEdit1;
911 QPushButton* pushButton1;
912 QVBoxLayout* Form2Layout;
913 QHBoxLayout* layout1;
914 QSpacerItem* spacer1;
915 QSpacerItem* spacer2;
917 about_dlg.setName( tr("about_box") );
918 about_dlg.setCaption( tr("About Free Music Instrument Tuner") );
919 Form2Layout = new QVBoxLayout( &about_dlg, 11, 6, "Form2Layout");
921 textEdit1 = new QTextEdit( &about_dlg, "textEdit1" );
922 textEdit1->setText(text);
923 textEdit1->setReadOnly(true);
924 Form2Layout->addWidget( textEdit1 );
926 layout1 = new QHBoxLayout( 0, 0, 6, "layout1");
927 spacer1 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
928 layout1->addItem( spacer1 );
930 pushButton1 = new QPushButton( &about_dlg, "pushButton1" );
931 pushButton1->setText( tr( "OK" ) );
932 layout1->addWidget( pushButton1 );
933 connect(pushButton1, SIGNAL(clicked()), &about_dlg, SLOT(close()));
934 spacer2 = new QSpacerItem( 40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
935 layout1->addItem( spacer2 );
936 Form2Layout->addLayout( layout1 );
937 about_dlg.resize( QSize(400, textEdit1->heightForWidth(400)+100) );
938 // about_dlg.clearWState( WState_Polished );
943 CustomInstrumentTunerForm::~CustomInstrumentTunerForm()
945 if(m_config_form.ui_chkAutoSaveOnExit->isChecked())
949 m_settings.beginGroup("Auto/");
950 m_settings.save(m_config_form.ui_chkAutoSaveOnExit);
951 m_settings.endGroup();
956 bool displayInBrowser(const QString& theURL)
959 //TODO replace with less buggy ShellExecuteEx?
961 int)::ShellExecute(qApp->mainWidget()->winId(),NULL,theURL,NULL,NULL,SW_SHOW)
964 OFMessageBox::criticalMessageOK(QMessageBox::tr("Unable to display a
965 web browser. Ensure that you have a web browser installed."));
968 //TODO warn if netscape not installed
969 QString aCommand("netscape ");
972 if (system(aCommand) != 0)
974 OFMessageBox::criticalMessageOK(QMessageBox::tr("Unable to display a
975 netscape browser. You need to have netscape installed and in the