/*
* vis_runner.c
* Copyright 2009 John Lindgren
*
* This file is part of Audacious.
*
* Audacious is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3 of the License.
*
* Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Audacious. If not, see .
*
* The Audacious team does not consider modular code linking to Audacious or
* using our public API to be a derived work.
*/
#include
#include "effect.h"
#include "hook.h"
#include "input.h"
#include "output.h"
#include "vis_runner.h"
#define INTERVAL 30 /* milliseconds */
static GMutex * mutex;
static gboolean playing, paused, active;
static GList * vis_list, * vis_tail, * hooks;
static gint send_source, clear_source;
static gboolean send_audio (void * unused)
{
gint outputted;
VisNode * vis_node, * next;
g_mutex_lock (mutex);
if (! send_source)
{
g_mutex_unlock (mutex);
return FALSE;
}
vis_node = NULL;
outputted = new_effect_output_to_decoder_time
(current_output_plugin->output_time ());
while (vis_list != NULL)
{
next = vis_list->data;
if ((vis_node != NULL) ? (next->time > outputted) : (next->time >
outputted + INTERVAL))
break;
g_free (vis_node);
vis_node = next;
vis_list = g_list_delete_link (vis_list, vis_list);
if (vis_list == NULL)
vis_tail = NULL;
}
g_mutex_unlock (mutex);
if (vis_node != NULL)
{
gint channel;
GList * node;
for (channel = 0; channel < vis_node->nch; channel ++)
memset (vis_node->data[channel] + vis_node->length, 0, 2 * (512 -
vis_node->length));
vis_node->length = 512;
for (node = hooks; node != NULL; node = node->next)
{
HookItem * item = node->data;
item->func (vis_node, item->user_data);
}
}
g_free (vis_node);
return TRUE;
}
static gboolean send_clear (void * unused)
{
g_mutex_lock (mutex);
clear_source = 0;
g_mutex_unlock (mutex);
hook_call ("visualization clear", NULL);
return FALSE;
}
static void flush_locked (void)
{
while (vis_list != NULL)
{
g_free (vis_list->data);
vis_list = g_list_delete_link (vis_list, vis_list);
}
vis_tail = NULL;
clear_source = g_timeout_add (0, send_clear, NULL);
}
void vis_runner_init (void)
{
mutex = g_mutex_new ();
playing = FALSE;
paused = FALSE;
active = FALSE;
hooks = NULL;
vis_list = NULL;
vis_tail = NULL;
send_source = 0;
clear_source = 0;
}
void vis_runner_start_stop (gboolean new_playing, gboolean new_paused)
{
g_mutex_lock (mutex);
playing = new_playing;
paused = new_paused;
active = playing && hooks != NULL;
if (send_source)
{
g_source_remove (send_source);
send_source = 0;
}
if (clear_source)
{
g_source_remove (clear_source);
clear_source = 0;
}
if (! active)
flush_locked ();
else if (! paused)
send_source = g_timeout_add (INTERVAL, send_audio, NULL);
g_mutex_unlock (mutex);
}
void vis_runner_pass_audio (gint time, gfloat * data, gint samples, gint
channels)
{
VisNode * vis_node;
gint channel;
g_mutex_lock (mutex);
if (! active)
goto UNLOCK;
time -= time % INTERVAL;
if (vis_tail == NULL)
vis_node = NULL;
else
{
vis_node = vis_tail->data;
if (vis_node->time != time || vis_node->nch != MIN (channels, 2))
vis_node = NULL;
}
if (vis_node == NULL)
{
vis_node = g_malloc (sizeof (VisNode));
vis_node->time = time;
vis_node->nch = MIN (channels, 2);
vis_node->length = 0;
if (vis_tail == NULL)
{
vis_tail = g_list_append (NULL, vis_node);
vis_list = vis_tail;
}
else
vis_tail = g_list_append (vis_tail, vis_node)->next;
}
if (samples > channels * (512 - vis_node->length))
samples = channels * (512 - vis_node->length);
for (channel = 0; channel < vis_node->nch; channel ++)
{
gfloat * from = data + channel;
gfloat * end = from + samples;
gint16 * to = vis_node->data[channel] + vis_node->length;
while (from < end)
{
register gfloat temp = * from;
* to ++ = CLAMP (temp, -1, 1) * 32767;
from += channels;
}
}
vis_node->length += samples / channels;
UNLOCK:
g_mutex_unlock (mutex);
}
void vis_runner_time_offset (gint offset)
{
GList * node;
g_mutex_lock (mutex);
for (node = vis_list; node != NULL; node = node->next)
((VisNode *) (node->data))->time += offset;
g_mutex_unlock (mutex);
}
void vis_runner_flush (void)
{
g_mutex_lock (mutex);
flush_locked ();
g_mutex_unlock (mutex);
}
void vis_runner_add_hook (HookFunction func, gpointer user_data)
{
HookItem * item = g_malloc (sizeof (HookItem));
item->func = func;
item->user_data = user_data;
hooks = g_list_prepend (hooks, item);
vis_runner_start_stop (playing, paused);
}
void vis_runner_remove_hook (HookFunction func)
{
GList * node;
HookItem * item;
for (node = hooks; node != NULL; node = node->next)
{
item = node->data;
if (item->func == func)
goto FOUND;
}
return;
FOUND:
hooks = g_list_delete_link (hooks, node);
g_free (item);
vis_runner_start_stop (playing, paused);
}