1 /*
2  * Copyright (C) 2013 Hermann Meyer, Andreas Degert
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  * --------------------------------------------------------------------------
18  *
19  *
20  *    This is part of the Guitarix Audio Engine
21  *
22  *
23  *
24  * --------------------------------------------------------------------------
25  */
26 
27 
28 
29 #define MAXRECSIZE 131072
30 #define MAXFILESIZE INT_MAX-MAXRECSIZE // 2147352576  //2147483648-MAXRECSIZE
31 
SCapture(EngineControl & engine_,int channel_)32 SCapture::SCapture(EngineControl& engine_, int channel_)
33     : PluginDef(),
34       recfile(NULL),
35       engine(engine_),
36       channel(channel_),
37       fRec0(0),
38       fRec1(0),
39       tape(fRec0),
40       m_pthr(0),
41       keep_stream(false),
42       mem_allocated(false),
43       err(false) {
44     version = PLUGINDEF_VERSION;
45     flags = PGN_NO_PRESETS;
46     if (channel == 1) {
47     id = "recorder";
48     name = N_("Recorder");
49     groups = 0;
50     description = N_("Digital Record"); // description (tooltip)
51     category = N_("Misc");       // category
52     shortname = "";     // shortname
53     mono_audio = compute_static;
54     stereo_audio = 0;
55     } else {
56     id = "st_recorder";
57     name = N_("Stereo Recorder");
58     groups = 0;
59     description = N_("Digital Record"); // description (tooltip)
60     category = N_("Misc");       // category
61     shortname = N_("St-Recorder");     // shortname
62     mono_audio = 0;
63     stereo_audio = compute_static_st;
64     }
65     set_samplerate = init_static;
66     activate_plugin = activate_static;
67     register_params = register_params_static;
68     load_ui = load_ui_f_static;
69     clear_state = clear_state_f_static;
70     delete_instance = del_instance;
71     plugin = this;
72     sem_init(&m_trig, 0, 0);
73     start_thread();
74 }
75 
~SCapture()76 SCapture::~SCapture() {
77     stop_thread();
78     activate(false);
79 }
80 
get_ffilename()81 inline std::string SCapture::get_ffilename() {
82     struct stat buffer;
83     struct stat sb;
84     std::string pPath = getenv("HOME");
85     is_wav = int(fformat) ? false : true;
86     pPath +="/gxrecord/";
87     if (!(stat(pPath.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))) {
88         mkdir(pPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
89     }
90     std::string name = "guitarix_session0.wav";
91     switch(int(fformat)) {
92       case(0) :
93           break;
94       case(1) :
95           name = "guitarix_session0.ogg";
96           break;
97       case(2) :
98           name = "guitarix_session0.w64";
99           break;
100       default :
101           break;
102     }
103     int i = 0;
104     while (stat ((pPath+name).c_str(), &buffer) == 0) {
105         name.replace(name.begin()+16,name.end()-4,gx_system::to_string(i));
106         i+=1;
107     }
108     return pPath+name;
109 }
110 
disc_stream()111 void SCapture::disc_stream() {
112     for (;;) {
113         sem_wait(&m_trig);
114         if (!recfile) {
115             recfile = open_stream(get_ffilename());
116         }
117         save_to_wave(recfile, tape, savesize);
118         filesize +=savesize;
119         if ((!keep_stream && recfile) || (filesize >MAXFILESIZE && is_wav)) {
120             close_stream(&recfile);
121             filesize = 0;
122         }
123     }
124 }
125 
run_thread(void * p)126 void *SCapture::run_thread(void *p) {
127     (reinterpret_cast<SCapture *>(p))->disc_stream();
128     return NULL;
129 }
130 
stop_thread()131 void SCapture::stop_thread() {
132     pthread_cancel (m_pthr);
133     pthread_join (m_pthr, NULL);
134 }
135 
start_thread()136 void SCapture::start_thread() {
137     pthread_attr_t      attr;
138     struct sched_param  spar;
139     int priority, policy;
140     engine.get_sched_priority(policy, priority, 12);
141     spar.sched_priority = priority;
142     pthread_attr_init(&attr);
143     pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE );
144     pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
145     pthread_attr_setschedpolicy(&attr, policy);
146     pthread_attr_setschedparam(&attr, &spar);
147     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
148     pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
149     // pthread_attr_setstacksize(&attr, 0x10000);
150     if (pthread_create(&m_pthr, &attr, run_thread,
151                        reinterpret_cast<void*>(this))) {
152         err = true;
153     }
154     pthread_attr_destroy(&attr);
155 }
156 
clear_state_f()157 inline void SCapture::clear_state_f()
158 {
159     for (int i=0; i<MAXRECSIZE; i++) fRec0[i] = 0;
160     for (int i=0; i<MAXRECSIZE; i++) fRec1[i] = 0;
161     for (int i=0; i<2; i++) fRecb0[i] = 0;
162     for (int i=0; i<2; i++) iRecb1[i] = 0;
163     for (int i=0; i<2; i++) fRecb2[i] = 0;
164     for (int i=0; i<2; i++) fRecC0[i] = 0;
165 }
166 
clear_state_f_static(PluginDef * p)167 void SCapture::clear_state_f_static(PluginDef *p)
168 {
169     static_cast<SCapture*>(p)->clear_state_f();
170 }
171 
init(unsigned int samplingFreq)172 inline void SCapture::init(unsigned int samplingFreq)
173 {
174     fSamplingFreq = samplingFreq;
175     IOTA = 0;
176     fConst0 = (1.0f / float(fmin(192000, fmax(1, fSamplingFreq))));
177 }
178 
init_static(unsigned int samplingFreq,PluginDef * p)179 void SCapture::init_static(unsigned int samplingFreq, PluginDef *p)
180 {
181     static_cast<SCapture*>(p)->init(samplingFreq);
182 }
183 
save_to_wave(SNDFILE * sf,float * tape,int lSize)184 inline void SCapture::save_to_wave(SNDFILE * sf, float *tape, int lSize)
185 {
186     if (sf) {
187         sf_write_float(sf,tape, lSize);
188         sf_write_sync(sf);
189     }
190 }
191 
open_stream(std::string fname)192 SNDFILE *SCapture::open_stream(std::string fname)
193 {
194     SF_INFO sfinfo ;
195     sfinfo.channels = channel;
196     sfinfo.samplerate = fSamplingFreq;
197     switch(int(fformat)) {
198       case(0) :
199           sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
200           break;
201       case(1) :
202           sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
203           break;
204       case(2) :
205           sfinfo.format = SF_FORMAT_W64 | SF_FORMAT_PCM_24;
206           break;
207       default :
208           sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
209           break;
210       }
211 
212     SNDFILE * sf = sf_open(fname.c_str(), SFM_WRITE, &sfinfo);
213     if (sf) return sf;
214     else return NULL;
215 }
216 
close_stream(SNDFILE ** sf)217 inline void SCapture::close_stream(SNDFILE **sf)
218 {
219     if (*sf) sf_close(*sf);
220     *sf = NULL;
221 }
222 
mem_alloc()223 void SCapture::mem_alloc()
224 {
225     if (!fRec0) fRec0 = new float[MAXRECSIZE];
226     if (!fRec1) fRec1 = new float[MAXRECSIZE];
227     mem_allocated = true;
228 }
229 
mem_free()230 void SCapture::mem_free()
231 {
232     mem_allocated = false;
233     if (fRec0) { delete fRec0; fRec0 = 0; }
234     if (fRec1) { delete fRec1; fRec1 = 0; }
235 }
236 
activate(bool start)237 int SCapture::activate(bool start)
238 {
239     if (start) {
240         if (!mem_allocated) {
241             mem_alloc();
242             clear_state_f();
243         }
244     } else if (mem_allocated) {
245         mem_free();
246     }
247     return 0;
248 }
249 
activate_static(bool start,PluginDef * p)250 int SCapture::activate_static(bool start, PluginDef *p)
251 {
252     return static_cast<SCapture*>(p)->activate(start);
253 }
254 
compute(int count,float * input0,float * output0)255 void always_inline SCapture::compute(int count, float *input0, float *output0)
256 {
257     if (err) fcheckbox0 = 0.0;
258     int     iSlow0 = int(fcheckbox0);
259     fcheckbox1 = int(fRecb2[0]);
260     float 	fSlow0 = (0.0010000000000000009f * powf(10,(0.05f * fslider0)));
261     for (int i=0; i<count; i++) {
262         float fTemp0 = (float)input0[i];
263         fRecC0[0] = (fSlow0 + (0.999f * fRecC0[1]));
264         float fTemp1 = fTemp0 * fRecC0[0];
265         // check if we run into clipping
266         float 	fRec3 = fmax(fConst0, fabsf(fTemp1));
267         int iTemp1 = int((iRecb1[1] < 4096));
268         fRecb0[0] = ((iTemp1)?fmax(fRecb0[1], fRec3):fRec3);
269         iRecb1[0] = ((iTemp1)?(1 + iRecb1[1]):1);
270         fRecb2[0] = ((iTemp1)?fRecb2[1]:fRecb0[1]);
271         fbargraph0 = fRecb2[0];
272 
273         if (iSlow0) { //record
274             if (iA) {
275                 fRec1[IOTA] = fTemp1;
276             } else {
277                 fRec0[IOTA] = fTemp1;
278             }
279             IOTA = (IOTA<MAXRECSIZE-1) ? IOTA+1 : 0;
280             if (!IOTA) { // when buffer is full, flush to stream
281                 iA = iA ? 0 : 1 ;
282                 tape = iA ? fRec0 : fRec1;
283                 keep_stream = true;
284                 savesize = MAXRECSIZE;
285                 sem_post(&m_trig);
286             }
287         } else if (IOTA) { // when record stopped, flush the rest to stream
288             tape = iA ? fRec1 : fRec0;
289             savesize = IOTA;
290             keep_stream = false;
291             sem_post(&m_trig);
292             IOTA = 0;
293             iA = 0;
294         }
295         output0[i] = fTemp0;
296         // post processing
297         fRecb2[1] = fRecb2[0];
298         iRecb1[1] = iRecb1[0];
299         fRecb0[1] = fRecb0[0];
300         fRecC0[1] = fRecC0[0];
301     }
302 }
303 
compute_static(int count,float * input0,float * output0,PluginDef * p)304 void __rt_func SCapture::compute_static(int count, float *input0, float *output0, PluginDef *p)
305 {
306     static_cast<SCapture*>(p)->compute(count, input0, output0);
307 }
308 
compute_st(int count,float * input0,float * input1,float * output0,float * output1)309 void always_inline SCapture::compute_st(int count, float *input0, float *input1, float *output0, float *output1)
310 {
311     if (err) fcheckbox0 = 0.0;
312     int iSlow0 = int(fcheckbox0);
313     fcheckbox1 = int(fRecb2[0]);
314     float 	fSlow0 = (0.0010000000000000009f * powf(10,(0.05f * fslider0)));
315     for (int i=0; i<count; i++) {
316         float fTemp0 = (float)input0[i];
317         float fTemp1 = (float)input1[i];
318         fRecC0[0] = (fSlow0 + (0.999f * fRecC0[1]));
319         float fTemp2 = fTemp0 * fRecC0[0];
320         float fTemp3 = fTemp1 * fRecC0[0];
321         // check if we run into clipping
322         float 	fRec3 = fmax(fConst0,fmax(fabsf(fTemp2),fabsf(fTemp3)));
323         int iTemp1 = int((iRecb1[1] < 4096));
324         fRecb0[0] = ((iTemp1)?fmax(fRecb0[1], fRec3):fRec3);
325         iRecb1[0] = ((iTemp1)?(1 + iRecb1[1]):1);
326         fRecb2[0] = ((iTemp1)?fRecb2[1]:fRecb0[1]);
327         fbargraph0 = fRecb2[0];
328 
329         if (iSlow0) { //record
330             if (iA) {
331                 fRec1[IOTA] = fTemp2;
332                 fRec1[IOTA+1] = fTemp3;
333             } else {
334                 fRec0[IOTA] = fTemp2;
335                 fRec0[IOTA+1] = fTemp3;
336             }
337             IOTA = (IOTA<MAXRECSIZE-2) ? IOTA+2 : 0;
338             if (!IOTA) { // when buffer is full, flush to stream
339                 iA = iA ? 0 : 1 ;
340                 tape = iA ? fRec0 : fRec1;
341                 keep_stream = true;
342                 savesize = MAXRECSIZE;
343                 sem_post(&m_trig);
344             }
345         } else if (IOTA) { // when record stopped, flush the rest to stream
346             tape = iA ? fRec1 : fRec0;
347             savesize = IOTA;
348             keep_stream = false;
349             sem_post(&m_trig);
350             IOTA = 0;
351             iA = 0;
352         }
353         output0[i] = fTemp0;
354         output1[i] = fTemp1;
355         // post processing
356         fRecb2[1] = fRecb2[0];
357         iRecb1[1] = iRecb1[0];
358         fRecb0[1] = fRecb0[0];
359         fRecC0[1] = fRecC0[0];
360     }
361 }
362 
compute_static_st(int count,float * input0,float * input1,float * output0,float * output1,PluginDef * p)363 void SCapture::compute_static_st(int count, float *input0, float *input1, float *output0, float *output1, PluginDef *p)
364 {
365     static_cast<SCapture*>(p)->compute_st(count, input0, input1, output0, output1);
366 }
367 
register_par(const ParamReg & reg)368 int SCapture::register_par(const ParamReg& reg)
369 {
370     static const value_pair fformat_values[] = {{"wav"},{"ogg"},{"w64"},{0}};
371     if (channel == 1) {
372 	reg.registerFloatVar("recorder.file","","S",N_("select file format"),&fformat, 0.0, 0.0, 2.0, 1.0, fformat_values);
373 	reg.registerFloatVar("recorder.rec","","B",N_("Record files to ~/gxrecord/"),&fcheckbox0, 0.0, 0.0, 1.0, 1.0, 0);
374 	reg.registerFloatVar("recorder.gain","","S",N_("Record gain control"),&fslider0, 0.0f, -7e+01f, 4.0f, 0.1f, 0);
375 	reg.registerFloatVar("recorder.clip","","BON","",&fcheckbox1, 0.0, 0.0, 1.0, 1.0, 0);
376 	reg.registerFloatVar("recorder.v1","","SOLN","",&fbargraph0, -70.0, -70.0, 4.0, 0.00001, 0);
377     } else {
378 	reg.registerFloatVar("st_recorder.file","","S",N_("select file format"),&fformat, 0.0, 0.0, 2.0, 1.0, fformat_values);
379 	reg.registerFloatVar("st_recorder.rec","","B",N_("Record files to ~/gxrecord/"),&fcheckbox0, 0.0, 0.0, 1.0, 1.0, 0);
380 	reg.registerFloatVar("st_recorder.gain","","S",N_("Record gain control"),&fslider0, 0.0f, -7e+01f, 4.0f, 0.1f, 0);
381 	reg.registerFloatVar("st_recorder.clip","","BON","",&fcheckbox1, 0.0, 0.0, 1.0, 1.0, 0);
382 	reg.registerFloatVar("st_recorder.v1","","SOLN","",&fbargraph0, -70.0, -70.0, 4.0, 0.00001, 0);
383     }
384 
385     return 0;
386 }
387 
register_params_static(const ParamReg & reg)388 int SCapture::register_params_static(const ParamReg& reg)
389 {
390     return static_cast<SCapture*>(reg.plugin)->register_par(reg);
391 }
392 
load_ui_f(const UiBuilder & b,int form)393 inline int SCapture::load_ui_f(const UiBuilder& b, int form)
394 {
395     if (form & UI_FORM_GLADE) {
396         if (channel == 1) {
397             b.load_glade_file("gx_record_ui.glade");
398         } else {
399             b.load_glade_file("gx_st_record_ui.glade");
400         }
401         return 0;
402     }
403     if (form & UI_FORM_STACK) {
404 
405         if (channel == 1) {
406 #define PARAM(p) ("recorder" "." p)
407             b.openHorizontalhideBox("");
408             b.create_feedback_switch(sw_rbutton,PARAM("rec"));
409 
410             b.closeBox();
411 
412             b.openHorizontalBox("");
413             b.create_small_rackknob(PARAM("gain"), N_("gain(db)"));
414             b.create_feedback_switch(sw_rbutton,PARAM("rec"));
415             b.create_feedback_switch(sw_led,PARAM("clip"));
416             b.create_selector_no_caption(PARAM("file"));
417 
418             b.closeBox();
419 
420 #undef PARAM
421         } else {
422 #define PARAM(p) ("st_recorder" "." p)
423             b.openHorizontalhideBox("");
424             b.create_feedback_switch(sw_rbutton,PARAM("rec"));
425 
426             b.closeBox();
427 
428             b.openHorizontalBox("");
429             b.create_small_rackknob(PARAM("gain"), N_("gain(db)"));
430             b.create_feedback_switch(sw_rbutton,PARAM("rec"));
431             b.create_feedback_switch(sw_led,PARAM("clip"));
432             b.create_selector_no_caption(PARAM("file"));
433 
434             b.closeBox();
435 
436 #undef PARAM
437         }
438         return 0;
439     }
440      return -1;
441 }
442 
load_ui_f_static(const UiBuilder & b,int form)443 int SCapture::load_ui_f_static(const UiBuilder& b, int form)
444 {
445     return static_cast<SCapture*>(b.plugin)->load_ui_f(b, form);
446 }
447 
del_instance(PluginDef * p)448 void SCapture::del_instance(PluginDef *p)
449 {
450     delete static_cast<SCapture*>(p);
451 }
452