3 This file is part of a program that implements a Software-Defined Radio.
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
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.
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.
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
21 The authors can be reached by email at
29 The DTTS Microwave Society
36 /////////////////////////////////////////////////////////////////////////
37 // elementary defaults
41 /////////////////////////////////////////////////////////////////////////
42 // most of what little we know here about the inner loop,
43 // functionally speaking
45 extern void process_samples(float *, float *, float *, float *, int);
46 extern void setup_workspace(void);
47 extern void destroy_workspace(void);
49 //========================================================================
52 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
55 for (i = 0; i < nbytes; i++)
56 jack_ringbuffer_write(rb, &zero, 1);
59 //========================================================================
62 monitor_thread(void) {
64 sem_wait(&top.sync.mon.sem);
66 "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n",
72 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
77 //========================================================================
80 process_updates_thread(void) {
83 while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
84 do_update(top.parm.buff, top.verbose ? stderr : 0);
91 if (jack_ringbuffer_write_space(top.jack.ring.o.l)
92 < top.hold.size.bytes) {
94 jack_ringbuffer_reset(top.jack.ring.o.l);
95 jack_ringbuffer_reset(top.jack.ring.o.r);
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) {
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++;
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);
135 jack_ringbuffer_read_space(top.jack.ring.i.l)
136 >= top.hold.size.bytes;
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);
149 run_pass(void) { uni.tick++; }
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);
158 // NB do not set RUN_SWCH directly via setRunState;
159 // use setSWCH instead
163 if (top.swch.bfct.have == 0) {
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;
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) {
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++;
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;
187 uni.mode.trx = top.swch.trx.next;
189 // move this out of main! -----------------------------
192 for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0;
195 //-----------------------------------------------------
197 top.state = top.swch.run.last;
198 top.swch.bfct.want = top.swch.bfct.have = 0;
200 jack_ringbuffer_reset(top.jack.ring.o.l);
201 jack_ringbuffer_reset(top.jack.ring.o.r);
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);
209 //========================================================================
213 audio_callback(jack_nframes_t nframes, void *arg) {
215 int nbytes = nframes * sizeof(float);
217 if (nframes == top.jack.size) {
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);
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++;
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++;
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);
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);
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);
280 process_samples_thread(void) {
281 while (top.running) {
282 sem_wait(&top.sync.buf.sem);
285 sem_wait(&top.sync.upd.sem);
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;
292 sem_post(&top.sync.upd.sem);
298 //========================================================================
303 sem_post(&top.sync.upd.sem);
307 FILE *frc = find_rcfile(loc.path.rcfile);
309 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
310 do_update(top.parm.buff, top.verbose ? stderr : 0);
315 // start audio processing
316 if (jack_activate(top.jack.client))
317 perror("cannot activate jack client"), exit(1);
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);
324 // stop audio processing
325 jack_client_close(top.jack.client);
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);
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);
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");
370 arg = fcntl(fd, F_GETFL);
372 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
373 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
374 fcntl(fd, F_SETFL, arg);
377 //........................................................................
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);
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");
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);
411 jack_xrun(void *arg) {
413 sem_post(&top.sync.mon.sem);
417 jack_shutdown(void *arg) {}
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);
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));
432 top.jack.port.i.l = jack_port_register(top.jack.client,
434 JACK_DEFAULT_AUDIO_TYPE,
437 top.jack.port.i.r = jack_port_register(top.jack.client,
439 JACK_DEFAULT_AUDIO_TYPE,
442 top.jack.port.o.l = jack_port_register(top.jack.client,
444 JACK_DEFAULT_AUDIO_TYPE,
447 top.jack.port.o.r = jack_port_register(top.jack.client,
449 JACK_DEFAULT_AUDIO_TYPE,
452 top.jack.auxp.i.l = jack_port_register(top.jack.client,
454 JACK_DEFAULT_AUDIO_TYPE,
457 top.jack.auxp.i.r = jack_port_register(top.jack.client,
459 JACK_DEFAULT_AUDIO_TYPE,
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));
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);
482 //========================================================================
483 // hard defaults, then environment
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;
496 loc.mult.ring = RINGMULT;
497 loc.mult.meter = METERMULT;
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);
514 //========================================================================
516 setup(int argc, char **argv) {
521 top.start_tv = now_tv();
524 top.state = RUN_PLAY;
528 for (i = 1; i < argc; i++)
529 if (argv[i][0] == '-')
530 switch (argv[i][1]) {
535 strcpy(loc.path.rcfile, argv[++i]);
538 uni.meter.flag = TRUE;
541 strcpy(loc.path.parm, argv[++i]);
544 strcpy(loc.path.meter, argv[++i]);
547 strcpy(loc.path.wisdom, argv[++i]);
550 loc.def.rate = atof(argv[++i]);
553 loc.def.size = atoi(argv[++i]);
556 loc.def.mode = atoi(argv[++i]);
559 loc.mult.ring = atoi(argv[++i]);
562 loc.mult.meter = atoi(argv[++i]);
569 if (!freopen(argv[i], "r", stdin))
570 perror(argv[i]), exit(1);
578 setup_system_audio();
584 //========================================================================
587 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }