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