Update the xineScope to remove global variables.

This patch updates xineScope.c to better align with Amarok's code. As a
result, the analyzer is more accurate.  For instance, when playing an
audio file and there is a silent spot, the blocks will correctly "drop"
to the bottom, leaving an empty analyzer.  The previous behaviour would
leave some blocks "stuck" in their position

See: TDE/codeine#23
Signed-off-by: mio <stigma@disroot.org>
pull/27/head
mio 4 weeks ago
parent a3ea0ee70f
commit ff2a5768dd

@ -36,8 +36,9 @@ VideoWindow::VideoWindow( TQWidget *parent )
, m_eventQueue( nullptr ) , m_eventQueue( nullptr )
, m_videoPort( nullptr ) , m_videoPort( nullptr )
, m_audioPort( nullptr ) , m_audioPort( nullptr )
, m_scope( nullptr ) , m_post( nullptr )
, m_xine( nullptr ) , m_xine( nullptr )
, m_scope( Analyzer::SCOPE_SIZE * 2 ) // Multiply by two to account for interleaved PCM.
, m_current_vpts( 0 ) , m_current_vpts( 0 )
{ {
DEBUG_BLOCK DEBUG_BLOCK
@ -51,10 +52,6 @@ VideoWindow::VideoWindow( TQWidget *parent )
setPaletteBackgroundColor( TQt::black ); setPaletteBackgroundColor( TQt::black );
setFocusPolicy( ClickFocus ); setFocusPolicy( ClickFocus );
//TODO sucks
//TODO namespace this?
myList->next = myList; //init the buffer list
// Detect xine version, this is used for volume adjustment. // Detect xine version, this is used for volume adjustment.
// Xine versions prior to 1.2.13 use linear volume, so the engine uses logarithmic volume. // Xine versions prior to 1.2.13 use linear volume, so the engine uses logarithmic volume.
// Xine versions starting from 1.2.13 use logarithmic volume, so the engine uses linear volume. // Xine versions starting from 1.2.13 use logarithmic volume, so the engine uses linear volume.
@ -101,7 +98,7 @@ VideoWindow::~VideoWindow()
if( m_stream ) xine_dispose( m_stream ); if( m_stream ) xine_dispose( m_stream );
if( m_audioPort ) xine_close_audio_driver( m_xine, m_audioPort ); if( m_audioPort ) xine_close_audio_driver( m_xine, m_audioPort );
if( m_videoPort ) xine_close_video_driver( m_xine, m_videoPort ); if( m_videoPort ) xine_close_video_driver( m_xine, m_videoPort );
if( m_scope ) xine_post_dispose( m_xine, m_scope ); if( m_post ) xine_post_dispose( m_xine, m_post );
if( m_xine ) xine_exit( m_xine ); if( m_xine ) xine_exit( m_xine );
cleanUpVideo(); cleanUpVideo();
@ -119,7 +116,7 @@ VideoWindow::init()
if( !m_xine ) if( !m_xine )
return false; return false;
xine_engine_set_param( m_xine, XINE_ENGINE_PARAM_VERBOSITY, 99 ); xine_engine_set_param(m_xine, XINE_ENGINE_PARAM_VERBOSITY, XINE_VERBOSITY_DEBUG);
debug() << "xine_config_load()\n"; debug() << "xine_config_load()\n";
xine_config_load( m_xine, TQFile::encodeName( TQDir::homeDirPath() + "/.xine/config" ) ); xine_config_load( m_xine, TQFile::encodeName( TQDir::homeDirPath() + "/.xine/config" ) );
@ -161,9 +158,9 @@ VideoWindow::init()
debug() << "scope_plugin_new()\n"; debug() << "scope_plugin_new()\n";
#if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \ #if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10) (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
m_scope = xine_post_init( m_xine, "codeine-scope", 1, &m_audioPort, nullptr ); m_post = xine_post_init( m_xine, "codeine-scope", 1, &m_audioPort, nullptr );
#else #else
m_scope = scope_plugin_new( m_xine, m_audioPort ); m_post = scope_plugin_new( m_xine, m_audioPort );
//FIXME this one seems to make seeking unstable for Codeine, perhaps //FIXME this one seems to make seeking unstable for Codeine, perhaps
xine_set_param( m_stream, XINE_PARAM_METRONOM_PREBUFFER, 6000 ); //less buffering, faster seeking.. xine_set_param( m_stream, XINE_PARAM_METRONOM_PREBUFFER, 6000 ); //less buffering, faster seeking..
@ -281,9 +278,9 @@ VideoWindow::load( const KURL &url )
// FIXME leaves one erroneous buffer // FIXME leaves one erroneous buffer
timerEvent( nullptr ); timerEvent( nullptr );
if( m_scope ) { if( m_post ) {
xine_post_out_t *source = xine_get_audio_source( m_stream ); xine_post_out_t *source = xine_get_audio_source( m_stream );
xine_post_in_t *target = (xine_post_in_t*)xine_post_input( m_scope, const_cast<char*>("audio in") ); xine_post_in_t *target = (xine_post_in_t*)xine_post_input( m_post, const_cast<char*>("audio in") );
xine_post_wire( source, target ); xine_post_wire( source, target );
} }
@ -373,6 +370,8 @@ VideoWindow::stop()
{ {
xine_stop( m_stream ); xine_stop( m_stream );
std::fill(m_scope.begin(), m_scope.end(), 0);
announceStateChange(); announceStateChange();
} }
@ -590,7 +589,7 @@ VideoWindow::setStreamParameter( int value )
else if( sender == "volume" ) else if( sender == "volume" )
{ {
parameter = XINE_PARAM_AUDIO_AMP_LEVEL; parameter = XINE_PARAM_AUDIO_AMP_LEVEL;
value = 100 - value; // TQt sliders are wrong way round when vertical value = 100 - value; // TQt sliders are the wrong way round when vertical
if (s_logarithmicVolume) if (s_logarithmicVolume)
{ {
value = makeVolumeLogarithmic(value); value = makeVolumeLogarithmic(value);
@ -607,16 +606,25 @@ VideoWindow::scope()
{ {
using Analyzer::SCOPE_SIZE; using Analyzer::SCOPE_SIZE;
static Engine::Scope scope( SCOPE_SIZE ); if (!m_post || !m_stream || xine_get_status(m_stream) != XINE_STATUS_PLAY)
{
return m_scope;
}
MyNode *const myList = scope_plugin_list(m_post);
const int64_t pts_per_smpls = scope_plugin_pts_per_smpls(m_post);
const int channels = scope_plugin_channels(m_post);
int scopeIdx = 0;
if( xine_get_status( m_stream ) != XINE_STATUS_PLAY ) if (channels > 2)
return scope; {
return m_scope;
}
//prune the buffer list and update the m_current_vpts timestamp //prune the buffer list and update the m_current_vpts timestamp
timerEvent( nullptr ); timerEvent( nullptr );
const int64_t pts_per_smpls = scope_plugin_pts_per_smpls(m_scope); for (int n, frame = 0; frame < SCOPE_SIZE; /* no-op */)
for( int channels = xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_CHANNELS ), frame = 0; frame < SCOPE_SIZE; )
{ {
MyNode *best_node = nullptr; MyNode *best_node = nullptr;
@ -637,10 +645,10 @@ VideoWindow::scope()
data16 = best_node->mem; data16 = best_node->mem;
data16 += diff; data16 += diff;
diff += diff % channels; //important correction to ensure we don't overflow the buffer diff += diff % channels; // important correction to ensure we don't overflow the buffer.
diff /= channels; diff /= channels; // use units of frames, not samples.
int // calculate the number of available samples in this buffer.
n = best_node->num_frames; n = best_node->num_frames;
n -= diff; n -= diff;
n += frame; //clipping for # of frames we need n += frame; //clipping for # of frames we need
@ -650,33 +658,52 @@ VideoWindow::scope()
for( int a, c; frame < n; ++frame, data16 += channels ) { for( int a, c; frame < n; ++frame, data16 += channels ) {
for( a = c = 0; c < channels; ++c ) for( a = c = 0; c < channels; ++c )
a += data16[c]; {
// now we give interleaved PCM to the scope.
a /= channels; m_scope[scopeIdx++] = data16[c];
scope[frame] = a; if (channels == 1)
{
// Duplicate mono samples.
m_scope[scopeIdx++] = data16[c];
}
}
} }
m_current_vpts = best_node->vpts_end; m_current_vpts = best_node->vpts_end;
m_current_vpts++; //FIXME needs to be done for some reason, or you get situations where it uses same buffer again and again m_current_vpts++; //FIXME needs to be done for some reason, or you get situations where it uses same buffer again and again
} }
return scope; return m_scope;
} }
void void
VideoWindow::timerEvent( TQTimerEvent* ) VideoWindow::timerEvent( TQTimerEvent* )
{ {
if (!m_stream)
{
return;
}
/// here we prune the buffer list regularly /// here we prune the buffer list regularly
MyNode * const first_node = myList->next; MyNode *myList = scope_plugin_list(m_post);
MyNode const * const list_end = myList;
if (!myList)
{
return;
}
// We operate on a subset of the list for thread-safety.
MyNode *const firstNode = myList->next;
const MyNode *const listEnd = myList;
// If we're not playing or paused, empty the list.
m_current_vpts = (xine_get_status( m_stream ) == XINE_STATUS_PLAY) m_current_vpts = (xine_get_status( m_stream ) == XINE_STATUS_PLAY)
? xine_get_current_vpts( m_stream ) ? xine_get_current_vpts( m_stream )
: std::numeric_limits<int64_t>::max(); : std::numeric_limits<int64_t>::max();
for( MyNode *prev = first_node, *node = first_node->next; node != list_end; node = node->next ) for( MyNode *prev = firstNode, *node = firstNode->next; node != listEnd; node = node->next )
{ {
// we never delete first_node // we never delete firstNode
// this maintains thread-safety // this maintains thread-safety
if( node->vpts_end < m_current_vpts ) { if( node->vpts_end < m_current_vpts ) {
prev->next = node->next; prev->next = node->next;

@ -104,9 +104,10 @@ namespace Codeine
xine_event_queue_t *m_eventQueue; xine_event_queue_t *m_eventQueue;
xine_video_port_t *m_videoPort; xine_video_port_t *m_videoPort;
xine_audio_port_t *m_audioPort; xine_audio_port_t *m_audioPort;
xine_post_t *m_scope; xine_post_t *m_post;
xine_t *m_xine; xine_t *m_xine;
Engine::Scope m_scope;
int64_t m_current_vpts; int64_t m_current_vpts;
KURL m_url; KURL m_url;

@ -4,16 +4,38 @@
/* need access to port_ticket */ /* need access to port_ticket */
#define XINE_ENGINE_INTERNAL #define XINE_ENGINE_INTERNAL
#define LOG_MODULE "codine-scope"
#define LOG_LEVEL LOG_LEVEL_DEBUG
// #define LOG
#include "xineScope.h" #include "xineScope.h"
#include <xine/post.h> #include <xine/post.h>
#include <xine/xine_internal.h> #include <xine/xine_internal.h>
typedef struct scope_plugin_s {
post_plugin_t super;
int channels;
int64_t pts_per_smpls;
MyNode *list;
} scope_plugin_t;
int scope_plugin_channels(void *post)
{
scope_plugin_t *self = post;
return self->channels;
}
static MyNode theList; MyNode *scope_plugin_list(void *post)
static int myChannels = 0; {
static int64_t pts_per_smpls; scope_plugin_t *self = post;
return self->list;
}
MyNode* const myList = &theList; int64_t scope_plugin_pts_per_smpls(void *post)
{
scope_plugin_t *self = post;
return self->pts_per_smpls;
}
/************************* /*************************
* post plugin functions * * post plugin functions *
@ -22,7 +44,10 @@ MyNode* const myList = &theList;
static int static int
scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode ) scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bits, uint32_t rate, int mode )
{ {
lprintf("scope_port_open()\n");
post_audio_port_t *port = (post_audio_port_t *)port_gen; post_audio_port_t *port = (post_audio_port_t *)port_gen;
scope_plugin_t *self = (scope_plugin_t *)port->post;
_x_post_rewire( (post_plugin_t*)port->post ); _x_post_rewire( (post_plugin_t*)port->post );
_x_post_inc_usage( port ); _x_post_inc_usage( port );
@ -32,14 +57,14 @@ scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bi
port->rate = rate; port->rate = rate;
port->mode = mode; port->mode = mode;
myChannels = _x_ao_mode2channels( mode ); self->channels = _x_ao_mode2channels(mode);
int ret = port->original_port->open( port->original_port, stream, bits, rate, mode ); int ret = port->original_port->open( port->original_port, stream, bits, rate, mode );
#if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \ #if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10) (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
pts_per_smpls = ((uint32_t)90000 * (uint32_t)32768) / rate; self->pts_per_smpls = ((uint32_t)90000 * (uint32_t)32768) / rate;
#else #else
pts_per_smpls = stream->metronom->pts_per_smpls; self->pts_per_smpls = stream->metronom->pts_per_smpls;
#endif #endif
return ret; return ret;
} }
@ -47,7 +72,17 @@ scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bi
static void static void
scope_port_close( xine_audio_port_t *port_gen, xine_stream_t *stream ) scope_port_close( xine_audio_port_t *port_gen, xine_stream_t *stream )
{ {
lprintf("scope_port_close()\n");
post_audio_port_t *port = (post_audio_port_t *)port_gen; post_audio_port_t *port = (post_audio_port_t *)port_gen;
scope_plugin_t *self = (scope_plugin_t *)port->post;
/* ensure the buffers are deleted during the next VideoWindow::timerEvent */
MyNode *node;
for (node = self->list->next; node != self->list; node = node->next)
{
node->vpts = node->vpts_end - 1;
}
port->stream = NULL; port->stream = NULL;
port->original_port->close( port->original_port, stream ); port->original_port->close( port->original_port, stream );
@ -59,6 +94,7 @@ static void
scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream ) scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream )
{ {
post_audio_port_t *port = (post_audio_port_t *)port_gen; post_audio_port_t *port = (post_audio_port_t *)port_gen;
scope_plugin_t *self = (scope_plugin_t *)port->post;
/* we are too simple to handle 8bit */ /* we are too simple to handle 8bit */
/* what does it mean when stream == NULL? */ /* what does it mean when stream == NULL? */
@ -66,7 +102,7 @@ scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_st
port->original_port->put_buffer( port->original_port, buf, stream ); return; } port->original_port->put_buffer( port->original_port, buf, stream ); return; }
MyNode *new_node; MyNode *new_node;
const int num_samples = buf->num_frames * myChannels; const int num_samples = buf->num_frames * self->channels;
new_node = malloc( sizeof(MyNode) ); new_node = malloc( sizeof(MyNode) );
#ifdef METRONOM_VPTS #ifdef METRONOM_VPTS
@ -80,7 +116,7 @@ scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_st
{ {
int64_t int64_t
K = pts_per_smpls; /*smpls = 1<<16 samples*/ K = self->pts_per_smpls; /*smpls = 1<<16 samples*/
K *= num_samples; K *= num_samples;
K /= (1<<16); K /= (1<<16);
K += new_node->vpts; K += new_node->vpts;
@ -93,13 +129,28 @@ scope_port_put_buffer( xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_st
/* finally we should append the current buffer to the list /* finally we should append the current buffer to the list
* NOTE this is thread-safe due to the way we handle the list in the GUI thread */ * NOTE this is thread-safe due to the way we handle the list in the GUI thread */
new_node->next = myList->next; new_node->next = self->list->next;
myList->next = new_node; self->list->next = new_node;
} }
static void static void
scope_dispose( post_plugin_t *this ) scope_dispose( post_plugin_t *this )
{ {
MyNode *list = ((scope_plugin_t *)this)->list;
MyNode *prev;
MyNode *node = list;
/* Free all elements of the list (a ring buffer) */
do
{
prev = node->next;
free(node->mem);
free(node);
node = prev;
} while(node != list);
free(this); free(this);
} }
@ -118,15 +169,15 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
if( audio_target == NULL ) if( audio_target == NULL )
return NULL; return NULL;
post_plugin_t *post_plugin = calloc( 1, sizeof(post_plugin_t) ); scope_plugin_t *scope_plugin = calloc(1, sizeof(scope_plugin_t));
post_plugin_t *post_plugin = (post_plugin_t *)scope_plugin;
{ {
post_plugin_t *this = post_plugin;
post_in_t *input; post_in_t *input;
post_out_t *output; post_out_t *output;
post_audio_port_t *port; post_audio_port_t *port;
_x_post_init( this, 1, 0 ); _x_post_init(post_plugin, 1, 0);
#if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \ #if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10) (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
@ -138,10 +189,10 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
port->new_port.close = scope_port_close; port->new_port.close = scope_port_close;
port->new_port.put_buffer = scope_port_put_buffer; port->new_port.put_buffer = scope_port_put_buffer;
this->xine_post.audio_input[0] = &port->new_port; post_plugin->xine_post.audio_input[0] = &port->new_port;
this->xine_post.type = PLUGIN_POST; post_plugin->xine_post.type = PLUGIN_POST;
this->dispose = scope_dispose; post_plugin->dispose = scope_dispose;
} }
#if XINE_MAJOR_VERSION < 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION < 2) || \ #if XINE_MAJOR_VERSION < 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION < 2) || \
@ -153,6 +204,12 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
post_plugin->xine = xine; post_plugin->xine = xine;
#endif #endif
/* scope_plugin_t init */
scope_plugin->channels = 0;
scope_plugin->pts_per_smpls = 0;
scope_plugin->list = calloc(1, sizeof(MyNode));
scope_plugin->list->next = scope_plugin->list;
#if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \ #if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10) (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
return post_plugin; return post_plugin;
@ -161,11 +218,6 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
#endif #endif
} }
int64_t scope_plugin_pts_per_smpls( void *post )
{
return pts_per_smpls;
}
#if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \ #if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10) (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
static void *scope_init_plugin(xine_t *xine, const void *data) static void *scope_init_plugin(xine_t *xine, const void *data)

@ -33,18 +33,23 @@ struct my_node_s
int64_t vpts_end; int64_t vpts_end;
}; };
extern MyNode* const myList;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif
#if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \ #if XINE_MAJOR_VERSION > 1 || (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION > 2) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10) (XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
extern const plugin_info_t scope_plugin_info[]; extern const plugin_info_t scope_plugin_info[];
#else #else
xine_post_t* scope_plugin_new( xine_t*, xine_audio_port_t* ); xine_post_t* scope_plugin_new( xine_t*, xine_audio_port_t* );
#endif #endif
int scope_plugin_channels(void *);
MyNode *scope_plugin_list(void *);
int64_t scope_plugin_pts_per_smpls(void *); int64_t scope_plugin_pts_per_smpls(void *);
#ifdef __cplusplus
} }
#endif #endif

Loading…
Cancel
Save