Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * vis_runner.c 00003 * Copyright 2009-2011 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 <pthread.h> 00024 #include <string.h> 00025 00026 #include "output.h" 00027 #include "vis_runner.h" 00028 #include "visualization.h" 00029 00030 #define INTERVAL 30 /* milliseconds */ 00031 00032 typedef struct { 00033 int time; 00034 float * data; 00035 int channels; 00036 } VisNode; 00037 00038 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 00039 static bool_t enabled = FALSE; 00040 static bool_t playing = FALSE, paused = FALSE, active = FALSE; 00041 static VisNode * current_node = NULL; 00042 static int current_frames; 00043 static GQueue vis_list = G_QUEUE_INIT; 00044 static int send_source = 0, clear_source = 0; 00045 00046 static void vis_node_free (VisNode * node) 00047 { 00048 g_free (node->data); 00049 g_free (node); 00050 } 00051 00052 static bool_t send_audio (void * unused) 00053 { 00054 pthread_mutex_lock (& mutex); 00055 00056 if (! send_source) 00057 { 00058 pthread_mutex_unlock (& mutex); 00059 return FALSE; 00060 } 00061 00062 int outputted = get_raw_output_time (); 00063 00064 VisNode * vis_node = NULL; 00065 VisNode * next; 00066 00067 while ((next = g_queue_peek_head (& vis_list))) 00068 { 00069 /* If we are considering a node, stop searching and use it if it is the 00070 * most recent (that is, the next one is in the future). Otherwise, 00071 * consider the next node if it is not in the future by more than the 00072 * length of an interval. */ 00073 if (next->time > outputted + (vis_node ? 0 : INTERVAL)) 00074 break; 00075 00076 if (vis_node) 00077 vis_node_free (vis_node); 00078 00079 vis_node = g_queue_pop_head (& vis_list); 00080 } 00081 00082 pthread_mutex_unlock (& mutex); 00083 00084 if (! vis_node) 00085 return TRUE; 00086 00087 vis_send_audio (vis_node->data, vis_node->channels); 00088 00089 vis_node_free (vis_node); 00090 return TRUE; 00091 } 00092 00093 static bool_t send_clear (void * unused) 00094 { 00095 pthread_mutex_lock (& mutex); 00096 clear_source = 0; 00097 pthread_mutex_unlock (& mutex); 00098 00099 vis_send_clear (); 00100 00101 return FALSE; 00102 } 00103 00104 static bool_t locked = FALSE; 00105 00106 void vis_runner_lock (void) 00107 { 00108 pthread_mutex_lock (& mutex); 00109 locked = TRUE; 00110 } 00111 00112 void vis_runner_unlock (void) 00113 { 00114 locked = FALSE; 00115 pthread_mutex_unlock (& mutex); 00116 } 00117 00118 bool_t vis_runner_locked (void) 00119 { 00120 return locked; 00121 } 00122 00123 void vis_runner_flush (void) 00124 { 00125 if (current_node) 00126 { 00127 vis_node_free (current_node); 00128 current_node = NULL; 00129 } 00130 00131 g_queue_foreach (& vis_list, (GFunc) vis_node_free, NULL); 00132 g_queue_clear (& vis_list); 00133 00134 if (! clear_source) 00135 clear_source = g_timeout_add (0, send_clear, NULL); 00136 } 00137 00138 void vis_runner_start_stop (bool_t new_playing, bool_t new_paused) 00139 { 00140 playing = new_playing; 00141 paused = new_paused; 00142 active = playing && enabled; 00143 00144 if (send_source) 00145 { 00146 g_source_remove (send_source); 00147 send_source = 0; 00148 } 00149 00150 if (clear_source) 00151 { 00152 g_source_remove (clear_source); 00153 clear_source = 0; 00154 } 00155 00156 if (! active) 00157 vis_runner_flush (); 00158 else if (! paused) 00159 send_source = g_timeout_add (INTERVAL, send_audio, NULL); 00160 } 00161 00162 void vis_runner_pass_audio (int time, float * data, int samples, int 00163 channels, int rate) 00164 { 00165 if (! active) 00166 return; 00167 00168 /* We can build a single node from multiple calls; we can also build 00169 * multiple nodes from the same call. If current_node is present, it was 00170 * partly built in the last call and needs to be finished. */ 00171 00172 if (current_node && current_node->channels != channels) 00173 { 00174 vis_node_free (current_node); 00175 current_node = NULL; 00176 } 00177 00178 int at = 0; 00179 00180 while (1) 00181 { 00182 if (! current_node) 00183 { 00184 int node_time = time; 00185 VisNode * last; 00186 00187 /* There is no partly-built node, so start a new one. Normally 00188 * there will be nodes in the queue already; if so, we want to copy 00189 * audio data from the signal starting at 30 milliseconds after the 00190 * beginning of the most recent node. If there are no nodes in the 00191 * queue, we are at the beginning of the song or had an underrun, 00192 * and we want to copy the earliest audio data we have. */ 00193 00194 if ((last = g_queue_peek_tail (& vis_list))) 00195 node_time = last->time + INTERVAL; 00196 00197 at = channels * (int) ((int64_t) (node_time - time) * rate / 1000); 00198 00199 if (at < 0) 00200 at = 0; 00201 if (at >= samples) 00202 break; 00203 00204 current_node = g_malloc (sizeof (VisNode)); 00205 current_node->time = node_time; 00206 current_node->data = g_malloc (sizeof (float) * channels * 512); 00207 current_node->channels = channels; 00208 current_frames = 0; 00209 } 00210 00211 /* Copy as much data as we can, limited by how much we have and how much 00212 * space is left in the node. If we cannot fill the node, we return and 00213 * wait for more data to be passed in the next call. If we do fill the 00214 * node, we loop and start building a new one. */ 00215 00216 int copy = MIN (samples - at, channels * (512 - current_frames)); 00217 memcpy (current_node->data + channels * current_frames, data + at, sizeof (float) * copy); 00218 current_frames += copy / channels; 00219 00220 if (current_frames < 512) 00221 break; 00222 00223 g_queue_push_tail (& vis_list, current_node); 00224 current_node = NULL; 00225 } 00226 } 00227 00228 static void time_offset_cb (VisNode * vis_node, void * offset) 00229 { 00230 vis_node->time += GPOINTER_TO_INT (offset); 00231 } 00232 00233 void vis_runner_time_offset (int offset) 00234 { 00235 if (current_node) 00236 current_node->time += offset; 00237 00238 g_queue_foreach (& vis_list, (GFunc) time_offset_cb, GINT_TO_POINTER (offset)); 00239 } 00240 00241 void vis_runner_enable (bool_t enable) 00242 { 00243 pthread_mutex_lock (& mutex); 00244 enabled = enable; 00245 vis_runner_start_stop (playing, paused); 00246 pthread_mutex_unlock (& mutex); 00247 }