1 /*
2 * effect.c
3 * Copyright 2010-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 "drct.h"
23 #include "list.h"
24 #include "plugin.h"
25 #include "plugins.h"
26 #include "runtime.h"
27 #include "threads.h"
28
29 struct Effect : public ListNode
30 {
31 PluginHandle * plugin;
32 int position;
33 EffectPlugin * header;
34 int channels_returned, rate_returned;
35 bool remove_flag;
36 };
37
38 static aud::mutex mutex;
39 static List<Effect> effects;
40 static int input_channels, input_rate;
41
effect_start(int & channels,int & rate)42 void effect_start(int & channels, int & rate)
43 {
44 auto mh = mutex.take();
45
46 AUDDBG("Starting effects.\n");
47
48 effects.clear();
49
50 input_channels = channels;
51 input_rate = rate;
52
53 auto & list = aud_plugin_list(PluginType::Effect);
54
55 for (int i = 0; i < list.len(); i++)
56 {
57 PluginHandle * plugin = list[i];
58 if (!aud_plugin_get_enabled(plugin))
59 continue;
60
61 AUDINFO("Starting %s at %d channels, %d Hz.\n",
62 aud_plugin_get_name(plugin), channels, rate);
63
64 EffectPlugin * header = (EffectPlugin *)aud_plugin_get_header(plugin);
65 if (!header)
66 continue;
67
68 header->start(channels, rate);
69
70 Effect * effect = new Effect();
71 effect->plugin = plugin;
72 effect->position = i;
73 effect->header = header;
74 effect->channels_returned = channels;
75 effect->rate_returned = rate;
76
77 effects.append(effect);
78 }
79 }
80
effect_process(Index<float> & data)81 Index<float> & effect_process(Index<float> & data)
82 {
83 auto mh = mutex.take();
84 Index<float> * cur = &data;
85
86 Effect * e = effects.head();
87 while (e)
88 {
89 Effect * next = effects.next(e);
90
91 if (e->remove_flag)
92 {
93 cur = &e->header->finish(*cur, false);
94
95 // simulate end-of-playlist call
96 // first save the current data
97 Index<float> save = std::move(*cur);
98 cur = &e->header->finish(*cur, true);
99
100 // combine the saved and new data
101 save.move_from(*cur, 0, -1, -1, true, true);
102 *cur = std::move(save);
103
104 effects.remove(e);
105 delete e;
106 }
107 else
108 cur = &e->header->process(*cur);
109
110 e = next;
111 }
112
113 return *cur;
114 }
115
effect_flush(bool force)116 bool effect_flush(bool force)
117 {
118 auto mh = mutex.take();
119 bool flushed = true;
120
121 for (Effect * e = effects.head(); e; e = effects.next(e))
122 {
123 if (!e->header->flush(force) && !force)
124 {
125 flushed = false;
126 break;
127 }
128 }
129
130 return flushed;
131 }
132
effect_finish(Index<float> & data,bool end_of_playlist)133 Index<float> & effect_finish(Index<float> & data, bool end_of_playlist)
134 {
135 auto mh = mutex.take();
136 Index<float> * cur = &data;
137
138 for (Effect * e = effects.head(); e; e = effects.next(e))
139 cur = &e->header->finish(*cur, end_of_playlist);
140
141 return *cur;
142 }
143
effect_adjust_delay(int delay)144 int effect_adjust_delay(int delay)
145 {
146 auto mh = mutex.take();
147
148 for (Effect * e = effects.tail(); e; e = effects.prev(e))
149 delay = e->header->adjust_delay(delay);
150
151 return delay;
152 }
153
effect_insert(aud::mutex::holder &,PluginHandle * plugin,EffectPlugin * header)154 static void effect_insert(aud::mutex::holder &, PluginHandle * plugin,
155 EffectPlugin * header)
156 {
157 int position = aud_plugin_list(PluginType::Effect).find(plugin);
158
159 Effect * prev = nullptr;
160
161 for (Effect * e = effects.head(); e; e = effects.next(e))
162 {
163 if (e->plugin == plugin)
164 {
165 e->remove_flag = false;
166 return;
167 }
168
169 if (e->position > position)
170 break;
171
172 prev = e;
173 }
174
175 AUDDBG("Adding %s without reset.\n", aud_plugin_get_name(plugin));
176
177 int channels, rate;
178 if (prev)
179 {
180 AUDDBG("Adding %s after %s.\n", aud_plugin_get_name(plugin),
181 aud_plugin_get_name(prev->plugin));
182 channels = prev->channels_returned;
183 rate = prev->rate_returned;
184 }
185 else
186 {
187 AUDDBG("Adding %s as first effect.\n", aud_plugin_get_name(plugin));
188 channels = input_channels;
189 rate = input_rate;
190 }
191
192 AUDINFO("Starting %s at %d channels, %d Hz.\n", aud_plugin_get_name(plugin),
193 channels, rate);
194 header->start(channels, rate);
195
196 Effect * effect = new Effect();
197 effect->plugin = plugin;
198 effect->position = position;
199 effect->header = header;
200 effect->channels_returned = channels;
201 effect->rate_returned = rate;
202
203 effects.insert_after(prev, effect);
204 }
205
effect_remove(aud::mutex::holder &,PluginHandle * plugin)206 static void effect_remove(aud::mutex::holder &, PluginHandle * plugin)
207 {
208 for (Effect * e = effects.head(); e; e = effects.next(e))
209 {
210 if (e->plugin == plugin)
211 {
212 AUDDBG("Removing %s without reset.\n", aud_plugin_get_name(plugin));
213 e->remove_flag = true;
214 return;
215 }
216 }
217 }
218
effect_enable(PluginHandle * plugin,EffectPlugin * ep,bool enable)219 static void effect_enable(PluginHandle * plugin, EffectPlugin * ep, bool enable)
220 {
221 if (ep->preserves_format)
222 {
223 auto mh = mutex.take();
224
225 if (enable)
226 effect_insert(mh, plugin, ep);
227 else
228 effect_remove(mh, plugin);
229 }
230 else
231 {
232 AUDDBG("Reset to add/remove %s.\n", aud_plugin_get_name(plugin));
233 aud_output_reset(OutputReset::EffectsOnly);
234 }
235 }
236
effect_plugin_start(PluginHandle * plugin)237 bool effect_plugin_start(PluginHandle * plugin)
238 {
239 if (aud_drct_get_playing())
240 {
241 EffectPlugin * ep = (EffectPlugin *)aud_plugin_get_header(plugin);
242 if (!ep)
243 return false;
244
245 effect_enable(plugin, ep, true);
246 }
247
248 return true;
249 }
250
effect_plugin_stop(PluginHandle * plugin)251 void effect_plugin_stop(PluginHandle * plugin)
252 {
253 if (aud_drct_get_playing())
254 {
255 EffectPlugin * ep = (EffectPlugin *)aud_plugin_get_header(plugin);
256 if (!ep)
257 return;
258
259 effect_enable(plugin, ep, false);
260 }
261 }
262