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 "glib-compat.h"
00026 #include "misc.h"
00027 #include "output.h"
00028 #include "vis_runner.h"
00029
00030 #define INTERVAL 30
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
00062
00063
00064
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 }