Skip to content

Commit

Permalink
audiomonitor: use mipmapped waveform
Browse files Browse the repository at this point in the history
Since we have these now, may as well use them. This should
improve general playback performance since the audio monitor
will be able to iterate far fewer
  • Loading branch information
itsmattkc committed Apr 14, 2021
1 parent 46fb53f commit 3ffb2fd
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 45 deletions.
3 changes: 3 additions & 0 deletions app/audio/audiomanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <QtConcurrent/QtConcurrent>
#include <QThread>

#include "audiovisualwaveform.h"
#include "common/define.h"
#include "outputmanager.h"
#include "render/audioparams.h"
Expand Down Expand Up @@ -94,6 +95,8 @@ class AudioManager : public QObject

void OutputDeviceStarted(AudioPlaybackCache* cache, qint64 offset, int playback_speed);

void OutputWaveformStarted(const AudioVisualWaveform* waveform, const rational &start, int playback_speed);

void AudioParamsChanged(const AudioParams& params);

void OutputPushed(const QByteArray& data);
Expand Down
37 changes: 28 additions & 9 deletions app/audio/audiovisualwaveform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,19 @@ void AudioVisualWaveform::Shift(const rational &from, const rational &to)
}
}

AudioVisualWaveform::Sample AudioVisualWaveform::GetSummaryFromTime(const rational &start, const rational &length) const
{
// Find mipmap that requries
auto using_mipmap = GetMipmapForScale(length.flipped().toDouble());

double rate_dbl = using_mipmap->first.toDouble();

int start_sample = time_to_samples(start, rate_dbl);
int sample_length = time_to_samples(length, rate_dbl);

return ReSumSamples(&using_mipmap->second.constData()[start_sample], sample_length, channels_);
}

AudioVisualWaveform::Sample AudioVisualWaveform::SumSamples(const float *samples, int nb_samples, int nb_channels)
{
return SumSamplesInternal(samples, nb_samples, nb_channels);
Expand Down Expand Up @@ -285,15 +298,7 @@ void AudioVisualWaveform::DrawWaveform(QPainter *painter, const QRect& rect, con
return;
}

// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
auto using_mipmap = samples.mipmapped_data_.cend();
using_mipmap--;
for (auto it=samples.mipmapped_data_.cbegin(); it!=samples.mipmapped_data_.cend(); it++) {
if (it->first.toDouble() >= scale) {
using_mipmap = it;
break;
}
}
auto using_mipmap = samples.GetMipmapForScale(scale);

rational rate = using_mipmap->first;
double rate_dbl = rate.toDouble();
Expand Down Expand Up @@ -350,6 +355,20 @@ int AudioVisualWaveform::time_to_samples(const double &time, double sample_rate)
return qFloor(time * sample_rate) * channels_;
}

std::map<rational, AudioVisualWaveform::Sample>::const_iterator AudioVisualWaveform::GetMipmapForScale(double scale) const
{
// Find largest mipmap for this scale (or the largest if we don't find one sufficient)
auto using_mipmap = mipmapped_data_.cend();
using_mipmap--;
for (auto it=mipmapped_data_.cbegin(); it!=mipmapped_data_.cend(); it++) {
if (it->first.toDouble() >= scale) {
using_mipmap = it;
break;
}
}
return using_mipmap;
}

template<typename T>
AudioVisualWaveform::Sample AudioVisualWaveform::SumSamplesInternal(const T *samples, int nb_samples, int nb_channels)
{
Expand Down
4 changes: 4 additions & 0 deletions app/audio/audiovisualwaveform.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class AudioVisualWaveform {

void Shift(const rational& from, const rational& to);

Sample GetSummaryFromTime(const rational& start, const rational& length) const;

static Sample SumSamples(const float* samples, int nb_samples, int nb_channels);
static Sample SumSamples(SampleBufferPtr samples, int start_index, int length);

Expand All @@ -108,6 +110,8 @@ class AudioVisualWaveform {
int time_to_samples(const rational& time, double sample_rate) const;
int time_to_samples(const double& time, double sample_rate) const;

std::map<rational, Sample>::const_iterator GetMipmapForScale(double scale) const;

int channels_;

std::map<rational, Sample> mipmapped_data_;
Expand Down
94 changes: 60 additions & 34 deletions app/widget/audiomonitor/audiomonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ const int kMaximumSmoothness = 8;
AudioMonitor::AudioMonitor(QWidget *parent) :
QOpenGLWidget(parent),
file_(nullptr),
waveform_(nullptr),
cached_channels_(0)
{
values_.resize(kMaximumSmoothness);

connect(AudioManager::instance(), &AudioManager::OutputDeviceStarted, this, &AudioMonitor::OutputDeviceSet);
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);
Expand Down Expand Up @@ -84,6 +85,7 @@ void AudioMonitor::Stop()
{
delete file_;
file_ = nullptr;
waveform_ = nullptr;
}

void AudioMonitor::OutputPushed(const QByteArray &d)
Expand All @@ -97,6 +99,20 @@ void AudioMonitor::OutputPushed(const QByteArray &d)
SetUpdateLoop(true);
}

void AudioMonitor::OutputAudioVisualWaveformSet(const AudioVisualWaveform *waveform, const rational &start, int playback_speed)
{
Stop();

waveform_ = waveform;
waveform_time_ = start;

playback_speed_ = playback_speed;

last_time_ = QDateTime::currentMSecsSinceEpoch();

SetUpdateLoop(true);
}

void AudioMonitor::SetUpdateLoop(bool e)
{
if (e) {
Expand Down Expand Up @@ -211,8 +227,24 @@ void AudioMonitor::paintGL()

QVector<double> v(params_.channel_count(), 0);

if (file_) {
UpdateValuesFromFile(v);
if (file_ || waveform_) {
// Determines how many milliseconds have passed since last update
qint64 current_time = QDateTime::currentMSecsSinceEpoch();
qint64 delta_time = current_time - last_time_;
int abs_speed = qAbs(playback_speed_);

// Multiply by speed if the speed is not 1
if (abs_speed != 1) {
delta_time *= abs_speed;
}

if (file_) {
UpdateValuesFromFile(v, delta_time);
} else if (waveform_) {
UpdateValuesFromWaveform(v, delta_time);
}

last_time_ = current_time;
}

PushValue(v);
Expand Down Expand Up @@ -254,7 +286,7 @@ void AudioMonitor::paintGL()
}
}

if (all_zeroes && !file_) {
if (all_zeroes && !file_ && !waveform_) {
// Optimize by disabling the update loop
SetUpdateLoop(false);
}
Expand All @@ -266,20 +298,10 @@ void AudioMonitor::mousePressEvent(QMouseEvent *)
update();
}

void AudioMonitor::UpdateValuesFromFile(QVector<double>& v)
void AudioMonitor::UpdateValuesFromFile(QVector<double>& v, qint64 delta_time)
{
// Determines how many milliseconds have passed since last update
qint64 current_time = QDateTime::currentMSecsSinceEpoch();
qint64 time_passed = current_time - last_time_;
int abs_speed = qAbs(playback_speed_);

// Multiply by speed if the speed is not 1
if (abs_speed != 1) {
time_passed *= abs_speed;
}

// Convert ms to float seconds and determine how many bytes that is
qint64 bytes_to_read = params_.time_to_bytes(static_cast<double>(time_passed) * 0.001);
qint64 bytes_to_read = params_.time_to_bytes(static_cast<double>(delta_time) * 0.001);

if (playback_speed_ < 0) {
// If reversing, jump back by the amount of bytes we're going to read
Expand All @@ -297,31 +319,35 @@ void AudioMonitor::UpdateValuesFromFile(QVector<double>& v)
file_->seek(file_->pos() - bytes_to_read);
}

// If speed is not 1, transform it here
if (abs_speed != 1) {
int sample_sz = params_.samples_to_bytes(1);
int in_nb_samples = params_.bytes_to_samples(b.size());
int out_nb_samples = in_nb_samples / abs_speed;
QByteArray speed_adjusted(out_nb_samples * sample_sz, Qt::Uninitialized);

for (int i=0;i<out_nb_samples;i++) {
memcpy(speed_adjusted.data() + i * sample_sz,
b.constData() + i * abs_speed * sample_sz,
sample_sz);
}
BytesToSampleSummary(b, v);
}

b = speed_adjusted;
}
void AudioMonitor::UpdateValuesFromWaveform(QVector<double> &v, qint64 delta_time)
{
// Delta time is provided in milliseconds, so we convert to seconds in rational
rational length(delta_time, 1000);

BytesToSampleSummary(b, v);
AudioVisualWaveform::Sample sum = waveform_->GetSummaryFromTime(waveform_time_, length);

last_time_ = current_time;
for (int i=0; i<sum.size(); i++) {
float max = qMax(qAbs(sum.at(i).min), qAbs(sum.at(i).max));

int output_index = i%v.size();
if (max > v.at(output_index)) {
v[output_index] = max;
}
}

waveform_time_ += length;
}

void AudioMonitor::PushValue(const QVector<double> &v)
{
values_.removeFirst();
values_.append(v);
int lim = values_.size()-1;
for (int i=0; i<lim; i++) {
values_[i] = values_[i+1];
}
values_[lim] = v;
}

void AudioMonitor::BytesToSampleSummary(const QByteArray &b, QVector<double> &v)
Expand Down
11 changes: 9 additions & 2 deletions app/widget/audiomonitor/audiomonitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <QOpenGLWidget>
#include <QTimer>

#include "audio/audiovisualwaveform.h"
#include "common/define.h"
#include "render/audioparams.h"
#include "render/audioplaybackcache.h"
Expand All @@ -46,16 +47,19 @@ public slots:

void OutputPushed(const QByteArray& d);

void OutputAudioVisualWaveformSet(const AudioVisualWaveform *waveform, const rational& start, int playback_speed);

protected:
//virtual void paintEvent(QPaintEvent* event) override;
virtual void paintGL() override;

virtual void mousePressEvent(QMouseEvent* event) override;

private:
void SetUpdateLoop(bool e);

void UpdateValuesFromFile(QVector<double> &v);
void UpdateValuesFromFile(QVector<double> &v, qint64 delta_time);

void UpdateValuesFromWaveform(QVector<double> &v, qint64 delta_time);

void PushValue(const QVector<double>& v);

Expand All @@ -68,6 +72,9 @@ public slots:
QIODevice* file_;
qint64 last_time_;

const AudioVisualWaveform* waveform_;
rational waveform_time_;

int playback_speed_;

QVector< QVector<double> > values_;
Expand Down
5 changes: 5 additions & 0 deletions app/widget/viewer/audiowaveformview.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class AudioWaveformView : public SeekableWidget

void SetViewer(AudioPlaybackCache *playback);

const AudioVisualWaveform* waveform() const
{
return &waveform_;
}

protected:
virtual void paintEvent(QPaintEvent* event) override;

Expand Down
2 changes: 2 additions & 0 deletions app/widget/viewer/viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ void ViewerWidget::StartAudioOutput()
AudioManager::instance()->StartOutput(audio_cache,
audio_cache->GetParameters().time_to_bytes(GetTime()),
playback_speed_);
emit AudioManager::instance()->OutputWaveformStarted(waveform_view_->waveform(),
GetTime(), playback_speed_);
}
}

Expand Down

0 comments on commit 3ffb2fd

Please sign in to comment.