From 0cf14dfcc9e3f3a6f353bda809c3f4d06ad27c91 Mon Sep 17 00:00:00 2001 From: Bill Somerville Date: Tue, 11 Aug 2020 13:48:01 +0100 Subject: [PATCH] Remove user adjustable audio buffer sizes from Settings Fixed buffer sizes are used. Rx use s 3456 x 1st downsample rate x 5 audio frames of buffer space. On Windows this means that each chunk (periodSize()) delivered from the audio stream is our initial DSP processing chunk size, thus matching audio buffer latency exactly with WSJT-X's own front end latency. This should result in optimal resilience to high system loads that might starve the soundcard ADC of buffers to fill and case dropped audio frames. For Tx a buffer sufficient for 1 s of audio is used at present, on Windows the period size will be set to 1/40 of that which gives reasonably low latency and plenty of resilience to high system loads that might starve the soundcard DAC of audio frames to render. Note that a 1 s buffer will make the "Pwr" slider slow to respond, we may have to reduce the Tx audio buffer size if this is seen as a problem. --- Audio/soundin.cpp | 13 ++-- Audio/soundout.cpp | 19 ++---- Configuration.cpp | 27 -------- Configuration.hpp | 2 - Configuration.ui | 135 +++++++++++----------------------------- Detector/Detector.cpp | 2 +- Modulator/Modulator.cpp | 2 +- widgets/mainwindow.cpp | 40 ++++++++---- 8 files changed, 79 insertions(+), 161 deletions(-) diff --git a/Audio/soundin.cpp b/Audio/soundin.cpp index 8fede3524..19c41f61b 100644 --- a/Audio/soundin.cpp +++ b/Audio/soundin.cpp @@ -79,15 +79,16 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged); - qDebug () << "SoundIn default buffer size (bytes):" << m_stream->bufferSize (); - m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer)); - m_stream->setBufferSize (m_stream->format ().bytesForFrames (3456 * 4 * 5)); - qDebug () << "SoundIn selected buffer size (bytes):" << m_stream->bufferSize (); + //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 + // size for period size other platforms seem to optimize themselves + m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer * 5)); if (sink->initialize (QIODevice::WriteOnly, channel)) { m_stream->start (sink); audioError (); cummulative_lost_usec_ = -1; + //qDebug () << "SoundIn selected buffer size (bytes):" << m_stream->bufferSize () << "peirod size:" << m_stream->periodSize (); } else { @@ -121,7 +122,7 @@ void SoundInput::resume () void SoundInput::handleStateChanged (QAudio::State newState) { - qDebug () << "SoundInput::handleStateChanged: newState:" << newState; + //qDebug () << "SoundInput::handleStateChanged: newState:" << newState; switch (newState) { @@ -167,7 +168,7 @@ void SoundInput::reset (bool report_dropped_frames) { auto lost_usec = m_stream->elapsedUSecs () - m_stream->processedUSecs () - cummulative_lost_usec_; Q_EMIT dropped_frames (m_stream->format ().framesForDuration (lost_usec), lost_usec); - qDebug () << "SoundInput::reset: frames dropped:" << m_stream->format ().framesForDuration (lost_usec) << "sec:" << lost_usec / 1.e6; + //qDebug () << "SoundInput::reset: frames dropped:" << m_stream->format ().framesForDuration (lost_usec) << "sec:" << lost_usec / 1.e6; } cummulative_lost_usec_ = m_stream->elapsedUSecs () - m_stream->processedUSecs (); } diff --git a/Audio/soundout.cpp b/Audio/soundout.cpp index ecd7ce723..71625a3f6 100644 --- a/Audio/soundout.cpp +++ b/Audio/soundout.cpp @@ -79,24 +79,17 @@ void SoundOutput::restart (QIODevice * source) { Q_ASSERT (m_stream); - // - // This buffer size is critical since for proper sound streaming. If - // it is too short; high activity levels on the machine can starve - // the audio buffer. On the other hand the Windows implementation - // seems to take the length of the buffer in time to stop the audio - // stream even if reset() is used. - // - // 2 seconds seems a reasonable compromise except for Windows - // where things are probably broken. - // // we have to set this before every start on the stream because the // Windows implementation seems to forget the buffer size after a // stop. - qDebug () << "SoundOut default buffer size (bytes):" << m_stream->bufferSize (); - m_stream->setBufferSize (m_stream->format().bytesForFrames (m_framesBuffered)); - qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize (); + //qDebug () << "SoundOut default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize (); + if (m_framesBuffered) + { + m_stream->setBufferSize (m_stream->format().bytesForFrames (m_framesBuffered)); + } m_stream->setCategory ("production"); m_stream->start (source); + //qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize (); } void SoundOutput::suspend () diff --git a/Configuration.cpp b/Configuration.cpp index 2a09dad6a..9b7d99eaf 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -242,8 +242,6 @@ namespace // Magic numbers for file validation constexpr quint32 qrg_magic {0xadbccbdb}; constexpr quint32 qrg_version {100}; // M.mm - - constexpr int default_audio_buffer_size = 10; } @@ -648,10 +646,8 @@ private: QAudioDeviceInfo audio_input_device_; AudioDevice::Channel audio_input_channel_; - int audio_input_buffer_size_; QAudioDeviceInfo audio_output_device_; AudioDevice::Channel audio_output_channel_; - int audio_output_buffer_size_; friend class Configuration; }; @@ -681,10 +677,8 @@ bool Configuration::is_active () const {return m_->isVisible ();} QAudioDeviceInfo const& Configuration::audio_input_device () const {return m_->audio_input_device_;} AudioDevice::Channel Configuration::audio_input_channel () const {return m_->audio_input_channel_;} -int Configuration::audio_input_buffer_size () const {return m_->audio_input_buffer_size_ * 1024;} QAudioDeviceInfo const& Configuration::audio_output_device () const {return m_->audio_output_device_;} AudioDevice::Channel Configuration::audio_output_channel () const {return m_->audio_output_channel_;} -int Configuration::audio_output_buffer_size () const {return m_->audio_output_buffer_size_ * 1024;} bool Configuration::restart_audio_input () const {return m_->restart_sound_input_device_;} bool Configuration::restart_audio_output () const {return m_->restart_sound_output_device_;} auto Configuration::type_2_msg_gen () const -> Type2MsgGen {return m_->type_2_msg_gen_;} @@ -978,8 +972,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network , transceiver_command_number_ {0} , degrade_ {0.} // initialize to zero each run, not // saved in settings - , audio_input_buffer_size_ {default_audio_buffer_size} - , audio_output_buffer_size_ {default_audio_buffer_size} { ui_->setupUi (this); @@ -1242,9 +1234,7 @@ void Configuration::impl::initialize_models () 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_->audio_ip_buffer_spin_box->setValue (audio_input_buffer_size_); ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_); - ui_->audio_op_buffer_spin_box->setValue (audio_output_buffer_size_); ui_->save_path_display_label->setText (save_directory_.absolutePath ()); ui_->azel_path_display_label->setText (azel_directory_.absolutePath ()); @@ -1430,10 +1420,6 @@ void Configuration::impl::read_settings () audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ()); audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ()); - // retrieve audio buffer size values - audio_input_buffer_size_ = settings_->value ("AudioInputBufferSize", default_audio_buffer_size).toInt (); - audio_output_buffer_size_ = settings_->value ("AudioOutputBufferSize", default_audio_buffer_size).toInt (); - 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 (); @@ -1558,8 +1544,6 @@ void Configuration::impl::write_settings () settings_->setValue ("SoundOutName", audio_output_device_.deviceName ()); settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_)); settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_)); - settings_->setValue ("AudioInputBufferSize", audio_input_buffer_size_); - settings_->setValue ("AudioOutputBufferSize", audio_output_buffer_size_); settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_)); settings_->setValue ("MonitorOFF", monitor_off_at_startup_); settings_->setValue ("MonitorLastUsed", monitor_last_used_); @@ -1988,17 +1972,6 @@ void Configuration::impl::accept () } Q_ASSERT (audio_output_channel_ <= AudioDevice::Both); - if (audio_input_buffer_size_ != ui_->audio_ip_buffer_spin_box->value ()) - { - audio_input_buffer_size_ = ui_->audio_ip_buffer_spin_box->value (); - restart_sound_input_device_ = true; - } - if (audio_output_buffer_size_ != ui_->audio_op_buffer_spin_box->value ()) - { - audio_output_buffer_size_ = ui_->audio_op_buffer_spin_box->value (); - restart_sound_output_device_ = true; - } - my_callsign_ = ui_->callsign_line_edit->text (); my_grid_ = ui_->grid_line_edit->text (); FD_exchange_= ui_->Field_Day_Exchange->text ().toUpper (); diff --git a/Configuration.hpp b/Configuration.hpp index 3087ea855..882a78629 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -87,10 +87,8 @@ public: QAudioDeviceInfo const& audio_input_device () const; AudioDevice::Channel audio_input_channel () const; - int audio_input_buffer_size () const; QAudioDeviceInfo const& audio_output_device () const; AudioDevice::Channel audio_output_channel () const; - int audio_output_buffer_size () const; // These query methods should be used after a call to exec() to // determine if either the audio input or audio output stream diff --git a/Configuration.ui b/Configuration.ui index eaa68bf30..610bc85ad 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -6,8 +6,8 @@ 0 0 - 553 - 563 + 554 + 557 @@ -1349,31 +1349,14 @@ radio interface behave as expected. Soundcard - - - - Select the channel to use for receiving. + + + + Ou&tput: + + + sound_output_combo_box - - - Mono - - - - - Left - - - - - Right - - - - - Both - - @@ -1416,24 +1399,6 @@ both here. - - - - Audio output buffer size in kilo-frames - -Adjust for minimum dropped samples reported. Too low values are likely to cause dropped samples causing audio drop-outs. - - - k - - - 4 - - - 128 - - - @@ -1447,27 +1412,6 @@ Adjust for minimum dropped samples reported. Too low values are likely to cause - - - - Audio input buffer size in kilo-frames - -Adjust for minimum dropped samples reported. Too low values are likely to cause dropped samples causing audio drop-outs. - - - k - - - - - - 4 - - - 128 - - - @@ -1485,34 +1429,31 @@ transmitting periods. - - - - Ou&tput: - - - sound_output_combo_box - - - - - - - Buffer: - - - audio_ip_buffer_spin_box - - - - - - - Buffer: - - - audio_op_buffer_spin_box + + + + Select the channel to use for receiving. + + + Mono + + + + + Left + + + + + Right + + + + + Both + + @@ -3064,10 +3005,8 @@ Right click for insert and delete options. test_PTT_push_button sound_input_combo_box sound_input_channel_combo_box - audio_ip_buffer_spin_box sound_output_combo_box sound_output_channel_combo_box - audio_op_buffer_spin_box save_path_select_push_button azel_path_select_push_button checkBoxPwrBandTxMemory @@ -3194,13 +3133,13 @@ Right click for insert and delete options. - - - + - + + + diff --git a/Detector/Detector.cpp b/Detector/Detector.cpp index 7feb5a298..d70dd42ff 100644 --- a/Detector/Detector.cpp +++ b/Detector/Detector.cpp @@ -56,7 +56,7 @@ void Detector::clear () qint64 Detector::writeData (char const * data, qint64 maxSize) { - qDebug () << "Detector::writeData: size:" << 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 d43de8292..e99b21a46 100644 --- a/Modulator/Modulator.cpp +++ b/Modulator/Modulator.cpp @@ -142,7 +142,7 @@ void Modulator::close () qint64 Modulator::readData (char * data, qint64 maxSize) { - // qDebug () << "readData: maxSize:" << maxSize; + //qDebug () << "readData: maxSize:" << maxSize; double toneFrequency=1500.0; if(m_nsps==6) { diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 56c99f1a6..beadaadc8 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -209,6 +209,8 @@ namespace QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"}; auto quint32_max = std::numeric_limits::max (); constexpr int N_WIDGETS {34}; + constexpr int rx_chunk_size {3456}; // audio samples at 12000 Hz + constexpr int tx_audio_buffer_size {48000}; // audio samples at 48000 Hz bool message_is_73 (int type, QStringList const& msg_parts) { @@ -474,7 +476,18 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, connect(m_soundInput, &SoundInput::error, this, &MainWindow::showSoundInError); // connect(m_soundInput, &SoundInput::status, this, &MainWindow::showStatusMessage); connect (m_soundInput, &SoundInput::dropped_frames, this, [this] (qint32 dropped_frames, qint64 usec) { - showStatusMessage (tr ("%1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3)); + if (dropped_frames > 4800) // 1/10 second + { + showStatusMessage (tr ("%1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3)); + } + if (dropped_frames > 24000) // 1/2 + // second + { + MessageBox::warning_message (this + , tr ("Audio Source") + , tr ("Excessive dropped samples") + , tr ("Reduce system load, or increase audio buffer size")); + } }); connect (&m_audioThread, &QThread::finished, m_soundInput, &QObject::deleteLater); @@ -942,14 +955,14 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, if (!m_config.audio_input_device ().isNull ()) { Q_EMIT startAudioInputStream (m_config.audio_input_device () - , m_config.audio_input_buffer_size () + , rx_chunk_size * m_downSampleFactor , m_detector, m_downSampleFactor, m_config.audio_input_channel ()); } if (!m_config.audio_output_device ().isNull ()) { Q_EMIT initializeAudioOutputStream (m_config.audio_output_device () , AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2 - , m_config.audio_output_buffer_size ()); + , tx_audio_buffer_size); } Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT); @@ -1628,14 +1641,15 @@ QString MainWindow::save_wave_file (QString const& name, short const * data, int format.setChannelCount (1); format.setSampleSize (16); format.setSampleType (QAudioFormat::SignedInt); - auto source = QString {"%1, %2"}.arg (my_callsign).arg (my_grid); - auto comment = QString {"Mode=%1%2, Freq=%3%4"} - .arg (mode) - .arg (QString {(mode.contains ('J') && !mode.contains ('+')) || mode.startsWith ("FST4") - ? QString {", Sub Mode="} + QChar {'A' + sub_mode} - : QString {}}) - .arg (Radio::frequency_MHz_string (frequency)) - .arg (QString {mode!="WSPR" ? QString {", DXCall=%1, DXGrid=%2"} + auto source = QString {"%1; %2"}.arg (my_callsign).arg (my_grid); + auto comment = QString {"Mode=%1%2; Freq=%3%4"} + .arg (mode) + .arg (QString {(mode.contains ('J') && !mode.contains ('+')) + || mode.startsWith ("FST4") || mode.startsWith ("QRA") + ? QString {"; Sub Mode="} + QString::number (int (samples / 12000)) + QChar {'A' + sub_mode} + : QString {}}) + .arg (Radio::frequency_MHz_string (frequency)) + .arg (QString {mode!="WSPR" ? QString {"; DXCall=%1; DXGrid=%2"} .arg (his_call) .arg (his_grid).toLocal8Bit () : ""}); BWFFile::InfoDictionary list_info { @@ -1814,7 +1828,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog if(m_config.restart_audio_input ()) { Q_EMIT startAudioInputStream (m_config.audio_input_device () - , m_config.audio_input_buffer_size () + , rx_chunk_size * m_downSampleFactor , m_detector, m_downSampleFactor , m_config.audio_input_channel ()); } @@ -1822,7 +1836,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog if(m_config.restart_audio_output ()) { Q_EMIT initializeAudioOutputStream (m_config.audio_output_device () , AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2 - , m_config.audio_output_buffer_size ()); + , tx_audio_buffer_size); } displayDialFrequency ();