]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/main.c
added on/off switching for aux input port mix
[dttsp.git] / jDttSP / main.c
1 /* main.c
2
3 This file is part of a program that implements a Software-Defined Radio.
4
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 The authors can be reached by email at
22
23 ab2kt@arrl.net
24 or
25 rwmcgwier@comcast.net
26
27 or by paper mail at
28
29 The DTTS Microwave Society
30 6 Kathleen Place
31 Bridgewater, NJ 08807
32 */  
33   
34 #include <common.h>
35   
36 /////////////////////////////////////////////////////////////////////////
37 // elementary defaults
38
39 struct _loc loc;  
40
41 /////////////////////////////////////////////////////////////////////////
42 // most of what little we know here about the inner loop,
43 // functionally speaking
44
45 extern void process_samples(float *, float *, float *, float *, int);
46 extern void setup_workspace(void);
47 extern void destroy_workspace(void);
48
49 //========================================================================
50
51 void
52 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
53   int i;
54   char zero = 0;
55   for (i = 0; i < nbytes; i++)
56     jack_ringbuffer_write(rb, &zero, 1);
57 }
58
59 //========================================================================
60
61 PRIVATE void
62 monitor_thread(void) {
63   while (top.running) {
64     sem_wait(&top.sync.mon.sem);
65     fprintf(stderr,
66             "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n",
67             uni.tick,
68             top.jack.blow.cb,
69             top.jack.blow.rb.i,
70             top.jack.blow.rb.o,
71             top.jack.blow.xr);
72     memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
73   }
74   pthread_exit(0);
75 }
76
77 //========================================================================
78
79 PRIVATE void 
80 process_updates_thread(void) {
81   while (top.running) {
82     pthread_testcancel();
83     while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
84       do_update(top.parm.buff, top.verbose ? stderr : 0);
85   }
86   pthread_exit(0);
87 }
88
89 PRIVATE void 
90 gethold(void) {
91   if (jack_ringbuffer_write_space(top.jack.ring.o.l)
92       < top.hold.size.bytes) {
93     // pathology
94     jack_ringbuffer_reset(top.jack.ring.o.l);
95     jack_ringbuffer_reset(top.jack.ring.o.r);
96     top.jack.blow.rb.o++;
97   }
98   jack_ringbuffer_write(top.jack.ring.o.l,
99                         (char *) top.hold.buf.l,
100                         top.hold.size.bytes);
101   jack_ringbuffer_write(top.jack.ring.o.r,
102                         (char *) top.hold.buf.r,
103                         top.hold.size.bytes);
104   if (jack_ringbuffer_read_space(top.jack.ring.i.l)
105       < top.hold.size.bytes) {
106     // pathology
107     jack_ringbuffer_reset(top.jack.ring.i.l);
108     jack_ringbuffer_reset(top.jack.ring.i.r);
109     memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
110     memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
111     jack_ringbuffer_reset(top.jack.auxr.i.l);
112     jack_ringbuffer_reset(top.jack.auxr.i.r);
113     memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
114     memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
115     top.jack.blow.rb.i++;
116   } else {
117     jack_ringbuffer_read(top.jack.ring.i.l,
118                          (char *) top.hold.buf.l,
119                          top.hold.size.bytes);
120     jack_ringbuffer_read(top.jack.ring.i.r,
121                          (char *) top.hold.buf.r,
122                          top.hold.size.bytes);
123     jack_ringbuffer_read(top.jack.auxr.i.l,
124                          (char *) top.hold.aux.l,
125                          top.hold.size.bytes);
126     jack_ringbuffer_read(top.jack.auxr.i.r,
127                          (char *) top.hold.aux.r,
128                          top.hold.size.bytes);
129   }
130 }
131
132 PRIVATE BOOLEAN
133 canhold(void) {
134   return
135     jack_ringbuffer_read_space(top.jack.ring.i.l)
136     >= top.hold.size.bytes;
137 }
138
139 PRIVATE void 
140 run_mute(void) {
141   memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
142   memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
143   memset((char *) top.hold.aux.l, 0, top.hold.size.bytes);
144   memset((char *) top.hold.aux.r, 0, top.hold.size.bytes);
145   uni.tick++;
146 }
147
148 PRIVATE void 
149 run_pass(void) { uni.tick++; }
150
151 PRIVATE void 
152 run_play(void) {
153   process_samples(top.hold.buf.l, top.hold.buf.r,
154                   top.hold.aux.l, top.hold.aux.r,
155                   top.hold.size.frames);
156
157
158 // NB do not set RUN_SWCH directly via setRunState;
159 // use setSWCH instead
160
161 PRIVATE void 
162 run_swch(void) {
163   if (top.swch.bfct.have == 0) {
164     // first time
165     // apply ramp down
166     int i, m = top.swch.fade, n = top.swch.tail;
167     for (i = 0; i < m; i++) {
168       float w = (float) 1.0 - (float) i / m;
169       top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
170     }
171     memset((char *) (top.hold.buf.l + m), 0, n);
172     memset((char *) (top.hold.buf.r + m), 0, n);
173     top.swch.bfct.have++;
174   } else if (top.swch.bfct.have < top.swch.bfct.want) {
175     // in medias res
176     memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
177     memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
178     top.swch.bfct.have++;
179   } else {
180     // last time
181     // apply ramp up
182     int i, m = top.swch.fade, n = top.swch.tail;
183     for (i = 0; i < m; i++) {
184       float w = (float) i / m;
185       top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
186     }
187     uni.mode.trx = top.swch.trx.next;
188
189     // move this out of main! -----------------------------
190     {
191       int k;
192       for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0;
193     }
194     tx.tick = 0;
195     //-----------------------------------------------------
196
197     top.state = top.swch.run.last;
198     top.swch.bfct.want = top.swch.bfct.have = 0;
199
200     jack_ringbuffer_reset(top.jack.ring.o.l);
201     jack_ringbuffer_reset(top.jack.ring.o.r);
202   }
203
204   process_samples(top.hold.buf.l, top.hold.buf.r,
205                   top.hold.aux.l, top.hold.aux.r,
206                   top.hold.size.frames);
207
208
209 //========================================================================
210
211
212 PRIVATE void 
213 audio_callback(jack_nframes_t nframes, void *arg) {
214   float *lp, *rp;
215   int nbytes = nframes * sizeof(float);
216
217   if (nframes == top.jack.size) {
218
219     // output: copy from ring to port
220     lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
221     rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
222
223     if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
224       jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
225       jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
226     } else { // rb pathology
227       memset((char *) lp, 0, nbytes);
228       memset((char *) rp, 0, nbytes);
229       jack_ringbuffer_reset(top.jack.ring.o.l);
230       jack_ringbuffer_reset(top.jack.ring.o.r);
231       clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
232       clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
233       top.jack.blow.rb.o++;
234     }
235     
236     // input: copy from port to ring
237     if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
238       lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
239       rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
240       jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
241       jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
242       lp = (float *) jack_port_get_buffer(top.jack.auxp.i.l, nframes);
243       rp = (float *) jack_port_get_buffer(top.jack.auxp.i.r, nframes);
244       jack_ringbuffer_write(top.jack.auxr.i.l, (char *) lp, nbytes);
245       jack_ringbuffer_write(top.jack.auxr.i.r, (char *) rp, nbytes);
246     } else { // rb pathology
247       jack_ringbuffer_reset(top.jack.ring.i.l);
248       jack_ringbuffer_reset(top.jack.ring.i.r);
249       clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
250       clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
251       jack_ringbuffer_reset(top.jack.auxr.i.l);
252       jack_ringbuffer_reset(top.jack.auxr.i.r);
253       clear_jack_ringbuffer(top.jack.auxr.i.l, nbytes);
254       clear_jack_ringbuffer(top.jack.auxr.i.r, nbytes);
255       top.jack.blow.rb.i++;
256     }
257
258   } else { // callback pathology
259     jack_ringbuffer_reset(top.jack.ring.i.l);
260     jack_ringbuffer_reset(top.jack.ring.i.r);
261     jack_ringbuffer_reset(top.jack.ring.o.l);
262     jack_ringbuffer_reset(top.jack.ring.o.r);
263     clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
264     clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
265     top.jack.blow.cb++;
266   }
267
268   // if enough accumulated in ring, fire dsp
269   if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
270     sem_post(&top.sync.buf.sem);
271
272   // check for blowups
273   if ((top.jack.blow.cb > 0) ||
274       (top.jack.blow.rb.i > 0) ||
275       (top.jack.blow.rb.o > 0))
276     sem_post(&top.sync.mon.sem);
277 }
278
279 PRIVATE void 
280 process_samples_thread(void) {
281   while (top.running) {
282     sem_wait(&top.sync.buf.sem);
283     do {
284       gethold();
285       sem_wait(&top.sync.upd.sem);
286       switch (top.state) {
287       case RUN_MUTE: run_mute(); break;
288       case RUN_PASS: run_pass(); break;
289       case RUN_PLAY: run_play(); break;
290       case RUN_SWCH: run_swch(); break;
291       }
292       sem_post(&top.sync.upd.sem);
293     } while (canhold());
294   }
295   pthread_exit(0);
296 }
297
298 //========================================================================
299
300 PRIVATE void 
301 execute(void) {
302   // let updates run
303   sem_post(&top.sync.upd.sem);
304   
305   // rcfile
306   {
307     FILE *frc = find_rcfile(loc.path.rcfile);
308     if (frc) {
309       while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
310         do_update(top.parm.buff, top.verbose ? stderr : 0);
311       fclose(frc);
312     }
313   }
314
315   // start audio processing
316   if (jack_activate(top.jack.client))
317     perror("cannot activate jack client"), exit(1);
318   
319   // wait for threads to terminate
320   pthread_join(top.thrd.trx.id, 0);
321   pthread_join(top.thrd.upd.id, 0);
322   pthread_join(top.thrd.mon.id, 0);
323   
324   // stop audio processing
325   jack_client_close(top.jack.client);
326 }
327
328 PRIVATE void 
329 closeup(void) {
330   jack_ringbuffer_free(top.jack.auxr.i.r);
331   jack_ringbuffer_free(top.jack.auxr.i.l);
332   jack_ringbuffer_free(top.jack.ring.o.r);
333   jack_ringbuffer_free(top.jack.ring.o.l);
334   jack_ringbuffer_free(top.jack.ring.i.r);
335   jack_ringbuffer_free(top.jack.ring.i.l);
336
337   safefree((char *) top.hold.buf.r);
338   safefree((char *) top.hold.buf.l);
339   safefree((char *) top.hold.aux.r);
340   safefree((char *) top.hold.aux.l);
341
342   destroy_workspace();
343
344   exit(0);
345 }
346
347 PRIVATE void 
348 usage(void) {
349   fprintf(stderr, "usage:\n");
350   fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
351   fprintf(stderr, "flags:\n");
352   fprintf(stderr, "     -v              verbose commentary\n");
353   fprintf(stderr, "     -m              do metering\n");
354   fprintf(stderr, "     -l file         execute update commands in file at startup\n");
355   fprintf(stderr, "     -P cmdpath      path to command/update pipe\n");
356   fprintf(stderr, "     -S s-mtrpath    path to S-meter output channel\n");
357   fprintf(stderr, "     -W wispath      path to FFTW wisdom file\n");
358   fprintf(stderr, "     -R rate         sampling rate\n");
359   fprintf(stderr, "     -B bufsize      internal DSP buffer size\n");
360   fprintf(stderr, "     -M mode         start up in mode (SAM, USB, LCW, etc.)\n");
361   fprintf(stderr, "     -G num          use num as ringbuffer mult\n");
362   fprintf(stderr, "     -E num          use num as meter chan mult\n");
363   fprintf(stderr, "'file' arg unused, but available\n");
364   exit(1);
365 }
366
367 PRIVATE void 
368 nonblock(int fd) {
369   long arg;
370   arg = fcntl(fd, F_GETFL);
371   arg |= O_NONBLOCK;
372 /*  if (fcntl(fd, F_GETFL, &arg) >= 0)
373     fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ 
374   fcntl(fd, F_SETFL, arg);
375
376
377 //........................................................................
378
379 PRIVATE void 
380 setup_switching(void) {
381   top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
382   top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
383 }
384
385 PRIVATE void 
386 setup_local_audio(void) {
387   top.hold.size.frames = uni.buflen;
388   top.hold.size.bytes = top.hold.size.frames * sizeof(float);
389   top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
390                                        "main hold buffer left");
391   top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
392                                        "main hold buffer right");
393   top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
394                                        "aux hold buffer left");
395   top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
396                                        "aux hold buffer right");
397
398
399 PRIVATE void 
400 setup_updates(void) {
401   top.parm.path = loc.path.parm;
402   if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
403     perror(top.parm.path), exit(1);
404   if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
405     fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
406     exit(1);
407   }
408 }
409
410 PRIVATE void
411 jack_xrun(void *arg) {
412   top.jack.blow.xr++;
413   sem_post(&top.sync.mon.sem);
414 }
415
416 PRIVATE void
417 jack_shutdown(void *arg) {}
418
419 PRIVATE void 
420 setup_system_audio(void) {
421   if (loc.name[0]) strcpy(top.jack.name, loc.name);
422   else sprintf(top.jack.name, "sdr-%d", top.pid);
423   if (!(top.jack.client = jack_client_new(top.jack.name)))
424     perror("can't make client -- jack not running?"), exit(1);
425
426   jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
427   jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
428   jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
429   top.jack.size = jack_get_buffer_size(top.jack.client);
430   memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
431
432   top.jack.port.i.l = jack_port_register(top.jack.client,
433                                          "il",
434                                          JACK_DEFAULT_AUDIO_TYPE,
435                                          JackPortIsInput,
436                                          0);
437   top.jack.port.i.r = jack_port_register(top.jack.client,
438                                          "ir",
439                                          JACK_DEFAULT_AUDIO_TYPE,
440                                          JackPortIsInput,
441                                          0);
442   top.jack.port.o.l = jack_port_register(top.jack.client,
443                                          "ol",
444                                          JACK_DEFAULT_AUDIO_TYPE,
445                                          JackPortIsOutput,
446                                          0);
447   top.jack.port.o.r = jack_port_register(top.jack.client,
448                                          "or",
449                                          JACK_DEFAULT_AUDIO_TYPE,
450                                          JackPortIsOutput,
451                                          0);
452   top.jack.auxp.i.l = jack_port_register(top.jack.client,
453                                          "al",
454                                          JACK_DEFAULT_AUDIO_TYPE,
455                                          JackPortIsInput,
456                                          0);
457   top.jack.auxp.i.r = jack_port_register(top.jack.client,
458                                          "ar",
459                                          JACK_DEFAULT_AUDIO_TYPE,
460                                          JackPortIsInput,
461                                          0);
462   top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
463   top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
464   top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
465   top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
466   top.jack.auxr.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
467   top.jack.auxr.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
468   clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
469   clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
470 }
471
472 PRIVATE void 
473 setup_threading(void) {
474   sem_init(&top.sync.upd.sem, 0, 0);
475   pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
476   sem_init(&top.sync.buf.sem, 0, 0);
477   pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
478   sem_init(&top.sync.mon.sem, 0, 0);
479   pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
480
481
482 //========================================================================
483 // hard defaults, then environment
484
485 PRIVATE void
486 setup_defaults(void) {
487   loc.name[0] = 0; // no default name for jack client
488   strcpy(loc.path.rcfile, RCBASE);
489   strcpy(loc.path.parm, PARMPATH);
490   strcpy(loc.path.meter, METERPATH);
491   strcpy(loc.path.wisdom, WISDOMPATH);
492   loc.def.rate = DEFRATE;
493   loc.def.size = DEFSIZE;
494   loc.def.mode = DEFMODE;
495   loc.def.nrx = MAXRX;
496   loc.mult.ring = RINGMULT;
497   loc.mult.meter = METERMULT;
498
499   {
500     char *ep;
501     if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
502     if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
503     if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
504     if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
505     if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
506     if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
507     if ((ep = getenv("SDR_METERMULT"))) loc.mult.meter = atoi(ep);
508     if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
509     if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
510     if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
511   }
512 }
513
514 //========================================================================
515 PRIVATE void 
516 setup(int argc, char **argv) {
517   int i;
518
519   top.uid = getuid();
520   top.pid = getpid();
521   top.start_tv = now_tv();
522   top.running = TRUE;
523   top.verbose = FALSE;
524   top.state = RUN_PLAY;
525
526   setup_defaults();
527   
528   for (i = 1; i < argc; i++)
529     if (argv[i][0] == '-')
530       switch (argv[i][1]) {
531       case 'v':
532         top.verbose = TRUE;
533         break;
534       case 'l':
535         strcpy(loc.path.rcfile, argv[++i]);
536         break;
537       case 'm':
538         uni.meter.flag = TRUE;
539         break;
540       case 'P':
541         strcpy(loc.path.parm, argv[++i]);
542         break;
543       case 'S':
544         strcpy(loc.path.meter, argv[++i]);
545         break;
546       case 'W':
547         strcpy(loc.path.wisdom, argv[++i]);
548         break;
549       case 'R':
550         loc.def.rate = atof(argv[++i]);
551         break;
552       case 'B':
553         loc.def.size = atoi(argv[++i]);
554         break;
555       case 'M':
556         loc.def.mode = atoi(argv[++i]);
557         break;
558       case 'G':
559         loc.mult.ring = atoi(argv[++i]);
560         break;
561       case 'E':
562         loc.mult.meter = atoi(argv[++i]);
563         break;
564       default:
565         usage();
566       }
567     else break;
568   if (i < argc) {
569     if (!freopen(argv[i], "r", stdin))
570       perror(argv[i]), exit(1);
571     i++;
572   }
573
574   setup_workspace();
575   setup_updates();
576
577   setup_local_audio();
578   setup_system_audio();
579
580   setup_threading();
581   setup_switching();
582 }
583
584 //========================================================================
585
586 int 
587 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }