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