reimplemented reversing and shuttling in new audio

This commit is contained in:
itsmattkc
2021-09-26 14:22:50 -07:00
parent 6f3f7e558b
commit db873dfc69
8 changed files with 95 additions and 104 deletions
+2 -3
View File
@@ -94,7 +94,7 @@ void AudioManager::PushToOutput(const QByteArray &samples)
emit OutputPushed(samples);
}
void AudioManager::StartOutput(std::shared_ptr<QIODevice> device, int playback_speed)
void AudioManager::StartOutput(std::shared_ptr<QIODevice> device)
{
// Move to output manager's thread
device->moveToThread(&output_thread_);
@@ -103,8 +103,7 @@ void AudioManager::StartOutput(std::shared_ptr<QIODevice> device, int playback_s
QMetaObject::invokeMethod(output_manager_,
"PullFromDevice",
Qt::QueuedConnection,
Q_ARG(std::shared_ptr<QIODevice>, device),
Q_ARG(int, playback_speed));
Q_ARG(std::shared_ptr<QIODevice>, device));
}
void AudioManager::StopOutput()
+1 -1
View File
@@ -68,7 +68,7 @@ public:
/**
* @brief Start playing audio from AudioPlaybackCache
*/
void StartOutput(std::shared_ptr<QIODevice> device, int playback_speed);
void StartOutput(std::shared_ptr<QIODevice> device);
/**
* @brief Stop audio output immediately
+3 -63
View File
@@ -35,7 +35,7 @@ void AudioOutputDeviceProxy::SetParameters(const AudioParams &params)
params_ = params;
}
void AudioOutputDeviceProxy::SetDevice(std::shared_ptr<QIODevice> device, int playback_speed)
void AudioOutputDeviceProxy::SetDevice(std::shared_ptr<QIODevice> device)
{
device_ = device;
@@ -44,12 +44,6 @@ void AudioOutputDeviceProxy::SetDevice(std::shared_ptr<QIODevice> 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<int>(maxlen))) == 0) {
int dev_read = static_cast<int>(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<int>(read_count), params_.samples_to_bytes(1));
}
return read_count;
return -1;
}
}
+1 -7
View File
@@ -39,7 +39,7 @@ public:
void SetParameters(const AudioParams& params);
void SetDevice(std::shared_ptr<QIODevice> device, int playback_speed);
void SetDevice(std::shared_ptr<QIODevice> 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<QIODevice> device_;
TempoProcessor tempo_processor_;
AudioParams params_;
int playback_speed_;
};
}
+2 -2
View File
@@ -90,7 +90,7 @@ void AudioOutputManager::Close()
}
}
void AudioOutputManager::PullFromDevice(std::shared_ptr<QIODevice> device, int playback_speed)
void AudioOutputManager::PullFromDevice(std::shared_ptr<QIODevice> device)
{
if (!output_) {
return;
@@ -102,7 +102,7 @@ void AudioOutputManager::PullFromDevice(std::shared_ptr<QIODevice> 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_);
}
+1 -1
View File
@@ -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<QIODevice> device, int playback_speed);
void PullFromDevice(std::shared_ptr<QIODevice> device);
// Queued
void ResetToPushMode();
+81 -27
View File
@@ -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<SampleBufferPtr>();
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<RenderTicketWatcher *>(sender());
if (watcher->HasResult()) {
if (SampleBufferPtr samples = watcher->Get().value<SampleBufferPtr>()) {
/* Fade code
const int kFadeSz = qMin(200, samples->sample_count()/4);
for (int i=0; i<kFadeSz; i++) {
float amt = float(i)/float(kFadeSz);
samples->transform_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<PreviewAudioDevice>();
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)
+4
View File
@@ -29,6 +29,7 @@
#include <QWidget>
#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<RenderTicketWatcher*> audio_playback_queue_;
rational audio_playback_queue_time_;
PackedProcessor packed_processor_;
TempoProcessor tempo_processor_;
static QVector<ViewerWidget*> instances_;
@@ -311,6 +313,8 @@ private slots:
void ReceivedAudioBufferForPlayback();
void ReceivedAudioBufferForScrubbing();
};
}