begin switch to portaudio
Qt's audio system has served us well, but it's a little limited moving forward. PortAudio is a mature and versatile library that will allow for better audio for users.
This commit is contained in:
@@ -20,10 +20,6 @@ set(OLIVE_SOURCES
|
||||
audio/audiomanager.h
|
||||
audio/audiovisualwaveform.cpp
|
||||
audio/audiovisualwaveform.h
|
||||
audio/outputdeviceproxy.cpp
|
||||
audio/outputdeviceproxy.h
|
||||
audio/outputmanager.cpp
|
||||
audio/outputmanager.h
|
||||
audio/packedprocessor.cpp
|
||||
audio/packedprocessor.h
|
||||
audio/tempoprocessor.cpp
|
||||
|
||||
+90
-56
@@ -89,35 +89,68 @@ bool AudioManager::IsRefreshingInputs()
|
||||
|
||||
void AudioManager::PushToOutput(const QByteArray &samples)
|
||||
{
|
||||
output_manager_->Push(samples);
|
||||
if (!output_stream_) {
|
||||
// Start output with no callback so it'll be in "push" mode
|
||||
StartOutputStream();
|
||||
}
|
||||
|
||||
emit OutputPushed(samples);
|
||||
Pa_WriteStream(output_stream_, samples.constData(), output_params_.bytes_to_samples(samples.size()));
|
||||
}
|
||||
|
||||
void AudioManager::StartOutput(std::shared_ptr<QIODevice> device)
|
||||
static PaSampleFormat GetPortAudioSampleFormat(AudioParams::Format fmt)
|
||||
{
|
||||
// Move to output manager's thread
|
||||
device->moveToThread(&output_thread_);
|
||||
switch (fmt) {
|
||||
case AudioParams::kFormatUnsigned8:
|
||||
return paUInt8;
|
||||
case AudioParams::kFormatSigned16:
|
||||
return paInt16;
|
||||
case AudioParams::kFormatSigned32:
|
||||
return paInt32;
|
||||
case AudioParams::kFormatFloat32:
|
||||
return paFloat32;
|
||||
case AudioParams::kFormatSigned64:
|
||||
case AudioParams::kFormatFloat64:
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue to output manager in other thread
|
||||
QMetaObject::invokeMethod(output_manager_,
|
||||
"PullFromDevice",
|
||||
Qt::QueuedConnection,
|
||||
Q_ARG(std::shared_ptr<QIODevice>, device));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OutputCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
|
||||
{
|
||||
PreviewAudioDevice *device = static_cast<PreviewAudioDevice*>(userData);
|
||||
|
||||
device->read(reinterpret_cast<char*>(output), frameCount * device->bytes_per_frame());
|
||||
|
||||
return paContinue;
|
||||
}
|
||||
|
||||
void AudioManager::StartOutput(std::shared_ptr<PreviewAudioDevice> device)
|
||||
{
|
||||
// First stop any current device
|
||||
StopOutputStream();
|
||||
|
||||
// Store device
|
||||
output_device_ = device;
|
||||
|
||||
// Start output stream that pulls from this device
|
||||
StartOutputStream(OutputCallback);
|
||||
}
|
||||
|
||||
void AudioManager::StopOutput()
|
||||
{
|
||||
QMetaObject::invokeMethod(output_manager_,
|
||||
"ResetToPushMode",
|
||||
Qt::QueuedConnection);
|
||||
// Stop playback first so the callback won't get called again
|
||||
StopOutputStream();
|
||||
|
||||
emit Stopped();
|
||||
// Then clear our reference to the output devicec
|
||||
output_device_ = nullptr;
|
||||
}
|
||||
|
||||
void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
{
|
||||
qInfo() << "Setting output audio device to" << info.deviceName();
|
||||
/*qInfo() << "Setting output audio device to" << info.deviceName();
|
||||
|
||||
StopOutput();
|
||||
|
||||
@@ -142,24 +175,18 @@ void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
} else {
|
||||
qWarning() << "Output format not supported by device";
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void AudioManager::SetOutputParams(const AudioParams ¶ms)
|
||||
{
|
||||
if (output_params_ != params) {
|
||||
// If an output stream is running, stop it now
|
||||
StopOutputStream();
|
||||
|
||||
// Update parameters
|
||||
output_params_ = params;
|
||||
|
||||
QMetaObject::invokeMethod(output_manager_,
|
||||
"SetParameters",
|
||||
Qt::QueuedConnection,
|
||||
OLIVE_NS_ARG(AudioParams, params));
|
||||
|
||||
// Refresh output device
|
||||
SetOutputDevice(output_device_info_);
|
||||
}
|
||||
|
||||
emit AudioParamsChanged(output_params_);
|
||||
}
|
||||
|
||||
void AudioManager::SetInputDevice(const QAudioDeviceInfo &info)
|
||||
@@ -178,50 +205,57 @@ const QList<QAudioDeviceInfo> &AudioManager::ListOutputDevices()
|
||||
return output_devices_;
|
||||
}
|
||||
|
||||
void AudioManager::ReverseBuffer(char *buffer, int buffer_size, int sample_size)
|
||||
{
|
||||
int half_buffer_sz = buffer_size / 2;
|
||||
char* temp_buffer = new char[sample_size];
|
||||
|
||||
for (int src_index=0;src_index<half_buffer_sz;src_index+=sample_size) {
|
||||
char* src_ptr = buffer + src_index;
|
||||
char* dst_ptr = buffer + buffer_size - sample_size - src_index;
|
||||
|
||||
// Simple swap
|
||||
memcpy(temp_buffer, src_ptr, static_cast<size_t>(sample_size));
|
||||
memcpy(src_ptr, dst_ptr, static_cast<size_t>(sample_size));
|
||||
memcpy(dst_ptr, temp_buffer, static_cast<size_t>(sample_size));
|
||||
}
|
||||
|
||||
delete [] temp_buffer;
|
||||
}
|
||||
|
||||
AudioManager::AudioManager() :
|
||||
is_refreshing_inputs_(false),
|
||||
is_refreshing_outputs_(false),
|
||||
output_is_set_(false),
|
||||
output_stream_(nullptr),
|
||||
input_(nullptr),
|
||||
input_file_(nullptr)
|
||||
{
|
||||
RefreshDevices();
|
||||
//RefreshDevices();
|
||||
|
||||
output_thread_.start(QThread::TimeCriticalPriority);
|
||||
output_manager_ = new AudioOutputManager();
|
||||
output_manager_->moveToThread(&output_thread_);
|
||||
Pa_Initialize();
|
||||
|
||||
connect(output_manager_, &AudioOutputManager::OutputNotified, this, &AudioManager::OutputNotified);
|
||||
output_ = Pa_GetDefaultOutputDevice();
|
||||
}
|
||||
|
||||
AudioManager::~AudioManager()
|
||||
{
|
||||
QMetaObject::invokeMethod(output_manager_, "deleteLater", Qt::BlockingQueuedConnection);
|
||||
output_thread_.quit();
|
||||
output_thread_.wait();
|
||||
StopOutputStream();
|
||||
|
||||
Pa_Terminate();
|
||||
}
|
||||
|
||||
void AudioManager::StartOutputStream(PaStreamCallback *streamCallback)
|
||||
{
|
||||
if (output_stream_) {
|
||||
StopOutputStream();
|
||||
}
|
||||
|
||||
PaStreamParameters p;
|
||||
|
||||
p.channelCount = output_params_.channel_count();
|
||||
p.device = output_;
|
||||
p.hostApiSpecificStreamInfo = nullptr;
|
||||
p.sampleFormat = GetPortAudioSampleFormat(output_params_.format());
|
||||
p.suggestedLatency = Pa_GetDeviceInfo(output_)->defaultLowOutputLatency;
|
||||
|
||||
Pa_OpenStream(&output_stream_, nullptr, &p, output_params_.sample_rate(), paFramesPerBufferUnspecified, paNoFlag, streamCallback, output_device_.get());
|
||||
Pa_StartStream(output_stream_);
|
||||
}
|
||||
|
||||
void AudioManager::StopOutputStream()
|
||||
{
|
||||
if (output_stream_) {
|
||||
Pa_AbortStream(output_stream_);
|
||||
Pa_CloseStream(output_stream_);
|
||||
output_stream_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::OutputDevicesRefreshed()
|
||||
{
|
||||
QFutureWatcher< QList<QAudioDeviceInfo> >* watcher = static_cast<QFutureWatcher< QList<QAudioDeviceInfo> >*>(sender());
|
||||
/*QFutureWatcher< QList<QAudioDeviceInfo> >* watcher = static_cast<QFutureWatcher< QList<QAudioDeviceInfo> >*>(sender());
|
||||
|
||||
output_devices_ = watcher->result();
|
||||
watcher->deleteLater();
|
||||
@@ -229,7 +263,7 @@ void AudioManager::OutputDevicesRefreshed()
|
||||
|
||||
QString preferred_audio_output = Config::Current()["AudioOutput"].toString();
|
||||
|
||||
if (!output_is_set_
|
||||
if (output_ == paNoDevice
|
||||
|| (!preferred_audio_output.isEmpty() && output_device_info_.deviceName() != preferred_audio_output)) {
|
||||
if (preferred_audio_output.isEmpty()) {
|
||||
SetOutputDevice(QAudioDeviceInfo::defaultOutputDevice());
|
||||
@@ -243,7 +277,7 @@ void AudioManager::OutputDevicesRefreshed()
|
||||
}
|
||||
}
|
||||
|
||||
emit OutputListReady();
|
||||
emit OutputListReady();*/
|
||||
}
|
||||
|
||||
void AudioManager::InputDevicesRefreshed()
|
||||
|
||||
+10
-20
@@ -23,15 +23,15 @@
|
||||
|
||||
#include <memory>
|
||||
#include <QAudioInput>
|
||||
#include <QAudioOutput>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QThread>
|
||||
#include <portaudio.h>
|
||||
|
||||
#include "audiovisualwaveform.h"
|
||||
#include "common/define.h"
|
||||
#include "outputmanager.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/audioplaybackcache.h"
|
||||
#include "render/previewaudiodevice.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
/**
|
||||
* @brief Start playing audio from AudioPlaybackCache
|
||||
*/
|
||||
void StartOutput(std::shared_ptr<QIODevice> device);
|
||||
void StartOutput(std::shared_ptr<PreviewAudioDevice> device);
|
||||
|
||||
/**
|
||||
* @brief Stop audio output immediately
|
||||
@@ -84,28 +84,20 @@ public:
|
||||
const QList<QAudioDeviceInfo>& ListInputDevices();
|
||||
const QList<QAudioDeviceInfo>& ListOutputDevices();
|
||||
|
||||
static void ReverseBuffer(char* buffer, int size, int resolution);
|
||||
|
||||
signals:
|
||||
void OutputListReady();
|
||||
|
||||
void InputListReady();
|
||||
|
||||
void OutputNotified();
|
||||
|
||||
void OutputWaveformStarted(const AudioVisualWaveform* waveform, const rational &start, int playback_speed);
|
||||
|
||||
void AudioParamsChanged(const AudioParams& params);
|
||||
|
||||
void OutputPushed(const QByteArray& data);
|
||||
|
||||
void Stopped();
|
||||
|
||||
private:
|
||||
AudioManager();
|
||||
|
||||
virtual ~AudioManager() override;
|
||||
|
||||
void StartOutputStream(PaStreamCallback *streamCallback = nullptr);
|
||||
|
||||
void StopOutputStream();
|
||||
|
||||
QList<QAudioDeviceInfo> input_devices_;
|
||||
QList<QAudioDeviceInfo> output_devices_;
|
||||
|
||||
@@ -114,12 +106,10 @@ private:
|
||||
|
||||
static AudioManager* instance_;
|
||||
|
||||
QThread output_thread_;
|
||||
AudioOutputManager* output_manager_;
|
||||
bool output_is_set_;
|
||||
|
||||
QAudioDeviceInfo output_device_info_;
|
||||
PaDeviceIndex output_;
|
||||
PaStream *output_stream_;
|
||||
AudioParams output_params_;
|
||||
std::shared_ptr<QIODevice> output_device_;
|
||||
|
||||
std::unique_ptr<QAudioInput> input_;
|
||||
QAudioDeviceInfo input_device_info_;
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "outputdeviceproxy.h"
|
||||
|
||||
#include "audiomanager.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
AudioOutputDeviceProxy::AudioOutputDeviceProxy(QObject *parent) :
|
||||
QIODevice(parent),
|
||||
device_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void AudioOutputDeviceProxy::SetParameters(const AudioParams ¶ms)
|
||||
{
|
||||
params_ = params;
|
||||
}
|
||||
|
||||
void AudioOutputDeviceProxy::SetDevice(std::shared_ptr<QIODevice> device)
|
||||
{
|
||||
device_ = device;
|
||||
|
||||
if (!device_->open(QFile::ReadOnly)) {
|
||||
qCritical() << "Failed to open IO device for audio playback";
|
||||
device_ = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputDeviceProxy::close()
|
||||
{
|
||||
QIODevice::close();
|
||||
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
qint64 AudioOutputDeviceProxy::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
if (!device_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return device_->read(data, maxlen);
|
||||
}
|
||||
|
||||
qint64 AudioOutputDeviceProxy::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(maxSize)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef AUDIOOUTPUTDEVICEPROXY_H
|
||||
#define AUDIOOUTPUTDEVICEPROXY_H
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "common/define.h"
|
||||
#include "tempoprocessor.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
/**
|
||||
* @brief QIODevice wrapper that can adjust speed/reverse an audio file
|
||||
*/
|
||||
class AudioOutputDeviceProxy : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioOutputDeviceProxy(QObject* parent = nullptr);
|
||||
|
||||
void SetParameters(const AudioParams& params);
|
||||
|
||||
void SetDevice(std::shared_ptr<QIODevice> device);
|
||||
|
||||
virtual void close() override;
|
||||
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxlen) override;
|
||||
|
||||
virtual qint64 writeData(const char *data, qint64 maxSize) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<QIODevice> device_;
|
||||
|
||||
AudioParams params_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // AUDIOOUTPUTDEVICEPROXY_H
|
||||
@@ -1,156 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "outputmanager.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
namespace olive {
|
||||
|
||||
AudioOutputManager::AudioOutputManager(QObject *parent) :
|
||||
QObject(parent),
|
||||
output_(nullptr),
|
||||
push_device_(nullptr),
|
||||
device_proxy_(this)
|
||||
{
|
||||
}
|
||||
|
||||
AudioOutputManager::~AudioOutputManager()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void AudioOutputManager::Push(const QByteArray& samples)
|
||||
{
|
||||
// This function is not queued and is intended to be called from the caller's thread
|
||||
QMutexLocker lock(&push_sample_lock_);
|
||||
|
||||
// Replace sample buffer with this one
|
||||
push_samples_ = samples;
|
||||
push_sample_index_ = 0;
|
||||
|
||||
// If we had another device connected, disconnect it now
|
||||
QMetaObject::invokeMethod(this, "ResetToPushMode", Qt::QueuedConnection);
|
||||
|
||||
// Start pushing samples to the output
|
||||
QMetaObject::invokeMethod(this, "PushMoreSamples", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void AudioOutputManager::ResetToPushMode()
|
||||
{
|
||||
// If we have a null push device, then we currently have the output in pull mode. We restore it to push mode here.
|
||||
if (output_ && !push_device_) {
|
||||
output_->stop();
|
||||
|
||||
device_proxy_.close();
|
||||
|
||||
// Put QAudioOutput back into push mode
|
||||
push_device_ = output_->start();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputManager::SetParameters(AudioParams params)
|
||||
{
|
||||
device_proxy_.SetParameters(params);
|
||||
}
|
||||
|
||||
void AudioOutputManager::Close()
|
||||
{
|
||||
if (output_) {
|
||||
output_->stop();
|
||||
|
||||
push_device_ = nullptr;
|
||||
|
||||
if (device_proxy_.isOpen()) {
|
||||
device_proxy_.close();
|
||||
}
|
||||
|
||||
delete output_;
|
||||
output_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputManager::PullFromDevice(std::shared_ptr<QIODevice> device)
|
||||
{
|
||||
if (!output_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop any current output and disable push mode
|
||||
output_->stop();
|
||||
push_device_ = nullptr;
|
||||
push_samples_.clear();
|
||||
|
||||
// Pull from the device
|
||||
device_proxy_.SetDevice(device);
|
||||
device_proxy_.open(QIODevice::ReadOnly);
|
||||
output_->start(&device_proxy_);
|
||||
}
|
||||
|
||||
void AudioOutputManager::PushMoreSamples()
|
||||
{
|
||||
QMutexLocker lock(&push_sample_lock_);
|
||||
|
||||
// Check if we're currently in push mode and if we have samples to push
|
||||
if (!push_device_ || push_samples_.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* read_ptr = push_samples_.constData() + push_sample_index_;
|
||||
|
||||
// Push the bytes we have to the audio output
|
||||
qint64 write_count = push_device_->write(read_ptr,
|
||||
push_samples_.size() - push_sample_index_);
|
||||
|
||||
// Increment sample buffer index (faster than shift the bytes up)
|
||||
push_sample_index_ += static_cast<int>(write_count);
|
||||
|
||||
// If we've pushed all samples, we can clear this array
|
||||
if (push_sample_index_ == push_samples_.size()) {
|
||||
push_samples_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputManager::SetOutputDevice(QAudioDeviceInfo info, QAudioFormat format)
|
||||
{
|
||||
// Whatever the output is doing right now, stop it
|
||||
Close();
|
||||
|
||||
// Create a new output device and start it in push mode
|
||||
output_ = new QAudioOutput(info, format, this);
|
||||
output_->setBufferSize(16384);
|
||||
output_->setNotifyInterval(1);
|
||||
push_device_ = output_->start();
|
||||
connect(output_, &QAudioOutput::notify, this, &AudioOutputManager::PushMoreSamples);
|
||||
connect(output_, &QAudioOutput::notify, this, &AudioOutputManager::OutputNotified);
|
||||
|
||||
// Un-comment this to get debug information about what the audio output is doing
|
||||
//connect(output_, &QAudioOutput::stateChanged, this, &AudioOutputManager::OutputStateChanged);
|
||||
}
|
||||
|
||||
void AudioOutputManager::OutputStateChanged(QAudio::State state)
|
||||
{
|
||||
qDebug() << state << output_->error();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2021 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef AUDIOHYBRIDDEVICE_H
|
||||
#define AUDIOHYBRIDDEVICE_H
|
||||
|
||||
#include <memory>
|
||||
#include <QAudioOutput>
|
||||
#include <QBuffer>
|
||||
#include <QIODevice>
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
|
||||
#include "outputdeviceproxy.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class AudioOutputManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioOutputManager(QObject* parent = nullptr);
|
||||
|
||||
virtual ~AudioOutputManager() override;
|
||||
|
||||
// Thread-safe
|
||||
void Push(const QByteArray &samples);
|
||||
|
||||
public slots:
|
||||
// Queued
|
||||
void SetOutputDevice(QAudioDeviceInfo info, QAudioFormat format);
|
||||
|
||||
/**
|
||||
* @brief Connect a QIODevice (e.g. QFile) to start sending to the audio output
|
||||
*
|
||||
* This will clear any pushed samples or QIODevices currently being read and will start reading from this next time
|
||||
* the audio output requests data.
|
||||
*/
|
||||
void PullFromDevice(std::shared_ptr<QIODevice> device);
|
||||
|
||||
// Queued
|
||||
void ResetToPushMode();
|
||||
|
||||
// Queued
|
||||
void SetParameters(olive::AudioParams params);
|
||||
|
||||
// Queued
|
||||
void Close();
|
||||
|
||||
signals:
|
||||
void OutputNotified();
|
||||
|
||||
private:
|
||||
QAudioOutput* output_;
|
||||
QIODevice* push_device_;
|
||||
|
||||
QMutex push_sample_lock_;
|
||||
QByteArray push_samples_;
|
||||
int push_sample_index_;
|
||||
|
||||
AudioOutputDeviceProxy device_proxy_;
|
||||
|
||||
private slots:
|
||||
void PushMoreSamples();
|
||||
|
||||
void OutputStateChanged(QAudio::State state);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(std::shared_ptr<QIODevice>)
|
||||
|
||||
#endif // AUDIOHYBRIDDEVICE_H
|
||||
Reference in New Issue
Block a user