1 /*
2  * Copyright (C) 2013 Hermann Meyer
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 namespace screcord {
21 
22 #define fmax(x, y) (((x) > (y)) ? (x) : (y))
23 #define fmin(x, y) (((x) < (y)) ? (x) : (y))
24 
25 #define always_inline inline __attribute__((always_inline))
26 
27 #define MAXRECSIZE 131072
28 #define MAXFILESIZE INT_MAX-MAXRECSIZE // 2147352576  //2147483648-MAXRECSIZE
29 
30 class SCapture {
31 private:
32     SNDFILE *       recfile;
33     int             fSamplingFreq;
34     int             channel;
35     float           *fcheckbox0;
36     float           *fcheckbox1;
37     float           *fformat;
38     int             IOTA;
39     int             iA;
40     int             savesize;
41     int             filesize;
42     float           *fRec0;
43     float           *fRec1;
44     float           *tape;
45     sem_t           m_trig;
46     pthread_t       m_pthr;
47     volatile bool   keep_stream;
48     bool            mem_allocated;
49     bool            is_wav;
50     bool            err;
51     float           fConst0;
52     float           fRecb0[2];
53     int             iRecb1[2];
54     float           fRecb2[2];
55     void        mem_alloc();
56     void        mem_free();
57     void        clear_state_f();
58     int         activate(bool start);
59     void        init(unsigned int samplingFreq);
60     void        compute(int count, float *input0, float *output0);
61     void        compute_st(int count, float *input0, float *input1, float *output0, float *output1);
62     void        save_to_wave(SNDFILE * sf, float *tape, int lSize);
63     SNDFILE     *open_stream(std::string fname);
64     void        close_stream(SNDFILE **sf);
65     void        stop_thread();
66     void        start_thread();
67     void        disc_stream();
68     inline std::string get_ffilename();
69     void connect(uint32_t port,void* data);
70     static void *run_thread(void* p);
71 
72 public:
73     static void clear_state(SCapture*);
74     static int  activate_plugin(bool start, SCapture*);
75     static void set_samplerate(unsigned int samplingFreq, SCapture*);
76     static void mono_audio(int count, float *input0, float *output0, SCapture*);
77     static void stereo_audio(int count, float *input0, float *input1, float *output0, float *output1, SCapture*);
78     static void delete_instance(SCapture *p);
79     static void connect_ports(uint32_t port,void* data, SCapture *p);
80     SCapture(int channel_);
81     ~SCapture();
82 };
83 
84 template <class T>
to_string(const T & t)85 inline std::string to_string(const T& t) {
86     std::stringstream ss;
87     ss << t;
88     return ss.str();
89 }
90 
SCapture(int channel_)91 SCapture::SCapture(int channel_)
92     : recfile(NULL),
93       channel(channel_),
94       fRec0(0),
95       fRec1(0),
96       tape(fRec0),
97       m_pthr(0),
98       keep_stream(false),
99       mem_allocated(false),
100       err(false) {
101     sem_init(&m_trig, 0, 0);
102     start_thread();
103 }
104 
~SCapture()105 SCapture::~SCapture() {
106     stop_thread();
107     activate(false);
108 }
109 
get_ffilename()110 inline std::string SCapture::get_ffilename() {
111     struct stat buffer;
112     struct stat sb;
113     std::string pPath = getenv("HOME");
114     is_wav = int(*fformat) ? false : true;
115     pPath +="/lv2record/";
116     if (!(stat(pPath.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode))) {
117         mkdir(pPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
118     }
119     std::string name = is_wav ?  "lv2_session0.wav" : "lv2_session0.ogg" ;
120     int i = 0;
121     while (stat ((pPath+name).c_str(), &buffer) == 0) {
122         name.replace(name.begin()+11,name.end()-4,to_string(i));
123         i+=1;
124     }
125     return pPath+name;
126 }
127 
disc_stream()128 void SCapture::disc_stream() {
129     for (;;) {
130         sem_wait(&m_trig);
131         if (!recfile) {
132             std::string fname = get_ffilename();
133             recfile = open_stream(fname);
134         }
135         save_to_wave(recfile, tape, savesize);
136         filesize +=savesize;
137         if ((!keep_stream && recfile) || (filesize >MAXFILESIZE && is_wav)) {
138             close_stream(&recfile);
139             filesize = 0;
140         }
141     }
142 }
143 
run_thread(void * p)144 void *SCapture::run_thread(void *p) {
145     (reinterpret_cast<SCapture *>(p))->disc_stream();
146     return NULL;
147 }
148 
stop_thread()149 void SCapture::stop_thread() {
150     pthread_cancel (m_pthr);
151     pthread_join (m_pthr, NULL);
152 }
153 
start_thread()154 void SCapture::start_thread() {
155     pthread_attr_t      attr;
156     struct sched_param  spar;
157     int prio = 0;
158     int priomax = sched_get_priority_max(SCHED_FIFO);
159     if ((priomax/5) > 0) prio = priomax/5;
160     spar.sched_priority = prio;
161     pthread_attr_init(&attr);
162     pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE );
163     pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
164     pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
165     pthread_attr_setschedparam(&attr, &spar);
166     pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
167     pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
168     // pthread_attr_setstacksize(&attr, 0x10000);
169     if (pthread_create(&m_pthr, &attr, run_thread,
170                        reinterpret_cast<void*>(this))) {
171         err = true;
172     }
173     pthread_attr_destroy(&attr);
174 }
175 
clear_state_f()176 inline void SCapture::clear_state_f()
177 {
178     for (int i=0; i<MAXRECSIZE; i++) fRec0[i] = 0;
179     for (int i=0; i<MAXRECSIZE; i++) fRec1[i] = 0;
180     for (int i=0; i<2; i++) fRecb0[i] = 0;
181     for (int i=0; i<2; i++) iRecb1[i] = 0;
182     for (int i=0; i<2; i++) fRecb2[i] = 0;
183 }
184 
clear_state(SCapture * p)185 void SCapture::clear_state(SCapture *p)
186 {
187     static_cast<SCapture*>(p)->clear_state_f();
188 }
189 
init(unsigned int samplingFreq)190 inline void SCapture::init(unsigned int samplingFreq)
191 {
192     fSamplingFreq = samplingFreq;
193     IOTA = 0;
194     fConst0 = (1.0f / float(fmin(192000, fmax(1, fSamplingFreq))));
195 }
196 
set_samplerate(unsigned int samplingFreq,SCapture * p)197 void SCapture::set_samplerate(unsigned int samplingFreq, SCapture *p)
198 {
199     static_cast<SCapture*>(p)->init(samplingFreq);
200 }
201 
save_to_wave(SNDFILE * sf,float * tape,int lSize)202 inline void SCapture::save_to_wave(SNDFILE * sf, float *tape, int lSize)
203 {
204     if (sf) {
205         sf_write_float(sf,tape, lSize);
206         sf_write_sync(sf);
207     } else {
208         err = true;
209     }
210 }
211 
open_stream(std::string fname)212 SNDFILE *SCapture::open_stream(std::string fname)
213 {
214     SF_INFO sfinfo ;
215     sfinfo.channels = channel;
216     sfinfo.samplerate = fSamplingFreq;
217     sfinfo.format = is_wav ? SF_FORMAT_WAV | SF_FORMAT_FLOAT : SF_FORMAT_OGG | SF_FORMAT_VORBIS;
218 
219     SNDFILE * sf = sf_open(fname.c_str(), SFM_WRITE, &sfinfo);
220     if (sf) return sf;
221     else return NULL;
222 }
223 
close_stream(SNDFILE ** sf)224 inline void SCapture::close_stream(SNDFILE **sf)
225 {
226     if (*sf) sf_close(*sf);
227     *sf = NULL;
228 }
229 
mem_alloc()230 void SCapture::mem_alloc()
231 {
232     if (!fRec0) fRec0 = new float[MAXRECSIZE];
233     if (!fRec1) fRec1 = new float[MAXRECSIZE];
234     mem_allocated = true;
235 }
236 
mem_free()237 void SCapture::mem_free()
238 {
239     mem_allocated = false;
240     if (fRec0) { delete fRec0; fRec0 = 0; }
241     if (fRec1) { delete fRec1; fRec1 = 0; }
242 }
243 
activate(bool start)244 int SCapture::activate(bool start)
245 {
246     if (start) {
247         if (!mem_allocated) {
248             mem_alloc();
249             clear_state_f();
250         }
251     } else if (mem_allocated) {
252         mem_free();
253     }
254     return 0;
255 }
256 
activate_plugin(bool start,SCapture * p)257 int SCapture::activate_plugin(bool start, SCapture *p)
258 {
259     (p)->activate(start);
260     return 0;
261 }
262 
compute(int count,float * input0,float * output0)263 void always_inline SCapture::compute(int count, float *input0, float *output0)
264 {
265     if (err) *fcheckbox0 = 0.0;
266     int     iSlow0 = int(*fcheckbox0);
267     *fcheckbox1 = int(fRecb2[0]);
268     for (int i=0; i<count; i++) {
269         float fTemp0 = (float)input0[i];
270 
271         float 	fRec3 = fmax(fConst0, fabsf(fTemp0));
272         int iTemp1 = int((iRecb1[1] < 4096));
273         fRecb0[0] = ((iTemp1)?fmax(fRecb0[1], fRec3):fRec3);
274         iRecb1[0] = ((iTemp1)?(1 + iRecb1[1]):1);
275         fRecb2[0] = ((iTemp1)?fRecb2[1]:fRecb0[1]);
276 
277         if (iSlow0) { //record
278             if (iA) {
279                 fRec1[IOTA] = fTemp0;
280             } else {
281                 fRec0[IOTA] = fTemp0;
282             }
283             IOTA = (IOTA<MAXRECSIZE-1) ? IOTA+1 : 0;
284             if (!IOTA) { // when buffer is full, flush to stream
285                 iA = iA ? 0 : 1 ;
286                 tape = iA ? fRec0 : fRec1;
287                 keep_stream = true;
288                 savesize = MAXRECSIZE;
289                 sem_post(&m_trig);
290             }
291         } else if (IOTA) { // when record stoped, flush the rest to stream
292             tape = iA ? fRec1 : fRec0;
293             savesize = IOTA;
294             keep_stream = false;
295             sem_post(&m_trig);
296             IOTA = 0;
297             iA = 0;
298         }
299         output0[i] = fTemp0;
300         // post processing
301         fRecb2[1] = fRecb2[0];
302         iRecb1[1] = iRecb1[0];
303         fRecb0[1] = fRecb0[0];
304     }
305 }
306 
mono_audio(int count,float * input0,float * output0,SCapture * p)307 void SCapture::mono_audio(int count, float *input0, float *output0, SCapture *p)
308 {
309     (p)->compute(count, input0, output0);
310 }
311 
compute_st(int count,float * input0,float * input1,float * output0,float * output1)312 void always_inline SCapture::compute_st(int count, float *input0, float *input1, float *output0, float *output1)
313 {
314     if (err) *fcheckbox0 = 0.0;
315     int iSlow0 = int(*fcheckbox0);
316     *fcheckbox1 = int(fRecb2[0]);
317     for (int i=0; i<count; i++) {
318         float fTemp0 = (float)input0[i];
319         float fTemp1 = (float)input1[i];
320         // check if we run into clipping
321         float 	fRec3 = fmax(fConst0,fmax(fabsf(fTemp0),fabsf(fTemp1)));
322         int iTemp1 = int((iRecb1[1] < 4096));
323         fRecb0[0] = ((iTemp1)?fmax(fRecb0[1], fRec3):fRec3);
324         iRecb1[0] = ((iTemp1)?(1 + iRecb1[1]):1);
325         fRecb2[0] = ((iTemp1)?fRecb2[1]:fRecb0[1]);
326 
327         if (iSlow0) { //record
328             if (iA) {
329                 fRec1[IOTA] = fTemp0;
330                 fRec1[IOTA+1] = fTemp1;
331             } else {
332                 fRec0[IOTA] = fTemp0;
333                 fRec0[IOTA+1] = fTemp1;
334             }
335             IOTA = (IOTA<MAXRECSIZE-2) ? IOTA+2 : 0;
336             if (!IOTA) { // when buffer is full, flush to stream
337                 iA = iA ? 0 : 1 ;
338                 tape = iA ? fRec0 : fRec1;
339                 keep_stream = true;
340                 savesize = MAXRECSIZE;
341                 sem_post(&m_trig);
342             }
343         } else if (IOTA) { // when record stoped, flush the rest to stream
344             tape = iA ? fRec1 : fRec0;
345             savesize = IOTA;
346             keep_stream = false;
347             sem_post(&m_trig);
348             IOTA = 0;
349             iA = 0;
350         }
351         output0[i] = fTemp0;
352         output1[i] = fTemp1;
353         // post processing
354         fRecb2[1] = fRecb2[0];
355         iRecb1[1] = iRecb1[0];
356         fRecb0[1] = fRecb0[0];
357     }
358 }
359 
stereo_audio(int count,float * input0,float * input1,float * output0,float * output1,SCapture * p)360 void SCapture::stereo_audio(int count, float *input0, float *input1, float *output0, float *output1, SCapture *p)
361 {
362     (p)->compute_st(count, input0, input1, output0, output1);
363 }
364 
connect(uint32_t port,void * data)365 void SCapture::connect(uint32_t port,void* data)
366 {
367 	switch ((PortIndex)port)
368 	{
369 	case FORM:
370 		fformat = (float*)data; //  {{"wav"},{"ogg"},{0}};
371 		break;
372 	case REC:
373 		fcheckbox0 = (float*)data; // , 0.0f, 0.0f, 1.0f, 1.0f
374 		break;
375 	case CLIP:
376 		fcheckbox1 = (float*)data; // , 0.0f, 0.0f, 1.0f, 1.0f
377 		break;
378 	default:
379 		break;
380 	}
381 }
382 
connect_ports(uint32_t port,void * data,SCapture * p)383 void SCapture::connect_ports(uint32_t port,void* data, SCapture *p)
384 {
385 	(p)->connect(port, data);
386 }
387 
delete_instance(SCapture * p)388 void SCapture::delete_instance(SCapture *p)
389 {
390 	delete (p);
391 }
392 
393 } // end namespace gxrecord
394