Trying out new notification code
This commit is contained in:
parent
8bec6fdce0
commit
05a625dfe8
119
AudioDecoder.cpp
Normal file
119
AudioDecoder.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "AudioDecoder.h"
|
||||
|
||||
AudioDecoder::AudioDecoder(QObject *parent) :
|
||||
QIODevice(parent),
|
||||
m_state { AudioDecoder::Stopped },
|
||||
m_input { &m_data, this },
|
||||
m_output { &m_data, this },
|
||||
m_init { false },
|
||||
m_isDecodingFinished { false }
|
||||
{
|
||||
setOpenMode(QIODevice::ReadOnly);
|
||||
|
||||
m_decoder = new QAudioDecoder(this);
|
||||
connect(m_decoder, &QAudioDecoder::bufferReady, this, &AudioDecoder::bufferReady);
|
||||
connect(m_decoder, static_cast<void(QAudioDecoder::*)(QAudioDecoder::Error)>(&QAudioDecoder::error), this, &AudioDecoder::errored);
|
||||
connect(m_decoder, &QAudioDecoder::finished, this, &AudioDecoder::finished);
|
||||
}
|
||||
|
||||
AudioDecoder::~AudioDecoder(){
|
||||
stop();
|
||||
}
|
||||
|
||||
// initialize an audio device
|
||||
void AudioDecoder::init(const QAudioFormat &format) {
|
||||
m_decoder->setAudioFormat(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 AudioDecoder::start(const QString &filePath){
|
||||
if(!m_init){
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_state == AudioDecoder::Decoding){
|
||||
return;
|
||||
}
|
||||
|
||||
m_state = AudioDecoder::Decoding;
|
||||
m_decoder->setSourceFilename(filePath);
|
||||
m_decoder->start();
|
||||
}
|
||||
|
||||
// Stop playing audio
|
||||
void AudioDecoder::stop() {
|
||||
m_state = AudioDecoder::Stopped;
|
||||
m_decoder->stop();
|
||||
m_data.clear();
|
||||
m_isDecodingFinished = false;
|
||||
}
|
||||
|
||||
|
||||
// io device, read into buffer.
|
||||
qint64 AudioDecoder::readData(char* data, qint64 maxlen) {
|
||||
memset(data, 0, maxlen);
|
||||
|
||||
if (m_state == AudioDecoder::Decoding){
|
||||
m_output.read(data, maxlen);
|
||||
|
||||
// Emulate QAudioProbe behaviour for audio data that is sent to output device
|
||||
if (maxlen > 0){
|
||||
QByteArray buff(data, maxlen);
|
||||
emit newData(buff);
|
||||
}
|
||||
|
||||
// Is finish of file
|
||||
if (atEnd()){
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
return maxlen;
|
||||
}
|
||||
|
||||
// io device, unused.
|
||||
qint64 AudioDecoder::writeData(const char* data, qint64 len) {
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// io device, at end of device
|
||||
bool AudioDecoder::atEnd() const {
|
||||
bool value = m_output.size()
|
||||
&& m_output.atEnd()
|
||||
&& m_isDecodingFinished;
|
||||
return value;
|
||||
}
|
||||
|
||||
// handle buffered data ready
|
||||
void AudioDecoder::bufferReady() {
|
||||
const QAudioBuffer &buffer = m_decoder->read();
|
||||
if(!buffer.isValid()){
|
||||
return;
|
||||
}
|
||||
|
||||
const int length = buffer.byteCount();
|
||||
const char *data = buffer.constData<char>();
|
||||
|
||||
m_input.write(data, length);
|
||||
}
|
||||
|
||||
// handle buffered data decoding is finished
|
||||
void AudioDecoder::finished() {
|
||||
m_isDecodingFinished = true;
|
||||
}
|
||||
|
||||
// handle buffered data decoding error
|
||||
void AudioDecoder::errored(QAudioDecoder::Error /*error*/) {
|
||||
stop();
|
||||
}
|
50
AudioDecoder.h
Normal file
50
AudioDecoder.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef AUDIODECODER_H
|
||||
#define AUDIODECODER_H
|
||||
|
||||
#include <QAudioDecoder>
|
||||
#include <QAudioFormat>
|
||||
#include <QBuffer>
|
||||
#include <QIODevice>
|
||||
#include <QPointer>
|
||||
|
||||
|
||||
class AudioDecoder :
|
||||
public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State { Decoding, Stopped };
|
||||
explicit AudioDecoder(QObject *parent = 0);
|
||||
~AudioDecoder();
|
||||
|
||||
bool atEnd() const override;
|
||||
|
||||
public slots:
|
||||
void init(const QAudioFormat &format);
|
||||
void start(const QString &filePath);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
qint64 readData(char* data, qint64 maxlen) override;
|
||||
qint64 writeData(const char* data, qint64 len) override;
|
||||
|
||||
private:
|
||||
State m_state;
|
||||
QPointer<QAudioDecoder> m_decoder;
|
||||
QBuffer m_input;
|
||||
QBuffer m_output;
|
||||
QByteArray m_data;
|
||||
bool m_init;
|
||||
bool m_isDecodingFinished;
|
||||
|
||||
private slots:
|
||||
void bufferReady();
|
||||
void finished();
|
||||
void errored(QAudioDecoder::Error);
|
||||
|
||||
signals:
|
||||
void initialized();
|
||||
void newData(const QByteArray& data);
|
||||
};
|
||||
|
||||
#endif // AUDIODECODER_H
|
@ -253,6 +253,7 @@ set (wsjtx_CXXSRCS
|
||||
wsprnet.cpp
|
||||
WSPRBandHopping.cpp
|
||||
TransmitTextEdit.cpp
|
||||
AudioDecoder.cpp
|
||||
NotificationAudio.cpp
|
||||
)
|
||||
|
||||
|
@ -2,162 +2,27 @@
|
||||
|
||||
|
||||
NotificationAudio::NotificationAudio(QObject *parent):
|
||||
QIODevice(parent),
|
||||
m_state(State::Stopped),
|
||||
m_input(&m_data, this),
|
||||
m_output(&m_data, this),
|
||||
m_decoder(nullptr),
|
||||
m_audio(nullptr)
|
||||
QObject(parent)
|
||||
{
|
||||
setOpenMode(QIODevice::ReadOnly);
|
||||
|
||||
m_init = false;
|
||||
m_isDecodingFinished = false;
|
||||
m_stream = new SoundOutput();
|
||||
m_decoder = new AudioDecoder(this);
|
||||
}
|
||||
|
||||
NotificationAudio::~NotificationAudio(){
|
||||
stop();
|
||||
|
||||
if(m_decoder){
|
||||
delete m_decoder;
|
||||
m_decoder = nullptr;
|
||||
}
|
||||
|
||||
if(m_audio){
|
||||
delete m_audio;
|
||||
m_audio = nullptr;
|
||||
}
|
||||
void NotificationAudio::setDevice(const QAudioDeviceInfo &device, unsigned channels, unsigned msBuffer){
|
||||
m_stream->setFormat(device, channels, msBuffer);
|
||||
m_decoder->init(m_stream->format());
|
||||
}
|
||||
|
||||
// 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, static_cast<void(QAudioDecoder::*)(QAudioDecoder::Error)>(&QAudioDecoder::error), this, &NotificationAudio::errored);
|
||||
connect(m_decoder, &QAudioDecoder::finished, this, &NotificationAudio::finished);
|
||||
}
|
||||
m_decoder->setAudioFormat(m_format);
|
||||
|
||||
if(!m_audio){
|
||||
m_audio = new QAudioOutput(m_device, m_format, this);
|
||||
}
|
||||
|
||||
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){
|
||||
if(m_state == NotificationAudio::Playing){
|
||||
return;
|
||||
m_decoder->start(filePath);
|
||||
m_stream->restart(m_decoder);
|
||||
}
|
||||
|
||||
if(!m_init || !m_decoder || !m_audio){
|
||||
return;
|
||||
}
|
||||
|
||||
resetBuffers();
|
||||
|
||||
m_state = State::Playing;
|
||||
emit stateChanged(m_state);
|
||||
|
||||
m_decoder->setSourceFilename(filePath);
|
||||
m_decoder->start();
|
||||
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::SuspendedState){
|
||||
m_audio->suspend();
|
||||
}
|
||||
}
|
||||
|
||||
if(m_decoder){
|
||||
if(m_decoder->state() != QAudioDecoder::StoppedState){
|
||||
m_decoder->stop();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
bool value = m_output.size()
|
||||
&& m_output.atEnd()
|
||||
&& m_isDecodingFinished;
|
||||
return value;
|
||||
}
|
||||
|
||||
// handle buffered data ready
|
||||
void NotificationAudio::bufferReady() {
|
||||
const QAudioBuffer &buffer = m_decoder->read();
|
||||
if(!buffer.isValid()){
|
||||
return;
|
||||
}
|
||||
|
||||
const int length = buffer.byteCount();
|
||||
const char *data = buffer.constData<char>();
|
||||
|
||||
m_input.write(data, length);
|
||||
}
|
||||
|
||||
// handle buffered data decoding is finished
|
||||
void NotificationAudio::finished() {
|
||||
m_isDecodingFinished = true;
|
||||
}
|
||||
|
||||
// handle buffered data decoding error
|
||||
void NotificationAudio::errored(QAudioDecoder::Error /*error*/) {
|
||||
stop();
|
||||
m_stream->stop();
|
||||
}
|
||||
|
@ -3,16 +3,18 @@
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QBuffer>
|
||||
#include <QAudioDecoder>
|
||||
#include <QAudioDeviceInfo>
|
||||
#include <QAudioFormat>
|
||||
#include <QAudioOutput>
|
||||
#include <QFile>
|
||||
#include <QPointer>
|
||||
|
||||
// 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
|
||||
#include "AudioDevice.hpp"
|
||||
#include "AudioDecoder.h"
|
||||
#include "soundout.h"
|
||||
|
||||
class NotificationAudio :
|
||||
public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -20,46 +22,14 @@ public:
|
||||
NotificationAudio(QObject * parent=nullptr);
|
||||
~NotificationAudio();
|
||||
|
||||
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 setDevice(const QAudioDeviceInfo &device, unsigned channels, unsigned msBuffer=0);
|
||||
void play(const QString &filePath);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
|
||||
qint64 readData(char* data, qint64 maxlen) override;
|
||||
qint64 writeData(const char* data, qint64 len) override;
|
||||
|
||||
private:
|
||||
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 resetBuffers();
|
||||
|
||||
private slots:
|
||||
void bufferReady();
|
||||
void finished();
|
||||
void errored(QAudioDecoder::Error);
|
||||
|
||||
signals:
|
||||
void initialized();
|
||||
void stateChanged(NotificationAudio::State state);
|
||||
void newData(const QByteArray& data);
|
||||
QPointer<SoundOutput> m_stream;
|
||||
QPointer<AudioDecoder> m_decoder;
|
||||
};
|
||||
|
||||
#endif // NOTIFICATIONAUDIO_H
|
||||
|
@ -86,7 +86,8 @@ SOURCES += \
|
||||
TCPClient.cpp \
|
||||
TransmitTextEdit.cpp \
|
||||
NotificationAudio.cpp \
|
||||
CallsignValidator.cpp
|
||||
CallsignValidator.cpp \
|
||||
AudioDecoder.cpp
|
||||
|
||||
HEADERS += qt_helpers.hpp \
|
||||
pimpl_h.hpp pimpl_impl.hpp \
|
||||
@ -123,7 +124,8 @@ HEADERS += qt_helpers.hpp \
|
||||
TCPClient.h \
|
||||
logbook/n3fjp.h \
|
||||
TransmitTextEdit.h \
|
||||
NotificationAudio.h
|
||||
NotificationAudio.h \
|
||||
AudioDecoder.h
|
||||
|
||||
|
||||
INCLUDEPATH += qmake_only
|
||||
|
@ -514,7 +514,7 @@ 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 (this, &MainWindow::initializeNotificationAudioOutputStream, m_notification, &NotificationAudio::setDevice);
|
||||
connect (&m_config, &Configuration::test_notify, this, &MainWindow::tryNotify);
|
||||
connect (this, &MainWindow::playNotification, m_notification, &NotificationAudio::play);
|
||||
connect (&m_notificationAudioThread, &QThread::finished, m_notification, &QObject::deleteLater);
|
||||
@ -945,7 +945,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
// Q_EMIT startAudioInputStream (m_config.audio_input_device (), m_framesAudioInputBuffered, &m_detector, m_downSampleFactor, m_config.audio_input_channel ());
|
||||
Q_EMIT startAudioInputStream (m_config.audio_input_device (), m_framesAudioInputBuffered, m_detector, m_downSampleFactor, m_config.audio_input_channel ());
|
||||
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (), AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2, m_msAudioOutputBuffered);
|
||||
Q_EMIT initializeNotificationAudioOutputStream(m_config.notification_audio_output_device(), m_config.notification_audio_output_device().preferredFormat());
|
||||
Q_EMIT initializeNotificationAudioOutputStream(m_config.notification_audio_output_device(), AudioDevice::Mono == m_config.notification_audio_output_channel () ? 1 : 2, m_msAudioOutputBuffered);
|
||||
Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
|
||||
|
||||
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
|
||||
@ -3023,9 +3023,9 @@ void MainWindow::openSettings(int tab){
|
||||
}
|
||||
|
||||
if(m_config.restart_notification_audio_output ()) {
|
||||
Q_EMIT initializeNotificationAudioOutputStream(
|
||||
m_config.notification_audio_output_device(),
|
||||
m_config.notification_audio_output_device().preferredFormat());
|
||||
Q_EMIT initializeNotificationAudioOutputStream(m_config.notification_audio_output_device(),
|
||||
AudioDevice::Mono == m_config.notification_audio_output_channel () ? 1 : 2,
|
||||
m_msAudioOutputBuffered);
|
||||
}
|
||||
|
||||
ui->bandComboBox->view ()->setMinimumWidth (ui->bandComboBox->view ()->sizeHintForColumn (FrequencyList_v2::frequency_mhz_column));
|
||||
|
@ -438,7 +438,7 @@ private slots:
|
||||
|
||||
private:
|
||||
Q_SIGNAL void playNotification(const QString &name);
|
||||
Q_SIGNAL void initializeNotificationAudioOutputStream(QAudioDeviceInfo, QAudioFormat);
|
||||
Q_SIGNAL void initializeNotificationAudioOutputStream(const QAudioDeviceInfo &, unsigned, unsigned) const;
|
||||
Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo,
|
||||
unsigned channels, unsigned msBuffered) const;
|
||||
Q_SIGNAL void stopAudioOutputStream () const;
|
||||
|
@ -71,6 +71,7 @@ void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels,
|
||||
}
|
||||
// qDebug () << "Selected audio output format:" << format;
|
||||
|
||||
m_format = format;
|
||||
m_stream.reset (new QAudioOutput (device, format));
|
||||
audioError ();
|
||||
m_stream->setVolume (m_volume);
|
||||
@ -146,6 +147,10 @@ qreal SoundOutput::attenuation () const
|
||||
return -(20. * qLn (m_volume) / qLn (10.));
|
||||
}
|
||||
|
||||
QAudioFormat SoundOutput::format() const{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
void SoundOutput::setAttenuation (qreal a)
|
||||
{
|
||||
Q_ASSERT (0. <= a && a <= 999.);
|
||||
|
@ -24,6 +24,7 @@ public:
|
||||
}
|
||||
|
||||
qreal attenuation () const;
|
||||
QAudioFormat format() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setFormat (QAudioDeviceInfo const& device, unsigned channels, unsigned msBuffered = 0u);
|
||||
@ -47,6 +48,7 @@ private Q_SLOTS:
|
||||
|
||||
private:
|
||||
QScopedPointer<QAudioOutput> m_stream;
|
||||
QAudioFormat m_format;
|
||||
unsigned m_msBuffered;
|
||||
qreal m_volume;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user