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