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