diff --git a/Audio/AudioDevice.cpp b/Audio/AudioDevice.cpp index 2b8525826..16fec3148 100644 --- a/Audio/AudioDevice.cpp +++ b/Audio/AudioDevice.cpp @@ -7,4 +7,3 @@ bool AudioDevice::initialize (OpenMode mode, Channel channel) // open and ensure we are unbuffered if possible return QIODevice::open (mode | QIODevice::Unbuffered); } - diff --git a/Audio/AudioDevice.hpp b/Audio/AudioDevice.hpp index 3ab19f0f4..2d47af89b 100644 --- a/Audio/AudioDevice.hpp +++ b/Audio/AudioDevice.hpp @@ -33,8 +33,8 @@ public: Channel channel () const {return m_channel;} protected: - AudioDevice (QObject * parent = 0) - : QIODevice (parent) + AudioDevice (QObject * parent = nullptr) + : QIODevice {parent} { } diff --git a/Audio/soundin.cpp b/Audio/soundin.cpp index 7dc940406..00129311a 100644 --- a/Audio/soundin.cpp +++ b/Audio/soundin.cpp @@ -10,11 +10,9 @@ #include "moc_soundin.cpp" -bool SoundInput::audioError () const +bool SoundInput::checkStream () { - bool result (true); - - Q_ASSERT_X (m_stream, "SoundInput", "programming error"); + bool result (false); if (m_stream) { switch (m_stream->error ()) @@ -36,9 +34,13 @@ bool SoundInput::audioError () const break; case QAudio::NoError: - result = false; + result = true; break; } + if (!result) + { + stop (); + } } return result; } @@ -74,12 +76,13 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi // qDebug () << "Selected audio input format:" << format; m_stream.reset (new QAudioInput {device, format}); - if (audioError ()) + if (!checkStream ()) { return; } connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged); + connect (m_stream.data(), &QAudioInput::notify, [this] () {checkStream ();}); //qDebug () << "SoundIn default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize (); // the Windows MME version of QAudioInput uses 1/5 of the buffer @@ -89,10 +92,10 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi #else Q_UNUSED (framesPerBuffer); #endif - if (sink->initialize (QIODevice::WriteOnly, channel)) + if (m_sink->initialize (QIODevice::WriteOnly, channel)) { m_stream->start (sink); - audioError (); + checkStream (); cummulative_lost_usec_ = -1; //qDebug () << "SoundIn selected buffer size (bytes):" << m_stream->bufferSize () << "peirod size:" << m_stream->periodSize (); } @@ -107,7 +110,7 @@ void SoundInput::suspend () if (m_stream) { m_stream->suspend (); - audioError (); + checkStream (); } } @@ -122,14 +125,12 @@ void SoundInput::resume () if (m_stream) { m_stream->resume (); - audioError (); + checkStream (); } } void SoundInput::handleStateChanged (QAudio::State newState) { - //qDebug () << "SoundInput::handleStateChanged: newState:" << newState; - switch (newState) { case QAudio::IdleState: @@ -152,7 +153,7 @@ void SoundInput::handleStateChanged (QAudio::State newState) #endif case QAudio::StoppedState: - if (audioError ()) + if (!checkStream ()) { Q_EMIT status (tr ("Error")); } @@ -193,11 +194,6 @@ void SoundInput::stop() m_stream->stop (); } m_stream.reset (); - - if (m_sink) - { - m_sink->close (); - } } SoundInput::~SoundInput () diff --git a/Audio/soundin.h b/Audio/soundin.h index a126fbbd1..c35b3d7d8 100644 --- a/Audio/soundin.h +++ b/Audio/soundin.h @@ -24,7 +24,6 @@ class SoundInput public: SoundInput (QObject * parent = nullptr) : QObject {parent} - , m_sink {nullptr} , cummulative_lost_usec_ {std::numeric_limits::min ()} { } @@ -47,7 +46,7 @@ private: // used internally Q_SLOT void handleStateChanged (QAudio::State); - bool audioError () const; + bool checkStream (); QScopedPointer m_stream; QPointer m_sink; diff --git a/Audio/soundout.cpp b/Audio/soundout.cpp index 40eff6012..5e89de147 100644 --- a/Audio/soundout.cpp +++ b/Audio/soundout.cpp @@ -7,11 +7,13 @@ #include #include +#include "Audio/AudioDevice.hpp" + #include "moc_soundout.cpp" -bool SoundOutput::audioError () const +bool SoundOutput::checkStream () const { - bool result (true); + bool result {false}; Q_ASSERT_X (m_stream, "SoundOutput", "programming error"); if (m_stream) { @@ -34,7 +36,7 @@ bool SoundOutput::audioError () const break; case QAudio::NoError: - result = false; + result = true; break; } } @@ -43,15 +45,19 @@ bool SoundOutput::audioError () const void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered) { - if (!device.isNull ()) + Q_ASSERT (0 < channels && channels < 3); + m_device = device; + m_channels = channels; + m_framesBuffered = frames_buffered; +} + +void SoundOutput::restart (AudioDevice * source) +{ + if (!m_device.isNull ()) { - Q_ASSERT (0 < channels && channels < 3); - - m_framesBuffered = frames_buffered; - - QAudioFormat format (device.preferredFormat ()); + QAudioFormat format (m_device.preferredFormat ()); // qDebug () << "Preferred audio output format:" << format; - format.setChannelCount (channels); + format.setChannelCount (m_channels); format.setCodec ("audio/pcm"); format.setSampleRate (48000); format.setSampleType (QAudioFormat::SignedInt); @@ -61,29 +67,25 @@ void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, { Q_EMIT error (tr ("Requested output audio format is not valid.")); } - else if (!device.isFormatSupported (format)) + else if (!m_device.isFormatSupported (format)) { Q_EMIT error (tr ("Requested output audio format is not supported on device.")); } else { // qDebug () << "Selected audio output format:" << format; - - m_stream.reset (new QAudioOutput (device, format)); - audioError (); + m_stream.reset (new QAudioOutput (m_device, format)); + checkStream (); m_stream->setVolume (m_volume); - m_stream->setNotifyInterval(100); + m_stream->setNotifyInterval(1000); error_ = false; connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged); + connect (m_stream.data(), &QAudioOutput::notify, [this] () {checkStream ();}); // qDebug() << "A" << m_volume << m_stream->notifyInterval(); } } -} - -void SoundOutput::restart (QIODevice * source) -{ if (!m_stream) { if (!error_) @@ -109,6 +111,7 @@ void SoundOutput::restart (QIODevice * source) #endif } m_stream->setCategory ("production"); + m_source = source; m_stream->start (source); // qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize (); } @@ -118,7 +121,7 @@ void SoundOutput::suspend () if (m_stream && QAudio::ActiveState == m_stream->state ()) { m_stream->suspend (); - audioError (); + checkStream (); } } @@ -127,7 +130,7 @@ void SoundOutput::resume () if (m_stream && QAudio::SuspendedState == m_stream->state ()) { m_stream->resume (); - audioError (); + checkStream (); } } @@ -136,7 +139,7 @@ void SoundOutput::reset () if (m_stream) { m_stream->reset (); - audioError (); + checkStream (); } } @@ -144,9 +147,10 @@ void SoundOutput::stop () { if (m_stream) { + m_stream->reset (); m_stream->stop (); - audioError (); } + m_stream.reset (); } qreal SoundOutput::attenuation () const @@ -176,8 +180,6 @@ void SoundOutput::resetAttenuation () void SoundOutput::handleStateChanged (QAudio::State newState) { - // qDebug () << "SoundOutput::handleStateChanged: newState:" << newState; - switch (newState) { case QAudio::IdleState: @@ -199,7 +201,7 @@ void SoundOutput::handleStateChanged (QAudio::State newState) #endif case QAudio::StoppedState: - if (audioError ()) + if (!checkStream ()) { Q_EMIT status (tr ("Error")); } diff --git a/Audio/soundout.h b/Audio/soundout.h index c46e1563f..95efaeb15 100644 --- a/Audio/soundout.h +++ b/Audio/soundout.h @@ -6,7 +6,9 @@ #include #include #include +#include +class AudioDevice; class QAudioDeviceInfo; // An instance of this sends audio data to a specified soundcard. @@ -28,7 +30,7 @@ public: public Q_SLOTS: void setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered = 0); - void restart (QIODevice *); + void restart (AudioDevice *); void suspend (); void resume (); void reset (); @@ -41,13 +43,16 @@ Q_SIGNALS: void status (QString message) const; private: - bool audioError () const; + bool checkStream () const; private Q_SLOTS: void handleStateChanged (QAudio::State); private: + QAudioDeviceInfo m_device; + unsigned m_channels; QScopedPointer m_stream; + QPointer m_source; int m_framesBuffered; qreal m_volume; bool error_; diff --git a/CMakeLists.txt b/CMakeLists.txt index a2a31b388..f24abbbeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,6 +293,7 @@ set (wsjt_qt_CXXSRCS logbook/WorkedBefore.cpp logbook/Multiplier.cpp Network/NetworkAccessManager.cpp + widgets/LazyFillComboBox.cpp ) set (wsjt_qtmm_CXXSRCS diff --git a/Configuration.cpp b/Configuration.cpp index 91f9b1926..65b510b8e 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -188,6 +188,7 @@ #include "Network/LotWUsers.hpp" #include "models/DecodeHighlightingModel.hpp" #include "logbook/logbook.h" +#include "widgets/LazyFillComboBox.hpp" #include "ui_Configuration.h" #include "moc_Configuration.cpp" @@ -432,7 +433,6 @@ private: void read_settings (); void write_settings (); - Q_SLOT void lazy_models_load (int); void find_audio_devices (); QAudioDeviceInfo find_audio_device (QAudio::Mode, QComboBox *, QString const& device_name); void load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *); @@ -653,9 +653,13 @@ private: bool pwrBandTuneMemory_; QAudioDeviceInfo audio_input_device_; + QAudioDeviceInfo next_audio_input_device_; AudioDevice::Channel audio_input_channel_; + AudioDevice::Channel next_audio_input_channel_; QAudioDeviceInfo audio_output_device_; + QAudioDeviceInfo next_audio_output_device_; AudioDevice::Channel audio_output_channel_; + AudioDevice::Channel next_audio_output_channel_; friend class Configuration; }; @@ -856,6 +860,16 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s } } +void Configuration::invalidate_audio_input_device (QString /* error */) +{ + m_->audio_input_device_ = QAudioDeviceInfo {}; +} + +void Configuration::invalidate_audio_output_device (QString /* error */) +{ + m_->audio_output_device_ = QAudioDeviceInfo {}; +} + bool Configuration::valid_n1mm_info () const { // do very rudimentary checking on the n1mm server name and port number. @@ -1029,6 +1043,17 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network // this must be done after the default paths above are set read_settings (); + connect (ui_->sound_input_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () { + load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &next_audio_input_device_); + update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false); + ui_->sound_input_channel_combo_box->setCurrentIndex (next_audio_input_channel_); + }); + connect (ui_->sound_output_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () { + load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &next_audio_output_device_); + update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true); + ui_->sound_output_channel_combo_box->setCurrentIndex (next_audio_output_channel_); + }); + // set up LoTW users CSV file fetching connect (&lotw_users_, &LotWUsers::load_finished, [this] () { ui_->LotW_CSV_fetch_push_button->setEnabled (true); @@ -1102,7 +1127,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network // // setup hooks to keep audio channels aligned with devices // - connect (ui_->configuration_tabs, &QTabWidget::currentChanged, this, &Configuration::impl::lazy_models_load); { using namespace std; using namespace std::placeholders; @@ -1199,6 +1223,11 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network enumerate_rigs (); initialize_models (); + audio_input_device_ = next_audio_input_device_; + audio_input_channel_ = next_audio_input_channel_; + audio_output_device_ = next_audio_output_device_; + audio_output_channel_ = next_audio_output_channel_; + transceiver_thread_ = new QThread {this}; transceiver_thread_->start (); } @@ -1210,31 +1239,14 @@ Configuration::impl::~impl () write_settings (); } -void Configuration::impl::lazy_models_load (int current_tab_index) -{ - switch (current_tab_index) - { - case 2: // Audio - // - // load combo boxes with audio setup choices - // - load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &audio_input_device_); - load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &audio_output_device_); - - update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false); - update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true); - - ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_); - ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_); - break; - - default: - break; - } -} - void Configuration::impl::initialize_models () { + next_audio_input_device_ = audio_input_device_; + next_audio_input_channel_ = audio_input_channel_; + next_audio_output_device_ = audio_output_device_; + next_audio_output_channel_ = audio_output_channel_; + restart_sound_input_device_ = false; + restart_sound_output_device_ = false; { SettingsGroup g {settings_, "Configuration"}; find_audio_devices (); @@ -1413,8 +1425,6 @@ void Configuration::impl::read_settings () save_directory_.setPath (settings_->value ("SaveDir", default_save_directory_.absolutePath ()).toString ()); azel_directory_.setPath (settings_->value ("AzElDir", default_azel_directory_.absolutePath ()).toString ()); - find_audio_devices (); - type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value (); monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool (); @@ -1522,24 +1532,24 @@ void Configuration::impl::find_audio_devices () // retrieve audio input device // auto saved_name = settings_->value ("SoundInName").toString (); - if (audio_input_device_.deviceName () != saved_name) + if (next_audio_input_device_.deviceName () != saved_name || next_audio_input_device_.isNull ()) { - audio_input_device_ = find_audio_device (QAudio::AudioInput, ui_->sound_input_combo_box, saved_name); - audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ()); + next_audio_input_device_ = find_audio_device (QAudio::AudioInput, ui_->sound_input_combo_box, saved_name); + next_audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ()); update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false); - ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_); + ui_->sound_input_channel_combo_box->setCurrentIndex (next_audio_input_channel_); } // // retrieve audio output device // saved_name = settings_->value("SoundOutName").toString(); - if (audio_output_device_.deviceName () != saved_name) + if (next_audio_output_device_.deviceName () != saved_name || next_audio_output_device_.isNull ()) { - audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ()); - audio_output_device_ = find_audio_device (QAudio::AudioOutput, ui_->sound_output_combo_box, saved_name); + next_audio_output_device_ = find_audio_device (QAudio::AudioOutput, ui_->sound_output_combo_box, saved_name); + next_audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ()); update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true); - ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_); + ui_->sound_output_channel_combo_box->setCurrentIndex (next_audio_output_channel_); } } @@ -1562,10 +1572,16 @@ void Configuration::impl::write_settings () settings_->setValue ("PTTport", rig_params_.ptt_port); settings_->setValue ("SaveDir", save_directory_.absolutePath ()); settings_->setValue ("AzElDir", azel_directory_.absolutePath ()); - settings_->setValue ("SoundInName", audio_input_device_.deviceName ()); - settings_->setValue ("SoundOutName", audio_output_device_.deviceName ()); - settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_)); - settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_)); + if (!audio_input_device_.isNull ()) + { + settings_->setValue ("SoundInName", audio_input_device_.deviceName ()); + settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_)); + } + if (!audio_output_device_.isNull ()) + { + settings_->setValue ("SoundOutName", audio_output_device_.deviceName ()); + settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_)); + } settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_)); settings_->setValue ("MonitorOFF", monitor_off_at_startup_); settings_->setValue ("MonitorLastUsed", monitor_last_used_); @@ -1770,7 +1786,7 @@ void Configuration::impl::set_rig_invariants () bool Configuration::impl::validate () { if (ui_->sound_input_combo_box->currentIndex () < 0 - && audio_input_device_.isNull ()) + && next_audio_input_device_.isNull ()) { find_tab (ui_->sound_input_combo_box); MessageBox::critical_message (this, tr ("Invalid audio input device")); @@ -1778,7 +1794,7 @@ bool Configuration::impl::validate () } if (ui_->sound_input_channel_combo_box->currentIndex () < 0 - && audio_input_device_.isNull ()) + && next_audio_input_device_.isNull ()) { find_tab (ui_->sound_input_combo_box); MessageBox::critical_message (this, tr ("Invalid audio input device")); @@ -1786,7 +1802,7 @@ bool Configuration::impl::validate () } if (ui_->sound_output_combo_box->currentIndex () < 0 - && audio_output_device_.isNull ()) + && next_audio_output_device_.isNull ()) { find_tab (ui_->sound_output_combo_box); MessageBox::information_message (this, tr ("Invalid audio output device")); @@ -1842,7 +1858,6 @@ int Configuration::impl::exec () rig_changed_ = false; initialize_models (); - lazy_models_load (ui_->configuration_tabs->currentIndex ()); return QDialog::exec(); } @@ -1941,39 +1956,60 @@ void Configuration::impl::accept () // related configuration parameters rig_is_dummy_ = TransceiverFactory::basic_transceiver_name_ == rig_params_.rig_name; - // Check to see whether SoundInThread must be restarted, - // and save user parameters. { auto const& selected_device = ui_->sound_input_combo_box->currentData ().value ().first; - if (selected_device != audio_input_device_) + if (selected_device != next_audio_input_device_) { - audio_input_device_ = selected_device; - restart_sound_input_device_ = true; + next_audio_input_device_ = selected_device; } } { auto const& selected_device = ui_->sound_output_combo_box->currentData ().value ().first; - if (selected_device != audio_output_device_) + if (selected_device != next_audio_output_device_) { - audio_output_device_ = selected_device; - restart_sound_output_device_ = true; + next_audio_output_device_ = selected_device; } } - if (audio_input_channel_ != static_cast (ui_->sound_input_channel_combo_box->currentIndex ())) + if (next_audio_input_channel_ != static_cast (ui_->sound_input_channel_combo_box->currentIndex ())) { - audio_input_channel_ = static_cast (ui_->sound_input_channel_combo_box->currentIndex ()); + next_audio_input_channel_ = static_cast (ui_->sound_input_channel_combo_box->currentIndex ()); + } + Q_ASSERT (next_audio_input_channel_ <= AudioDevice::Right); + + if (next_audio_output_channel_ != static_cast (ui_->sound_output_channel_combo_box->currentIndex ())) + { + next_audio_output_channel_ = static_cast (ui_->sound_output_channel_combo_box->currentIndex ()); + } + Q_ASSERT (next_audio_output_channel_ <= AudioDevice::Both); + + if (audio_input_device_ != next_audio_input_device_ || next_audio_input_device_.isNull ()) + { + audio_input_device_ = next_audio_input_device_; restart_sound_input_device_ = true; } - Q_ASSERT (audio_input_channel_ <= AudioDevice::Right); - - if (audio_output_channel_ != static_cast (ui_->sound_output_channel_combo_box->currentIndex ())) + if (audio_input_channel_ != next_audio_input_channel_) { - audio_output_channel_ = static_cast (ui_->sound_output_channel_combo_box->currentIndex ()); + audio_input_channel_ = next_audio_input_channel_; + restart_sound_input_device_ = true; + } + if (audio_output_device_ != next_audio_output_device_ || next_audio_output_device_.isNull ()) + { + audio_output_device_ = next_audio_output_device_; restart_sound_output_device_ = true; } - Q_ASSERT (audio_output_channel_ <= AudioDevice::Both); + if (audio_output_channel_ != next_audio_output_channel_) + { + audio_output_channel_ = next_audio_output_channel_; + restart_sound_output_device_ = true; + } + // qDebug () << "Configure::accept: audio i/p:" << audio_input_device_.deviceName () + // << "chan:" << audio_input_channel_ + // << "o/p:" << audio_output_device_.deviceName () + // << "chan:" << audio_output_channel_ + // << "reset i/p:" << restart_sound_input_device_ + // << "reset o/p:" << restart_sound_output_device_; my_callsign_ = ui_->callsign_line_edit->text (); my_grid_ = ui_->grid_line_edit->text (); @@ -2112,6 +2148,13 @@ void Configuration::impl::reject () } } + // qDebug () << "Configure::reject: audio i/p:" << audio_input_device_.deviceName () + // << "chan:" << audio_input_channel_ + // << "o/p:" << audio_output_device_.deviceName () + // << "chan:" << audio_output_channel_ + // << "reset i/p:" << restart_sound_input_device_ + // << "reset o/p:" << restart_sound_output_device_; + QDialog::reject (); } @@ -2772,27 +2815,25 @@ QAudioDeviceInfo Configuration::impl::find_audio_device (QAudio::Mode mode, QCom if (device_name.size ()) { - combo_box->clear (); - - int current_index = -1; + Q_EMIT self_->enumerating_audio_devices (); auto const& devices = QAudioDeviceInfo::availableDevices (mode); Q_FOREACH (auto const& p, devices) { - - // convert supported channel counts into something we can store in the item model - QList channel_counts; - auto scc = p.supportedChannelCounts (); - copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts)); - - combo_box->addItem (p.deviceName (), QVariant::fromValue (audio_info_type {p, channel_counts})); + qDebug () << "Configuration::impl::find_audio_device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes (); if (p.deviceName () == device_name) { - current_index = combo_box->count () - 1; - combo_box->setCurrentIndex (current_index); + // convert supported channel counts into something we can store in the item model + QList channel_counts; + auto scc = p.supportedChannelCounts (); + copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts)); + combo_box->insertItem (0, device_name, QVariant::fromValue (audio_info_type {p, channel_counts})); + combo_box->setCurrentIndex (0); return p; } } - combo_box->setCurrentIndex (current_index); + // insert a place holder for the not found device + combo_box->insertItem (0, device_name + " (" + tr ("Not found", "audio device missing") + ")", QVariant::fromValue (audio_info_type {})); + combo_box->setCurrentIndex (0); } return {}; } @@ -2811,7 +2852,7 @@ void Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * com auto const& devices = QAudioDeviceInfo::availableDevices (mode); Q_FOREACH (auto const& p, devices) { - // qDebug () << "Audio device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes (); + // qDebug () << "Configuration::impl::load_audio_devices: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes (); // convert supported channel counts into something we can store in the item model QList channel_counts; diff --git a/Configuration.hpp b/Configuration.hpp index 45fafe59b..8594a4a6f 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -260,6 +260,8 @@ public: // i.e. the transceiver is ready for use. Q_SLOT void sync_transceiver (bool force_signal = false, bool enforce_mode_and_split = false); + Q_SLOT void invalidate_audio_input_device (QString error); + Q_SLOT void invalidate_audio_output_device (QString error); // // These signals indicate a font has been selected and accepted for diff --git a/Configuration.ui b/Configuration.ui index 4c8035754..d22032d53 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -7,7 +7,7 @@ 0 0 554 - 557 + 556 @@ -1364,71 +1364,8 @@ radio interface behave as expected. Soundcard - - - - Ou&tput: - - - sound_output_combo_box - - - - - - - &Input: - - - sound_input_combo_box - - - - - - - Select the audio channel used for transmission. -Unless you have multiple radios connected on different -channels; then you will usually want to select mono or -both here. - - - - Mono - - - - - Left - - - - - Right - - - - - Both - - - - - - - - - 1 - 0 - - - - Select the audio CODEC to use for receiving. - - - - + 1 @@ -1471,6 +1408,69 @@ transmitting periods. + + + + + 1 + 0 + + + + Select the audio CODEC to use for receiving. + + + + + + + Ou&tput: + + + sound_output_combo_box + + + + + + + Select the audio channel used for transmission. +Unless you have multiple radios connected on different +channels; then you will usually want to select mono or +both here. + + + + Mono + + + + + Left + + + + + Right + + + + + Both + + + + + + + + &Input: + + + sound_input_combo_box + + + @@ -2997,6 +2997,11 @@ Right click for insert and delete options. QListView
widgets/DecodeHighlightingListView.hpp
+ + LazyFillComboBox + QComboBox +
widgets/LazyFillComboBox.hpp
+
configuration_tabs @@ -3187,13 +3192,13 @@ Right click for insert and delete options. + + + - - - - + diff --git a/Detector/Detector.cpp b/Detector/Detector.cpp index d70dd42ff..164db2ab2 100644 --- a/Detector/Detector.cpp +++ b/Detector/Detector.cpp @@ -36,7 +36,7 @@ void Detector::setBlockSize (unsigned n) bool Detector::reset () { clear (); - // don't call base call reset because it calls seek(0) which causes + // don't call base class reset because it calls seek(0) which causes // a warning return isOpen (); } @@ -56,7 +56,6 @@ void Detector::clear () qint64 Detector::writeData (char const * data, qint64 maxSize) { - //qDebug () << "Detector::writeData: size:" << maxSize; static unsigned mstr0=999999; qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000; unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time diff --git a/Modulator/Modulator.cpp b/Modulator/Modulator.cpp index df365c507..96963029f 100644 --- a/Modulator/Modulator.cpp +++ b/Modulator/Modulator.cpp @@ -149,8 +149,6 @@ void Modulator::close () qint64 Modulator::readData (char * data, qint64 maxSize) { - // qDebug () << "readData: maxSize:" << maxSize; - double toneFrequency=1500.0; if(m_nsps==6) { toneFrequency=1000.0; diff --git a/widgets/LazyFillComboBox.cpp b/widgets/LazyFillComboBox.cpp new file mode 100644 index 000000000..08968f357 --- /dev/null +++ b/widgets/LazyFillComboBox.cpp @@ -0,0 +1,3 @@ +#include "LazyFillComboBox.hpp" + +#include "moc_LazyFillComboBox.cpp" diff --git a/widgets/LazyFillComboBox.hpp b/widgets/LazyFillComboBox.hpp new file mode 100644 index 000000000..7d9052673 --- /dev/null +++ b/widgets/LazyFillComboBox.hpp @@ -0,0 +1,40 @@ +#ifndef LAZY_FILL_COMBO_BOX_HPP__ +#define LAZY_FILL_COMBO_BOX_HPP__ + +#include + +class QWidget; + +// +// Class LazyFillComboBox +// +// QComboBox derivative that signals show and hide of the pop up list. +// +class LazyFillComboBox final + : public QComboBox +{ + Q_OBJECT + +public: + Q_SIGNAL void about_to_show_popup (); + Q_SIGNAL void popup_hidden (); + + explicit LazyFillComboBox (QWidget * parent = nullptr) + : QComboBox {parent} + { + } + + void showPopup () override + { + Q_EMIT about_to_show_popup (); + QComboBox::showPopup (); + } + + void hidePopup () override + { + QComboBox::hidePopup (); + Q_EMIT popup_hidden (); + } +}; + +#endif diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 8b27440c5..d8461bd67 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -454,6 +454,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, // hook up sound output stream slots & signals and disposal connect (this, &MainWindow::initializeAudioOutputStream, m_soundOutput, &SoundOutput::setFormat); connect (m_soundOutput, &SoundOutput::error, this, &MainWindow::showSoundOutError); + connect (m_soundOutput, &SoundOutput::error, &m_config, &Configuration::invalidate_audio_output_device); // connect (m_soundOutput, &SoundOutput::status, this, &MainWindow::showStatusMessage); connect (this, &MainWindow::outAttenuationChanged, m_soundOutput, &SoundOutput::setAttenuation); connect (&m_audioThread, &QThread::finished, m_soundOutput, &QObject::deleteLater); @@ -472,13 +473,14 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, connect (this, &MainWindow::reset_audio_input_stream, m_soundInput, &SoundInput::reset); connect (this, &MainWindow::finished, m_soundInput, &SoundInput::stop); connect(m_soundInput, &SoundInput::error, this, &MainWindow::showSoundInError); + connect(m_soundInput, &SoundInput::error, &m_config, &Configuration::invalidate_audio_input_device); // connect(m_soundInput, &SoundInput::status, this, &MainWindow::showStatusMessage); connect (m_soundInput, &SoundInput::dropped_frames, this, [this] (qint32 dropped_frames, qint64 usec) { if (dropped_frames > 48000 / 5) // 1/5 second { showStatusMessage (tr ("%1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3)); } - if (dropped_frames > 48000) // 1 second + if (dropped_frames > 5 * 48000) // seconds { auto period = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addMSecs (-m_TRperiod / 2.), m_TRperiod * 1e3); MessageBox::warning_message (this @@ -1824,14 +1826,14 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog m_psk_Reporter.sendReport (true); } - if(m_config.restart_audio_input ()) { + if(m_config.restart_audio_input () && !m_config.audio_input_device ().isNull ()) { Q_EMIT startAudioInputStream (m_config.audio_input_device () , rx_chunk_size * m_downSampleFactor , m_detector, m_downSampleFactor , m_config.audio_input_channel ()); } - if(m_config.restart_audio_output ()) { + if(m_config.restart_audio_output () && !m_config.audio_output_device ().isNull ()) { Q_EMIT initializeAudioOutputStream (m_config.audio_output_device () , AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2 , tx_audio_buffer_size);