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