1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3 * Copyright (C) 2011 Pete Shorthose
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * --------------------------------------------------------------------------
19 */
20
21 #pragma once
22
23 namespace gx_engine {
24
25 /****************************************************************
26 ** class ModuleSelector
27 */
28
29 class ModuleSelector {
30 protected:
31 EngineControl& seq;
32 public:
ModuleSelector(EngineControl & seq_)33 ModuleSelector(EngineControl& seq_)
34 : seq(seq_) {}
~ModuleSelector()35 virtual ~ModuleSelector() {}
36 virtual void set_module() = 0;
37 };
38
39
40 /****************************************************************
41 ** class ProcessingChainBase
42 ** members and methods accessed by the rt thread are marked RT
43 */
44
45 class ProcessingChainBase {
46 public:
47 enum RampMode { ramp_mode_down_dead, ramp_mode_down, ramp_mode_up_dead, ramp_mode_up, ramp_mode_off };
48 private:
49 sem_t sync_sem; // RT
50 list<Plugin*> to_release;
51 int ramp_value; // RT
52 int ramp_mode; // RT should be RampMode, but gcc 4.5 doesn't accept it for g_atomic_int_compare_and_exchange
53 volatile bool stopped;
54 protected:
55 int steps_up; // RT; >= 1
56 int steps_up_dead; // RT; >= 0
57 int steps_down; // RT; >= 1
58 list<Plugin*> modules;
set_ramp_value(int n)59 inline void set_ramp_value(int n) { gx_system::atomic_set(&ramp_value, n); } // RT
set_ramp_mode(RampMode n)60 inline void set_ramp_mode(RampMode n) { gx_system::atomic_set(&ramp_mode, n); } // RT
61 void try_set_ramp_mode(RampMode oldmode, RampMode newmode, int oldrv, int newrv); // RT
62 public:
63 bool next_commit_needs_ramp;
64 ProcessingChainBase();
get_ramp_mode()65 inline RampMode get_ramp_mode() {
66 return static_cast<RampMode>(gx_system::atomic_get(ramp_mode)); // RT
67 }
get_ramp_value()68 inline int get_ramp_value() { return gx_system::atomic_get(ramp_value); } // RT
69 void set_samplerate(int samplerate);
70 bool set_plugin_list(const list<Plugin*> &p);
71 void clear_module_states();
post_rt_finished()72 inline void post_rt_finished() { // RT
73 int val;
74 sem_getvalue(&sync_sem, &val);
75 if (val == 0) {
76 sem_post(&sync_sem);
77 }
78 }
79 bool wait_rt_finished();
80 void set_latch();
wait_latch()81 void wait_latch() { wait_rt_finished(); }
sync()82 void sync() { set_latch(); wait_latch(); }
check_release()83 inline bool check_release() { return !to_release.empty(); }
84 void release();
85 void wait_ramp_down_finished();
86 void start_ramp_up();
87 void start_ramp_down();
set_down_dead()88 inline void set_down_dead() { set_ramp_mode(ramp_mode_down_dead); }
is_down_dead()89 inline bool is_down_dead() { return get_ramp_mode() == ramp_mode_down_dead; }
90 void set_stopped(bool v);
is_stopped()91 bool is_stopped() { return stopped; }
92 #ifndef NDEBUG
93 void print_chain_state(const char *title);
94 #endif
95 };
96
97
98 /****************************************************************
99 ** template class ThreadSafeChainPointer
100 ** members and methods accessed by the rt thread are marked RT
101 */
102
103 template <class F>
104 class ThreadSafeChainPointer: public ProcessingChainBase {
105 private:
106 F *rack_order_ptr[2]; // RT
107 int size[2];
108 int current_index;
109 F *current_pointer;
110 void setsize(int n);
111 inline F get_audio(PluginDef *p);
112 protected:
113 F *processing_pointer; // RT
get_rt_chain()114 inline F* get_rt_chain() { return gx_system::atomic_get(processing_pointer); } // RT
115 public:
116 ThreadSafeChainPointer();
117 ~ThreadSafeChainPointer();
empty_chain(ParamMap & pmap)118 inline void empty_chain(ParamMap& pmap) {
119 list<Plugin*> p;
120 if (set_plugin_list(p)) {
121 commit(true, pmap);
122 }
123 }
124 void commit(bool clear, ParamMap& pmap);
125 };
126
127 typedef void (*monochainorder)(int count, float *output, float *output1,
128 PluginDef *plugin);
129 typedef void (*stereochainorder)(int count, float* input, float* input1,
130 float *output, float *output1, PluginDef *plugin);
131
132 struct monochain_data {
133 monochainorder func;
134 PluginDef *plugin;
monochain_datamonochain_data135 monochain_data(monochainorder func_, PluginDef *plugin_): func(func_), plugin(plugin_) {}
monochain_datamonochain_data136 monochain_data(): func(), plugin() {}
137 };
138
139 struct stereochain_data {
140 stereochainorder func;
141 PluginDef *plugin;
stereochain_datastereochain_data142 stereochain_data(stereochainorder func_, PluginDef *plugin_): func(func_), plugin(plugin_) {}
stereochain_datastereochain_data143 stereochain_data(): func(), plugin() {}
144 };
145
146 template <>
get_audio(PluginDef * p)147 inline monochain_data ThreadSafeChainPointer<monochain_data>::get_audio(PluginDef *p)
148 {
149 return monochain_data(p->mono_audio, p);
150 }
151
152 template <>
get_audio(PluginDef * p)153 inline stereochain_data ThreadSafeChainPointer<stereochain_data>::get_audio(PluginDef *p)
154 {
155 return stereochain_data(p->stereo_audio, p);
156 }
157
158 template <class F>
ThreadSafeChainPointer()159 ThreadSafeChainPointer<F>::ThreadSafeChainPointer():
160 rack_order_ptr(),
161 size(),
162 current_index(0),
163 current_pointer(),
164 processing_pointer() {
165 setsize(1);
166 current_pointer[0].func = 0;
167 processing_pointer = current_pointer;
168 current_index = 1;
169 current_pointer = rack_order_ptr[1];
170 }
171
172 template <class F>
~ThreadSafeChainPointer()173 ThreadSafeChainPointer<F>::~ThreadSafeChainPointer() {
174 delete[] rack_order_ptr[0];
175 delete[] rack_order_ptr[1];
176 }
177
178 template <class F>
setsize(int n)179 void ThreadSafeChainPointer<F>::setsize(int n)
180 {
181 if (n <= size[current_index]) {
182 return;
183 }
184 delete[] rack_order_ptr[current_index];
185 rack_order_ptr[current_index] = new F[n];
186 size[current_index] = n;
187 current_pointer = rack_order_ptr[current_index];
188 }
189
190 template <class F>
commit(bool clear,ParamMap & pmap)191 void ThreadSafeChainPointer<F>::commit(bool clear, ParamMap& pmap) {
192 setsize(modules.size()+1); // leave one slot for 0 marker
193 int active_counter = 0;
194 for (list<Plugin*>::const_iterator p = modules.begin(); p != modules.end(); p++) {
195 PluginDef* pd = (*p)->get_pdef();
196 if (pd->activate_plugin) {
197 if (pd->activate_plugin(true, pd) != 0) {
198 (*p)->set_on_off(false);
199 continue;
200 }
201 } else if (pd->clear_state && clear) {
202 pd->clear_state(pd);
203 }
204 F f = get_audio(pd);
205 assert(f.func);
206 current_pointer[active_counter++] = f;
207 }
208 current_pointer[active_counter].func = 0;
209 gx_system::atomic_set(&processing_pointer, current_pointer);
210 set_latch();
211 current_index = (current_index+1) % 2;
212 current_pointer = rack_order_ptr[current_index];
213 }
214
215 /****************************************************************
216 ** class MonoModuleChain, class StereoModuleChain
217 */
218
219 class MonoModuleChain: public ThreadSafeChainPointer<monochain_data> {
220 public:
MonoModuleChain()221 MonoModuleChain(): ThreadSafeChainPointer<monochain_data>() {}
222 void process(int count, float *input, float *output);
print()223 inline void print() { printlist("Mono", modules); }
224 };
225
226 class StereoModuleChain: public ThreadSafeChainPointer<stereochain_data> {
227 public:
StereoModuleChain()228 StereoModuleChain(): ThreadSafeChainPointer<stereochain_data>() {}
229 void process(int count, float *input1, float *input2, float *output1, float *output2);
print()230 inline void print() { printlist("Stereo", modules); }
231 };
232
233
234 /****************************************************************
235 ** class EngineControl
236 */
237
238 class EngineControl {
239 protected:
240 list<ModuleSelector*> selectors; // selectors that modify the on/off state of
241 // modules at start of reconfiguration
242 sigc::connection rack_changed; // idle signal for reconfiguration of module chains
243 ParamMap pmap;
244 int policy; // jack realtime policy,
245 int priority; // and priority, for internal modules
246 // signal anyone who needs to be synchronously notified
247 // BE CAREFUL: executed by RT thread (though not concurrent with audio
248 // modules, and timing requirements are relaxed)
249 sigc::signal<void, unsigned int> buffersize_change;
250 sigc::signal<void, unsigned int> samplerate_change;
251 unsigned int buffersize;
252 unsigned int samplerate;
253 public:
254 enum OverloadType { // type of overload condition
255 ov_User = 0x1, // idle thread probe starved
256 ov_Convolver = 0x2, // convolver overload
257 ov_XRun = 0x4, // jack audio loop overload
258 ov_NoWarn = 0x8 // disable overlaod warning
259 };
260 PluginList pluginlist;
261 EngineControl();
262 ~EngineControl();
263 void init(unsigned int samplerate, unsigned int buffersize,
264 int policy, int priority);
265 virtual void wait_ramp_down_finished() = 0;
266 virtual bool update_module_lists() = 0;
267 virtual void start_ramp_up() = 0;
268 virtual void start_ramp_down() = 0;
269 virtual void overload(OverloadType tp, const char *reason) = 0; // RT
270 void set_samplerate(unsigned int samplerate_);
get_samplerate()271 unsigned int get_samplerate() { return samplerate; }
272 void set_buffersize(unsigned int buffersize_);
get_buffersize()273 unsigned int get_buffersize() { return buffersize; }
274 virtual void set_rack_changed() = 0;
275 void clear_rack_changed();
276 bool get_rack_changed();
signal_buffersize_change()277 sigc::signal<void, unsigned int>& signal_buffersize_change() { return buffersize_change; }
signal_samplerate_change()278 sigc::signal<void, unsigned int>& signal_samplerate_change() { return samplerate_change; }
279 void add_selector(ModuleSelector& sel);
280 void registerParameter(ParameterGroups& groups);
281 void get_sched_priority(int &policy, int &priority, int prio_dim = 0);
get_param()282 ParamMap& get_param() { return pmap; }
283 };
284
285
286 /****************************************************************
287 ** class ModuleSequencer
288 */
289
290 enum GxEngineState { // engine states set by user (ModuleSequencer set_state/get_state)
291 kEngineOff = 0, // mute, no output (but tuner or something might run)
292 kEngineOn = 1, // normal operation
293 kEngineBypass = 2 // just some balance or level control
294 };
295
296
297 class ModuleSequencer: public EngineControl {
298 protected:
299 int audio_mode; // GxEngineState coded as PGN_MODE_XX flags
300 boost::mutex stateflags_mutex;
301 int stateflags;
302 sigc::signal<void, GxEngineState> state_change;
303 Glib::Dispatcher overload_detected;
304 const char *overload_reason; // name of unit which detected overload
305 int ov_disabled; // bitmask of OverloadType
306 static int sporadic_interval; // seconds; overload if at least 2 events in the timespan
307 protected:
308 void check_overload();
309 public:
310 MonoModuleChain mono_chain; // active modules (amp chain, input to insert output)
311 StereoModuleChain stereo_chain; // active stereo modules (effect chain, after insert input)
312 enum StateFlag { // engine is off if one of these flags is set
313 SF_NO_CONNECTION = 0x01, // no jack connection at amp input
314 SF_JACK_RECONFIG = 0x02, // jack buffersize reconfiguration in progress
315 SF_INITIALIZING = 0x04, // jack or engine not ready
316 SF_OVERLOAD = 0x08, // engine overload
317 };
318 public:
319 ModuleSequencer();
320 ~ModuleSequencer();
clear_module_states()321 void clear_module_states() {
322 mono_chain.clear_module_states();
323 stereo_chain.clear_module_states();
324 }
325 virtual void set_samplerate(unsigned int samplerate);
326 virtual void start_ramp_up();
327 virtual void start_ramp_down();
328 virtual void wait_ramp_down_finished();
ramp_down()329 void ramp_down() {
330 start_ramp_down();
331 wait_ramp_down_finished();
332 }
set_down_dead()333 void set_down_dead() {
334 mono_chain.set_down_dead();
335 stereo_chain.set_down_dead();
336 }
337 bool prepare_module_lists();
338 void commit_module_lists();
339 virtual void set_rack_changed();
340 virtual bool update_module_lists();
341 bool check_module_lists();
342 virtual void overload(OverloadType tp, const char *reason); // RT
343 void set_stateflag(StateFlag flag); // RT
344 void clear_stateflag(StateFlag flag); // RT
345 void set_state(GxEngineState state);
346 GxEngineState get_state();
signal_state_change()347 sigc::signal<void, GxEngineState>& signal_state_change() { return state_change; }
set_overload_interval(int i)348 static void set_overload_interval(int i) { sporadic_interval = i; }
349 #ifndef NDEBUG
350 void print_engine_state();
351 #endif
352 };
353
354 } /* end of gx_engine namespace */
355