/* Author: Max Howell , (C) 2004 Copyright: See COPYING file that comes with this distribution */ /* 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 #include 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; } 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 * *************************/ 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 ); port->stream = stream; port->bits = bits; port->rate = rate; port->mode = 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) self->pts_per_smpls = ((uint32_t)90000 * (uint32_t)32768) / rate; #else self->pts_per_smpls = stream->metronom->pts_per_smpls; #endif return ret; } 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 ); _x_post_dec_usage( port ); } 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? */ if( port->bits == 8 ) { port->original_port->put_buffer( port->original_port, buf, stream ); return; } MyNode *new_node; const int num_samples = buf->num_frames * self->channels; new_node = malloc( sizeof(MyNode) ); #ifdef METRONOM_VPTS new_node->vpts = stream->metronom->get_option(stream->metronom, METRONOM_VPTS); #else new_node->vpts = stream->metronom->got_audio_samples( stream->metronom, 0, 0 ); #endif new_node->num_frames = buf->num_frames; new_node->mem = malloc( num_samples * 2 ); memcpy( new_node->mem, buf->mem, num_samples * 2 ); { int64_t K = self->pts_per_smpls; /*smpls = 1<<16 samples*/ K *= num_samples; K /= (1<<16); K += new_node->vpts; new_node->vpts_end = K; } /* pass data to original port */ port->original_port->put_buffer( port->original_port, buf, stream ); /* 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 = self->list->next; self->list->next = new_node; } static void 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); } /************************ * plugin init function * ************************/ #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 post_plugin_t* scope_plugin_new( post_class_t *class_gen, int inputs, xine_audio_port_t *audio_target[], xine_video_port_t *video_target[] ) #else xine_post_t* scope_plugin_new( xine_t *xine, xine_audio_port_t *audio_target ) #endif { if( audio_target == NULL ) return NULL; scope_plugin_t *scope_plugin = calloc(1, sizeof(scope_plugin_t)); post_plugin_t *post_plugin = (post_plugin_t *)scope_plugin; { post_in_t *input; post_out_t *output; post_audio_port_t *port; _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) port = _x_post_intercept_audio_port( post_plugin, audio_target[0], &input, &output ); #else port = _x_post_intercept_audio_port( post_plugin, audio_target, &input, &output ); #endif port->new_port.open = scope_port_open; port->new_port.close = scope_port_close; port->new_port.put_buffer = scope_port_put_buffer; post_plugin->xine_post.audio_input[0] = &port->new_port; post_plugin->xine_post.type = PLUGIN_POST; post_plugin->dispose = scope_dispose; } #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) /* code is straight from xine_init_post() can't use that function as it only dlopens the plugins and our plugin is statically linked in */ post_plugin->running_ticket = xine->port_ticket; 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; #else return &post_plugin->xine_post; #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) static void *scope_init_plugin(xine_t *xine, const void *data) { static const post_class_t post_scope_class = { .open_plugin = scope_plugin_new, .identifier = "codeine-scope", .description = "Codeine Scope", .dispose = NULL, }; (void)xine; (void)data; return (void*)&post_scope_class; } static const post_info_t scope_special_info = { .type = XINE_POST_TYPE_AUDIO_VISUALIZATION, }; const plugin_info_t scope_plugin_info[] = { { PLUGIN_POST, 10, "codeine-scope", XINE_VERSION_CODE, &scope_special_info, scope_init_plugin }, { PLUGIN_NONE, 0, NULL, 0, NULL, NULL } }; #endif