reimplemented reversing and shuttling in new audio
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,7 +35,7 @@ void AudioOutputDeviceProxy::SetParameters(const AudioParams ¶ms)
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user