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:
@@ -33,18 +33,22 @@ const int kDecibelStep = 6;
|
||||
const int kDecibelMinimum = -200;
|
||||
const int kMaximumSmoothness = 8;
|
||||
|
||||
QVector<AudioMonitor*> AudioMonitor::instances_;
|
||||
|
||||
AudioMonitor::AudioMonitor(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
file_(nullptr),
|
||||
waveform_(nullptr),
|
||||
cached_channels_(0)
|
||||
{
|
||||
values_.resize(kMaximumSmoothness);
|
||||
instances_.append(this);
|
||||
|
||||
connect(AudioManager::instance(), &AudioManager::OutputWaveformStarted, this, &AudioMonitor::OutputAudioVisualWaveformSet);
|
||||
connect(AudioManager::instance(), &AudioManager::OutputPushed, this, &AudioMonitor::OutputPushed);
|
||||
connect(AudioManager::instance(), &AudioManager::AudioParamsChanged, this, &AudioMonitor::SetParams);
|
||||
connect(AudioManager::instance(), &AudioManager::Stopped, this, &AudioMonitor::Stop);
|
||||
values_.resize(kMaximumSmoothness);
|
||||
}
|
||||
|
||||
AudioMonitor::~AudioMonitor()
|
||||
{
|
||||
instances_.removeOne(this);
|
||||
}
|
||||
|
||||
void AudioMonitor::SetParams(const AudioParams ¶ms)
|
||||
@@ -64,32 +68,6 @@ void AudioMonitor::SetParams(const AudioParams ¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMonitor::OutputDeviceSet(AudioPlaybackCache *cache, qint64 offset, int playback_speed)
|
||||
{
|
||||
Stop();
|
||||
|
||||
file_ = cache->CreatePlaybackDevice(this);
|
||||
|
||||
if (!file_->open(QFile::ReadOnly)) {
|
||||
qWarning() << "Failed to open IO device for AudioMonitor display";
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset > file_->size()) {
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
file_->seek(offset);
|
||||
|
||||
playback_speed_ = playback_speed;
|
||||
|
||||
last_time_ = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
SetUpdateLoop(true);
|
||||
}
|
||||
|
||||
void AudioMonitor::Stop()
|
||||
{
|
||||
delete file_;
|
||||
@@ -100,7 +78,7 @@ void AudioMonitor::Stop()
|
||||
// loop will stop itself since file_ and waveform_ are null.
|
||||
}
|
||||
|
||||
void AudioMonitor::OutputPushed(const QByteArray &d)
|
||||
void AudioMonitor::PushBytes(const QByteArray &d)
|
||||
{
|
||||
QVector<double> v(params_.channel_count(), 0);
|
||||
|
||||
@@ -111,7 +89,7 @@ void AudioMonitor::OutputPushed(const QByteArray &d)
|
||||
SetUpdateLoop(true);
|
||||
}
|
||||
|
||||
void AudioMonitor::OutputAudioVisualWaveformSet(const AudioVisualWaveform *waveform, const rational &start, int playback_speed)
|
||||
void AudioMonitor::StartWaveform(const AudioVisualWaveform *waveform, const rational &start, int playback_speed)
|
||||
{
|
||||
Stop();
|
||||
|
||||
|
||||
@@ -38,21 +38,42 @@ class AudioMonitor : public QOpenGLWidget
|
||||
public:
|
||||
AudioMonitor(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~AudioMonitor() override;
|
||||
|
||||
bool IsPlaying() const
|
||||
{
|
||||
return file_ || waveform_;
|
||||
}
|
||||
|
||||
static void StartWaveformOnAll(const AudioVisualWaveform *waveform, const rational& start, int playback_speed)
|
||||
{
|
||||
foreach (AudioMonitor *m, instances_) {
|
||||
m->StartWaveform(waveform, start, playback_speed);
|
||||
}
|
||||
}
|
||||
|
||||
static void StopOnAll()
|
||||
{
|
||||
foreach (AudioMonitor *m, instances_) {
|
||||
m->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
static void PushBytesOnAll(const QByteArray &d)
|
||||
{
|
||||
foreach (AudioMonitor *m, instances_) {
|
||||
m->PushBytes(d);
|
||||
}
|
||||
}
|
||||
|
||||
public slots:
|
||||
void SetParams(const AudioParams& params);
|
||||
|
||||
void OutputDeviceSet(AudioPlaybackCache* cache, qint64 offset, int playback_speed);
|
||||
|
||||
void Stop();
|
||||
|
||||
void OutputPushed(const QByteArray& d);
|
||||
void PushBytes(const QByteArray& d);
|
||||
|
||||
void OutputAudioVisualWaveformSet(const AudioVisualWaveform *waveform, const rational& start, int playback_speed);
|
||||
void StartWaveform(const AudioVisualWaveform *waveform, const rational& start, int playback_speed);
|
||||
|
||||
protected:
|
||||
virtual void paintGL() override;
|
||||
@@ -88,6 +109,8 @@ private:
|
||||
QPixmap cached_background_;
|
||||
int cached_channels_;
|
||||
|
||||
static QVector<AudioMonitor*> instances_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -128,11 +128,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
instances_.append(this);
|
||||
|
||||
setAcceptDrops(true);
|
||||
|
||||
audio_queue_next_timer_ = new QTimer(this);
|
||||
audio_queue_next_timer_->setInterval(kAudioPlaybackInterval * 1000);
|
||||
audio_queue_next_timer_->setSingleShot(true);
|
||||
connect(audio_queue_next_timer_, &QTimer::timeout, this, &ViewerWidget::QueueNextAudioBuffer);
|
||||
}
|
||||
|
||||
ViewerWidget::~ViewerWidget()
|
||||
@@ -412,15 +407,10 @@ void ViewerWidget::ClearVideoAutoCacherQueue()
|
||||
|
||||
void ViewerWidget::StartAudioOutput()
|
||||
{
|
||||
AudioParams params = GetConnectedNode()->GetAudioParams();
|
||||
|
||||
if (params.is_valid()) {
|
||||
AudioManager::instance()->SetOutputParams(params);
|
||||
AudioManager::instance()->StartOutput(audio_playback_device_);
|
||||
|
||||
emit AudioManager::instance()->OutputWaveformStarted(&GetConnectedNode()->audio_playback_cache()->visual(),
|
||||
GetTime(), playback_speed_);
|
||||
}
|
||||
AudioManager::instance()->SetOutputParams(GetConnectedNode()->GetAudioParams());
|
||||
AudioManager::instance()->StartOutput(audio_playback_device_);
|
||||
AudioMonitor::StartWaveformOnAll(&GetConnectedNode()->audio_playback_cache()->visual(),
|
||||
GetTime(), playback_speed_);
|
||||
}
|
||||
|
||||
void ViewerWidget::QueueNextAudioBuffer()
|
||||
@@ -470,7 +460,7 @@ void ViewerWidget::ReceivedAudioBufferForPlayback()
|
||||
|
||||
// TempoProcessor may have emptied the array
|
||||
if (!pack.isEmpty()) {
|
||||
audio_playback_device_->Push(pack);
|
||||
audio_playback_device_->write(pack);
|
||||
|
||||
if (prequeuing_audio_) {
|
||||
prequeuing_audio_--;
|
||||
@@ -483,10 +473,6 @@ void ViewerWidget::ReceivedAudioBufferForPlayback()
|
||||
}
|
||||
}
|
||||
|
||||
// Do this in the loop so that clearing the array effectively prevents a queue
|
||||
audio_queue_next_timer_->stop();
|
||||
audio_queue_next_timer_->start();
|
||||
|
||||
delete watcher;
|
||||
}
|
||||
}
|
||||
@@ -508,8 +494,10 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
||||
samples->transform_volume_for_sample(samples->sample_count() - i - 1, amt);
|
||||
}*/
|
||||
|
||||
QByteArray data = packed_processor_.Convert(samples);
|
||||
AudioManager::instance()->SetOutputParams(samples->audio_params());
|
||||
AudioManager::instance()->PushToOutput(packed_processor_.Convert(samples));
|
||||
AudioManager::instance()->PushToOutput(data);
|
||||
AudioMonitor::PushBytesOnAll(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,14 +662,23 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
}
|
||||
}
|
||||
|
||||
if (std::abs(playback_speed_) > 1) {
|
||||
tempo_processor_.Open(GetConnectedNode()->GetAudioParams(), std::abs(playback_speed_));
|
||||
}
|
||||
audio_playback_device_ = std::make_shared<PreviewAudioDevice>();
|
||||
prequeuing_audio_ = 2; // Queue two buffers ahead of time
|
||||
audio_playback_queue_time_ = GetTime();
|
||||
for (int i=0; i<prequeuing_audio_; i++) {
|
||||
QueueNextAudioBuffer();
|
||||
AudioParams ap = GetConnectedNode()->GetAudioParams();
|
||||
if (ap.is_valid()) {
|
||||
audio_playback_device_ = std::make_shared<PreviewAudioDevice>(ap.bytes_per_sample_per_channel() * ap.channel_count());
|
||||
audio_playback_device_->set_notify_interval(ap.time_to_bytes(kAudioPlaybackInterval));
|
||||
connect(audio_playback_device_.get(), &PreviewAudioDevice::Notify, this, &ViewerWidget::QueueNextAudioBuffer, Qt::QueuedConnection);
|
||||
|
||||
if (audio_playback_device_->open(QIODevice::ReadWrite)) {
|
||||
if (std::abs(playback_speed_) > 1) {
|
||||
tempo_processor_.Open(GetConnectedNode()->GetAudioParams(), std::abs(playback_speed_));
|
||||
}
|
||||
|
||||
prequeuing_audio_ = 2; // Queue two buffers ahead of time
|
||||
audio_playback_queue_time_ = GetTime();
|
||||
for (int i=0; i<prequeuing_audio_; i++) {
|
||||
QueueNextAudioBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,6 +686,8 @@ void ViewerWidget::PauseInternal()
|
||||
{
|
||||
if (IsPlaying()) {
|
||||
AudioManager::instance()->StopOutput();
|
||||
AudioMonitor::StopOnAll();
|
||||
|
||||
playback_speed_ = 0;
|
||||
controls_->ShowPlayButton();
|
||||
|
||||
@@ -704,13 +703,13 @@ void ViewerWidget::PauseInternal()
|
||||
playback_queue_.clear();
|
||||
playback_backup_timer_.stop();
|
||||
|
||||
disconnect(audio_playback_device_.get(), &PreviewAudioDevice::Notify, this, &ViewerWidget::QueueNextAudioBuffer);
|
||||
audio_playback_device_ = nullptr;
|
||||
qDeleteAll(audio_playback_queue_);
|
||||
audio_playback_queue_.clear();
|
||||
if (tempo_processor_.IsOpen()) {
|
||||
tempo_processor_.Close();
|
||||
}
|
||||
audio_queue_next_timer_->stop();
|
||||
|
||||
UpdateTextureFromNode();
|
||||
}
|
||||
@@ -834,7 +833,9 @@ void ViewerWidget::FinishPlayPreprocess()
|
||||
|
||||
int64_t playback_start_time = GetTimestamp();
|
||||
|
||||
StartAudioOutput();
|
||||
if (audio_playback_device_) {
|
||||
StartAudioOutput();
|
||||
}
|
||||
|
||||
playback_timer_.Start(playback_start_time, playback_speed_, timebase_dbl());
|
||||
display_widget_->ResetFPSTimer();
|
||||
|
||||
@@ -262,7 +262,6 @@ private:
|
||||
PackedProcessor packed_processor_;
|
||||
TempoProcessor tempo_processor_;
|
||||
static const int kAudioPlaybackInterval;
|
||||
QTimer *audio_queue_next_timer_;
|
||||
|
||||
static QVector<ViewerWidget*> instances_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user