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