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