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 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 top.jack.blow.rb.i++;
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);
125 jack_ringbuffer_read_space(top.jack.ring.i.l)
126 >= top.hold.size.bytes;
131 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
132 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
137 run_pass(void) { uni.tick++; }
141 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
144 // NB do not set RUN_SWCH directly via setRunState;
145 // use setSWCH instead
149 if (top.swch.bfct.have == 0) {
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;
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) {
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++;
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;
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;
178 jack_ringbuffer_reset(top.jack.ring.o.l);
179 jack_ringbuffer_reset(top.jack.ring.o.r);
182 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
185 //========================================================================
189 audio_callback(jack_nframes_t nframes, void *arg) {
191 int nbytes = nframes * sizeof(float);
193 if (nframes == top.jack.size) {
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);
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++;
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++;
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);
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);
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);
248 process_samples_thread(void) {
249 while (top.running) {
250 sem_wait(&top.sync.buf.sem);
253 sem_wait(&top.sync.upd.sem);
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;
260 sem_post(&top.sync.upd.sem);
266 //========================================================================
271 sem_post(&top.sync.upd.sem);
275 FILE *frc = find_rcfile(loc.path.rcfile);
277 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
278 do_update(top.parm.buff, top.verbose ? stderr : 0);
283 // start audio processing
284 if (jack_activate(top.jack.client))
285 perror("cannot activate jack client"), exit(1);
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);
292 // stop audio processing
293 jack_client_close(top.jack.client);
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);
303 safefree((char *) top.hold.buf.r);
304 safefree((char *) top.hold.buf.l);
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");
334 arg = fcntl(fd, F_GETFL);
336 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
337 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
338 fcntl(fd, F_SETFL, arg);
341 //........................................................................
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);
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");
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);
371 jack_xrun(void *arg) {
373 sem_post(&top.sync.mon.sem);
377 jack_shutdown(void *arg) {}
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);
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));
392 top.jack.port.i.l = jack_port_register(top.jack.client,
394 JACK_DEFAULT_AUDIO_TYPE,
397 top.jack.port.i.r = jack_port_register(top.jack.client,
399 JACK_DEFAULT_AUDIO_TYPE,
402 top.jack.port.o.l = jack_port_register(top.jack.client,
404 JACK_DEFAULT_AUDIO_TYPE,
407 top.jack.port.o.r = jack_port_register(top.jack.client,
409 JACK_DEFAULT_AUDIO_TYPE,
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));
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);
430 //========================================================================
431 // hard defaults, then environment
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;
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);
461 //========================================================================
463 setup(int argc, char **argv) {
468 top.start_tv = now_tv();
471 top.state = RUN_PLAY;
475 for (i = 1; i < argc; i++)
476 if (argv[i][0] == '-')
477 switch (argv[i][1]) {
482 strcpy(loc.path.rcfile, argv[++i]);
485 uni.meter.flag = TRUE;
488 strcpy(loc.path.parm, argv[++i]);
491 strcpy(loc.path.meter, argv[++i]);
494 strcpy(loc.path.wisdom, argv[++i]);
497 loc.def.rate = atof(argv[++i]);
500 loc.def.size = atoi(argv[++i]);
503 loc.def.mode = atoi(argv[++i]);
506 loc.mult.ring = atoi(argv[++i]);
509 loc.mult.meter = atoi(argv[++i]);
516 if (!freopen(argv[i], "r", stdin))
517 perror(argv[i]), exit(1);
525 setup_system_audio();
531 //========================================================================
534 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }