From: Steinar H. Gunderson Date: Fri, 15 Jan 2016 00:42:19 +0000 (+0100) Subject: Add a stereo correlation meter. X-Git-Tag: 1.0.0~10 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=64ecf5ca155de13a76edaaefc6c4fd1f8e8f92f7 Add a stereo correlation meter. --- diff --git a/Makefile b/Makefile index 0ef287e..27d0750 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ CXX=g++ PKG_MODULES = Qt5Core Qt5Gui Qt5Widgets Qt5OpenGLExtensions Qt5OpenGL libusb-1.0 movit lua5.2 libmicrohttpd -CXXFLAGS := -O2 -march=native -g -std=gnu++11 -Wall -Wno-deprecated-declarations -fPIC $(shell pkg-config --cflags $(PKG_MODULES)) -pthread -DMOVIT_SHADER_DIR=\"$(shell pkg-config --variable=shaderdir movit)\" +CXXFLAGS := -O2 -march=native -g -std=gnu++11 -Wall -Wno-deprecated-declarations -Werror -fPIC $(shell pkg-config --cflags $(PKG_MODULES)) -pthread -DMOVIT_SHADER_DIR=\"$(shell pkg-config --variable=shaderdir movit)\" LDFLAGS=$(shell pkg-config --libs $(PKG_MODULES)) -lEGL -lGL -pthread -lva -lva-drm -lva-x11 -lX11 -lavformat -lavcodec -lavutil -lswscale -lzita-resampler -lasound # Qt objects -OBJS=glwidget.o main.o mainwindow.o vumeter.o lrameter.o vu_common.o aboutdialog.o -OBJS += glwidget.moc.o mainwindow.moc.o vumeter.moc.o lrameter.moc.o aboutdialog.moc.o +OBJS=glwidget.o main.o mainwindow.o vumeter.o lrameter.o vu_common.o correlation_meter.o aboutdialog.o +OBJS += glwidget.moc.o mainwindow.moc.o vumeter.moc.o lrameter.moc.o correlation_meter.moc.o aboutdialog.moc.o # Mixer objects OBJS += h264encode.o mixer.o bmusb/bmusb.o pbo_frame_allocator.o context.o ref_counted_frame.o theme.o resampling_queue.o httpd.o ebu_r128_proc.o flags.o image_input.o stereocompressor.o filter.o alsa_output.o correlation_measurer.o diff --git a/correlation_measurer.cpp b/correlation_measurer.cpp index 888ebff..1e8ff75 100644 --- a/correlation_measurer.cpp +++ b/correlation_measurer.cpp @@ -32,6 +32,11 @@ CorrelationMeasurer::CorrelationMeasurer(unsigned sample_rate, { } +void CorrelationMeasurer::reset() +{ + zl = zr = zll = zlr = zrr = 0.0f; +} + void CorrelationMeasurer::process_samples(const std::vector &samples) { assert(samples.size() % 2 == 0); diff --git a/correlation_measurer.h b/correlation_measurer.h index 6d84340..0c0ac72 100644 --- a/correlation_measurer.h +++ b/correlation_measurer.h @@ -38,6 +38,7 @@ public: CorrelationMeasurer(unsigned sample_rate, float lowpass_cutoff_hz = 1000.0f, float falloff_seconds = 0.150f); void process_samples(const std::vector &samples); // Taken to be stereo, interleaved. + void reset(); float get_correlation() const; private: diff --git a/correlation_meter.cpp b/correlation_meter.cpp new file mode 100644 index 0000000..b1080ca --- /dev/null +++ b/correlation_meter.cpp @@ -0,0 +1,54 @@ +#include + +#include "correlation_meter.h" + +using namespace std; + +CorrelationMeter::CorrelationMeter(QWidget *parent) + : QWidget(parent) +{ +} + +void CorrelationMeter::resizeEvent(QResizeEvent *event) +{ + on_pixmap = QPixmap(width(), height()); + QPainter on_painter(&on_pixmap); + QLinearGradient on(0, 0, width(), 0); + on.setColorAt(0.0f, QColor(255, 0, 0)); + on.setColorAt(0.5f, QColor(255, 255, 0)); + on.setColorAt(0.8f, QColor(0, 255, 0)); + on.setColorAt(0.95f, QColor(255, 255, 0)); + on_painter.fillRect(0, 0, width(), height(), Qt::black); + on_painter.fillRect(1, 1, width() - 2, height() - 2, on); + + off_pixmap = QPixmap(width(), height()); + QPainter off_painter(&off_pixmap); + QLinearGradient off(0, 0, width(), 0); + off.setColorAt(0.0f, QColor(127, 0, 0)); + off.setColorAt(0.5f, QColor(127, 127, 0)); + off.setColorAt(0.8f, QColor(0, 127, 0)); + off.setColorAt(0.95f, QColor(127, 127, 0)); + off_painter.fillRect(0, 0, width(), height(), Qt::black); + off_painter.fillRect(1, 1, width() - 2, height() - 2, off); +} + +void CorrelationMeter::paintEvent(QPaintEvent *event) +{ + QPainter painter(this); + + float correlation; + { + unique_lock lock(correlation_mutex); + correlation = this->correlation; + } + + // Just in case. + correlation = std::min(std::max(correlation, -1.0f), 1.0f); + + int pos = 3 + lrintf(0.5f * (correlation + 1.0f) * (width() - 6)); + QRect off_rect(0, 0, width(), height()); + QRect on_rect(pos - 2, 0, 5, height()); + + painter.drawPixmap(off_rect, off_pixmap, off_rect); + painter.drawPixmap(on_rect, on_pixmap, on_rect); +} diff --git a/correlation_meter.h b/correlation_meter.h new file mode 100644 index 0000000..6dc0684 --- /dev/null +++ b/correlation_meter.h @@ -0,0 +1,31 @@ +#ifndef CORRELATION_METER_H +#define CORRELATION_METER_H + +#include +#include +#include + +class CorrelationMeter : public QWidget +{ + Q_OBJECT + +public: + CorrelationMeter(QWidget *parent); + + void set_correlation(float correlation) { + std::unique_lock lock(correlation_mutex); + this->correlation = correlation; + QMetaObject::invokeMethod(this, "update", Qt::AutoConnection); + } + +private: + void resizeEvent(QResizeEvent *event) override; + void paintEvent(QPaintEvent *event) override; + + std::mutex correlation_mutex; + float correlation = 0.0f; + + QPixmap on_pixmap, off_pixmap; +}; + +#endif diff --git a/mainwindow.cpp b/mainwindow.cpp index 311e573..77daf07 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -130,7 +130,7 @@ void MainWindow::mixer_created(Mixer *mixer) global_mixer->set_compressor_enabled(state == Qt::Checked); }); connect(ui->reset_meters_button, &QPushButton::clicked, this, &MainWindow::reset_meters_button_clicked); - mixer->set_audio_level_callback(bind(&MainWindow::audio_level_callback, this, _1, _2, _3, _4, _5, _6, _7)); + mixer->set_audio_level_callback(bind(&MainWindow::audio_level_callback, this, _1, _2, _3, _4, _5, _6, _7, _8)); } void MainWindow::mixer_shutting_down() @@ -215,7 +215,10 @@ void MainWindow::reset_meters_button_clicked() ui->peak_display->setStyleSheet(""); } -void MainWindow::audio_level_callback(float level_lufs, float peak_db, float global_level_lufs, float range_low_lufs, float range_high_lufs, float gain_staging_db, float final_makeup_gain_db) +void MainWindow::audio_level_callback(float level_lufs, float peak_db, float global_level_lufs, + float range_low_lufs, float range_high_lufs, + float gain_staging_db, float final_makeup_gain_db, + float correlation) { timeval now; gettimeofday(&now, nullptr); @@ -232,6 +235,7 @@ void MainWindow::audio_level_callback(float level_lufs, float peak_db, float glo post_to_main_thread([=]() { ui->vu_meter->set_level(level_lufs); ui->lra_meter->set_levels(global_level_lufs, range_low_lufs, range_high_lufs); + ui->correlation_meter->set_correlation(correlation); char buf[256]; snprintf(buf, sizeof(buf), "%.1f", peak_db); diff --git a/mainwindow.h b/mainwindow.h index 0132175..8ca728a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -53,7 +53,7 @@ private: void set_white_balance(int channel_number, int x, int y); // Called from the mixer. - void audio_level_callback(float level_lufs, float peak_db, float global_level_lufs, float range_low_lufs, float range_high_lufs, float gain_staging_db, float final_makeup_gain_db); + void audio_level_callback(float level_lufs, float peak_db, float global_level_lufs, float range_low_lufs, float range_high_lufs, float gain_staging_db, float final_makeup_gain_db, float correlation); timeval last_audio_level_callback{0, 0}; Ui::MainWindow *ui; diff --git a/mixer.cpp b/mixer.cpp index 00b3efe..39bae19 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -98,6 +98,7 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) num_cards(num_cards), mixer_surface(create_surface(format)), h264_encoder_surface(create_surface(format)), + correlation(OUTPUT_FREQUENCY), level_compressor(OUTPUT_FREQUENCY), limiter(OUTPUT_FREQUENCY), compressor(OUTPUT_FREQUENCY) @@ -534,7 +535,8 @@ void Mixer::thread_func() audio_level_callback(loudness_s, 20.0 * log10(peak), loudness_i, loudness_range_low, loudness_range_high, - gain_staging_db, 20.0 * log10(final_makeup_gain)); + gain_staging_db, 20.0 * log10(final_makeup_gain), + correlation.get_correlation()); } for (unsigned card_index = 1; card_index < num_cards; ++card_index) { @@ -822,13 +824,14 @@ void Mixer::process_audio_one_frame(int64_t frame_pts_int, int num_samples) final_makeup_gain = m; } - // Find R128 levels. + // Find R128 levels and L/R correlation. vector left, right; deinterleave_samples(samples_out, &left, &right); float *ptrs[] = { left.data(), right.data() }; { unique_lock lock(compressor_mutex); r128.process(left.size(), ptrs); + correlation.process_samples(samples_out); } // Send the samples to the sound card. @@ -933,6 +936,7 @@ void Mixer::reset_meters() peak = 0.0f; r128.reset(); r128.integr_start(); + correlation.reset(); } Mixer::OutputChannel::~OutputChannel() diff --git a/mixer.h b/mixer.h index ffae67a..961c2a6 100644 --- a/mixer.h +++ b/mixer.h @@ -35,6 +35,7 @@ #include "stereocompressor.h" #include "filter.h" #include "input_state.h" +#include "correlation_measurer.h" class H264Encoder; class QSurface; @@ -103,7 +104,8 @@ public: typedef std::function audio_level_callback_t; + float gain_staging_db, float final_makeup_gain_db, + float correlation)> audio_level_callback_t; void set_audio_level_callback(audio_level_callback_t callback) { audio_level_callback = callback; @@ -288,6 +290,7 @@ private: audio_level_callback_t audio_level_callback = nullptr; std::mutex compressor_mutex; Ebu_r128_proc r128; // Under compressor_mutex. + CorrelationMeasurer correlation; // Under compressor_mutex. Resampler peak_resampler; std::atomic peak{0.0f}; diff --git a/ui_mainwindow.ui b/ui_mainwindow.ui index c175c1d..cc041a8 100644 --- a/ui_mainwindow.ui +++ b/ui_mainwindow.ui @@ -27,7 +27,7 @@ - + @@ -183,244 +183,338 @@ - - + + 0 - - 4 - - - - 0 + + + + 0 + 0 + - - - - - 0 - 1 - - - - - 16 - 0 - - - - - 1 - 0 - - - - - 0 - 0 - - - - - - - - - 255 - 255 - 255 - - - - - - - 5 - 239 - 111 - - - - - - - - - 255 - 255 - 255 - - - - - - - 5 - 239 - 111 - - - - - - - - - 5 - 239 - 111 - - - - - - - 5 - 239 - 111 - - - - - - - - true - - - - - - - - 30 - 0 + 0 + 14 - - -0.0 + + + + + + + 255 + 255 + 255 + + + + + + + 239 + 0 + 4 + + + + + + + + + 255 + 255 + 255 + + + + + + + 239 + 0 + 4 + + + + + + + + + 239 + 0 + 4 + + + + + + + 239 + 0 + 4 + + + + + - - Qt::AlignCenter + + true - - - - - - 3 - - - 0 - - + - - - - 0 - 0 - + + + 0 - - - 24 - 0 - + + 4 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 239 - 219 - - - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 239 - 219 - - - - - - - - - 0 - 239 - 219 - - - - - - - 0 - 239 - 219 - - - - - + + + + 0 + + + + + + 0 + 1 + + + + + 16 + 0 + + + + + 1 + 0 + + + + + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 5 + 239 + 111 + + + + + + + + + 255 + 255 + 255 + + + + + + + 5 + 239 + 111 + + + + + + + + + 5 + 239 + 111 + + + + + + + 5 + 239 + 111 + + + + + + + + true + + + + + + + + + + 30 + 0 + + + + -0.0 + + + Qt::AlignCenter + + + + + + + + + 3 - - true + + 0 - + + + + + + + 0 + 0 + + + + + 24 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 239 + 219 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 239 + 219 + + + + + + + + + 0 + 239 + 219 + + + + + + + 0 + 239 + 219 + + + + + + + + true + + + + + + + + + + 30 + 20 + + + + RST + + + false + + + + - - - - - 30 - 20 - - - - RST - - - false - - - @@ -835,6 +929,12 @@
lrameter.h
1 + + CorrelationMeter + QWidget +
correlation_meter.h
+ 1 +