diff --git a/app/audio/audiomanager.cpp b/app/audio/audiomanager.cpp index 7484b7cb3..4cb9358fa 100644 --- a/app/audio/audiomanager.cpp +++ b/app/audio/audiomanager.cpp @@ -94,7 +94,7 @@ void AudioManager::PushToOutput(const QByteArray &samples) emit OutputPushed(samples); } -void AudioManager::StartOutput(std::shared_ptr device, int playback_speed) +void AudioManager::StartOutput(std::shared_ptr device) { // Move to output manager's thread device->moveToThread(&output_thread_); @@ -103,8 +103,7 @@ void AudioManager::StartOutput(std::shared_ptr device, int playback_s QMetaObject::invokeMethod(output_manager_, "PullFromDevice", Qt::QueuedConnection, - Q_ARG(std::shared_ptr, device), - Q_ARG(int, playback_speed)); + Q_ARG(std::shared_ptr, device)); } void AudioManager::StopOutput() diff --git a/app/audio/audiomanager.h b/app/audio/audiomanager.h index fee7ef462..a9ba75670 100644 --- a/app/audio/audiomanager.h +++ b/app/audio/audiomanager.h @@ -68,7 +68,7 @@ public: /** * @brief Start playing audio from AudioPlaybackCache */ - void StartOutput(std::shared_ptr device, int playback_speed); + void StartOutput(std::shared_ptr device); /** * @brief Stop audio output immediately diff --git a/app/audio/outputdeviceproxy.cpp b/app/audio/outputdeviceproxy.cpp index c6087a064..924cc359c 100644 --- a/app/audio/outputdeviceproxy.cpp +++ b/app/audio/outputdeviceproxy.cpp @@ -35,7 +35,7 @@ void AudioOutputDeviceProxy::SetParameters(const AudioParams ¶ms) params_ = params; } -void AudioOutputDeviceProxy::SetDevice(std::shared_ptr device, int playback_speed) +void AudioOutputDeviceProxy::SetDevice(std::shared_ptr device) { device_ = device; @@ -44,12 +44,6 @@ void AudioOutputDeviceProxy::SetDevice(std::shared_ptr device, int pl device_ = nullptr; return; } - - playback_speed_ = playback_speed; - - if (qAbs(playback_speed_) != 1) { - tempo_processor_.Open(params_, qAbs(playback_speed_)); - } } void AudioOutputDeviceProxy::close() @@ -57,10 +51,6 @@ void AudioOutputDeviceProxy::close() QIODevice::close(); device_ = nullptr; - - if (tempo_processor_.IsOpen()) { - tempo_processor_.Close(); - } } qint64 AudioOutputDeviceProxy::readData(char *data, qint64 maxlen) @@ -69,26 +59,7 @@ qint64 AudioOutputDeviceProxy::readData(char *data, qint64 maxlen) return 0; } - qint64 read_count; - - if (tempo_processor_.IsOpen()) { - - while ((read_count = tempo_processor_.Pull(data, static_cast(maxlen))) == 0) { - int dev_read = static_cast(ReverseAwareRead(data, maxlen)); - - if (!dev_read) { - break; - } - - tempo_processor_.Push(data, dev_read); - } - - } else { - // If we aren't doing any tempo processing, simply passthrough the read signal - read_count = ReverseAwareRead(data, maxlen); - } - - return read_count; + return device_->read(data, maxlen); } qint64 AudioOutputDeviceProxy::writeData(const char *data, qint64 maxSize) @@ -96,38 +67,7 @@ qint64 AudioOutputDeviceProxy::writeData(const char *data, qint64 maxSize) Q_UNUSED(data) Q_UNUSED(maxSize) - return 0; -} - -qint64 AudioOutputDeviceProxy::ReverseAwareRead(char *data, qint64 maxlen) -{ - qint64 new_pos = -1; - - if (playback_speed_ < 0) { - // If we're reversing, we'll seek back by maxlen bytes before we read - qint64 len_adjusted_by_channels = maxlen / params_.channel_count(); - - new_pos = device_->pos() - len_adjusted_by_channels; - - if (new_pos < 0) { - maxlen = device_->pos() * params_.channel_count(); - - new_pos = 0; - } - - device_->seek(new_pos); - } - - qint64 read_count = device_->read(data, maxlen); - - if (playback_speed_ < 0) { - device_->seek(new_pos); - - // Reverse the samples here - AudioManager::ReverseBuffer(data, static_cast(read_count), params_.samples_to_bytes(1)); - } - - return read_count; + return -1; } } diff --git a/app/audio/outputdeviceproxy.h b/app/audio/outputdeviceproxy.h index 58587ea2f..092776a7c 100644 --- a/app/audio/outputdeviceproxy.h +++ b/app/audio/outputdeviceproxy.h @@ -39,7 +39,7 @@ public: void SetParameters(const AudioParams& params); - void SetDevice(std::shared_ptr device, int playback_speed); + void SetDevice(std::shared_ptr device); virtual void close() override; @@ -49,16 +49,10 @@ protected: virtual qint64 writeData(const char *data, qint64 maxSize) override; private: - qint64 ReverseAwareRead(char* data, qint64 maxlen); - std::shared_ptr device_; - TempoProcessor tempo_processor_; - AudioParams params_; - int playback_speed_; - }; } diff --git a/app/audio/outputmanager.cpp b/app/audio/outputmanager.cpp index 9986772f8..9182c5040 100644 --- a/app/audio/outputmanager.cpp +++ b/app/audio/outputmanager.cpp @@ -90,7 +90,7 @@ void AudioOutputManager::Close() } } -void AudioOutputManager::PullFromDevice(std::shared_ptr device, int playback_speed) +void AudioOutputManager::PullFromDevice(std::shared_ptr device) { if (!output_) { return; @@ -102,7 +102,7 @@ void AudioOutputManager::PullFromDevice(std::shared_ptr device, int p push_samples_.clear(); // Pull from the device - device_proxy_.SetDevice(device, playback_speed); + device_proxy_.SetDevice(device); device_proxy_.open(QIODevice::ReadOnly); output_->start(&device_proxy_); } diff --git a/app/audio/outputmanager.h b/app/audio/outputmanager.h index 9f65dc544..cea14474e 100644 --- a/app/audio/outputmanager.h +++ b/app/audio/outputmanager.h @@ -53,7 +53,7 @@ public slots: * 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 device, int playback_speed); + void PullFromDevice(std::shared_ptr device); // Queued void ResetToPushMode(); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index b0b6603ba..01ea71517 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -192,6 +192,9 @@ void ViewerWidget::ConnectNodeEvent(ViewerOutput *n) last_length_ = 0; LengthChangedSlot(n->GetLength()); + AudioParams ap = n->GetAudioParams(); + packed_processor_.Open(ap); + ColorManager* color_manager = n->project()->color_manager(); display_widget_->ConnectColorManager(color_manager); @@ -225,6 +228,8 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n) disconnect(n->video_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); + packed_processor_.Close(); + SetDisplayImage(QVariant()); ruler()->SetPlaybackCache(nullptr); @@ -405,7 +410,7 @@ void ViewerWidget::StartAudioOutput() if (params.is_valid()) { AudioManager::instance()->SetOutputParams(params); - AudioManager::instance()->StartOutput(audio_playback_device_, playback_speed_); + AudioManager::instance()->StartOutput(audio_playback_device_); qDebug() << "STUB: Nothing to send to audio monitor"; /*emit AudioManager::instance()->OutputWaveformStarted(&audio_cache->visual(), @@ -416,13 +421,27 @@ void ViewerWidget::StartAudioOutput() void ViewerWidget::QueueNextAudioBuffer() { // NOTE: Hardcoded 2 second interval - TimeRange range(audio_playback_queue_time_, audio_playback_queue_time_ + 2); - audio_playback_queue_time_ = range.out(); + rational queue_end = audio_playback_queue_time_ + (2 * playback_speed_); + + if (playback_speed_ < 0) { + // Limit to 0 if playing in reverse + queue_end = qMax(rational(0), queue_end); + } else { + // Limit to audio length if playing forwards + queue_end = qMin(GetConnectedNode()->GetAudioLength(), queue_end); + } + + if (queue_end == audio_playback_queue_time_) { + // This will queue nothing, so stop the loop here + return; + } RenderTicketWatcher *watcher = new RenderTicketWatcher(this); connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForPlayback); audio_playback_queue_.push_back(watcher); - watcher->SetTicket(auto_cacher_.GetRangeOfAudio(range, true)); + watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(audio_playback_queue_time_, queue_end), true)); + + audio_playback_queue_time_ = queue_end; } void ViewerWidget::ReceivedAudioBufferForPlayback() @@ -434,18 +453,31 @@ void ViewerWidget::ReceivedAudioBufferForPlayback() if (watcher->HasResult()) { SampleBufferPtr samples = watcher->Get().value(); if (samples && audio_playback_device_) { - if (!packed_processor_.IsOpen()) { - packed_processor_.Open(samples->audio_params()); + // If the samples must be reversed, reverse them now + if (playback_speed_ < 0) { + samples->reverse(); } // Convert to packed data for audio output QByteArray pack = packed_processor_.Convert(samples); - audio_playback_device_->Push(pack); + // If the tempo must be adjusted, adjust now + if (tempo_processor_.IsOpen()) { + tempo_processor_.Push(pack.data(), pack.size()); + int actual = tempo_processor_.Pull(pack.data(), pack.size()); + if (actual != pack.size()) { + pack.resize(actual); + } + } - if (prequeuing_audio_) { - prequeuing_audio_ = false; - FinishPlayPreprocess(); + // TempoProcessor may have emptied the array + if (!pack.isEmpty()) { + audio_playback_device_->Push(pack); + + if (prequeuing_audio_) { + prequeuing_audio_ = false; + FinishPlayPreprocess(); + } } } } @@ -457,6 +489,28 @@ void ViewerWidget::ReceivedAudioBufferForPlayback() } } +void ViewerWidget::ReceivedAudioBufferForScrubbing() +{ + RenderTicketWatcher *watcher = static_cast(sender()); + + if (watcher->HasResult()) { + if (SampleBufferPtr samples = watcher->Get().value()) { + /* Fade code + const int kFadeSz = qMin(200, samples->sample_count()/4); + for (int i=0; itransform_volume_for_sample(i, amt); + samples->transform_volume_for_sample(samples->sample_count() - i - 1, amt); + }*/ + + AudioManager::instance()->SetOutputParams(samples->audio_params()); + AudioManager::instance()->PushToOutput(packed_processor_.Convert(samples)); + } + } + + delete watcher; +} + void ViewerWidget::UpdateTextureFromNode() { rational time = GetTime(); @@ -606,6 +660,9 @@ 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(); prequeuing_audio_ = true; audio_playback_queue_time_ = GetTime(); @@ -631,7 +688,9 @@ void ViewerWidget::PauseInternal() audio_playback_device_ = nullptr; qDeleteAll(audio_playback_queue_); audio_playback_queue_.clear(); - packed_processor_.Close(); + if (tempo_processor_.IsOpen()) { + tempo_processor_.Close(); + } UpdateTextureFromNode(); } @@ -642,27 +701,17 @@ void ViewerWidget::PauseInternal() void ViewerWidget::PushScrubbedAudio() { - if (!IsPlaying() && GetConnectedNode() && Config::Current()["AudioScrubbing"].toBool()) { + if (!IsPlaying() && GetConnectedNode() && Config::Current()[QStringLiteral("AudioScrubbing")].toBool()) { // Get audio src device from renderer const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters(); if (params.is_valid()) { - qDebug() << "STUB: Use PAC audio function directly"; - /*PreviewAudioDevice *audio_src = new PreviewAudioDevice(&auto_cacher_, GetTime()); + // NOTE: Hardcoded scrubbing interval (20ms) + rational interval = rational(50, 1000); - if (audio_src->open(QIODevice::ReadOnly)) { - // FIXME: Hardcoded scrubbing interval (20ms) - int size_of_sample = params.time_to_bytes(rational(20, 1000)); - - // Push audio - QByteArray frame_audio = audio_src->read(size_of_sample); - AudioManager::instance()->SetOutputParams(params); - AudioManager::instance()->PushToOutput(frame_audio); - - audio_src->close(); - } - - delete audio_src;*/ + RenderTicketWatcher *watcher = new RenderTicketWatcher(); + connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing); + watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval), true)); } } } @@ -1308,6 +1357,11 @@ void ViewerWidget::UpdateRendererVideoParameters() void ViewerWidget::UpdateRendererAudioParameters() { + packed_processor_.Close(); + + AudioParams ap = GetConnectedNode()->GetAudioParams(); + + packed_processor_.Open(ap); } void ViewerWidget::SetZoomFromMenu(QAction *action) diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index cca17f9d6..53a23dcd7 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -29,6 +29,7 @@ #include #include "audio/packedprocessor.h" +#include "audio/tempoprocessor.h" #include "audiowaveformview.h" #include "common/rational.h" #include "node/output/viewer/viewer.h" @@ -259,6 +260,7 @@ private: std::list audio_playback_queue_; rational audio_playback_queue_time_; PackedProcessor packed_processor_; + TempoProcessor tempo_processor_; static QVector instances_; @@ -311,6 +313,8 @@ private slots: void ReceivedAudioBufferForPlayback(); + void ReceivedAudioBufferForScrubbing(); + }; }