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 "compatibility.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 /* We need raw time, not changed for effects and gapless playback. */ 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 /* If we are considering a node, stop searching and use it if it is the 00063 * most recent (that is, the next one is in the future). Otherwise, 00064 * consider the next node if it is not in the future by more than the 00065 * length of an interval. */ 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 }