Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
vis_runner.c
Go to the documentation of this file.
00001 /*
00002  * vis_runner.c
00003  * Copyright 2009-2010 John Lindgren
00004  *
00005  * This file is part of Audacious.
00006  *
00007  * Audacious is free software: you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation, version 3 of the License.
00010  *
00011  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
00012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00013  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License along with
00016  * Audacious. If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * The Audacious team does not consider modular code linking to Audacious or
00019  * using our public API to be a derived work.
00020  */
00021 
00022 #include <glib.h>
00023 #include <libaudcore/hook.h>
00024 
00025 #include "compatibility.h"
00026 #include "misc.h"
00027 #include "output.h"
00028 #include "vis_runner.h"
00029 
00030 #define INTERVAL 30 /* milliseconds */
00031 
00032 typedef struct {
00033     VisHookFunc func;
00034     void * user;
00035 } VisHookItem;
00036 
00037 G_LOCK_DEFINE_STATIC (mutex);
00038 static gboolean playing = FALSE, paused = FALSE, active = FALSE;
00039 static GList * hooks = NULL;
00040 static VisNode * current_node = NULL;
00041 static GQueue vis_list = G_QUEUE_INIT;
00042 static gint send_source = 0, clear_source = 0;
00043 
00044 static gboolean send_audio (void * unused)
00045 {
00046     G_LOCK (mutex);
00047 
00048     if (! send_source)
00049     {
00050         G_UNLOCK (mutex);
00051         return FALSE;
00052     }
00053 
00054     /* We need raw time, not changed for effects and gapless playback. */
00055     gint outputted = current_output_plugin->output_time ();
00056 
00057     VisNode * vis_node = NULL;
00058     VisNode * next;
00059 
00060     while ((next = g_queue_peek_head (& vis_list)))
00061     {
00062         /* If we are considering a node, stop searching and use it if it is the
00063          * most recent (that is, the next one is in the future).  Otherwise,
00064          * consider the next node if it is not in the future by more than the
00065          * length of an interval. */
00066         if (next->time > outputted + (vis_node ? 0 : INTERVAL))
00067             break;
00068 
00069         g_free (vis_node);
00070         vis_node = g_queue_pop_head (& vis_list);
00071     }
00072 
00073     G_UNLOCK (mutex);
00074 
00075     if (! vis_node)
00076         return TRUE;
00077 
00078     for (GList * node = hooks; node; node = node->next)
00079     {
00080         VisHookItem * item = node->data;
00081         item->func (vis_node, item->user);
00082     }
00083 
00084     g_free (vis_node);
00085     return TRUE;
00086 }
00087 
00088 static gboolean send_clear (void * unused)
00089 {
00090     G_LOCK (mutex);
00091     clear_source = 0;
00092     G_UNLOCK (mutex);
00093 
00094     hook_call ("visualization clear", NULL);
00095     return FALSE;
00096 }
00097 
00098 static void flush_locked (void)
00099 {
00100     g_free (current_node);
00101     current_node = NULL;
00102     g_queue_foreach (& vis_list, (GFunc) g_free, NULL);
00103     g_queue_clear (& vis_list);
00104 
00105     clear_source = g_timeout_add (0, send_clear, NULL);
00106 }
00107 
00108 void vis_runner_start_stop (gboolean new_playing, gboolean new_paused)
00109 {
00110     G_LOCK (mutex);
00111 
00112     playing = new_playing;
00113     paused = new_paused;
00114     active = playing && hooks;
00115 
00116     if (send_source)
00117     {
00118         g_source_remove (send_source);
00119         send_source = 0;
00120     }
00121 
00122     if (clear_source)
00123     {
00124         g_source_remove (clear_source);
00125         clear_source = 0;
00126     }
00127 
00128     if (! active)
00129         flush_locked ();
00130     else if (! paused)
00131         send_source = g_timeout_add (INTERVAL, send_audio, NULL);
00132 
00133     G_UNLOCK (mutex);
00134 }
00135 
00136 void vis_runner_pass_audio (gint time, gfloat * data, gint samples, gint
00137  channels, gint rate)
00138 {
00139     G_LOCK (mutex);
00140 
00141     if (! active)
00142         goto UNLOCK;
00143 
00144     if (current_node && current_node->nch != MIN (channels, 2))
00145     {
00146         g_free (current_node);
00147         current_node = NULL;
00148     }
00149 
00150     gint at = 0;
00151 
00152     while (1)
00153     {
00154         if (! current_node)
00155         {
00156             gint node_time = time;
00157             VisNode * last;
00158 
00159             if ((last = g_queue_peek_tail (& vis_list)))
00160                 node_time = last->time + INTERVAL;
00161 
00162             at = channels * (gint) ((gint64) (node_time - time) * rate / 1000);
00163 
00164             if (at < 0)
00165                 at = 0;
00166             if (at >= samples)
00167                 break;
00168 
00169             current_node = g_malloc (sizeof (VisNode));
00170             current_node->time = node_time;
00171             current_node->nch = MIN (channels, 2);
00172             current_node->length = 0;
00173         }
00174 
00175         gint copy = MIN (samples - at, channels * (512 - current_node->length));
00176 
00177         for (gint channel = 0; channel < current_node->nch; channel ++)
00178         {
00179             gfloat * from = data + at + channel;
00180             gfloat * end = from + copy;
00181             gint16 * to = current_node->data[channel] + current_node->length;
00182 
00183             while (from < end)
00184             {
00185                 register gfloat temp = * from;
00186                 * to ++ = CLAMP (temp, -1, 1) * 32767;
00187                 from += channels;
00188             }
00189         }
00190 
00191         current_node->length += copy / channels;
00192 
00193         if (current_node->length < 512)
00194             break;
00195 
00196         g_queue_push_tail (& vis_list, current_node);
00197         current_node = NULL;
00198     }
00199 
00200 UNLOCK:
00201     G_UNLOCK (mutex);
00202 }
00203 
00204 static void time_offset_cb (VisNode * vis_node, void * offset)
00205 {
00206     vis_node->time += GPOINTER_TO_INT (offset);
00207 }
00208 
00209 void vis_runner_time_offset (gint offset)
00210 {
00211     G_LOCK (mutex);
00212 
00213     if (current_node)
00214         current_node->time += offset;
00215 
00216     g_queue_foreach (& vis_list, (GFunc) time_offset_cb, GINT_TO_POINTER (offset));
00217 
00218     G_UNLOCK (mutex);
00219 }
00220 
00221 void vis_runner_flush (void)
00222 {
00223     G_LOCK (mutex);
00224     flush_locked ();
00225     G_UNLOCK (mutex);
00226 }
00227 
00228 void vis_runner_add_hook (VisHookFunc func, void * user)
00229 {
00230     G_LOCK (mutex);
00231 
00232     VisHookItem * item = g_malloc (sizeof (VisHookItem));
00233     item->func = func;
00234     item->user = user;
00235     hooks = g_list_prepend (hooks, item);
00236 
00237     G_UNLOCK (mutex);
00238     vis_runner_start_stop (playing, paused);
00239 }
00240 
00241 void vis_runner_remove_hook (VisHookFunc func)
00242 {
00243     G_LOCK (mutex);
00244 
00245     for (GList * node = hooks; node; node = node->next)
00246     {
00247         if (((VisHookItem *) node->data)->func == func)
00248         {
00249             g_free (node->data);
00250             hooks = g_list_delete_link (hooks, node);
00251             break;
00252         }
00253     }
00254 
00255     G_UNLOCK (mutex);
00256     vis_runner_start_stop (playing, paused);
00257 }