Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
vis_runner.c
Go to the documentation of this file.
1 /*
2  * vis_runner.c
3  * Copyright 2009-2011 John Lindgren
4  *
5  * This file is part of Audacious.
6  *
7  * Audacious is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, version 3 of the License.
10  *
11  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * Audacious. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * The Audacious team does not consider modular code linking to Audacious or
19  * using our public API to be a derived work.
20  */
21 
22 #include <glib.h>
23 #include <pthread.h>
24 #include <string.h>
25 
26 #include "output.h"
27 #include "vis_runner.h"
28 #include "visualization.h"
29 
30 #define INTERVAL 30 /* milliseconds */
31 
32 typedef struct {
33  int time;
34  float * data;
35  int channels;
36 } VisNode;
37 
38 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
39 static bool_t enabled = FALSE;
42 static int current_frames;
43 static GQueue vis_list = G_QUEUE_INIT;
44 static int send_source = 0, clear_source = 0;
45 
46 static void vis_node_free (VisNode * node)
47 {
48  g_free (node->data);
49  g_free (node);
50 }
51 
52 static bool_t send_audio (void * unused)
53 {
54  pthread_mutex_lock (& mutex);
55 
56  if (! send_source)
57  {
58  pthread_mutex_unlock (& mutex);
59  return FALSE;
60  }
61 
62  int outputted = get_raw_output_time ();
63 
64  VisNode * vis_node = NULL;
65  VisNode * next;
66 
67  while ((next = g_queue_peek_head (& vis_list)))
68  {
69  /* If we are considering a node, stop searching and use it if it is the
70  * most recent (that is, the next one is in the future). Otherwise,
71  * consider the next node if it is not in the future by more than the
72  * length of an interval. */
73  if (next->time > outputted + (vis_node ? 0 : INTERVAL))
74  break;
75 
76  if (vis_node)
77  vis_node_free (vis_node);
78 
79  vis_node = g_queue_pop_head (& vis_list);
80  }
81 
82  pthread_mutex_unlock (& mutex);
83 
84  if (! vis_node)
85  return TRUE;
86 
87  vis_send_audio (vis_node->data, vis_node->channels);
88 
89  vis_node_free (vis_node);
90  return TRUE;
91 }
92 
93 static bool_t send_clear (void * unused)
94 {
95  pthread_mutex_lock (& mutex);
96  clear_source = 0;
97  pthread_mutex_unlock (& mutex);
98 
99  vis_send_clear ();
100 
101  return FALSE;
102 }
103 
104 static bool_t locked = FALSE;
105 
106 void vis_runner_lock (void)
107 {
108  pthread_mutex_lock (& mutex);
109  locked = TRUE;
110 }
111 
112 void vis_runner_unlock (void)
113 {
114  locked = FALSE;
115  pthread_mutex_unlock (& mutex);
116 }
117 
119 {
120  return locked;
121 }
122 
123 void vis_runner_flush (void)
124 {
125  if (current_node)
126  {
127  vis_node_free (current_node);
128  current_node = NULL;
129  }
130 
131  g_queue_foreach (& vis_list, (GFunc) vis_node_free, NULL);
132  g_queue_clear (& vis_list);
133 
134  if (! clear_source)
135  clear_source = g_timeout_add (0, send_clear, NULL);
136 }
137 
138 void vis_runner_start_stop (bool_t new_playing, bool_t new_paused)
139 {
140  playing = new_playing;
141  paused = new_paused;
142  active = playing && enabled;
143 
144  if (send_source)
145  {
146  g_source_remove (send_source);
147  send_source = 0;
148  }
149 
150  if (clear_source)
151  {
152  g_source_remove (clear_source);
153  clear_source = 0;
154  }
155 
156  if (! active)
157  vis_runner_flush ();
158  else if (! paused)
159  send_source = g_timeout_add (INTERVAL, send_audio, NULL);
160 }
161 
162 void vis_runner_pass_audio (int time, float * data, int samples, int
163  channels, int rate)
164 {
165  if (! active)
166  return;
167 
168  /* We can build a single node from multiple calls; we can also build
169  * multiple nodes from the same call. If current_node is present, it was
170  * partly built in the last call and needs to be finished. */
171 
172  if (current_node && current_node->channels != channels)
173  {
174  vis_node_free (current_node);
175  current_node = NULL;
176  }
177 
178  int at = 0;
179 
180  while (1)
181  {
182  if (! current_node)
183  {
184  int node_time = time;
185  VisNode * last;
186 
187  /* There is no partly-built node, so start a new one. Normally
188  * there will be nodes in the queue already; if so, we want to copy
189  * audio data from the signal starting at 30 milliseconds after the
190  * beginning of the most recent node. If there are no nodes in the
191  * queue, we are at the beginning of the song or had an underrun,
192  * and we want to copy the earliest audio data we have. */
193 
194  if ((last = g_queue_peek_tail (& vis_list)))
195  node_time = last->time + INTERVAL;
196 
197  at = channels * (int) ((int64_t) (node_time - time) * rate / 1000);
198 
199  if (at < 0)
200  at = 0;
201  if (at >= samples)
202  break;
203 
204  current_node = g_malloc (sizeof (VisNode));
205  current_node->time = node_time;
206  current_node->data = g_malloc (sizeof (float) * channels * 512);
207  current_node->channels = channels;
208  current_frames = 0;
209  }
210 
211  /* Copy as much data as we can, limited by how much we have and how much
212  * space is left in the node. If we cannot fill the node, we return and
213  * wait for more data to be passed in the next call. If we do fill the
214  * node, we loop and start building a new one. */
215 
216  int copy = MIN (samples - at, channels * (512 - current_frames));
217  memcpy (current_node->data + channels * current_frames, data + at, sizeof (float) * copy);
218  current_frames += copy / channels;
219 
220  if (current_frames < 512)
221  break;
222 
223  g_queue_push_tail (& vis_list, current_node);
224  current_node = NULL;
225  }
226 }
227 
228 static void time_offset_cb (VisNode * vis_node, void * offset)
229 {
230  vis_node->time += GPOINTER_TO_INT (offset);
231 }
232 
233 void vis_runner_time_offset (int offset)
234 {
235  if (current_node)
236  current_node->time += offset;
237 
238  g_queue_foreach (& vis_list, (GFunc) time_offset_cb, GINT_TO_POINTER (offset));
239 }
240 
242 {
243  pthread_mutex_lock (& mutex);
244  enabled = enable;
246  pthread_mutex_unlock (& mutex);
247 }