audio: rewrote audio cache to write to contiguous segments rather than one long file
Fixes issues with PCM file placement, optimizes some audio-based timeline and cache operations, just generally a better approach.
This commit is contained in:
@@ -35,6 +35,7 @@ const int kMaximumSmoothness = 8;
|
||||
|
||||
AudioMonitor::AudioMonitor(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
file_(nullptr),
|
||||
cached_channels_(0)
|
||||
{
|
||||
values_.resize(kMaximumSmoothness);
|
||||
@@ -45,11 +46,6 @@ AudioMonitor::AudioMonitor(QWidget *parent) :
|
||||
connect(AudioManager::instance(), &AudioManager::Stopped, this, &AudioMonitor::Stop);
|
||||
}
|
||||
|
||||
AudioMonitor::~AudioMonitor()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
void AudioMonitor::SetParams(const AudioParams ¶ms)
|
||||
{
|
||||
params_ = params;
|
||||
@@ -63,18 +59,19 @@ void AudioMonitor::SetParams(const AudioParams ¶ms)
|
||||
peaked_.fill(false);
|
||||
}
|
||||
|
||||
void AudioMonitor::OutputDeviceSet(const QString &filename, qint64 offset, int playback_speed)
|
||||
void AudioMonitor::OutputDeviceSet(AudioPlaybackCache *cache, qint64 offset, int playback_speed)
|
||||
{
|
||||
Stop();
|
||||
|
||||
file_.setFileName(filename);
|
||||
file_ = cache->CreatePlaybackDevice(this);
|
||||
|
||||
if (!file_.open(QFile::ReadOnly)) {
|
||||
qWarning() << "Failed to open" << filename;
|
||||
if (!file_->open(QFile::ReadOnly)) {
|
||||
qWarning() << "Failed to open IO device for AudioMonitor display";
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
|
||||
file_.seek(offset);
|
||||
file_->seek(offset);
|
||||
|
||||
playback_speed_ = playback_speed;
|
||||
|
||||
@@ -85,9 +82,8 @@ void AudioMonitor::OutputDeviceSet(const QString &filename, qint64 offset, int p
|
||||
|
||||
void AudioMonitor::Stop()
|
||||
{
|
||||
if (file_.isOpen()) {
|
||||
file_.close();
|
||||
}
|
||||
delete file_;
|
||||
file_ = nullptr;
|
||||
}
|
||||
|
||||
void AudioMonitor::OutputPushed(const QByteArray &d)
|
||||
@@ -215,7 +211,7 @@ void AudioMonitor::paintGL()
|
||||
|
||||
QVector<double> v(params_.channel_count(), 0);
|
||||
|
||||
if (file_.isOpen()) {
|
||||
if (file_) {
|
||||
UpdateValuesFromFile(v);
|
||||
}
|
||||
|
||||
@@ -258,7 +254,7 @@ void AudioMonitor::paintGL()
|
||||
}
|
||||
}
|
||||
|
||||
if (all_zeroes && !file_.isOpen()) {
|
||||
if (all_zeroes && !file_) {
|
||||
// Optimize by disabling the update loop
|
||||
SetUpdateLoop(false);
|
||||
}
|
||||
@@ -287,18 +283,18 @@ void AudioMonitor::UpdateValuesFromFile(QVector<double>& v)
|
||||
|
||||
if (playback_speed_ < 0) {
|
||||
// If reversing, jump back by the amount of bytes we're going to read
|
||||
bytes_to_read = qMin(bytes_to_read, file_.pos());
|
||||
bytes_to_read = qMin(bytes_to_read, file_->pos());
|
||||
|
||||
file_.seek(file_.pos() - bytes_to_read);
|
||||
file_->seek(file_->pos() - bytes_to_read);
|
||||
}
|
||||
|
||||
// Read bytes in from file
|
||||
QByteArray b = file_.read(bytes_to_read);
|
||||
QByteArray b = file_->read(bytes_to_read);
|
||||
|
||||
if (playback_speed_ < 0) {
|
||||
// If reversing, head back to where we were before the read so that the next read starts
|
||||
// from where we left off
|
||||
file_.seek(file_.pos() - bytes_to_read);
|
||||
file_->seek(file_->pos() - bytes_to_read);
|
||||
}
|
||||
|
||||
// If speed is not 1, transform it here
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "common/define.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/audioplaybackcache.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -36,12 +37,10 @@ class AudioMonitor : public QOpenGLWidget
|
||||
public:
|
||||
AudioMonitor(QWidget* parent = nullptr);
|
||||
|
||||
virtual ~AudioMonitor() override;
|
||||
|
||||
public slots:
|
||||
void SetParams(const AudioParams& params);
|
||||
|
||||
void OutputDeviceSet(const QString& filename, qint64 offset, int playback_speed);
|
||||
void OutputDeviceSet(AudioPlaybackCache* cache, qint64 offset, int playback_speed);
|
||||
|
||||
void Stop();
|
||||
|
||||
@@ -66,7 +65,7 @@ private:
|
||||
|
||||
AudioParams params_;
|
||||
|
||||
QFile file_;
|
||||
QIODevice* file_;
|
||||
qint64 last_time_;
|
||||
|
||||
int playback_speed_;
|
||||
|
||||
@@ -63,11 +63,13 @@ void AudioWaveformView::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
|
||||
if (!playback_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const AudioParams& params = playback_->GetParameters();
|
||||
|
||||
if (!playback_
|
||||
|| playback_->GetPCMFilename().isEmpty()
|
||||
|| !params.is_valid()) {
|
||||
if (!params.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,9 +80,9 @@ void AudioWaveformView::paintEvent(QPaintEvent *event)
|
||||
cached_waveform_ = QPixmap(size());
|
||||
cached_waveform_.fill(Qt::transparent);
|
||||
|
||||
QFile fs(playback_->GetPCMFilename());
|
||||
QIODevice* fs = playback_->CreatePlaybackDevice();
|
||||
|
||||
if (fs.open(QFile::ReadOnly)) {
|
||||
if (fs->open(QFile::ReadOnly)) {
|
||||
|
||||
QPainter wave_painter(&cached_waveform_);
|
||||
|
||||
@@ -89,13 +91,13 @@ void AudioWaveformView::paintEvent(QPaintEvent *event)
|
||||
|
||||
int drew = 0;
|
||||
|
||||
fs.seek(params.samples_to_bytes(ScreenToUnitRounded(0)));
|
||||
fs->seek(params.samples_to_bytes(ScreenToUnitRounded(0)));
|
||||
|
||||
for (int x=0; x<width() && !fs.atEnd(); x++) {
|
||||
for (int x=0; x<width() && !fs->atEnd(); x++) {
|
||||
int samples_len = ScreenToUnitRounded(x+1) - ScreenToUnitRounded(x);
|
||||
int max_read_size = params.samples_to_bytes(samples_len);
|
||||
|
||||
QByteArray read_buffer = fs.read(max_read_size);
|
||||
QByteArray read_buffer = fs->read(max_read_size);
|
||||
|
||||
// Detect whether we've reached EOF and recalculate sample count if so
|
||||
if (read_buffer.size() < max_read_size) {
|
||||
@@ -117,9 +119,11 @@ void AudioWaveformView::paintEvent(QPaintEvent *event)
|
||||
cached_scale_ = GetScale();
|
||||
cached_scroll_ = GetScroll();
|
||||
|
||||
fs.close();
|
||||
fs->close();
|
||||
|
||||
}
|
||||
|
||||
delete fs;
|
||||
}
|
||||
|
||||
QPainter p(this);
|
||||
|
||||
@@ -204,8 +204,8 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
|
||||
UpdateStack();
|
||||
|
||||
waveform_view_->SetViewer(GetConnectedNode()->audio_playback_cache());
|
||||
if (GetConnectedTimelinePoints()) {
|
||||
waveform_view_->SetViewer(GetConnectedNode()->audio_playback_cache());
|
||||
waveform_view_->ConnectTimelinePoints(GetConnectedTimelinePoints());
|
||||
}
|
||||
|
||||
@@ -512,23 +512,24 @@ void ViewerWidget::PushScrubbedAudio()
|
||||
{
|
||||
if (!IsPlaying() && Config::Current()["AudioScrubbing"].toBool()) {
|
||||
// Get audio src device from renderer
|
||||
QString audio_fn = GetConnectedNode()->audio_playback_cache()->GetPCMFilename();
|
||||
QFile audio_src(audio_fn);
|
||||
AudioPlaybackCache::PlaybackDevice* audio_src = GetConnectedNode()->audio_playback_cache()->CreatePlaybackDevice();
|
||||
|
||||
if (audio_src.open(QFile::ReadOnly)) {
|
||||
if (audio_src->open(QIODevice::ReadOnly)) {
|
||||
const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters();
|
||||
|
||||
// FIXME: Hardcoded scrubbing interval (20ms)
|
||||
int size_of_sample = params.time_to_bytes(rational(20, 1000));
|
||||
|
||||
// Push audio
|
||||
audio_src.seek(params.time_to_bytes(GetTime()));
|
||||
QByteArray frame_audio = audio_src.read(size_of_sample);
|
||||
audio_src->seek(params.time_to_bytes(GetTime()));
|
||||
QByteArray frame_audio = audio_src->read(size_of_sample);
|
||||
AudioManager::instance()->SetOutputParams(params);
|
||||
AudioManager::instance()->PushToOutput(frame_audio);
|
||||
|
||||
audio_src.close();
|
||||
audio_src->close();
|
||||
}
|
||||
|
||||
delete audio_src;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,13 +629,11 @@ void ViewerWidget::FinishPlayPreprocess()
|
||||
{
|
||||
int64_t playback_start_time = ruler()->GetTime();
|
||||
|
||||
QString audio_fn = GetConnectedNode()->audio_playback_cache()->GetPCMFilename();
|
||||
if (!audio_fn.isEmpty()) {
|
||||
AudioManager::instance()->SetOutputParams(GetConnectedNode()->audio_playback_cache()->GetParameters());
|
||||
AudioManager::instance()->StartOutput(audio_fn,
|
||||
GetConnectedNode()->audio_playback_cache()->GetParameters().time_to_bytes(GetTime()),
|
||||
playback_speed_);
|
||||
}
|
||||
AudioPlaybackCache* audio_cache = GetConnectedNode()->audio_playback_cache();
|
||||
AudioManager::instance()->SetOutputParams(audio_cache->GetParameters());
|
||||
AudioManager::instance()->StartOutput(audio_cache,
|
||||
audio_cache->GetParameters().time_to_bytes(GetTime()),
|
||||
playback_speed_);
|
||||
|
||||
playback_timer_.Start(playback_start_time, playback_speed_, timebase_dbl());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user