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  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <glib.h>
21 #include <pthread.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #include "output.h"
26 #include "vis_runner.h"
27 #include "visualization.h"
28 
29 #define INTERVAL 30 /* milliseconds */
30 
31 typedef struct {
32  int time;
33  float * data;
34  int channels;
35 } VisNode;
36 
37 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
38 static bool_t enabled = FALSE;
41 static int current_frames;
42 static GQueue vis_list = G_QUEUE_INIT;
43 static int send_source = 0, clear_source = 0;
44 
45 static void vis_node_free (VisNode * node)
46 {
47  g_free (node->data);
48  g_free (node);
49 }
50 
51 static bool_t send_audio (void * unused)
52 {
53  /* call before locking mutex to avoid deadlock */
54  int outputted = output_get_raw_time ();
55 
56  pthread_mutex_lock (& mutex);
57 
58  if (! send_source)
59  {
60  pthread_mutex_unlock (& mutex);
61  return FALSE;
62  }
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 void flush (void)
105 {
106  if (current_node)
107  {
108  vis_node_free (current_node);
109  current_node = NULL;
110  }
111 
112  g_queue_foreach (& vis_list, (GFunc) vis_node_free, NULL);
113  g_queue_clear (& vis_list);
114 
115  if (! clear_source)
116  clear_source = g_timeout_add (0, send_clear, NULL);
117 }
118 
119 void vis_runner_flush (void)
120 {
121  pthread_mutex_lock (& mutex);
122  flush ();
123  pthread_mutex_unlock (& mutex);
124 }
125 
126 static void start_stop (bool_t new_playing, bool_t new_paused)
127 {
128  playing = new_playing;
129  paused = new_paused;
130  active = playing && enabled;
131 
132  if (send_source)
133  {
134  g_source_remove (send_source);
135  send_source = 0;
136  }
137 
138  if (clear_source)
139  {
140  g_source_remove (clear_source);
141  clear_source = 0;
142  }
143 
144  if (! active)
145  flush ();
146  else if (! paused)
147  send_source = g_timeout_add (INTERVAL, send_audio, NULL);
148 }
149 
150 void vis_runner_start_stop (bool_t new_playing, bool_t new_paused)
151 {
152  pthread_mutex_lock (& mutex);
153  start_stop (new_playing, new_paused);
154  pthread_mutex_unlock (& mutex);
155 }
156 
157 void vis_runner_pass_audio (int time, float * data, int samples, int
158  channels, int rate)
159 {
160  pthread_mutex_lock (& mutex);
161 
162  if (! active)
163  goto UNLOCK;
164 
165  /* We can build a single node from multiple calls; we can also build
166  * multiple nodes from the same call. If current_node is present, it was
167  * partly built in the last call and needs to be finished. */
168 
169  if (current_node && current_node->channels != channels)
170  {
171  vis_node_free (current_node);
172  current_node = NULL;
173  }
174 
175  int at = 0;
176 
177  while (1)
178  {
179  if (! current_node)
180  {
181  int node_time = time;
182  VisNode * last;
183 
184  /* There is no partly-built node, so start a new one. Normally
185  * there will be nodes in the queue already; if so, we want to copy
186  * audio data from the signal starting at 30 milliseconds after the
187  * beginning of the most recent node. If there are no nodes in the
188  * queue, we are at the beginning of the song or had an underrun,
189  * and we want to copy the earliest audio data we have. */
190 
191  if ((last = g_queue_peek_tail (& vis_list)))
192  node_time = last->time + INTERVAL;
193 
194  at = channels * (int) ((int64_t) (node_time - time) * rate / 1000);
195 
196  if (at < 0)
197  at = 0;
198  if (at >= samples)
199  break;
200 
201  current_node = g_malloc (sizeof (VisNode));
202  current_node->time = node_time;
203  current_node->data = g_malloc (sizeof (float) * channels * 512);
204  current_node->channels = channels;
205  current_frames = 0;
206  }
207 
208  /* Copy as much data as we can, limited by how much we have and how much
209  * space is left in the node. If we cannot fill the node, we return and
210  * wait for more data to be passed in the next call. If we do fill the
211  * node, we loop and start building a new one. */
212 
213  int copy = MIN (samples - at, channels * (512 - current_frames));
214  memcpy (current_node->data + channels * current_frames, data + at, sizeof (float) * copy);
215  current_frames += copy / channels;
216 
217  if (current_frames < 512)
218  break;
219 
220  g_queue_push_tail (& vis_list, current_node);
221  current_node = NULL;
222  }
223 
224 UNLOCK:
225  pthread_mutex_unlock (& mutex);
226 }
227 
229 {
230  pthread_mutex_lock (& mutex);
231  enabled = enable;
233  pthread_mutex_unlock (& mutex);
234 }
static int channels
Definition: equalizer.c:54
void vis_send_clear(void)
Definition: visualization.c:68
static void start_stop(bool_t new_playing, bool_t new_paused)
Definition: vis_runner.c:126
#define MIN(a, b)
Definition: core.h:40
const char PluginHandle decoder const char PluginHandle decoder const char PluginHandle decoder void const PreferencesWidget int
Definition: misc-api.h:103
static int current_frames
Definition: vis_runner.c:41
int time
Definition: vis_runner.c:32
static void flush(void)
Definition: vis_runner.c:104
static int send_source
Definition: vis_runner.c:43
static bool_t send_audio(void *unused)
Definition: vis_runner.c:51
static bool_t paused
Definition: vis_runner.c:39
#define FALSE
Definition: core.h:35
Index Index bool_t
Definition: playlist-api.h:122
static bool_t enabled
Definition: vis_runner.c:38
static bool_t send_clear(void *unused)
Definition: vis_runner.c:93
static GQueue vis_list
Definition: vis_runner.c:42
static bool_t playing
Definition: vis_runner.c:39
int channels
Definition: vis_runner.c:34
void vis_runner_start_stop(bool_t new_playing, bool_t new_paused)
Definition: vis_runner.c:150
#define NULL
Definition: core.h:27
void vis_runner_pass_audio(int time, float *data, int samples, int channels, int rate)
Definition: vis_runner.c:157
#define INTERVAL
Definition: vis_runner.c:29
int output_get_raw_time(void)
Definition: output.c:467
#define TRUE
Definition: core.h:37
static bool_t active
Definition: vis_runner.c:39
at
Definition: playlist-api.h:122
static int clear_source
Definition: vis_runner.c:43
void vis_runner_flush(void)
Definition: vis_runner.c:119
static int rate
Definition: equalizer.c:54
static VisNode * current_node
Definition: vis_runner.c:40
float * data
Definition: vis_runner.c:33
static pthread_mutex_t mutex
Definition: vis_runner.c:37
void vis_send_audio(const float *data, int channels)
Definition: visualization.c:92
void vis_runner_enable(bool_t enable)
Definition: vis_runner.c:228
static void vis_node_free(VisNode *node)
Definition: vis_runner.c:45