summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/app/xineEngine.cpp85
-rw-r--r--src/app/xineEngine.h3
-rw-r--r--src/app/xineScope.c98
-rw-r--r--src/app/xineScope.h11
4 files changed, 141 insertions, 56 deletions
diff --git a/src/app/xineEngine.cpp b/src/app/xineEngine.cpp
index 2cb9cd3..7f73e0d 100644
--- a/src/app/xineEngine.cpp
+++ b/src/app/xineEngine.cpp
@@ -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;
diff --git a/src/app/xineEngine.h b/src/app/xineEngine.h
index 6f8170a..168080d 100644
--- a/src/app/xineEngine.h
+++ b/src/app/xineEngine.h
@@ -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;
diff --git a/src/app/xineScope.c b/src/app/xineScope.c
index 3e7cb69..55cf026 100644
--- a/src/app/xineScope.c
+++ b/src/app/xineScope.c
@@ -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)
diff --git a/src/app/xineScope.h b/src/app/xineScope.h
index 623d56d..99d7215 100644
--- a/src/app/xineScope.h
+++ b/src/app/xineScope.h
@@ -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