From 53f21584e49ec68e2788f5c37e46e0b585bac569 Mon Sep 17 00:00:00 2001 From: Mark Furneaux Date: Tue, 29 Apr 2014 21:38:21 -0400 Subject: [PATCH] Fix inconsistent buffer sizes sent to analyzer The analyzers are sent new buffers of audio data to process each time they pass through the gst pipeline. Different file formats and bit depths/ sample rates can change the size of these buffers, in some cases making them large and therefore infrequent. This causes choppiness in the analyzer as it is not getting new data with every frame. This patch chunks the buffers coming off the pipeline to correspond with the framerate of the analyzer. --- src/analyzers/analyzerbase.cpp | 5 ++-- src/analyzers/analyzerbase.h | 1 + src/engines/enginebase.h | 2 +- src/engines/gstengine.cpp | 46 +++++++++++++++++++++++++++------- src/engines/gstengine.h | 8 ++++-- 5 files changed, 48 insertions(+), 14 deletions(-) mode change 100644 => 100755 src/analyzers/analyzerbase.cpp mode change 100644 => 100755 src/analyzers/analyzerbase.h mode change 100644 => 100755 src/engines/enginebase.h mode change 100644 => 100755 src/engines/gstengine.cpp mode change 100644 => 100755 src/engines/gstengine.h diff --git a/src/analyzers/analyzerbase.cpp b/src/analyzers/analyzerbase.cpp old mode 100644 new mode 100755 index f0ca647b1..e2f25877e --- a/src/analyzers/analyzerbase.cpp +++ b/src/analyzers/analyzerbase.cpp @@ -52,6 +52,7 @@ Analyzer::Base::Base(QWidget* parent, uint scopeSize) m_fht(new FHT(scopeSize)), m_engine(nullptr), m_lastScope(512), + current_chunk(0), new_frame_(false), is_playing_(false) {} @@ -85,10 +86,10 @@ void Analyzer::Base::paintEvent(QPaintEvent* e) { switch (m_engine->state()) { case Engine::Playing: { - const Engine::Scope& thescope = m_engine->scope(); + const Engine::Scope& thescope = m_engine->scope(m_timeout); int i = 0; - // convert to mono here - our built in analyzers need mono, but we the + // convert to mono here - our built in analyzers need mono, but the // engines provide interleaved pcm for (uint x = 0; (int)x < m_fht->size(); ++x) { m_lastScope[x] = diff --git a/src/analyzers/analyzerbase.h b/src/analyzers/analyzerbase.h old mode 100644 new mode 100755 index f6c7eebfd..85a4c7a9f --- a/src/analyzers/analyzerbase.h +++ b/src/analyzers/analyzerbase.h @@ -73,6 +73,7 @@ class Base : public QWidget { FHT* m_fht; EngineBase* m_engine; Scope m_lastScope; + int current_chunk; bool new_frame_; bool is_playing_; diff --git a/src/engines/enginebase.h b/src/engines/enginebase.h old mode 100644 new mode 100755 index 7dc3c6b06..e6c30d2c3 --- a/src/engines/enginebase.h +++ b/src/engines/enginebase.h @@ -85,7 +85,7 @@ class Base : public QObject { // Simple accessors inline uint volume() const { return volume_; } - virtual const Scope& scope() { return scope_; } + virtual const Scope& scope(int chunk_length) { return scope_; } bool is_fadeout_enabled() const { return fadeout_enabled_; } bool is_crossfade_enabled() const { return crossfade_enabled_; } bool is_autocrossfade_enabled() const { return autocrossfade_enabled_; } diff --git a/src/engines/gstengine.cpp b/src/engines/gstengine.cpp old mode 100644 new mode 100755 index 105092f4d..524092b9b --- a/src/engines/gstengine.cpp +++ b/src/engines/gstengine.cpp @@ -22,6 +22,7 @@ #include "gstengine.h" #include +#include #include #include @@ -96,7 +97,9 @@ GstEngine::GstEngine(TaskManager* task_manager) timer_id_(-1), next_element_id_(0), is_fading_out_to_pause_(false), - has_faded_out_(false) { + has_faded_out_(false), + scope_chunk(0), + have_new_buffer(false) { seek_timer_->setSingleShot(true); seek_timer_->setInterval(kSeekDelayNanosec / kNsecPerMsec); connect(seek_timer_, SIGNAL(timeout()), SLOT(SeekNow())); @@ -235,19 +238,36 @@ void GstEngine::AddBufferToScope(GstBuffer* buf, int pipeline_id) { if (latest_buffer_ != nullptr) { gst_buffer_unref(latest_buffer_); } + latest_buffer_ = buf; + have_new_buffer = true; } -const Engine::Scope& GstEngine::scope() { - if (latest_buffer_ != nullptr) { - UpdateScope(); +const Engine::Scope& GstEngine::scope(int chunk_length) { + + // the new buffer could have a different size + if (have_new_buffer) { + if (latest_buffer_ != nullptr) { + scope_chunks = ceil(((double)GST_BUFFER_DURATION(latest_buffer_) / + (double)(chunk_length * 1000000))); + } + + // if the buffer is shorter than the chunk length + if (scope_chunks <= 0) { + scope_chunks = 1; } + scope_chunk = 0; + have_new_buffer = false; + } + + UpdateScope(chunk_length); return scope_; } -void GstEngine::UpdateScope() { +void GstEngine::UpdateScope(int chunk_length_) { typedef Engine::Scope::value_type sample_type; + int chunk_size = GST_BUFFER_SIZE(latest_buffer_) / (scope_chunks); // determine the number of channels GstStructure* structure = @@ -258,17 +278,25 @@ void GstEngine::UpdateScope() { // scope does not support >2 channels if (channels > 2) return; + // in case a buffer doesn't arrive in time + if (scope_chunk >= scope_chunks) { + scope_chunk = 0; + return; + } + + // pass the next chunk of the buffer to the analyser const sample_type* source = reinterpret_cast(GST_BUFFER_DATA(latest_buffer_)); + source += (chunk_size / 2 * scope_chunk); sample_type* dest = scope_.data(); + const int bytes = qMin( - static_cast(GST_BUFFER_SIZE(latest_buffer_)), + static_cast(chunk_size), scope_.size() * sizeof(sample_type)); - memcpy(dest, source, bytes); + scope_chunk++; - gst_buffer_unref(latest_buffer_); - latest_buffer_ = nullptr; + memcpy(dest, source, bytes); } void GstEngine::StartPreloading(const QUrl& url, bool force_stop_at_end, diff --git a/src/engines/gstengine.h b/src/engines/gstengine.h old mode 100644 new mode 100755 index bd2ceb73d..f50b51a05 --- a/src/engines/gstengine.h +++ b/src/engines/gstengine.h @@ -80,7 +80,7 @@ class GstEngine : public Engine::Base, public BufferConsumer { qint64 position_nanosec() const; qint64 length_nanosec() const; Engine::State state() const; - const Engine::Scope& scope(); + const Engine::Scope& scope(int chunk_length); OutputDetailsList GetOutputsList() const; @@ -161,7 +161,7 @@ class GstEngine : public Engine::Base, public BufferConsumer { std::shared_ptr CreatePipeline(const QUrl& url, qint64 end_nanosec); - void UpdateScope(); + void UpdateScope(int chunk_length_); int AddBackgroundStream(std::shared_ptr pipeline); @@ -224,6 +224,10 @@ class GstEngine : public Engine::Base, public BufferConsumer { bool is_fading_out_to_pause_; bool has_faded_out_; + int scope_chunk; + bool have_new_buffer; + int scope_chunks; + QList device_finders_; };