1
0
mirror of https://github.com/clementine-player/Clementine synced 2024-12-18 20:34:39 +01:00

Remove the precise analyzer timing code from Amarok and instead just use the

latest buffer from GStreamer.  This works just as well and fixes the occasional
freezing analyzer.  Fixes issue 2464
This commit is contained in:
David Sansome 2013-06-01 18:03:59 +10:00
parent 90898bd255
commit 619261f5e1
2 changed files with 23 additions and 137 deletions

View File

@ -70,8 +70,7 @@ GstEngine::GstEngine(TaskManager* task_manager)
: Engine::Base(),
task_manager_(task_manager),
buffering_task_id_(-1),
delayq_(g_queue_new()),
current_sample_(0),
latest_buffer_(NULL),
equalizer_enabled_(false),
stereo_balance_(0.0f),
rg_enabled_(false),
@ -98,10 +97,6 @@ GstEngine::~GstEngine() {
current_pipeline_.reset();
// Destroy scope delay queue
ClearScopeBuffers();
g_queue_free(delayq_);
// Save configuration
gst_deinit();
}
@ -187,97 +182,44 @@ void GstEngine::AddBufferToScope(GstBuffer* buf, int pipeline_id) {
return;
}
g_queue_push_tail(delayq_, buf);
if (latest_buffer_ != NULL) {
gst_buffer_unref(latest_buffer_);
}
latest_buffer_ = buf;
}
const Engine::Scope& GstEngine::scope() {
UpdateScope();
if (current_sample_ >= kScopeSize) {
// ok, we have a full buffer now, so give it to the scope
for (int i=0; i< kScopeSize; i++)
scope_[i] = current_scope_[i];
current_sample_ = 0;
if (latest_buffer_ != NULL) {
UpdateScope();
}
return scope_;
}
void GstEngine::UpdateScope() {
typedef int16_t sampletype;
// prune the scope and get the current pos of the audio device
const quint64 pos = PruneScope();
const quint64 segment_start = current_pipeline_->segment_start();
// head of the delay queue is the most delayed, so we work with that one
GstBuffer *buf = reinterpret_cast<GstBuffer *>( g_queue_peek_head(delayq_) );
if (!buf)
return;
// start time for this buffer
quint64 stime = GST_BUFFER_TIMESTAMP(buf) - segment_start;
// duration of the buffer...
quint64 dur = GST_BUFFER_DURATION(buf);
// therefore we can calculate the end time for the buffer
quint64 etime = stime + dur;
typedef typename Engine::Scope::value_type sample_type;
// determine the number of channels
GstStructure* structure = gst_caps_get_structure ( GST_BUFFER_CAPS( buf ), 0);
GstStructure* structure = gst_caps_get_structure(
GST_BUFFER_CAPS(latest_buffer_), 0);
int channels = 2;
gst_structure_get_int (structure, "channels", &channels);
gst_structure_get_int(structure, "channels", &channels);
// scope does not support >2 channels
if (channels > 2)
return;
// if the audio device is playing this buffer now
if (pos <= stime || pos >= etime)
return;
const sample_type* source = reinterpret_cast<sample_type*>(
GST_BUFFER_DATA(latest_buffer_));
sample_type* dest = scope_.data();
const int bytes = qMin(
static_cast<Engine::Scope::size_type>(GST_BUFFER_SIZE(latest_buffer_)),
scope_.size() * sizeof(sample_type));
// calculate the number of samples in the buffer
int sz = GST_BUFFER_SIZE(buf) / sizeof(sampletype);
// number of frames is the number of samples in each channel (frames like in the alsa sense)
int frames = sz / channels;
memcpy(dest, source, bytes);
// find the offset into the buffer to the sample closest to where the audio device is playing
// it is the (time into the buffer cooresponding to the audio device pos) / (the sample rate)
// sample rate = duration of the buffer / number of frames in the buffer
// then we multiply by the number of channels to find the offset of the left channel sample
// of the frame in the buffer
int off = channels * (pos - stime) / (dur / frames);
// note that we are assuming 32 bit samples, but this should probably be generalized...
sampletype* data = reinterpret_cast<sampletype *>(GST_BUFFER_DATA(buf));
if (off >= sz) // better be...
return;
int i = off; // starting at offset
// loop while we fill the current buffer. If we need another buffer and one is available,
// get it and keep filling. If there are no more buffers available (not too likely)
// then leave everything in this state and wait until the next time the scope updates
while (buf && current_sample_ < kScopeSize && i < sz) {
for (int j = 0; j < channels && current_sample_ < kScopeSize; j++) {
current_scope_[current_sample_ ++] = data[i + j];
}
i+=channels; // advance to the next frame
if (i >= sz - 1) {
// here we are out of samples in the current buffer, so we get another one
buf = reinterpret_cast<GstBuffer *>( g_queue_pop_head(delayq_) );
gst_buffer_unref(buf);
buf = reinterpret_cast<GstBuffer *>( g_queue_peek_head(delayq_) );
if (buf) {
stime = GST_BUFFER_TIMESTAMP(buf);
dur = GST_BUFFER_DURATION(buf);
etime = stime + dur;
i = 0;
sz = GST_BUFFER_SIZE(buf) / sizeof(sampletype);
data = reinterpret_cast<sampletype *>(GST_BUFFER_DATA(buf));
}
}
}
gst_buffer_unref(latest_buffer_);
latest_buffer_ = NULL;
}
void GstEngine::StartPreloading(const QUrl& url, bool force_stop_at_end,
@ -360,7 +302,6 @@ void GstEngine::StartFadeout() {
fadeout_pipeline_ = current_pipeline_;
disconnect(fadeout_pipeline_.get(), 0, 0, 0);
fadeout_pipeline_->RemoveAllBufferConsumers();
ClearScopeBuffers();
fadeout_pipeline_->StartFader(fadeout_duration_nanosec_, QTimeLine::Backward);
connect(fadeout_pipeline_.get(), SIGNAL(FaderFinished()), SLOT(FadeoutFinished()));
@ -433,8 +374,6 @@ void GstEngine::PlayDone() {
StartTimers();
current_sample_ = 0;
// initial offset
if(offset_nanosec != 0 || beginning_nanosec_ != 0) {
Seek(offset_nanosec);
@ -553,10 +492,9 @@ void GstEngine::SeekNow() {
if (!current_pipeline_)
return;
if (current_pipeline_->Seek(seek_pos_))
ClearScopeBuffers();
else
if (!current_pipeline_->Seek(seek_pos_)) {
qLog(Warning) << "Seek failed";
}
}
void GstEngine::SetEqualizerEnabled(bool enabled) {
@ -604,11 +542,6 @@ void GstEngine::timerEvent(QTimerEvent* e) {
if (e->timerId() != timer_id_)
return;
// keep the scope from building while we are not visible
// this is why the timer must run as long as we are playing, and not just when
// we are fading
PruneScope();
if (current_pipeline_) {
const qint64 current_position = position_nanosec();
const qint64 current_length = length_nanosec();
@ -666,7 +599,6 @@ void GstEngine::EndOfStreamReached(int pipeline_id, bool has_next_track) {
current_pipeline_.reset();
BufferingFinished();
}
ClearScopeBuffers();
emit TrackEnded();
}
@ -772,48 +704,6 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url,
return ret;
}
qint64 GstEngine::PruneScope() {
if (!current_pipeline_)
return 0;
// get the position playing in the audio device
const qint64 pos = current_pipeline_->position();
const qint64 segment_start = current_pipeline_->segment_start();
GstBuffer *buf = 0;
quint64 etime = 0;
// free up the buffers that the audio device has advanced past already
do {
// most delayed buffers are at the head of the queue
buf = reinterpret_cast<GstBuffer *>( g_queue_peek_head(delayq_) );
if (buf) {
// the start time of the buffer
quint64 stime = GST_BUFFER_TIMESTAMP(buf) - segment_start;
// the duration of the buffer
quint64 dur = GST_BUFFER_DURATION(buf);
// therefore we can calculate the end time of the buffer
etime = stime + dur;
// purge this buffer if the pos is past the end time of the buffer
if (pos > qint64(etime)) {
g_queue_pop_head(delayq_);
gst_buffer_unref(buf);
}
}
} while (buf && pos > qint64(etime));
return pos;
}
void GstEngine::ClearScopeBuffers() {
// just free them all
while (g_queue_get_length(delayq_)) {
GstBuffer* buf = reinterpret_cast<GstBuffer *>( g_queue_pop_head(delayq_) );
gst_buffer_unref(buf);
}
}
bool GstEngine::DoesThisSinkSupportChangingTheOutputDeviceToAUserEditableString(const QString &name) {
return (name == "alsasink" || name == "osssink" || name == "pulsesink");
}

View File

@ -121,7 +121,6 @@ class GstEngine : public Engine::Base, public BufferConsumer {
void EndOfStreamReached(int pipeline_id, bool has_next_track);
void HandlePipelineError(int pipeline_id, const QString& message, int domain, int error_code);
void NewMetaData(int pipeline_id, const Engine::SimpleMetaBundle& bundle);
void ClearScopeBuffers();
void AddBufferToScope(GstBuffer* buf, int pipeline_id);
void FadeoutFinished();
void FadeoutPauseFinished();
@ -151,7 +150,6 @@ class GstEngine : public Engine::Base, public BufferConsumer {
boost::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl& url, qint64 end_nanosec);
void UpdateScope();
qint64 PruneScope();
int AddBackgroundStream(boost::shared_ptr<GstEnginePipeline> pipeline);
@ -180,9 +178,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
QList<BufferConsumer*> buffer_consumers_;
GQueue* delayq_;
float current_scope_[kScopeSize];
int current_sample_;
GstBuffer* latest_buffer_;
bool equalizer_enabled_;
int equalizer_preamp_;