Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
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 "glib-compat.h" 00026 #include "misc.h" 00027 #include "output.h" 00028 #include "vis_runner.h" 00029 00030 #define INTERVAL 30 /* milliseconds */ 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 /* If we are considering a node, stop searching and use it if it is the 00062 * most recent (that is, the next one is in the future). Otherwise, 00063 * consider the next node if it is not in the future by more than the 00064 * length of an interval. */ 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 }