]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/main.c
b68ba9579eba3b202ac4e47251ea62cbc74c1879
[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 *, 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     top.jack.blow.rb.i++;
112   } else {
113     jack_ringbuffer_read(top.jack.ring.i.l,
114                          (char *) top.hold.buf.l,
115                          top.hold.size.bytes);
116     jack_ringbuffer_read(top.jack.ring.i.r,
117                          (char *) top.hold.buf.r,
118                          top.hold.size.bytes);
119   }
120 }
121
122 PRIVATE BOOLEAN
123 canhold(void) {
124   return
125     jack_ringbuffer_read_space(top.jack.ring.i.l)
126     >= top.hold.size.bytes;
127 }
128
129 PRIVATE void 
130 run_mute(void) {
131   memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
132   memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
133   uni.tick++;
134 }
135
136 PRIVATE void 
137 run_pass(void) { uni.tick++; }
138
139 PRIVATE void 
140 run_play(void) {
141   process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
142
143
144 // NB do not set RUN_SWCH directly via setRunState;
145 // use setSWCH instead
146
147 PRIVATE void 
148 run_swch(void) {
149   if (top.swch.bfct.have == 0) {
150     // first time
151     // apply ramp down
152     int i, m = top.swch.fade, n = top.swch.tail;
153     for (i = 0; i < m; i++) {
154       float w = (float) 1.0 - (float) i / m;
155       top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
156     }
157     memset((char *) (top.hold.buf.l + m), 0, n);
158     memset((char *) (top.hold.buf.r + m), 0, n);
159     top.swch.bfct.have++;
160   } else if (top.swch.bfct.have < top.swch.bfct.want) {
161     // in medias res
162     memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
163     memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
164     top.swch.bfct.have++;
165   } else {
166     // last time
167     // apply ramp up
168     int i, m = top.swch.fade, n = top.swch.tail;
169     for (i = 0; i < m; i++) {
170       float w = (float) i / m;
171       top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
172     }
173     uni.mode.trx = top.swch.trx.next;
174
175     // move this out of main! -----------------------------
176     {
177       int k;
178       for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0;
179     }
180     tx.tick = 0;
181     //-----------------------------------------------------
182
183     top.state = top.swch.run.last;
184     top.swch.bfct.want = top.swch.bfct.have = 0;
185
186     jack_ringbuffer_reset(top.jack.ring.o.l);
187     jack_ringbuffer_reset(top.jack.ring.o.r);
188   }
189
190   process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
191
192
193 //========================================================================
194
195
196 PRIVATE void 
197 audio_callback(jack_nframes_t nframes, void *arg) {
198   float *lp, *rp;
199   int nbytes = nframes * sizeof(float);
200
201   if (nframes == top.jack.size) {
202
203     // output: copy from ring to port
204     lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
205     rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
206
207     if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
208       jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
209       jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
210     } else { // rb pathology
211       memset((char *) lp, 0, nbytes);
212       memset((char *) rp, 0, nbytes);
213       jack_ringbuffer_reset(top.jack.ring.o.l);
214       jack_ringbuffer_reset(top.jack.ring.o.r);
215       clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
216       clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
217       top.jack.blow.rb.o++;
218     }
219     
220     // input: copy from port to ring
221     if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
222       lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
223       rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
224       jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
225       jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
226     } else { // rb pathology
227       jack_ringbuffer_reset(top.jack.ring.i.l);
228       jack_ringbuffer_reset(top.jack.ring.i.r);
229       clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
230       clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
231       top.jack.blow.rb.i++;
232     }
233
234   } else { // callback pathology
235     jack_ringbuffer_reset(top.jack.ring.i.l);
236     jack_ringbuffer_reset(top.jack.ring.i.r);
237     jack_ringbuffer_reset(top.jack.ring.o.l);
238     jack_ringbuffer_reset(top.jack.ring.o.r);
239     clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
240     clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
241     top.jack.blow.cb++;
242   }
243
244   // if enough accumulated in ring, fire dsp
245   if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
246     sem_post(&top.sync.buf.sem);
247
248   // check for blowups
249   if ((top.jack.blow.cb > 0) ||
250       (top.jack.blow.rb.i > 0) ||
251       (top.jack.blow.rb.o > 0))
252     sem_post(&top.sync.mon.sem);
253 }
254
255 PRIVATE void 
256 process_samples_thread(void) {
257   while (top.running) {
258     sem_wait(&top.sync.buf.sem);
259     do {
260       gethold();
261       sem_wait(&top.sync.upd.sem);
262       switch (top.state) {
263       case RUN_MUTE: run_mute(); break;
264       case RUN_PASS: run_pass(); break;
265       case RUN_PLAY: run_play(); break;
266       case RUN_SWCH: run_swch(); break;
267       }
268       sem_post(&top.sync.upd.sem);
269     } while (canhold());
270   }
271   pthread_exit(0);
272 }
273
274 //========================================================================
275
276 PRIVATE void 
277 execute(void) {
278   // let updates run
279   sem_post(&top.sync.upd.sem);
280   
281   // rcfile
282   {
283     FILE *frc = find_rcfile(loc.path.rcfile);
284     if (frc) {
285       while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
286         do_update(top.parm.buff, top.verbose ? stderr : 0);
287       fclose(frc);
288     }
289   }
290
291   // start audio processing
292   if (jack_activate(top.jack.client))
293     perror("cannot activate jack client"), exit(1);
294   
295   // wait for threads to terminate
296   pthread_join(top.thrd.trx.id, 0);
297   pthread_join(top.thrd.upd.id, 0);
298   pthread_join(top.thrd.mon.id, 0);
299   
300   // stop audio processing
301   jack_client_close(top.jack.client);
302 }
303
304 PRIVATE void 
305 closeup(void) {
306   jack_ringbuffer_free(top.jack.ring.o.r);
307   jack_ringbuffer_free(top.jack.ring.o.l);
308   jack_ringbuffer_free(top.jack.ring.i.r);
309   jack_ringbuffer_free(top.jack.ring.i.l);
310
311   safefree((char *) top.hold.buf.r);
312   safefree((char *) top.hold.buf.l);
313
314   destroy_workspace();
315
316   exit(0);
317 }
318
319 PRIVATE void 
320 usage(void) {
321   fprintf(stderr, "usage:\n");
322   fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
323   fprintf(stderr, "flags:\n");
324   fprintf(stderr, "     -v              verbose commentary\n");
325   fprintf(stderr, "     -m              do metering\n");
326   fprintf(stderr, "     -l file         execute update commands in file at startup\n");
327   fprintf(stderr, "     -P cmdpath      path to command/update pipe\n");
328   fprintf(stderr, "     -S s-mtrpath    path to S-meter output channel\n");
329   fprintf(stderr, "     -W wispath      path to FFTW wisdom file\n");
330   fprintf(stderr, "     -R rate         sampling rate\n");
331   fprintf(stderr, "     -B bufsize      internal DSP buffer size\n");
332   fprintf(stderr, "     -M mode         start up in mode (SAM, USB, LCW, etc.)\n");
333   fprintf(stderr, "     -G num          use num as ringbuffer mult\n");
334   fprintf(stderr, "     -E num          use num as meter chan mult\n");
335   fprintf(stderr, "'file' arg unused, but available\n");
336   exit(1);
337 }
338
339 PRIVATE void 
340 nonblock(int fd) {
341   long arg;
342   arg = fcntl(fd, F_GETFL);
343   arg |= O_NONBLOCK;
344 /*  if (fcntl(fd, F_GETFL, &arg) >= 0)
345     fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ 
346   fcntl(fd, F_SETFL, arg);
347
348
349 //........................................................................
350
351 PRIVATE void 
352 setup_switching(void) {
353   top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
354   top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
355 }
356
357 PRIVATE void 
358 setup_local_audio(void) {
359   top.hold.size.frames = uni.buflen;
360   top.hold.size.bytes = top.hold.size.frames * sizeof(float);
361   top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
362                                        "main hold buffer left");
363   top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
364                                        "main hold buffer right");
365
366
367 PRIVATE void 
368 setup_updates(void) {
369   top.parm.path = loc.path.parm;
370   if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
371     perror(top.parm.path), exit(1);
372   if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
373     fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
374     exit(1);
375   }
376 }
377
378 PRIVATE void
379 jack_xrun(void *arg) {
380   top.jack.blow.xr++;
381   sem_post(&top.sync.mon.sem);
382 }
383
384 PRIVATE void
385 jack_shutdown(void *arg) {}
386
387 PRIVATE void 
388 setup_system_audio(void) {
389   if (loc.name[0]) strcpy(top.jack.name, loc.name);
390   else sprintf(top.jack.name, "sdr-%d", top.pid);
391   if (!(top.jack.client = jack_client_new(top.jack.name)))
392     perror("can't make client -- jack not running?"), exit(1);
393
394   jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
395   jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
396   jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
397   top.jack.size = jack_get_buffer_size(top.jack.client);
398   memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
399
400   top.jack.port.i.l = jack_port_register(top.jack.client,
401                                          "il",
402                                          JACK_DEFAULT_AUDIO_TYPE,
403                                          JackPortIsInput,
404                                          0);
405   top.jack.port.i.r = jack_port_register(top.jack.client,
406                                          "ir",
407                                          JACK_DEFAULT_AUDIO_TYPE,
408                                          JackPortIsInput,
409                                          0);
410   top.jack.port.o.l = jack_port_register(top.jack.client,
411                                          "ol",
412                                          JACK_DEFAULT_AUDIO_TYPE,
413                                          JackPortIsOutput,
414                                          0);
415   top.jack.port.o.r = jack_port_register(top.jack.client,
416                                          "or",
417                                          JACK_DEFAULT_AUDIO_TYPE,
418                                          JackPortIsOutput,
419                                          0);
420   top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
421   top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
422   top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
423   top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
424   clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
425   clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
426 }
427
428 PRIVATE void 
429 setup_threading(void) {
430   sem_init(&top.sync.upd.sem, 0, 0);
431   pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
432   sem_init(&top.sync.buf.sem, 0, 0);
433   pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
434   sem_init(&top.sync.mon.sem, 0, 0);
435   pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
436
437
438 //========================================================================
439 // hard defaults, then environment
440
441 PRIVATE void
442 setup_defaults(void) {
443   loc.name[0] = 0; // no default name for jack client
444   strcpy(loc.path.rcfile, RCBASE);
445   strcpy(loc.path.parm, PARMPATH);
446   strcpy(loc.path.meter, METERPATH);
447   strcpy(loc.path.wisdom, WISDOMPATH);
448   loc.def.rate = DEFRATE;
449   loc.def.size = DEFSIZE;
450   loc.def.mode = DEFMODE;
451   loc.def.nrx = MAXRX;
452   loc.mult.ring = RINGMULT;
453   loc.mult.meter = METERMULT;
454
455   {
456     char *ep;
457     if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
458     if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
459     if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
460     if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
461     if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
462     if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
463     if ((ep = getenv("SDR_METERMULT"))) loc.mult.meter = atoi(ep);
464     if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
465     if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
466     if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
467   }
468 }
469
470 //========================================================================
471 PRIVATE void 
472 setup(int argc, char **argv) {
473   int i;
474
475   top.uid = getuid();
476   top.pid = getpid();
477   top.start_tv = now_tv();
478   top.running = TRUE;
479   top.verbose = FALSE;
480   top.state = RUN_PLAY;
481
482   setup_defaults();
483   
484   for (i = 1; i < argc; i++)
485     if (argv[i][0] == '-')
486       switch (argv[i][1]) {
487       case 'v':
488         top.verbose = TRUE;
489         break;
490       case 'l':
491         strcpy(loc.path.rcfile, argv[++i]);
492         break;
493       case 'm':
494         uni.meter.flag = TRUE;
495         break;
496       case 'P':
497         strcpy(loc.path.parm, argv[++i]);
498         break;
499       case 'S':
500         strcpy(loc.path.meter, argv[++i]);
501         break;
502       case 'W':
503         strcpy(loc.path.wisdom, argv[++i]);
504         break;
505       case 'R':
506         loc.def.rate = atof(argv[++i]);
507         break;
508       case 'B':
509         loc.def.size = atoi(argv[++i]);
510         break;
511       case 'M':
512         loc.def.mode = atoi(argv[++i]);
513         break;
514       case 'G':
515         loc.mult.ring = atoi(argv[++i]);
516         break;
517       case 'E':
518         loc.mult.meter = atoi(argv[++i]);
519         break;
520       default:
521         usage();
522       }
523     else break;
524   if (i < argc) {
525     if (!freopen(argv[i], "r", stdin))
526       perror(argv[i]), exit(1);
527     i++;
528   }
529
530   setup_workspace();
531   setup_updates();
532
533   setup_local_audio();
534   setup_system_audio();
535
536   setup_threading();
537   setup_switching();
538 }
539
540 //========================================================================
541
542 int 
543 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }