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 *, int);
46 extern void setup_workspace(void);
47 extern void destroy_workspace(void);
49 //========================================================================
52 monitor_thread(void) {
54 sem_wait(&top.sync.mon.sem);
56 "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n",
62 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
67 //========================================================================
70 process_updates_thread(void) {
73 while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
74 do_update(top.parm.buff, top.verbose ? stderr : 0);
81 if (jack_ringbuffer_write_space(top.jack.ring.o.l)
82 < top.hold.size.bytes) {
84 jack_ringbuffer_reset(top.jack.ring.o.l);
85 jack_ringbuffer_reset(top.jack.ring.o.r);
88 jack_ringbuffer_write(top.jack.ring.o.l,
89 (char *) top.hold.buf.l,
91 jack_ringbuffer_write(top.jack.ring.o.r,
92 (char *) top.hold.buf.r,
94 if (jack_ringbuffer_read_space(top.jack.ring.i.l)
95 < top.hold.size.bytes) {
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++;
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);
115 jack_ringbuffer_read_space(top.jack.ring.i.l)
116 >= top.hold.size.bytes;
121 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
122 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
127 run_pass(void) { uni.tick++; }
131 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
134 // NB do not set RUN_SWCH directly via setRunState;
135 // use setSWCH instead
139 if (top.swch.bfct.have == 0) {
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;
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) {
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++;
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;
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;
169 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
172 //========================================================================
175 clear_jack_ringbuffer(jack_ringbuffer_t *rb, int nbytes) {
178 for (i = 0; i < nbytes; i++)
179 jack_ringbuffer_write(rb, &zero, 1);
183 audio_callback(jack_nframes_t nframes, void *arg) {
185 int nbytes = nframes * sizeof(float);
187 if (nframes == top.jack.size) {
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);
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++;
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++;
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);
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);
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);
242 process_samples_thread(void) {
243 while (top.running) {
244 sem_wait(&top.sync.buf.sem);
247 sem_wait(&top.sync.upd.sem);
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;
254 sem_post(&top.sync.upd.sem);
260 //========================================================================
265 sem_post(&top.sync.upd.sem);
269 FILE *frc = find_rcfile(loc.path.rcfile);
271 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
272 do_update(top.parm.buff, top.verbose ? stderr : 0);
277 // start audio processing
278 if (jack_activate(top.jack.client))
279 perror("cannot activate jack client"), exit(1);
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);
286 // stop audio processing
287 jack_client_close(top.jack.client);
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);
297 safefree((char *) top.hold.buf.r);
298 safefree((char *) top.hold.buf.l);
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");
328 arg = fcntl(fd, F_GETFL);
330 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
331 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
332 fcntl(fd, F_SETFL, arg);
335 //........................................................................
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);
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");
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);
365 jack_xrun(void *arg) {
367 sem_post(&top.sync.mon.sem);
371 jack_shutdown(void *arg) {}
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);
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));
386 top.jack.port.i.l = jack_port_register(top.jack.client,
388 JACK_DEFAULT_AUDIO_TYPE,
391 top.jack.port.i.r = jack_port_register(top.jack.client,
393 JACK_DEFAULT_AUDIO_TYPE,
396 top.jack.port.o.l = jack_port_register(top.jack.client,
398 JACK_DEFAULT_AUDIO_TYPE,
401 top.jack.port.o.r = jack_port_register(top.jack.client,
403 JACK_DEFAULT_AUDIO_TYPE,
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);
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);
424 //========================================================================
425 // hard defaults, then environment
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;
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);
455 //========================================================================
457 setup(int argc, char **argv) {
462 top.start_tv = now_tv();
465 top.state = RUN_PLAY;
469 for (i = 1; i < argc; i++)
470 if (argv[i][0] == '-')
471 switch (argv[i][1]) {
476 strcpy(loc.path.rcfile, argv[++i]);
479 uni.meter.flag = TRUE;
482 strcpy(loc.path.parm, argv[++i]);
485 strcpy(loc.path.meter, argv[++i]);
488 strcpy(loc.path.wisdom, argv[++i]);
491 loc.def.rate = atof(argv[++i]);
494 loc.def.size = atoi(argv[++i]);
497 loc.def.mode = atoi(argv[++i]);
500 loc.mult.ring = atoi(argv[++i]);
503 loc.mult.meter = atoi(argv[++i]);
510 if (!freopen(argv[i], "r", stdin))
511 perror(argv[i]), exit(1);
519 setup_system_audio();
525 //========================================================================
528 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }