1 /* -*- c++ -*- */
2 /*
3  * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4  *           https://gqrx.dk/
5  *
6  * Copyright 2011-2014 Alexandru Csete OZ9AEC.
7  *
8  * Gqrx is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3, or (at your option)
11  * any later version.
12  *
13  * Gqrx is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Gqrx; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street,
21  * Boston, MA 02110-1301, USA.
22  */
23 #include <QDebug>
24 #include <QFile>
25 #include <QPushButton>
26 #include <QRegExp>
27 #include <QSettings>
28 #include <QString>
29 #include <QtGlobal>
30 #include <QVariant>
31 
32 #include <osmosdr/device.h>
33 #include <osmosdr/source.h>
34 #include <osmosdr/ranges.h>
35 
36 #ifdef WITH_PULSEAUDIO
37 #include "pulseaudio/pa_device_list.h"
38 #elif WITH_PORTAUDIO
39 #include "portaudio/device_list.h"
40 #elif defined(Q_OS_DARWIN)
41 #include "osxaudio/device_list.h"
42 #endif
43 
44 #include "qtgui/ioconfig.h"
45 #include "ui_ioconfig.h"
46 
47 
CIoConfig(QSettings * settings,std::map<QString,QVariant> & devList,QWidget * parent)48 CIoConfig::CIoConfig(QSettings * settings,
49                      std::map<QString, QVariant> &devList,
50                      QWidget *parent) :
51     QDialog(parent),
52     ui(new Ui::CIoConfig),
53     m_settings(settings),
54     m_devList(&devList)
55 {
56     ui->setupUi(this);
57 
58     // update input device list
59     updateInDev(settings, *m_devList);
60 
61     // input rate
62     updateInputSampleRates(settings->value("input/sample_rate", 0).toInt());
63 
64     // decimation
65     int idx = decim2idx(settings->value("input/decimation", 0).toInt());
66     ui->decimCombo->setCurrentIndex(idx);
67     decimationChanged(idx);
68 
69     // Analog bandwidth
70     ui->bwSpinBox->setValue(1.0e-6*settings->value("input/bandwidth", 0.0).toDouble());
71 
72     // LNB LO
73     ui->loSpinBox->setValue(1.0e-6 * settings->value("input/lnb_lo", 0.0).toDouble());
74 
75     // Output device
76     updateOutDev();
77 
78     m_scanButton = new QPushButton("&Device scan", ui->buttonBox);
79     ui->buttonBox->addButton(m_scanButton, QDialogButtonBox::ButtonRole::ActionRole);
80 
81     // Signals and slots
82     connect(this, SIGNAL(accepted()), this, SLOT(saveConfig()));
83     connect(ui->inDevCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(inputDeviceSelected(int)));
84     connect(ui->inDevEdit, SIGNAL(textChanged(QString)), this, SLOT(inputDevstrChanged(QString)));
85     connect(ui->inSrCombo, SIGNAL(editTextChanged(QString)), this, SLOT(inputRateChanged(QString)));
86     connect(ui->decimCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(decimationChanged(int)));
87     connect(m_scanButton, SIGNAL(clicked(bool)), this, SLOT(onScanButtonClicked()));
88 }
89 
~CIoConfig()90 CIoConfig::~CIoConfig()
91 {
92     delete m_scanButton;
93     delete ui;
94 }
95 
96 /**
97  * @brief get the list of devices
98  */
getDeviceList(std::map<QString,QVariant> & devList)99 void CIoConfig::getDeviceList(std::map<QString, QVariant> &devList)
100 {
101     QString         devstr;
102     QString         devlabel;
103 
104     // automatic discovery of FCD does not work on Mac
105     // so we do it ourselves
106 #if defined(Q_OS_DARWIN)
107 #ifdef WITH_PORTAUDIO
108     portaudio_device_list       devices;
109     vector<portaudio_device>    inDevList = devices.get_input_devices();
110 #else
111     osxaudio_device_list        devices;
112     vector<osxaudio_device>     inDevList = devices.get_input_devices();
113 #endif
114     string this_dev;
115     for (auto &device : inDevList)
116     {
117         this_dev = device.get_name();
118         if (this_dev.find("FUNcube Dongle V1.0") != string::npos)
119         {
120             devstr = "fcd,type=1,device='FUNcube Dongle V1.0'";
121             devList.insert(std::pair<QString, QVariant>(QString("FUNcube Dongle V1.0"), QVariant(devstr)));
122         }
123         else if (this_dev.find("FUNcube Dongle V1_0") != string::npos)      // since OS X 10.11.4
124         {
125             devstr = "fcd,type=1,device='FUNcube Dongle V1_0'";
126             devList.insert(std::pair<QString, QVariant>(QString("FUNcube Dongle V1_0"), QVariant(devstr)));
127         }
128         else if (this_dev.find("FUNcube Dongle V2.0") != string::npos)
129         {
130             devstr = "fcd,type=2,device='FUNcube Dongle V2.0'";
131             devList.insert(std::pair<QString, QVariant>(QString("FUNcube Dongle V2.0"), QVariant(devstr)));
132         }
133         else if (this_dev.find("FUNcube Dongle V2_0") != string::npos)      // since OS X 10.11.4
134         {
135             devstr = "fcd,type=2,device='FUNcube Dongle V2_0'";
136             devList.insert(std::pair<QString, QVariant>(QString("FUNcube Dongle V2_0"), QVariant(devstr)));
137         }
138     }
139 #endif
140 
141     // Get list of input devices discovered by gr-osmosdr and store them in
142     // the device list together with the device descriptor strings
143     osmosdr::devices_t devs = osmosdr::device::find();
144 
145     qDebug() << __FUNCTION__ << ": Available input devices:";
146     for (auto &dev : devs)
147     {
148         if (dev.count("label"))
149         {
150             devlabel = QString(dev["label"].c_str());
151             dev.erase("label");
152         }
153         else
154         {
155             devlabel = "Unknown";
156         }
157 
158         devstr = QString(dev.to_string().c_str());
159         devList.insert(std::pair<QString, QVariant>(devlabel, devstr));
160         qDebug() << "  " << devlabel;
161     }
162 }
163 
164 /** Save configuration. */
saveConfig()165 void CIoConfig::saveConfig()
166 {
167     int         idx;
168     int         int_val;
169     bool        conv_ok;
170 
171     qDebug() << __FUNCTION__;
172 
173     idx = ui->outDevCombo->currentIndex();
174 
175 #if defined(WITH_PULSEAUDIO) || defined(WITH_PORTAUDIO) || defined(Q_OS_DARWIN)
176     if (idx > 0)
177     {
178           qDebug() << "Output device" << idx << ":" << QString(outDevList[idx-1].get_name().c_str());
179           m_settings->setValue("output/device", QString(outDevList[idx-1].get_name().c_str()));
180     }
181 #else
182     if (idx > 0 || ui->outDevCombo->currentText() != "Default")
183     {
184         qDebug() << "Output device:" << ui->outDevCombo->currentText();
185         m_settings->setValue("output/device", ui->outDevCombo->currentText());
186     }
187 #endif
188     else
189     {
190         m_settings->remove("output/device");
191     }
192 
193     // input settings
194     m_settings->setValue("input/device", ui->inDevEdit->text());  // "OK" button disabled if empty
195 
196     qint64 value = (qint64)(ui->bwSpinBox->value()*1.e6);
197     if (value)
198         m_settings->setValue("input/bandwidth", value);
199     else
200         m_settings->remove("input/bandwidth");
201 
202     value = (qint64)(ui->loSpinBox->value()*1.e6);
203     if (value)
204         m_settings->setValue("input/lnb_lo", value);
205     else
206         m_settings->remove("input/lnb_lo");
207 
208     int_val = ui->inSrCombo->currentText().toInt(&conv_ok);
209     if (conv_ok)
210         m_settings->setValue("input/sample_rate", int_val);
211     else
212         m_settings->remove("input/sample_rate");
213 
214     idx = ui->decimCombo->currentIndex();
215     int_val = idx2decim(idx);
216     if (int_val < 2)
217         m_settings->remove("input/decimation");
218     else
219         m_settings->setValue("input/decimation", int_val);
220 }
221 
222 
223 /**
224  * @brief Update list of sample rates based on selected device.
225  * @param rate The current sample rate from the configuration.
226  */
updateInputSampleRates(int rate)227 void CIoConfig::updateInputSampleRates(int rate)
228 {
229     ui->inSrCombo->clear();
230 
231     if (ui->inDevEdit->text().isEmpty())
232         return;
233 
234     if (ui->inDevEdit->text().contains("fcd"))
235     {
236         if (ui->inDevCombo->currentText().contains("V2"))   // V2.0 or V2_0
237         {
238             ui->inSrCombo->addItem("192000");
239         }
240         else
241         {
242             ui->inSrCombo->addItem("96000");
243         }
244     }
245     else if (ui->inDevEdit->text().contains("rtl") || ui->inDevEdit->text().contains("rtl_tcp"))
246     {
247         ui->inSrCombo->addItem("240000");
248         ui->inSrCombo->addItem("300000");
249         ui->inSrCombo->addItem("960000");
250         ui->inSrCombo->addItem("1152000");
251         ui->inSrCombo->addItem("1200000");
252         ui->inSrCombo->addItem("1440000");
253         ui->inSrCombo->addItem("1600000");
254         ui->inSrCombo->addItem("1800000");
255         ui->inSrCombo->addItem("1920000");
256         ui->inSrCombo->addItem("2400000");
257         ui->inSrCombo->addItem("2880000");
258         ui->inSrCombo->addItem("3200000");
259         if (rate > 0)
260         {
261             ui->inSrCombo->addItem(QString("%1").arg(rate));
262             ui->inSrCombo->setCurrentIndex(12);
263         }
264         else
265         {
266             ui->inSrCombo->setCurrentIndex(7);
267         }
268     }
269     else if (ui->inDevEdit->text().contains("uhd"))
270     {
271         if (rate > 0)
272             ui->inSrCombo->addItem(QString("%1").arg(rate));
273         ui->inSrCombo->addItem("250000");
274         ui->inSrCombo->addItem("500000");
275         ui->inSrCombo->addItem("2000000");
276         ui->inSrCombo->addItem("4000000");
277         ui->inSrCombo->addItem("8000000");
278     }
279     else if (ui->inDevEdit->text().contains("hackrf"))
280     {
281         if (rate > 0)
282             ui->inSrCombo->addItem(QString("%1").arg(rate));
283         ui->inSrCombo->addItem("8000000");
284         ui->inSrCombo->addItem("10000000");
285         ui->inSrCombo->addItem("12500000");
286         ui->inSrCombo->addItem("16000000");
287         ui->inSrCombo->addItem("20000000");
288     }
289     else if (ui->inDevEdit->text().contains("bladerf"))
290     {
291         if (rate > 0)
292             ui->inSrCombo->addItem(QString("%1").arg(rate));
293         ui->inSrCombo->addItem("5000000");
294         ui->inSrCombo->addItem("8000000");
295         ui->inSrCombo->addItem("10000000");
296         ui->inSrCombo->addItem("15000000");
297         ui->inSrCombo->addItem("20000000");
298         ui->inSrCombo->addItem("25000000");
299         ui->inSrCombo->addItem("30000000");
300         ui->inSrCombo->addItem("35000000");
301         ui->inSrCombo->addItem("40000000");
302     }
303     else if (ui->inDevEdit->text().contains("sdr-iq"))
304     {
305         ui->inSrCombo->addItem("8138");
306         ui->inSrCombo->addItem("16276");
307         ui->inSrCombo->addItem("37793");
308         ui->inSrCombo->addItem("55556");
309         ui->inSrCombo->addItem("111111");
310         ui->inSrCombo->addItem("158730");
311         ui->inSrCombo->addItem("196078");
312         if (rate > 0)
313         {
314             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
315             ui->inSrCombo->setCurrentIndex(0);
316         }
317         else
318             ui->inSrCombo->setCurrentIndex(4); // select 111.111 kHz
319     }
320     else if (ui->inDevEdit->text().contains("sdr-ip"))
321     {
322         ui->inSrCombo->addItem("31250");
323         ui->inSrCombo->addItem("32000");
324         ui->inSrCombo->addItem("40000");
325         ui->inSrCombo->addItem("50000");
326         ui->inSrCombo->addItem("62500");
327         ui->inSrCombo->addItem("64000");
328         ui->inSrCombo->addItem("80000");
329         ui->inSrCombo->addItem("100000");
330         ui->inSrCombo->addItem("125000");
331         ui->inSrCombo->addItem("160000");
332         ui->inSrCombo->addItem("200000");
333         ui->inSrCombo->addItem("250000");
334         ui->inSrCombo->addItem("320000");
335         ui->inSrCombo->addItem("400000");
336         ui->inSrCombo->addItem("500000");
337         ui->inSrCombo->addItem("800000");
338         ui->inSrCombo->addItem("1000000");
339         ui->inSrCombo->addItem("1600000");
340         ui->inSrCombo->addItem("2000000");
341         if (rate > 0)
342         {
343             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
344             ui->inSrCombo->setCurrentIndex(0);
345         }
346         else
347             ui->inSrCombo->setCurrentIndex(11); // select 250 kHz
348     }
349     else if (ui->inDevEdit->text().contains("netsdr"))
350     {
351         ui->inSrCombo->addItem("32000");
352         ui->inSrCombo->addItem("40000");
353         ui->inSrCombo->addItem("50000");
354         ui->inSrCombo->addItem("62500");
355         ui->inSrCombo->addItem("78125");
356         ui->inSrCombo->addItem("80000");
357         ui->inSrCombo->addItem("100000");
358         ui->inSrCombo->addItem("125000");
359         ui->inSrCombo->addItem("156250");
360         ui->inSrCombo->addItem("160000");
361         ui->inSrCombo->addItem("200000");
362         ui->inSrCombo->addItem("250000");
363         ui->inSrCombo->addItem("312500");
364         ui->inSrCombo->addItem("400000");
365         ui->inSrCombo->addItem("500000");
366         ui->inSrCombo->addItem("625000");
367         ui->inSrCombo->addItem("800000");
368         ui->inSrCombo->addItem("1000000");
369         ui->inSrCombo->addItem("1250000");
370         ui->inSrCombo->addItem("2000000");
371         if (rate > 0)
372         {
373             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
374             ui->inSrCombo->setCurrentIndex(0);
375         }
376         else
377             ui->inSrCombo->setCurrentIndex(11); // select 250 kHz
378     }
379     else if (ui->inDevEdit->text().contains("cloudiq"))
380     {
381         ui->inSrCombo->addItem("48000");
382         ui->inSrCombo->addItem("61440");
383         ui->inSrCombo->addItem("96000");
384         ui->inSrCombo->addItem("122880");
385         ui->inSrCombo->addItem("240000");
386         ui->inSrCombo->addItem("256000");
387         ui->inSrCombo->addItem("370120");
388         ui->inSrCombo->addItem("495483");
389         ui->inSrCombo->addItem("512000");
390         ui->inSrCombo->addItem("614400");
391         ui->inSrCombo->addItem("1024000");
392         ui->inSrCombo->addItem("1228800");
393         ui->inSrCombo->addItem("1807058");
394         if (rate > 0)
395         {
396             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
397             ui->inSrCombo->setCurrentIndex(0);
398         }
399         else
400             ui->inSrCombo->setCurrentIndex(4); // select 240 kHz
401     }
402     else if (ui->inDevEdit->text().contains("airspyhf"))
403     {
404         ui->inSrCombo->addItem("192000");
405         ui->inSrCombo->addItem("256000");
406         ui->inSrCombo->addItem("384000");
407         ui->inSrCombo->addItem("456000");
408         ui->inSrCombo->addItem("768000");
409         ui->inSrCombo->addItem("912000");
410 
411         if (rate > 0)
412         {
413             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
414             ui->inSrCombo->setCurrentIndex(0);
415         }
416         else
417             ui->inSrCombo->setCurrentIndex(4); // select 768 kHz
418     }
419     // NB: must list airspyhf first
420     else if (ui->inDevEdit->text().contains("airspy"))
421     {
422         if (rate > 0)
423             ui->inSrCombo->addItem(QString("%1").arg(rate));
424 
425         ui->inSrCombo->addItem("2500000");
426         ui->inSrCombo->addItem("3000000");
427         ui->inSrCombo->addItem("6000000");
428         ui->inSrCombo->addItem("10000000");
429     }
430     else if (ui->inDevEdit->text().contains("redpitaya"))
431     {
432         ui->inSrCombo->addItem("20000");
433         ui->inSrCombo->addItem("50000");
434         ui->inSrCombo->addItem("100000");
435         ui->inSrCombo->addItem("250000");
436         ui->inSrCombo->addItem("500000");
437         ui->inSrCombo->addItem("1250000");
438         if (rate > 0)
439         {
440             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
441             ui->inSrCombo->setCurrentIndex(0);
442         }
443         else
444             ui->inSrCombo->setCurrentIndex(3); // select 250 kHz
445     }
446     else if (ui->inDevEdit->text().contains("sdrplay"))
447     {
448         ui->inSrCombo->addItem("62500");
449         ui->inSrCombo->addItem("125000");
450         ui->inSrCombo->addItem("250000");
451         ui->inSrCombo->addItem("500000");
452         ui->inSrCombo->addItem("1000000");
453         ui->inSrCombo->addItem("2000000");
454         ui->inSrCombo->addItem("3000000");
455         ui->inSrCombo->addItem("4000000");
456         ui->inSrCombo->addItem("5000000");
457         ui->inSrCombo->addItem("6000000");
458         ui->inSrCombo->addItem("7000000");
459         ui->inSrCombo->addItem("8000000");
460         ui->inSrCombo->addItem("9000000");
461         ui->inSrCombo->addItem("10000000");
462         if (rate > 0)
463         {
464             if (rate < 62500)
465                 rate = 62500;
466             if (rate > 10000000)
467                 rate = 10000000;
468             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
469             ui->inSrCombo->setCurrentIndex(0);
470         }
471         else
472             ui->inSrCombo->setCurrentIndex(5); // select 2 MHz
473     }
474     else if (ui->inDevEdit->text().contains("lime"))
475     {
476         ui->inSrCombo->addItem("100000");
477         ui->inSrCombo->addItem("500000");
478         ui->inSrCombo->addItem("1000000");
479         ui->inSrCombo->addItem("2500000");
480         ui->inSrCombo->addItem("5000000");
481         ui->inSrCombo->addItem("10000000");
482         ui->inSrCombo->addItem("20000000");
483         ui->inSrCombo->addItem("50000000");
484         if (rate > 0)
485         {
486             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
487             ui->inSrCombo->setCurrentIndex(0);
488         }
489         else
490             ui->inSrCombo->setCurrentIndex(4); // select 5 MHz
491     }
492     else if (ui->inDevEdit->text().contains("plutosdr"))
493     {
494         ui->inSrCombo->addItem("600000");
495         ui->inSrCombo->addItem("1000000");
496         ui->inSrCombo->addItem("1500000");
497         ui->inSrCombo->addItem("2000000");
498         ui->inSrCombo->addItem("3000000");
499         ui->inSrCombo->addItem("6000000");
500         ui->inSrCombo->addItem("16000000");
501         ui->inSrCombo->addItem("20000000");
502         ui->inSrCombo->addItem("56000000");
503         if (rate > 0)
504         {
505             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
506             ui->inSrCombo->setCurrentIndex(0);
507         }
508         else
509             ui->inSrCombo->setCurrentIndex(2); // select 2 MHz
510     }
511     else if (ui->inDevEdit->text().contains("perseus"))
512     {
513         ui->inSrCombo->addItem("48000");
514         ui->inSrCombo->addItem("95000");
515         ui->inSrCombo->addItem("96000");
516         ui->inSrCombo->addItem("125000");
517         ui->inSrCombo->addItem("192000");
518         ui->inSrCombo->addItem("250000");
519         ui->inSrCombo->addItem("500000");
520         ui->inSrCombo->addItem("1000000");
521         ui->inSrCombo->addItem("1600000");
522         ui->inSrCombo->addItem("2000000");
523         if (rate > 0)
524         {
525             ui->inSrCombo->insertItem(0, QString("%1").arg(rate));
526             ui->inSrCombo->setCurrentIndex(0);
527         }
528         else
529             ui->inSrCombo->setCurrentIndex(4); // select 192 kHz
530     }
531     else
532     {
533         if (rate > 0)
534             ui->inSrCombo->addItem(QString("%1").arg(rate));
535     }
536 
537     updateDecimations();
538 }
539 
540 /**
541  * @brief Update available decimations according to the current sample rate.
542  *
543  * This function will repopulate the decimation selector combo box to only
544  * include decimations up to a meaningful maximum value, so that the quadrature
545  * rate doesn't get below 48 ksps.
546  */
updateDecimations(void)547 void CIoConfig::updateDecimations(void)
548 {
549     bool        ok;
550     int         rate;
551 
552     // get current sample rate from combo box
553     rate= ui->inSrCombo->currentText().toInt(&ok);
554     if (!ok || rate < 0)
555         return;
556 
557     ui->decimCombo->clear();
558     ui->decimCombo->addItem("None", 0);
559     if (rate >= 96000)
560         ui->decimCombo->addItem("2", 0);
561     if (rate >= 192000)
562         ui->decimCombo->addItem("4", 0);
563     if (rate >= 384000)
564         ui->decimCombo->addItem("8", 0);
565     if (rate >= 768000)
566         ui->decimCombo->addItem("16", 0);
567     if (rate >= 1536000)
568         ui->decimCombo->addItem("32", 0);
569     if (rate >= 3072000)
570         ui->decimCombo->addItem("64", 0);
571     if (rate >= 6144000)
572         ui->decimCombo->addItem("128", 0);
573 //    if (rate >= 12288000)
574 //        ui->decimCombo->addItem("256", 0);
575 //    if (rate >= 24576000)
576 //        ui->decimCombo->addItem("512", 0);
577 
578     decimationChanged(0);
579 }
580 
updateInDev(const QSettings * settings,const std::map<QString,QVariant> & devList)581 void CIoConfig::updateInDev(const QSettings *settings, const std::map<QString, QVariant> &devList)
582 {
583     QString indev = settings->value("input/device", "").toString();
584     bool cfgmatch = false; //flag to indicate that device from config was found
585 
586     // insert the device list in device combo box
587     ui->inDevCombo->clear();
588     int i = 0;
589     for (auto it = devList.cbegin(); it != devList.cend(); it++, i++)
590     {
591         auto devstr = (*it).second.toString();
592         ui->inDevCombo->addItem((*it).first, devstr);
593 
594         // is this the device stored in config?
595         if (indev == devstr)
596         {
597             ui->inDevCombo->setCurrentIndex(i);
598             ui->inDevEdit->setText(devstr);
599             cfgmatch = true;
600         }
601     }
602 
603     ui->inDevCombo->addItem(tr("Other..."), QVariant(""));
604 
605     // If device string from config is not one of the detected devices
606     // it could be that device is not plugged in (in which case we select
607     // other) or that this is the first time (select the first detected device).
608     if (!cfgmatch)
609     {
610         if (indev.isEmpty())
611         {
612             // First time config: select the first detected device
613             ui->inDevCombo->setCurrentIndex(0);
614             ui->inDevEdit->setText(ui->inDevCombo->itemData(0).toString());
615             if (ui->inDevEdit->text().isEmpty())
616                 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
617         }
618         else
619         {
620             // Select other
621             ui->inDevCombo->setCurrentIndex(i);
622             ui->inDevEdit->setText(indev);
623         }
624     }
625 }
626 
updateOutDev()627 void CIoConfig::updateOutDev()
628 {
629     QString outdev = m_settings->value("output/device", "").toString();
630 
631     ui->outDevCombo->clear();
632     ui->outDevCombo->addItem("Default");
633 
634     // get list of audio output devices
635 #ifdef WITH_PULSEAUDIO
636    pa_device_list devices;
637    outDevList = devices.get_output_devices();
638 
639    qDebug() << __FUNCTION__ << ": Available output devices:";
640    for (size_t i = 0; i < outDevList.size(); i++)
641    {
642        qDebug() << "   " << i << ":" << QString(outDevList[i].get_description().c_str());
643        //qDebug() << "     " << QString(outDevList[i].get_name().c_str());
644        ui->outDevCombo->addItem(QString(outDevList[i].get_description().c_str()));
645 
646        // note that item #i in devlist will be item #(i+1)
647        // in combo box due to "default"
648        if (outdev == QString(outDevList[i].get_name().c_str()))
649            ui->outDevCombo->setCurrentIndex(i+1);
650    }
651 #elif WITH_PORTAUDIO
652    portaudio_device_list   devices;
653 
654    outDevList = devices.get_output_devices();
655    for (size_t i = 0; i < outDevList.size(); i++)
656    {
657        ui->outDevCombo->addItem(QString(outDevList[i].get_description().c_str()));
658 
659        // note that item #i in devlist will be item #(i+1)
660        // in combo box due to "default"
661        if (outdev == QString(outDevList[i].get_name().c_str()))
662            ui->outDevCombo->setCurrentIndex(i+1);
663    }
664    //ui->outDevCombo->setEditable(true);
665 
666 #elif defined(Q_OS_DARWIN)
667    osxaudio_device_list devices;
668    outDevList = devices.get_output_devices();
669 
670    qDebug() << __FUNCTION__ << ": Available output devices:";
671    for (size_t i = 0; i < outDevList.size(); i++)
672    {
673        qDebug() << "   " << i << ":" << QString(outDevList[i].get_name().c_str());
674        ui->outDevCombo->addItem(QString(outDevList[i].get_name().c_str()));
675 
676        // note that item #i in devlist will be item #(i+1)
677        // in combo box due to "default"
678        if (outdev == QString(outDevList[i].get_name().c_str()))
679            ui->outDevCombo->setCurrentIndex(i+1);
680    }
681 
682 #else
683    ui->outDevCombo->addItem(m_settings->value("output/device", "Default").toString(),
684                             m_settings->value("output/device", "Default").toString());
685    ui->outDevCombo->setEditable(true);
686 #endif // WITH_PULSEAUDIO
687 }
688 
689 /**
690  * @brief New input device has been selected by the user.
691  * @param index The index of the item that has been selected in the combo box.
692  */
inputDeviceSelected(int index)693 void CIoConfig::inputDeviceSelected(int index)
694 {
695     qDebug() << "New input device selected:" << index;
696     qDebug() << "  Label:" << ui->inDevCombo->itemText(index);
697     qDebug() << "  Devstr:" << ui->inDevCombo->itemData(index).toString();
698 
699     ui->inDevEdit->setText(ui->inDevCombo->itemData(index).toString());
700     updateInputSampleRates(0);
701 }
702 
703 
704 /**
705  * @brief Input device string has changed
706  * @param text THe new device string
707  *
708  * This slot is activated when the device string in the text edit box has changed
709  * either by the user or programmatically. We use this to enable/disable the OK
710  * button - we allo OK only if there is some text in the text entry.
711  */
inputDevstrChanged(const QString & text)712 void CIoConfig::inputDevstrChanged(const QString &text)
713 {
714     if (text.isEmpty())
715         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
716     else
717         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
718 }
719 
720 /**
721  * @brief Sample changed, either by selection of direct entry.
722  * @param text The new sample rate.
723  */
inputRateChanged(const QString & text)724 void CIoConfig::inputRateChanged(const QString &text)
725 {
726     (void) text;
727     updateDecimations();
728 }
729 
730 /**
731  * @brief New decimation rate selected.
732  * @param index The index of the selected item in the combo box.
733  *
734  * This function calculates the quadrature rate and updates the sample rate
735  * label just below the decimation combo box.
736  */
decimationChanged(int index)737 void CIoConfig::decimationChanged(int index)
738 {
739     float       quad_rate;
740     int         input_rate;
741     int         decim;
742     bool        ok;
743 
744     decim = idx2decim(index);
745     input_rate = ui->inSrCombo->currentText().toInt(&ok);
746     if (!ok)
747         return;
748 
749     quad_rate = (float)input_rate / (float)decim;
750     if (quad_rate > 1.e6f)
751         ui->sampRateLabel->setText(QString(" %1 Msps").
752                                    arg(quad_rate * 1.e-6, 0, 'f', 3));
753     else
754         ui->sampRateLabel->setText(QString(" %1 ksps").
755                                    arg(quad_rate * 1.e-3, 0, 'f', 3));
756 }
757 
758 /**
759  * @brief Slot to handle device list updates on m_scanButton clicks
760  */
onScanButtonClicked()761 void CIoConfig::onScanButtonClicked()
762 {
763     m_devList->clear();
764     CIoConfig::getDeviceList(*m_devList);
765     updateInDev(m_settings, *m_devList);
766     updateOutDev();
767 }
768 
769 /** Convert a combo box index to decimation. */
idx2decim(int idx) const770 int CIoConfig::idx2decim(int idx) const
771 {
772     if (idx < 1)
773         return 1;
774 
775     return (1 << idx);
776 }
777 
778 /** Convert a decimation to a combobox index */
decim2idx(int decim) const779 int CIoConfig::decim2idx(int decim) const
780 {
781     int         idx;
782 
783     if (decim == 0)
784         return 0;
785 
786     idx = 0;
787     while (decim >>= 1)
788         ++idx;
789 
790     return idx;
791 }
792