moved audio output manager and device to separate thread
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
|
||||
AudioHybridDevice::AudioHybridDevice(QObject *parent) :
|
||||
QIODevice(parent),
|
||||
output_(nullptr),
|
||||
device_(nullptr),
|
||||
sample_index_(0),
|
||||
enable_sending_samples_(false)
|
||||
@@ -32,20 +33,37 @@ AudioHybridDevice::AudioHybridDevice(QObject *parent) :
|
||||
|
||||
}
|
||||
|
||||
bool AudioHybridDevice::OutputIsSet()
|
||||
{
|
||||
qDebug() << "OIS";
|
||||
|
||||
output_set_lock_.lock();
|
||||
|
||||
bool output_is_set = (output_.get());
|
||||
|
||||
output_set_lock_.unlock();
|
||||
|
||||
return output_is_set;
|
||||
}
|
||||
|
||||
void AudioHybridDevice::Push(const QByteArray& samples)
|
||||
{
|
||||
qDebug() << "push";
|
||||
|
||||
Stop();
|
||||
|
||||
pushed_samples_ = samples;
|
||||
sample_index_ = 0;
|
||||
|
||||
if (!pushed_samples_.isEmpty()) {
|
||||
emit HasSamples();
|
||||
WakeOutputDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioHybridDevice::Stop()
|
||||
{
|
||||
qDebug() << "Stop";
|
||||
|
||||
// Whatever is happening, stop it
|
||||
pushed_samples_.clear();
|
||||
|
||||
@@ -53,17 +71,27 @@ void AudioHybridDevice::Stop()
|
||||
device_->close();
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
if (output_) {
|
||||
output_->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioHybridDevice::ConnectDevice(QIODevice *device)
|
||||
{
|
||||
qDebug() << "CD";
|
||||
|
||||
if (!output_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any previous device or pushed sample
|
||||
Stop();
|
||||
|
||||
device_ = device;
|
||||
|
||||
if (device_ != nullptr) {
|
||||
emit HasSamples();
|
||||
WakeOutputDevice();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +100,36 @@ bool AudioHybridDevice::IsIdle()
|
||||
return device_ == nullptr && pushed_samples_.isEmpty();
|
||||
}
|
||||
|
||||
void AudioHybridDevice::WakeOutputDevice()
|
||||
{
|
||||
if (output_ != nullptr && output_->state() != QAudio::ActiveState) {
|
||||
output_->start(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioHybridDevice::SetEnableSendingSamples(bool e)
|
||||
{
|
||||
enable_sending_samples_ = e;
|
||||
}
|
||||
|
||||
void AudioHybridDevice::SetOutputDevice(QAudioDeviceInfo info, QAudioFormat format)
|
||||
{
|
||||
output_set_lock_.lock();
|
||||
|
||||
output_ = std::unique_ptr<QAudioOutput>(new QAudioOutput(info, format, this));
|
||||
|
||||
output_set_lock_.unlock();
|
||||
|
||||
connect(output_.get(), &QAudioOutput::notify, this, &AudioHybridDevice::OutputNotified);
|
||||
}
|
||||
|
||||
void AudioHybridDevice::OutputNotified()
|
||||
{
|
||||
if (IsIdle()) {
|
||||
static_cast<QAudioOutput*>(sender())->stop();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 AudioHybridDevice::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
qint64 read_size = read_internal(data, maxSize);
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#ifndef AUDIOHYBRIDDEVICE_H
|
||||
#define AUDIOHYBRIDDEVICE_H
|
||||
|
||||
#include <QAudioOutput>
|
||||
#include <QIODevice>
|
||||
#include <QMutex>
|
||||
|
||||
/**
|
||||
* @brief A device that can be connected to QAudioOutput and provides both "push" and "pull" functionality
|
||||
@@ -44,6 +46,16 @@ class AudioHybridDevice : public QIODevice
|
||||
public:
|
||||
AudioHybridDevice(QObject* parent = nullptr);
|
||||
|
||||
bool OutputIsSet();
|
||||
|
||||
/**
|
||||
* @brief If enabled, this will emit the SentSamples() signal whenever samples are sent to the output device
|
||||
*/
|
||||
void SetEnableSendingSamples(bool e);
|
||||
|
||||
public slots:
|
||||
void SetOutputDevice(QAudioDeviceInfo info, QAudioFormat format);
|
||||
|
||||
void Push(const QByteArray &samples);
|
||||
|
||||
/**
|
||||
@@ -68,30 +80,7 @@ public:
|
||||
*/
|
||||
void ConnectDevice(QIODevice* device);
|
||||
|
||||
/**
|
||||
* @brief Returns true if there are no more samples to be sent
|
||||
*
|
||||
* This is true if a device was connected or samples were pushed but we reached the end and no more data is available
|
||||
* to be sent.
|
||||
*
|
||||
* In this state, this device will continue returning an empty buffer (all zeroes) to the device to workaround
|
||||
* QAudioOutput's buffer underrun prevention with pushed samples. Therefore the QAudioOutput will never be put into
|
||||
* QAudio::IdleState. Therefore only way to determine whether the output is no longer receiving usable audio is to
|
||||
* check this function.
|
||||
*/
|
||||
bool IsIdle();
|
||||
|
||||
/**
|
||||
* @brief If enabled, this will emit the SentSamples() signal whenever samples are sent to the output device
|
||||
*/
|
||||
void SetEnableSendingSamples(bool e);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Signal emitted when this leaves "idle" state and has valid audio data that is ready to be sent
|
||||
*/
|
||||
void HasSamples();
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when samples are sent to the output device
|
||||
*
|
||||
@@ -130,14 +119,35 @@ protected:
|
||||
virtual qint64 writeData(const char *data, qint64 maxSize) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Returns true if there are no more samples to be sent
|
||||
*
|
||||
* This is true if a device was connected or samples were pushed but we reached the end and no more data is available
|
||||
* to be sent.
|
||||
*
|
||||
* In this state, this device will continue returning an empty buffer (all zeroes) to the device to workaround
|
||||
* QAudioOutput's buffer underrun prevention with pushed samples. Therefore the QAudioOutput will never be put into
|
||||
* QAudio::IdleState. Therefore only way to determine whether the output is no longer receiving usable audio is to
|
||||
* check this function.
|
||||
*/
|
||||
bool IsIdle();
|
||||
|
||||
void WakeOutputDevice();
|
||||
|
||||
qint64 read_internal(char *data, qint64 maxSize);
|
||||
|
||||
QMutex output_set_lock_;
|
||||
std::unique_ptr<QAudioOutput> output_;
|
||||
|
||||
QIODevice* device_;
|
||||
|
||||
QByteArray pushed_samples_;
|
||||
qint64 sample_index_;
|
||||
|
||||
bool enable_sending_samples_;
|
||||
QAtomicInt enable_sending_samples_;
|
||||
|
||||
private slots:
|
||||
void OutputNotified();
|
||||
|
||||
};
|
||||
|
||||
|
||||
+22
-30
@@ -76,25 +76,25 @@ bool AudioManager::IsRefreshing()
|
||||
|
||||
void AudioManager::PushToOutput(const QByteArray &samples)
|
||||
{
|
||||
output_manager_.Push(samples);
|
||||
QMetaObject::invokeMethod(&output_manager_,
|
||||
"Push",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QByteArray, samples));
|
||||
}
|
||||
|
||||
void AudioManager::StartOutput(QIODevice *device)
|
||||
{
|
||||
if (output_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
output_manager_.ConnectDevice(device);
|
||||
QMetaObject::invokeMethod(&output_manager_,
|
||||
"ConnectDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QIODevice*, device));
|
||||
}
|
||||
|
||||
void AudioManager::StopOutput()
|
||||
{
|
||||
output_manager_.Stop();
|
||||
|
||||
if (output_ != nullptr) {
|
||||
output_->stop();
|
||||
}
|
||||
QMetaObject::invokeMethod(&output_manager_,
|
||||
"Stop",
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
@@ -142,8 +142,11 @@ void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
abort();
|
||||
}
|
||||
|
||||
output_ = std::unique_ptr<QAudioOutput>(new QAudioOutput(info, format, this));
|
||||
connect(output_.get(), &QAudioOutput::notify, this, &AudioManager::OutputNotified);
|
||||
QMetaObject::invokeMethod(&output_manager_,
|
||||
"SetOutputDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(QAudioDeviceInfo, info),
|
||||
Q_ARG(QAudioFormat, format));
|
||||
}
|
||||
|
||||
// Un-comment this to get debug information about what the audio output is doing
|
||||
@@ -177,18 +180,21 @@ const QList<QAudioDeviceInfo> &AudioManager::ListOutputDevices()
|
||||
}
|
||||
|
||||
AudioManager::AudioManager() :
|
||||
output_(nullptr),
|
||||
input_(nullptr),
|
||||
input_file_(nullptr),
|
||||
refreshing_devices_(false)
|
||||
{
|
||||
RefreshDevices();
|
||||
|
||||
connect(&output_manager_, &AudioHybridDevice::HasSamples, this, &AudioManager::OutputManagerHasSamples);
|
||||
connect(&output_manager_, &AudioHybridDevice::SentSamples, this, &AudioManager::SentSamples);
|
||||
|
||||
output_manager_.SetEnableSendingSamples(true);
|
||||
output_manager_.open(AudioHybridDevice::ReadOnly);
|
||||
|
||||
QThread* output_thread = new QThread();
|
||||
output_thread->start();
|
||||
|
||||
output_manager_.moveToThread(output_thread);
|
||||
}
|
||||
|
||||
void AudioManager::RefreshThreadDone()
|
||||
@@ -202,7 +208,7 @@ void AudioManager::RefreshThreadDone()
|
||||
|
||||
QString preferred_audio_output = Config::Current()["PreferredAudioOutput"].toString();
|
||||
|
||||
if (output_ == nullptr
|
||||
if (!output_manager_.OutputIsSet()
|
||||
|| (!preferred_audio_output.isEmpty() && output_device_info_.deviceName() != preferred_audio_output)) {
|
||||
if (preferred_audio_output.isEmpty()) {
|
||||
SetOutputDevice(QAudioDeviceInfo::defaultOutputDevice());
|
||||
@@ -238,25 +244,11 @@ void AudioManager::RefreshThreadDone()
|
||||
emit DeviceListReady();
|
||||
}
|
||||
|
||||
void AudioManager::OutputManagerHasSamples()
|
||||
{
|
||||
if (output_ != nullptr && output_->state() != QAudio::ActiveState) {
|
||||
output_->start(&output_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::OutputStateChanged(QAudio::State state)
|
||||
{
|
||||
qDebug() << state;
|
||||
}
|
||||
|
||||
void AudioManager::OutputNotified()
|
||||
{
|
||||
if (output_manager_.IsIdle()) {
|
||||
static_cast<QAudioOutput*>(sender())->stop();
|
||||
}
|
||||
}
|
||||
|
||||
AudioRefreshDevicesObject::AudioRefreshDevicesObject()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -111,7 +111,6 @@ private:
|
||||
|
||||
AudioHybridDevice output_manager_;
|
||||
|
||||
std::unique_ptr<QAudioOutput> output_;
|
||||
QAudioDeviceInfo output_device_info_;
|
||||
AudioRenderingParams output_params_;
|
||||
|
||||
@@ -124,12 +123,8 @@ private:
|
||||
private slots:
|
||||
void RefreshThreadDone();
|
||||
|
||||
void OutputManagerHasSamples();
|
||||
|
||||
void OutputStateChanged(QAudio::State state);
|
||||
|
||||
void OutputNotified();
|
||||
|
||||
};
|
||||
|
||||
#endif // AUDIOMANAGER_H
|
||||
|
||||
Reference in New Issue
Block a user