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 "glib-compat.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     gint outputted = get_raw_output_time ();
00055 
00056     VisNode * vis_node = NULL;
00057     VisNode * next;
00058 
00059     while ((next = g_queue_peek_head (& vis_list)))
00060     {
00061         /* If we are considering a node, stop searching and use it if it is the
00062          * most recent (that is, the next one is in the future).  Otherwise,
00063          * consider the next node if it is not in the future by more than the
00064          * length of an interval. */
00065         if (next->time > outputted + (vis_node ? 0 : INTERVAL))
00066             break;
00067 
00068         g_free (vis_node);
00069         vis_node = g_queue_pop_head (& vis_list);
00070     }
00071 
00072     G_UNLOCK (mutex);
00073 
00074     if (! vis_node)
00075         return TRUE;
00076 
00077     for (GList * node = hooks; node; node = node->next)
00078     {
00079         VisHookItem * item = node->data;
00080         item->func (vis_node, item->user);
00081     }
00082 
00083     g_free (vis_node);
00084     return TRUE;
00085 }
00086 
00087 static gboolean send_clear (void * unused)
00088 {
00089     G_LOCK (mutex);
00090     clear_source = 0;
00091     G_UNLOCK (mutex);
00092 
00093     hook_call ("visualization clear", NULL);
00094     return FALSE;
00095 }
00096 
00097 static gboolean locked = FALSE;
00098 
00099 void vis_runner_lock (void)
00100 {
00101     G_LOCK (mutex);
00102     locked = TRUE;
00103 }
00104 
00105 void vis_runner_unlock (void)
00106 {
00107     locked = FALSE;
00108     G_UNLOCK (mutex);
00109 }
00110 
00111 gboolean vis_runner_locked (void)
00112 {
00113     return locked;
00114 }
00115 
00116 void vis_runner_flush (void)
00117 {
00118     g_free (current_node);
00119     current_node = NULL;
00120     g_queue_foreach (& vis_list, (GFunc) g_free, NULL);
00121     g_queue_clear (& vis_list);
00122 
00123     clear_source = g_timeout_add (0, send_clear, NULL);
00124 }
00125 
00126 void vis_runner_start_stop (gboolean new_playing, gboolean new_paused)
00127 {
00128     playing = new_playing;
00129     paused = new_paused;
00130     active = playing && hooks;
00131 
00132     if (send_source)
00133     {
00134         g_source_remove (send_source);
00135         send_source = 0;
00136     }
00137 
00138     if (clear_source)
00139     {
00140         g_source_remove (clear_source);
00141         clear_source = 0;
00142     }
00143 
00144     if (! active)
00145         vis_runner_flush ();
00146     else if (! paused)
00147         send_source = g_timeout_add (INTERVAL, send_audio, NULL);
00148 }
00149 
00150 void vis_runner_pass_audio (gint time, gfloat * data, gint samples, gint
00151  channels, gint rate)
00152 {
00153     if (! active)
00154         return;
00155 
00156     if (current_node && current_node->nch != MIN (channels, 2))
00157     {
00158         g_free (current_node);
00159         current_node = NULL;
00160     }
00161 
00162     gint at = 0;
00163 
00164     while (1)
00165     {
00166         if (! current_node)
00167         {
00168             gint node_time = time;
00169             VisNode * last;
00170 
00171             if ((last = g_queue_peek_tail (& vis_list)))
00172                 node_time = last->time + INTERVAL;
00173 
00174             at = channels * (gint) ((gint64) (node_time - time) * rate / 1000);
00175 
00176             if (at < 0)
00177                 at = 0;
00178             if (at >= samples)
00179                 break;
00180 
00181             current_node = g_malloc (sizeof (VisNode));
00182             current_node->time = node_time;
00183             current_node->nch = MIN (channels, 2);
00184             current_node->length = 0;
00185         }
00186 
00187         gint copy = MIN (samples - at, channels * (512 - current_node->length));
00188 
00189         for (gint channel = 0; channel < current_node->nch; channel ++)
00190         {
00191             gfloat * from = data + at + channel;
00192             gfloat * end = from + copy;
00193             gint16 * to = current_node->data[channel] + current_node->length;
00194 
00195             while (from < end)
00196             {
00197                 register gfloat temp = * from;
00198                 * to ++ = CLAMP (temp, -1, 1) * 32767;
00199                 from += channels;
00200             }
00201         }
00202 
00203         current_node->length += copy / channels;
00204 
00205         if (current_node->length < 512)
00206             break;
00207 
00208         g_queue_push_tail (& vis_list, current_node);
00209         current_node = NULL;
00210     }
00211 }
00212 
00213 static void time_offset_cb (VisNode * vis_node, void * offset)
00214 {
00215     vis_node->time += GPOINTER_TO_INT (offset);
00216 }
00217 
00218 void vis_runner_time_offset (gint offset)
00219 {
00220     if (current_node)
00221         current_node->time += offset;
00222 
00223     g_queue_foreach (& vis_list, (GFunc) time_offset_cb, GINT_TO_POINTER (offset));
00224 }
00225 
00226 void vis_runner_add_hook (VisHookFunc func, void * user)
00227 {
00228     G_LOCK (mutex);
00229 
00230     VisHookItem * item = g_malloc (sizeof (VisHookItem));
00231     item->func = func;
00232     item->user = user;
00233     hooks = g_list_prepend (hooks, item);
00234 
00235     vis_runner_start_stop (playing, paused);
00236     G_UNLOCK (mutex);
00237 }
00238 
00239 void vis_runner_remove_hook (VisHookFunc func)
00240 {
00241     G_LOCK (mutex);
00242 
00243     for (GList * node = hooks; node; node = node->next)
00244     {
00245         if (((VisHookItem *) node->data)->func == func)
00246         {
00247             g_free (node->data);
00248             hooks = g_list_delete_link (hooks, node);
00249             break;
00250         }
00251     }
00252 
00253     vis_runner_start_stop (playing, paused);
00254     G_UNLOCK (mutex);
00255 }