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