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-2011 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 <pthread.h>
00024 #include <string.h>
00025 
00026 #include "output.h"
00027 #include "vis_runner.h"
00028 #include "visualization.h"
00029 
00030 #define INTERVAL 30 /* milliseconds */
00031 
00032 typedef struct {
00033     int time;
00034     float * data;
00035     int channels;
00036 } VisNode;
00037 
00038 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
00039 static bool_t enabled = FALSE;
00040 static bool_t playing = FALSE, paused = FALSE, active = FALSE;
00041 static VisNode * current_node = NULL;
00042 static int current_frames;
00043 static GQueue vis_list = G_QUEUE_INIT;
00044 static int send_source = 0, clear_source = 0;
00045 
00046 static void vis_node_free (VisNode * node)
00047 {
00048     g_free (node->data);
00049     g_free (node);
00050 }
00051 
00052 static bool_t send_audio (void * unused)
00053 {
00054     pthread_mutex_lock (& mutex);
00055 
00056     if (! send_source)
00057     {
00058         pthread_mutex_unlock (& mutex);
00059         return FALSE;
00060     }
00061 
00062     int outputted = get_raw_output_time ();
00063 
00064     VisNode * vis_node = NULL;
00065     VisNode * next;
00066 
00067     while ((next = g_queue_peek_head (& vis_list)))
00068     {
00069         /* If we are considering a node, stop searching and use it if it is the
00070          * most recent (that is, the next one is in the future).  Otherwise,
00071          * consider the next node if it is not in the future by more than the
00072          * length of an interval. */
00073         if (next->time > outputted + (vis_node ? 0 : INTERVAL))
00074             break;
00075 
00076         if (vis_node)
00077             vis_node_free (vis_node);
00078 
00079         vis_node = g_queue_pop_head (& vis_list);
00080     }
00081 
00082     pthread_mutex_unlock (& mutex);
00083 
00084     if (! vis_node)
00085         return TRUE;
00086 
00087     vis_send_audio (vis_node->data, vis_node->channels);
00088 
00089     vis_node_free (vis_node);
00090     return TRUE;
00091 }
00092 
00093 static bool_t send_clear (void * unused)
00094 {
00095     pthread_mutex_lock (& mutex);
00096     clear_source = 0;
00097     pthread_mutex_unlock (& mutex);
00098 
00099     vis_send_clear ();
00100 
00101     return FALSE;
00102 }
00103 
00104 static bool_t locked = FALSE;
00105 
00106 void vis_runner_lock (void)
00107 {
00108     pthread_mutex_lock (& mutex);
00109     locked = TRUE;
00110 }
00111 
00112 void vis_runner_unlock (void)
00113 {
00114     locked = FALSE;
00115     pthread_mutex_unlock (& mutex);
00116 }
00117 
00118 bool_t vis_runner_locked (void)
00119 {
00120     return locked;
00121 }
00122 
00123 void vis_runner_flush (void)
00124 {
00125     if (current_node)
00126     {
00127         vis_node_free (current_node);
00128         current_node = NULL;
00129     }
00130 
00131     g_queue_foreach (& vis_list, (GFunc) vis_node_free, NULL);
00132     g_queue_clear (& vis_list);
00133 
00134     if (! clear_source)
00135         clear_source = g_timeout_add (0, send_clear, NULL);
00136 }
00137 
00138 void vis_runner_start_stop (bool_t new_playing, bool_t new_paused)
00139 {
00140     playing = new_playing;
00141     paused = new_paused;
00142     active = playing && enabled;
00143 
00144     if (send_source)
00145     {
00146         g_source_remove (send_source);
00147         send_source = 0;
00148     }
00149 
00150     if (clear_source)
00151     {
00152         g_source_remove (clear_source);
00153         clear_source = 0;
00154     }
00155 
00156     if (! active)
00157         vis_runner_flush ();
00158     else if (! paused)
00159         send_source = g_timeout_add (INTERVAL, send_audio, NULL);
00160 }
00161 
00162 void vis_runner_pass_audio (int time, float * data, int samples, int
00163  channels, int rate)
00164 {
00165     if (! active)
00166         return;
00167 
00168     /* We can build a single node from multiple calls; we can also build
00169      * multiple nodes from the same call.  If current_node is present, it was
00170      * partly built in the last call and needs to be finished. */
00171 
00172     if (current_node && current_node->channels != channels)
00173     {
00174         vis_node_free (current_node);
00175         current_node = NULL;
00176     }
00177 
00178     int at = 0;
00179 
00180     while (1)
00181     {
00182         if (! current_node)
00183         {
00184             int node_time = time;
00185             VisNode * last;
00186 
00187             /* There is no partly-built node, so start a new one.  Normally
00188              * there will be nodes in the queue already; if so, we want to copy
00189              * audio data from the signal starting at 30 milliseconds after the
00190              * beginning of the most recent node.  If there are no nodes in the
00191              * queue, we are at the beginning of the song or had an underrun,
00192              * and we want to copy the earliest audio data we have. */
00193 
00194             if ((last = g_queue_peek_tail (& vis_list)))
00195                 node_time = last->time + INTERVAL;
00196 
00197             at = channels * (int) ((int64_t) (node_time - time) * rate / 1000);
00198 
00199             if (at < 0)
00200                 at = 0;
00201             if (at >= samples)
00202                 break;
00203 
00204             current_node = g_malloc (sizeof (VisNode));
00205             current_node->time = node_time;
00206             current_node->data = g_malloc (sizeof (float) * channels * 512);
00207             current_node->channels = channels;
00208             current_frames = 0;
00209         }
00210 
00211         /* Copy as much data as we can, limited by how much we have and how much
00212          * space is left in the node.  If we cannot fill the node, we return and
00213          * wait for more data to be passed in the next call.  If we do fill the
00214          * node, we loop and start building a new one. */
00215 
00216         int copy = MIN (samples - at, channels * (512 - current_frames));
00217         memcpy (current_node->data + channels * current_frames, data + at, sizeof (float) * copy);
00218         current_frames += copy / channels;
00219 
00220         if (current_frames < 512)
00221             break;
00222 
00223         g_queue_push_tail (& vis_list, current_node);
00224         current_node = NULL;
00225     }
00226 }
00227 
00228 static void time_offset_cb (VisNode * vis_node, void * offset)
00229 {
00230     vis_node->time += GPOINTER_TO_INT (offset);
00231 }
00232 
00233 void vis_runner_time_offset (int offset)
00234 {
00235     if (current_node)
00236         current_node->time += offset;
00237 
00238     g_queue_foreach (& vis_list, (GFunc) time_offset_cb, GINT_TO_POINTER (offset));
00239 }
00240 
00241 void vis_runner_enable (bool_t enable)
00242 {
00243     pthread_mutex_lock (& mutex);
00244     enabled = enable;
00245     vis_runner_start_stop (playing, paused);
00246     pthread_mutex_unlock (& mutex);
00247 }