From 9dc2c99ebda8d7b83ac5c203d5f4eb29f64d9036 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Tue, 8 Oct 2019 14:33:38 -0400 Subject: [PATCH] Initial commit of notifications spike --- CMakeLists.txt | 1 + Configuration.cpp | 160 +++++++++++++++++++++++++++++++++++ Configuration.hpp | 3 + Configuration.ui | 188 ++++++++++++++++++++++++++++++++++++++---- NotificationAudio.cpp | 158 +++++++++++++++++++++++++++++++++++ NotificationAudio.h | 65 +++++++++++++++ js8call.pro | 6 +- mainwindow.cpp | 16 ++++ mainwindow.h | 4 + 9 files changed, 584 insertions(+), 17 deletions(-) create mode 100644 NotificationAudio.cpp create mode 100644 NotificationAudio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fb25602..1daecdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,6 +253,7 @@ set (wsjtx_CXXSRCS wsprnet.cpp WSPRBandHopping.cpp TransmitTextEdit.cpp + NotificationAudio.cpp ) set (wsjt_CXXSRCS diff --git a/Configuration.cpp b/Configuration.cpp index 8214ab6..007b01c 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -530,6 +530,7 @@ private: bool restart_sound_input_device_; bool restart_sound_output_device_; + bool restart_notification_sound_output_device_; Type2MsgGen type_2_msg_gen_; @@ -688,10 +689,15 @@ private: QAudioDeviceInfo audio_input_device_; bool default_audio_input_device_selected_; AudioDevice::Channel audio_input_channel_; + QAudioDeviceInfo audio_output_device_; bool default_audio_output_device_selected_; AudioDevice::Channel audio_output_channel_; + QAudioDeviceInfo notification_audio_output_device_; + bool default_notification_audio_output_device_selected_; + AudioDevice::Channel notification_audio_output_channel_; + friend class Configuration; }; @@ -722,8 +728,11 @@ QAudioDeviceInfo const& Configuration::audio_input_device () const {return m_->a AudioDevice::Channel Configuration::audio_input_channel () const {return m_->audio_input_channel_;} QAudioDeviceInfo const& Configuration::audio_output_device () const {return m_->audio_output_device_;} AudioDevice::Channel Configuration::audio_output_channel () const {return m_->audio_output_channel_;} +QAudioDeviceInfo const& Configuration::notification_audio_output_device () const {return m_->notification_audio_output_device_;} +AudioDevice::Channel Configuration::notification_audio_output_channel () const {return m_->notification_audio_output_channel_;} bool Configuration::restart_audio_input () const {return m_->restart_sound_input_device_;} bool Configuration::restart_audio_output () const {return m_->restart_sound_output_device_;} +bool Configuration::restart_notification_audio_output () const {return m_->restart_notification_sound_output_device_;} auto Configuration::type_2_msg_gen () const -> Type2MsgGen {return m_->type_2_msg_gen_;} bool Configuration::use_dynamic_grid() const {return m_->use_dynamic_info_; } QString Configuration::my_callsign () const {return m_->my_callsign_;} @@ -1102,6 +1111,18 @@ template void setUppercase(T* t){ t->setFont(f); } +QWidget * centeredCheckBox(QWidget *parent, QCheckBox **ppCheckbox){ + auto w = new QWidget(parent); + auto cb = new QCheckBox(parent); + auto l = new QHBoxLayout(w); + l->setContentsMargins(1, 1, 1, 1); + l->setAlignment(Qt::AlignCenter); + l->addWidget(cb); + w->setLayout(l); + if(ppCheckbox) *ppCheckbox = cb; + return w; +} + Configuration::impl::impl (Configuration * self, QDir const& temp_directory, QSettings * settings, QWidget * parent) : QDialog {parent} @@ -1115,6 +1136,7 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory, , writeable_data_dir_ {QStandardPaths::writableLocation (QStandardPaths::DataLocation)} , restart_sound_input_device_ {false} , restart_sound_output_device_ {false} + , restart_notification_sound_output_device_ {false} , frequencies_ {&bands_} , next_frequencies_ {&bands_} , stations_ {&bands_} @@ -1277,8 +1299,12 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory, function cb (bind (&Configuration::impl::update_audio_channels, this, ui_->sound_input_combo_box, _1, ui_->sound_input_channel_combo_box, false)); connect (ui_->sound_input_combo_box, static_cast (&QComboBox::currentIndexChanged), cb); + cb = bind (&Configuration::impl::update_audio_channels, this, ui_->sound_output_combo_box, _1, ui_->sound_output_channel_combo_box, true); connect (ui_->sound_output_combo_box, static_cast (&QComboBox::currentIndexChanged), cb); + + cb = bind (&Configuration::impl::update_audio_channels, this, ui_->notification_sound_output_combo_box, _1, ui_->notification_sound_output_channel_combo_box, true); + connect (ui_->notification_sound_output_combo_box, static_cast (&QComboBox::currentIndexChanged), cb); } // @@ -1335,6 +1361,63 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory, ui_->frequencies_table_view->insertAction (nullptr, reset_frequencies_action_); connect (reset_frequencies_action_, &QAction::triggered, this, &Configuration::impl::reset_frequencies); + // + // setup notifications table view + // + QMap notifyRows = { + {"notify_cq", "CQ Message Received"}, + {"notify_hb", "HB Message Received"}, + {"notify_directed", "Directed Message Received"}, + {"notify_relay", "Relay Message Received"}, + {"notify_new_call", "New Callsign Heard"}, + {"notify_worked_call", "Worked Callsign Heard"}, + }; + + int i = 0; + auto table = ui_->notifications_table_widget; + foreach(QString key, notifyRows.keys()){ + QCheckBox *vcb; + auto w1 = centeredCheckBox(this, &vcb); + if(vcb){ + vcb->setChecked(true); + } + + QCheckBox *acb; + auto w2 = centeredCheckBox(this, &acb); + if(acb){ + acb->setChecked(true); + } + + QWidget *w3 = new QWidget(this); + QHBoxLayout *l3 = new QHBoxLayout(w3); + l3->setContentsMargins(9,1,1,1); + w3->setLayout(l3); + + QLabel *sflb = new QLabel(this); + sflb->setText("/tmp/file.wav"); + l3->addWidget(sflb); + + QPushButton *sfpb1 = new QPushButton(this); + sfpb1->setText("Select"); + l3->addWidget(sfpb1); + + QPushButton *sfpb3 = new QPushButton(this); + sfpb3->setText("Clear"); + l3->addWidget(sfpb3); + + + // + table->insertRow(i); + + auto labelItem = new QTableWidgetItem(notifyRows.value(key)); + table->setItem(i, 0, labelItem); + table->setCellWidget(i, 1, w1); + table->setCellWidget(i, 2, w2); + table->setCellWidget(i, 3, w3); + i++; + } + table->resizeColumnsToContents(); + // // setup stations table model & view @@ -1371,12 +1454,15 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory, // default_audio_input_device_selected_ = load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &audio_input_device_); default_audio_output_device_selected_ = load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &audio_output_device_); + default_notification_audio_output_device_selected_ = load_audio_devices (QAudio::AudioOutput, ui_->notification_sound_output_combo_box, ¬ification_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); + update_audio_channels (ui_->notification_sound_output_combo_box, ui_->notification_sound_output_combo_box->currentIndex (), ui_->notification_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_); + ui_->notification_sound_output_channel_combo_box->setCurrentIndex (notification_audio_output_channel_); enumerate_rigs (); initialize_models (); @@ -1718,9 +1804,36 @@ void Configuration::impl::read_settings () } } + { + // + // retrieve notification audio output device + // + auto saved_name = settings_->value("NotificationSoundOutName").toString(); + + // deal with special Windows default audio devices + auto default_device = QAudioDeviceInfo::defaultOutputDevice (); + if (saved_name == default_device.deviceName ()) + { + notification_audio_output_device_ = default_device; + default_notification_audio_output_device_selected_ = true; + } + else + { + default_notification_audio_output_device_selected_ = false; + Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)) // available audio output devices + { + if (p.deviceName () == saved_name) + { + notification_audio_output_device_ = p; + } + } + } + } + // retrieve audio channel info audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ()); audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ()); + notification_audio_output_channel_ = AudioDevice::fromString (settings_->value ("NotificationAudioOutputChannel", "Mono").toString ()); type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value (); @@ -1903,8 +2016,19 @@ void Configuration::impl::write_settings () settings_->setValue ("SoundOutName", audio_output_device_.deviceName ()); } + if (default_notification_audio_output_device_selected_) + { + settings_->setValue ("NotificationSoundOutName", QAudioDeviceInfo::defaultOutputDevice ().deviceName ()); + } + else + { + settings_->setValue ("NotificationSoundOutName", notification_audio_output_device_.deviceName ()); + } + + settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_)); settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_)); + settings_->setValue ("NotificationAudioOutputChannel", AudioDevice::toString (notification_audio_output_channel_)); settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_)); settings_->setValue ("TransmitDirected", transmit_directed_); settings_->setValue ("AutoreplyOnAtStartup", autoreply_on_at_startup_); @@ -2432,6 +2556,35 @@ void Configuration::impl::accept () } } + { + auto const& device_name = ui_->notification_sound_output_combo_box->currentText (); + if (device_name != notification_audio_output_device_.deviceName ()) + { + auto const& default_device = QAudioDeviceInfo::defaultOutputDevice (); + if (device_name == default_device.deviceName ()) + { + notification_audio_output_device_ = default_device; + } + else + { + bool found {false}; + Q_FOREACH (auto const& d, QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)) + { + if (device_name == d.deviceName ()) + { + notification_audio_output_device_ = d; + found = true; + } + } + if (!found) + { + notification_audio_output_device_ = default_device; + } + } + restart_notification_sound_output_device_ = true; + } + } + if (audio_input_channel_ != static_cast (ui_->sound_input_channel_combo_box->currentIndex ())) { audio_input_channel_ = static_cast (ui_->sound_input_channel_combo_box->currentIndex ()); @@ -2446,6 +2599,13 @@ void Configuration::impl::accept () } Q_ASSERT (audio_output_channel_ <= AudioDevice::Both); + if (notification_audio_output_channel_ != static_cast (ui_->notification_sound_output_channel_combo_box->currentIndex ())) + { + notification_audio_output_channel_ = static_cast (ui_->notification_sound_output_channel_combo_box->currentIndex ()); + restart_notification_sound_output_device_ = true; + } + Q_ASSERT (notification_audio_output_channel_ <= AudioDevice::Both); + auto_switch_bands_ = ui_->auto_switch_bands_check_box->isChecked(); my_callsign_ = ui_->callsign_line_edit->text ().toUpper().trimmed(); my_grid_ = ui_->grid_line_edit->text ().toUpper().trimmed(); diff --git a/Configuration.hpp b/Configuration.hpp index 95bc1ab..1bd7ab0 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -86,6 +86,8 @@ public: AudioDevice::Channel audio_input_channel () const; QAudioDeviceInfo const& audio_output_device () const; AudioDevice::Channel audio_output_channel () const; + QAudioDeviceInfo const& notification_audio_output_device () const; + AudioDevice::Channel notification_audio_output_channel () const; // These query methods should be used after a call to exec() to // determine if either the audio input or audio output stream @@ -93,6 +95,7 @@ public: // re-opened if they return true. bool restart_audio_input () const; bool restart_audio_output () const; + bool restart_notification_audio_output () const; bool use_dynamic_grid() const; QString my_callsign () const; diff --git a/Configuration.ui b/Configuration.ui index 5fcd312..cf8e20f 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -23,7 +23,7 @@ Select tab to change configuration parameters. - 0 + 6 @@ -793,8 +793,8 @@ text message. 0 0 - 738 - 453 + 616 + 331 @@ -1517,7 +1517,7 @@ a few, particularly some Kenwood rigs, require it). 0 0 - 494 + 718 490 @@ -1921,8 +1921,8 @@ this setting allows you to select which audio input will be used 0 0 - 266 - 329 + 760 + 502 @@ -1944,7 +1944,7 @@ this setting allows you to select which audio input will be used Souncard - Soundcard + Modulation Soundcard @@ -2057,6 +2057,72 @@ both here. + + + + Notification Soundcard + + + + + + O&utput: + + + sound_output_combo_box + + + + + + + + 1 + 0 + + + + Select the audio CODEC to use for transmitting. +If this is your default device for system sounds then +ensure that all system sounds are disabled otherwise +you will broadcast any systems sounds generated during +transmitting periods. + + + + + + + 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 + + + + + + + @@ -2201,6 +2267,9 @@ both here. + + + @@ -2234,7 +2303,7 @@ both here. 0 0 - 562 + 746 663 @@ -2729,8 +2798,8 @@ for assessing propagation and system performance. 0 0 - 487 - 341 + 760 + 502 @@ -3045,6 +3114,95 @@ QListView::item:hover { + + + &Notifications + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + true + + + + + 0 + 0 + 760 + 502 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::NoSelection + + + true + + + 125 + + + true + + + false + + + + Event + + + + + Visual Alert? + + + + + Audio Alert? + + + + + Sound File + + + + + + + + + + &UI @@ -3110,8 +3268,8 @@ QListView::item:hover { 0 0 - 274 - 690 + 724 + 418 @@ -4370,12 +4528,12 @@ soundcard changes - - - + + + diff --git a/NotificationAudio.cpp b/NotificationAudio.cpp new file mode 100644 index 0000000..1e1a5d4 --- /dev/null +++ b/NotificationAudio.cpp @@ -0,0 +1,158 @@ +#include "NotificationAudio.h" + + +NotificationAudio::NotificationAudio(QObject *parent) : + QIODevice(parent), + m_state(State::Stopped), + m_input(&m_data), + m_output(&m_data), + m_decoder(nullptr), + m_audio(nullptr) +{ + setOpenMode(QIODevice::ReadOnly); + + m_init = false; + m_isDecodingFinished = false; +} + +// initialize an audio device +void NotificationAudio::init(const QAudioDeviceInfo &device, const QAudioFormat& format) { + m_device = device; + m_format = format; + + if(!m_decoder){ + m_decoder = new QAudioDecoder(this); + connect(m_decoder, &QAudioDecoder::bufferReady, this, &NotificationAudio::bufferReady); + connect(m_decoder, &QAudioDecoder::finished, this, &NotificationAudio::finished); + } + m_decoder->setAudioFormat(m_format); + + if(!m_audio){ + m_audio = new QAudioOutput(m_device, m_format); + } + + if (!m_output.open(QIODevice::ReadOnly) || !m_input.open(QIODevice::WriteOnly)){ + m_init = false; + return; + } + + m_init = true; + emit initialized(); +} + +// play an audio file +void NotificationAudio::play(const QString &filePath) { + QFile *file = new QFile(this); + file->setFileName(filePath); + if (!file->open(QIODevice::ReadOnly)){ + return; + } + + playFile(file); +} + +void NotificationAudio::playFile(QFile *file){ + if(!m_init || !m_decoder || !m_audio || !file){ + return; + } + + resetBuffers(); + + m_file = file; + m_decoder->setSourceDevice(m_file); + m_decoder->start(); + + m_state = State::Playing; + emit stateChanged(m_state); + + m_audio->start(this); +} + +// Stop playing audio +void NotificationAudio::stop() { + resetBuffers(); + m_state = State::Stopped; + emit stateChanged(m_state); +} + +// Reset the internal buffers and ensure the decoder and audio device is stopped +void NotificationAudio::resetBuffers() { + if(m_audio){ + if(m_audio->state() != QAudio::StoppedState){ + m_audio->stop(); + } + } + + if(m_decoder){ + if(m_decoder->state() != QAudioDecoder::StoppedState){ + m_decoder->stop(); + } + } + + if(m_file){ + if(m_file->isOpen()){ + m_file->close(); + } + delete m_file; + m_file = nullptr; + } + + m_data.clear(); + m_isDecodingFinished = false; +} + +// io device, read into buffer. +qint64 NotificationAudio::readData(char* data, qint64 maxlen) { + memset(data, 0, maxlen); + + if (m_state == State::Playing) + { + m_output.read(data, maxlen); + + // There is we send readed audio data via signal, for ability get audio signal for the who listen this signal. + // Other word this emulate QAudioProbe behaviour for retrieve audio data which of sent to output device (speaker). + if (maxlen > 0) + { + QByteArray buff(data, maxlen); + emit newData(buff); + } + + // Is finish of file + if (atEnd()) + { + stop(); + } + } + + return maxlen; +} + +// io device, unused. +qint64 NotificationAudio::writeData(const char* data, qint64 len) { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; +} + +// io device, at end of device +bool NotificationAudio::atEnd() const { + return m_output.size() + && m_output.atEnd() + && m_isDecodingFinished; +} + +// handle buffered data ready +void NotificationAudio::bufferReady() { + const QAudioBuffer &buffer = m_decoder->read(); + + const int length = buffer.byteCount(); + const char *data = buffer.constData(); + + m_input.write(data, length); +} + +// handle buffered data decoding is finished +void NotificationAudio::finished() { + m_isDecodingFinished = true; +} diff --git a/NotificationAudio.h b/NotificationAudio.h new file mode 100644 index 0000000..cc9a64c --- /dev/null +++ b/NotificationAudio.h @@ -0,0 +1,65 @@ +#ifndef NOTIFICATIONAUDIO_H +#define NOTIFICATIONAUDIO_H + +#include +#include +#include +#include +#include +#include +#include + +// Class for decode audio files like MP3 and push decoded audio data to QOutputDevice (like speaker) and also signal newData(). +// For decoding it uses QAudioDecoder which uses QAudioFormat for decode audio file for desire format, then put decoded data to buffer. +// based on: https://github.com/Znurre/QtMixer +class NotificationAudio : public QIODevice +{ + Q_OBJECT + +public: + NotificationAudio(QObject * parent=nullptr); + + bool isInitialized() const { return m_init; } + + enum State { Playing, Stopped }; + + bool atEnd() const override; + +public slots: + void init(const QAudioDeviceInfo &device, const QAudioFormat& format); + void play(const QString &filePath); + void stop(); + +protected: + + qint64 readData(char* data, qint64 maxlen) override; + qint64 writeData(const char* data, qint64 len) override; + +private: + QFile *m_file; + State m_state; + QBuffer m_input; + QBuffer m_output; + QByteArray m_data; + QAudioFormat m_format; + QAudioDeviceInfo m_device; + QAudioDecoder * m_decoder; + QAudioOutput * m_audio; + + bool m_init; + bool m_isDecodingFinished; + + void playFile(QFile *file); + void resetBuffers(); + +private slots: + void bufferReady(); + void finished(); + +signals: + void initialized(); + void stateChanged(NotificationAudio::State state); + void newData(const QByteArray& data); +}; + +#endif // NOTIFICATIONAUDIO_H diff --git a/js8call.pro b/js8call.pro index 013ba79..aee517d 100644 --- a/js8call.pro +++ b/js8call.pro @@ -84,7 +84,8 @@ SOURCES += \ messagewindow.cpp \ SpotClient.cpp \ TCPClient.cpp \ - TransmitTextEdit.cpp + TransmitTextEdit.cpp \ + NotificationAudio.cpp HEADERS += qt_helpers.hpp \ pimpl_h.hpp pimpl_impl.hpp \ @@ -120,7 +121,8 @@ HEADERS += qt_helpers.hpp \ SpotClient.h \ TCPClient.h \ logbook/n3fjp.h \ - TransmitTextEdit.h + TransmitTextEdit.h \ + NotificationAudio.h INCLUDEPATH += qmake_only diff --git a/mainwindow.cpp b/mainwindow.cpp index 39c5b28..783f82f 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -72,6 +72,7 @@ #include "jsc_checker.h" #include "Inbox.h" #include "messagewindow.h" +#include "NotificationAudio.h" #include "ui_mainwindow.h" #include "moc_mainwindow.cpp" @@ -300,6 +301,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_soundInput {new SoundInput}, m_modulator {new Modulator {TX_SAMPLE_RATE, NTMAX}}, m_soundOutput {new SoundOutput}, + m_notification {new NotificationAudio}, m_msErase {0}, m_secBandChanged {0}, m_freqNominal {0}, @@ -501,6 +503,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, m_modulator->moveToThread (&m_audioThread); m_soundInput->moveToThread (&m_audioThread); m_detector->moveToThread (&m_audioThread); + m_notification->moveToThread(&m_audioThread); // hook up sound output stream slots & signals and disposal connect (this, &MainWindow::initializeAudioOutputStream, m_soundOutput, &SoundOutput::setFormat); @@ -509,6 +512,13 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, connect (this, &MainWindow::outAttenuationChanged, m_soundOutput, &SoundOutput::setAttenuation); connect (&m_audioThread, &QThread::finished, m_soundOutput, &QObject::deleteLater); + connect(this, &MainWindow::initializeNotificationAudioOutputStream, m_notification, &NotificationAudio::init); + connect(m_notification, &NotificationAudio::initialized, this, [this](){ + emit playNotification("/tmp/test.wav"); + }); + connect(this, &MainWindow::playNotification, m_notification, &NotificationAudio::play); + connect (&m_audioThread, &QThread::finished, m_notification, &QObject::deleteLater); + // hook up Modulator slots and disposal connect (this, &MainWindow::transmitFrequency, m_modulator, &Modulator::setFrequency); connect (this, &MainWindow::endTransmitMessage, m_modulator, &Modulator::stop); @@ -3003,6 +3013,12 @@ void MainWindow::openSettings(int tab){ m_msAudioOutputBuffered); } + if(m_config.restart_notification_audio_output ()) { + Q_EMIT initializeNotificationAudioOutputStream( + m_config.notification_audio_output_device(), + m_config.notification_audio_output_device().preferredFormat()); + } + ui->bandComboBox->view ()->setMinimumWidth (ui->bandComboBox->view ()->sizeHintForColumn (FrequencyList_v2::frequency_mhz_column)); displayDialFrequency (); diff --git a/mainwindow.h b/mainwindow.h index 8eb104d..09fb9e1 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -48,6 +48,7 @@ #include "SpotClient.h" #include "APRSISClient.h" #include "keyeater.h" +#include "NotificationAudio.h" #define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync #define NUM_JT65_SYMBOLS 126 //63 data + 63 sync @@ -434,6 +435,8 @@ private slots: void refreshTextDisplay(); private: + Q_SIGNAL void playNotification(const QString &name); + Q_SIGNAL void initializeNotificationAudioOutputStream(QAudioDeviceInfo, QAudioFormat); Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo, unsigned channels, unsigned msBuffered) const; Q_SIGNAL void stopAudioOutputStream () const; @@ -502,6 +505,7 @@ private: SoundInput * m_soundInput; Modulator * m_modulator; SoundOutput * m_soundOutput; + NotificationAudio * m_notification; QThread m_audioThread; qint64 m_msErase;