00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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
00061
00062
00063
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 }