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.
This commit is contained in:
Mark Furneaux 2014-04-29 21:38:21 -04:00
parent 41e9c15248
commit 53f21584e4
5 changed files with 48 additions and 14 deletions

5
src/analyzers/analyzerbase.cpp Normal file → Executable file
View File

@ -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] =

1
src/analyzers/analyzerbase.h Normal file → Executable file
View File

@ -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_;

2
src/engines/enginebase.h Normal file → Executable file
View File

@ -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_; }

46
src/engines/gstengine.cpp Normal file → Executable file
View File

@ -22,6 +22,7 @@
#include "gstengine.h"
#include <math.h>
#include <cmath>
#include <unistd.h>
#include <iostream>
@ -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<sample_type*>(GST_BUFFER_DATA(latest_buffer_));
source += (chunk_size / 2 * scope_chunk);
sample_type* dest = scope_.data();
const int bytes = qMin(
static_cast<Engine::Scope::size_type>(GST_BUFFER_SIZE(latest_buffer_)),
static_cast<Engine::Scope::size_type>(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,

8
src/engines/gstengine.h Normal file → Executable file
View File

@ -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<GstEnginePipeline> CreatePipeline(const QUrl& url,
qint64 end_nanosec);
void UpdateScope();
void UpdateScope(int chunk_length_);
int AddBackgroundStream(std::shared_ptr<GstEnginePipeline> 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<DeviceFinder*> device_finders_;
};