1 /*
2 * vis_runner.c
3 * Copyright 2009-2012 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 "internal.h"
21
22 #include <assert.h>
23 #include <stdint.h>
24 #include <string.h>
25
26 #include "hook.h"
27 #include "list.h"
28 #include "mainloop.h"
29 #include "output.h"
30 #include "threads.h"
31
32 #define INTERVAL 33 /* milliseconds */
33 #define FRAMES_PER_NODE 512
34
35 struct VisNode : public ListNode
36 {
VisNodeVisNode37 VisNode(int channels, int time)
38 : channels(channels), time(time),
39 data(new float[channels * FRAMES_PER_NODE])
40 {
41 }
42
~VisNodeVisNode43 ~VisNode() { delete[] data; }
44
45 const int channels;
46 int time;
47 float * data;
48 };
49
50 static aud::mutex mutex;
51 static bool enabled = false;
52 static bool playing = false, paused = false;
53 static VisNode * current_node = nullptr;
54 static int current_frames;
55 static List<VisNode> vis_list;
56 static List<VisNode> vis_pool;
57 static QueuedFunc queued_clear;
58
send_audio(void *)59 static void send_audio(void *)
60 {
61 /* call before locking mutex to avoid deadlock */
62 int outputted = output_get_raw_time();
63
64 auto mh = mutex.take();
65
66 if (!enabled || !playing || paused)
67 return;
68
69 VisNode * node = nullptr;
70 VisNode * next;
71
72 while ((next = vis_list.head()))
73 {
74 /* If we are considering a node, stop searching and use it if it is the
75 * most recent (that is, the next one is in the future). Otherwise,
76 * consider the next node if it is not in the future by more than the
77 * length of an interval. */
78 if (next->time > outputted + (node ? 0 : INTERVAL))
79 break;
80
81 if (node)
82 vis_pool.prepend(node);
83
84 node = next;
85 vis_list.remove(node);
86 }
87
88 if (!node)
89 return;
90
91 mh.unlock();
92 vis_send_audio(node->data, node->channels);
93 mh.lock();
94
95 vis_pool.prepend(node);
96 }
97
flush(aud::mutex::holder &)98 static void flush(aud::mutex::holder &)
99 {
100 delete current_node;
101 current_node = nullptr;
102
103 vis_list.clear();
104 vis_pool.clear();
105
106 if (enabled)
107 queued_clear.queue(vis_send_clear);
108 }
109
vis_runner_flush()110 void vis_runner_flush()
111 {
112 auto mh = mutex.take();
113 flush(mh);
114 }
115
start_stop(aud::mutex::holder & mh,bool new_playing,bool new_paused)116 static void start_stop(aud::mutex::holder & mh, bool new_playing,
117 bool new_paused)
118 {
119 playing = new_playing;
120 paused = new_paused;
121
122 queued_clear.stop();
123
124 if (!enabled || !playing)
125 flush(mh);
126
127 if (enabled && playing && !paused)
128 timer_add(TimerRate::Hz30, send_audio);
129 else
130 timer_remove(TimerRate::Hz30, send_audio);
131 }
132
vis_runner_start_stop(bool new_playing,bool new_paused)133 void vis_runner_start_stop(bool new_playing, bool new_paused)
134 {
135 auto mh = mutex.take();
136 start_stop(mh, new_playing, new_paused);
137 }
138
vis_runner_pass_audio(int time,const Index<float> & data,int channels,int rate)139 void vis_runner_pass_audio(int time, const Index<float> & data, int channels,
140 int rate)
141 {
142 auto mh = mutex.take();
143
144 if (!enabled || !playing)
145 return;
146
147 /* We can build a single node from multiple calls; we can also build
148 * multiple nodes from the same call. If current_node is present, it was
149 * partly built in the last call and needs to be finished. */
150
151 int at = 0;
152
153 while (1)
154 {
155 if (current_node)
156 assert(current_node->channels == channels);
157 else
158 {
159 int node_time = time;
160
161 /* There is no partly-built node, so start a new one. Normally
162 * there will be nodes in the queue already; if so, we want to copy
163 * audio data from the signal starting at 30 milliseconds after the
164 * beginning of the most recent node. If there are no nodes in the
165 * queue, we are at the beginning of the song or had an underrun,
166 * and we want to copy the earliest audio data we have. */
167
168 VisNode * tail = vis_list.tail();
169 if (tail)
170 node_time = tail->time + INTERVAL;
171
172 at = channels * (int)((int64_t)(node_time - time) * rate / 1000);
173
174 if (at < 0)
175 at = 0;
176 if (at >= data.len())
177 break;
178
179 current_node = vis_pool.head();
180
181 if (current_node)
182 {
183 assert(current_node->channels == channels);
184 vis_pool.remove(current_node);
185 current_node->time = node_time;
186 }
187 else
188 current_node = new VisNode(channels, node_time);
189
190 current_frames = 0;
191 }
192
193 /* Copy as much data as we can, limited by how much we have and how much
194 * space is left in the node. If we cannot fill the node, we return and
195 * wait for more data to be passed in the next call. If we do fill the
196 * node, we loop and start building a new one. */
197
198 int copy = aud::min(data.len() - at,
199 channels * (FRAMES_PER_NODE - current_frames));
200 memcpy(current_node->data + channels * current_frames, &data[at],
201 sizeof(float) * copy);
202 current_frames += copy / channels;
203
204 if (current_frames < FRAMES_PER_NODE)
205 break;
206
207 vis_list.append(current_node);
208 current_node = nullptr;
209 }
210 }
211
vis_runner_enable(bool enable)212 void vis_runner_enable(bool enable)
213 {
214 auto mh = mutex.take();
215 enabled = enable;
216 start_stop(mh, playing, paused);
217 }
218