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_videoPort( nullptr )
, m_audioPort( nullptr )
, m_scope( nullptr )
, m_post( nullptr )
, m_xine( nullptr )
, m_scope( Analyzer::SCOPE_SIZE * 2 ) // Multiply by two to account for interleaved PCM.
, m_current_vpts( 0 )
{
DEBUG_BLOCK
@ -51,10 +52,6 @@ VideoWindow::VideoWindow( TQWidget *parent )
setPaletteBackgroundColor( TQt::black );
setFocusPolicy( ClickFocus );
//TODO sucks
//TODO namespace this?
myList->next = myList; //init the buffer list
// 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 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_audioPort ) xine_close_audio_driver( m_xine, m_audioPort );
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 );
cleanUpVideo();
@ -119,7 +116,7 @@ VideoWindow::init()
if( !m_xine )
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";
xine_config_load( m_xine, TQFile::encodeName( TQDir::homeDirPath() + "/.xine/config" ) );
@ -161,9 +158,9 @@ VideoWindow::init()
debug() << "scope_plugin_new()\n";
#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)
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
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
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
timerEvent( nullptr );
if( m_scope ) {
if( m_post ) {
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 );
}
@ -373,6 +370,8 @@ VideoWindow::stop()
{
xine_stop( m_stream );
std::fill(m_scope.begin(), m_scope.end(), 0);
announceStateChange();
}
@ -590,7 +589,7 @@ VideoWindow::setStreamParameter( int value )
else if( sender == "volume" )
{
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)
{
value = makeVolumeLogarithmic(value);
@ -607,16 +606,25 @@ VideoWindow::scope()
{
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;
}
if( xine_get_status( m_stream ) != XINE_STATUS_PLAY )
return 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 (channels > 2)
{
return m_scope;
}
//prune the buffer list and update the m_current_vpts timestamp
timerEvent( nullptr );
const int64_t pts_per_smpls = scope_plugin_pts_per_smpls(m_scope);
for( int channels = xine_get_stream_info( m_stream, XINE_STREAM_INFO_AUDIO_CHANNELS ), frame = 0; frame < SCOPE_SIZE; )
for (int n, frame = 0; frame < SCOPE_SIZE; /* no-op */)
{
MyNode *best_node = nullptr;
@ -637,10 +645,10 @@ VideoWindow::scope()
data16 = best_node->mem;
data16 += diff;
diff += diff % channels; //important correction to ensure we don't overflow the buffer
diff /= channels;
diff += diff % channels; // important correction to ensure we don't overflow the buffer.
diff /= channels; // use units of frames, not samples.
int
// calculate the number of available samples in this buffer.
n = best_node->num_frames;
n -= diff;
n += frame; //clipping for # of frames we need
@ -650,33 +658,52 @@ VideoWindow::scope()
for( int a, c; frame < n; ++frame, data16 += channels ) {
for( a = c = 0; c < channels; ++c )
a += data16[c];
a /= channels;
scope[frame] = a;
{
// now we give interleaved PCM to the scope.
m_scope[scopeIdx++] = data16[c];
if (channels == 1)
{
// Duplicate mono samples.
m_scope[scopeIdx++] = data16[c];
}
}
}
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
}
return scope;
return m_scope;
}
void
VideoWindow::timerEvent( TQTimerEvent* )
{
if (!m_stream)
{
return;
}
/// here we prune the buffer list regularly
MyNode * const first_node = myList->next;
MyNode const * const list_end = myList;
MyNode *myList = scope_plugin_list(m_post);
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)
? xine_get_current_vpts( m_stream )
: 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
if( node->vpts_end < m_current_vpts ) {
prev->next = node->next;

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

@ -4,16 +4,38 @@
/* need access to port_ticket */
#define XINE_ENGINE_INTERNAL
#define LOG_MODULE "codine-scope"
#define LOG_LEVEL LOG_LEVEL_DEBUG
// #define LOG
#include "xineScope.h"
#include <xine/post.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;
static MyNode theList;
static int myChannels = 0;
static int64_t pts_per_smpls;
int scope_plugin_channels(void *post)
{
scope_plugin_t *self = post;
return self->channels;
}
MyNode* const myList = &theList;
MyNode *scope_plugin_list(void *post)
{
scope_plugin_t *self = post;
return self->list;
}
int64_t scope_plugin_pts_per_smpls(void *post)
{
scope_plugin_t *self = post;
return self->pts_per_smpls;
}
/*************************
* post plugin functions *
@ -22,7 +44,10 @@ MyNode* const myList = &theList;
static int
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;
scope_plugin_t *self = (scope_plugin_t *)port->post;
_x_post_rewire( (post_plugin_t*)port->post );
_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->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 );
#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)
pts_per_smpls = ((uint32_t)90000 * (uint32_t)32768) / rate;
self->pts_per_smpls = ((uint32_t)90000 * (uint32_t)32768) / rate;
#else
pts_per_smpls = stream->metronom->pts_per_smpls;
self->pts_per_smpls = stream->metronom->pts_per_smpls;
#endif
return ret;
}
@ -47,7 +72,17 @@ scope_port_open( xine_audio_port_t *port_gen, xine_stream_t *stream, uint32_t bi
static void
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;
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->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 )
{
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 */
/* 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; }
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) );
#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
K = pts_per_smpls; /*smpls = 1<<16 samples*/
K = self->pts_per_smpls; /*smpls = 1<<16 samples*/
K *= num_samples;
K /= (1<<16);
K += new_node->vpts;
@ -93,14 +129,29 @@ 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
* NOTE this is thread-safe due to the way we handle the list in the GUI thread */
new_node->next = myList->next;
myList->next = new_node;
new_node->next = self->list->next;
self->list->next = new_node;
}
static void
scope_dispose( post_plugin_t *this )
{
free( 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);
}
@ -118,15 +169,15 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
if( audio_target == 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_out_t *output;
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) || \
(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.put_buffer = scope_port_put_buffer;
this->xine_post.audio_input[0] = &port->new_port;
this->xine_post.type = PLUGIN_POST;
post_plugin->xine_post.audio_input[0] = &port->new_port;
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) || \
@ -153,6 +204,12 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
post_plugin->xine = xine;
#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) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
return post_plugin;
@ -161,11 +218,6 @@ xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target )
#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) || \
(XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION == 2 && XINE_SUB_VERSION >= 10)
static void *scope_init_plugin(xine_t *xine, const void *data)

@ -33,18 +33,23 @@ struct my_node_s
int64_t vpts_end;
};
extern MyNode* const myList;
#ifdef __cplusplus
extern "C"
{
#endif
#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)
extern const plugin_info_t scope_plugin_info[];
#else
xine_post_t* scope_plugin_new( xine_t*, xine_audio_port_t* );
#endif
int64_t scope_plugin_pts_per_smpls( void* );
int scope_plugin_channels(void *);
MyNode *scope_plugin_list(void *);
int64_t scope_plugin_pts_per_smpls(void *);
#ifdef __cplusplus
}
#endif

Loading…
Cancel
Save