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