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;
175 // move this out of main! -----------------------------
178 for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0;
181 //-----------------------------------------------------
183 top.state = top.swch.run.last;
184 top.swch.bfct.want = top.swch.bfct.have = 0;
186 jack_ringbuffer_reset(top.jack.ring.o.l);
187 jack_ringbuffer_reset(top.jack.ring.o.r);
190 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
193 //========================================================================
197 audio_callback(jack_nframes_t nframes, void *arg) {
199 int nbytes = nframes * sizeof(float);
201 if (nframes == top.jack.size) {
203 // output: copy from ring to port
204 lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
205 rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
207 if (jack_ringbuffer_read_space(top.jack.ring.o.l) >= nbytes) {
208 jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
209 jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
210 } else { // rb pathology
211 memset((char *) lp, 0, nbytes);
212 memset((char *) rp, 0, nbytes);
213 jack_ringbuffer_reset(top.jack.ring.o.l);
214 jack_ringbuffer_reset(top.jack.ring.o.r);
215 clear_jack_ringbuffer(top.jack.ring.o.l, nbytes);
216 clear_jack_ringbuffer(top.jack.ring.o.r, nbytes);
217 top.jack.blow.rb.o++;
220 // input: copy from port to ring
221 if (jack_ringbuffer_write_space(top.jack.ring.i.l) >= nbytes) {
222 lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
223 rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
224 jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
225 jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
226 } else { // rb pathology
227 jack_ringbuffer_reset(top.jack.ring.i.l);
228 jack_ringbuffer_reset(top.jack.ring.i.r);
229 clear_jack_ringbuffer(top.jack.ring.i.l, nbytes);
230 clear_jack_ringbuffer(top.jack.ring.i.r, nbytes);
231 top.jack.blow.rb.i++;
234 } else { // callback pathology
235 jack_ringbuffer_reset(top.jack.ring.i.l);
236 jack_ringbuffer_reset(top.jack.ring.i.r);
237 jack_ringbuffer_reset(top.jack.ring.o.l);
238 jack_ringbuffer_reset(top.jack.ring.o.r);
239 clear_jack_ringbuffer(top.jack.ring.o.l, top.hold.size.bytes);
240 clear_jack_ringbuffer(top.jack.ring.o.r, top.hold.size.bytes);
244 // if enough accumulated in ring, fire dsp
245 if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
246 sem_post(&top.sync.buf.sem);
249 if ((top.jack.blow.cb > 0) ||
250 (top.jack.blow.rb.i > 0) ||
251 (top.jack.blow.rb.o > 0))
252 sem_post(&top.sync.mon.sem);
256 process_samples_thread(void) {
257 while (top.running) {
258 sem_wait(&top.sync.buf.sem);
261 sem_wait(&top.sync.upd.sem);
263 case RUN_MUTE: run_mute(); break;
264 case RUN_PASS: run_pass(); break;
265 case RUN_PLAY: run_play(); break;
266 case RUN_SWCH: run_swch(); break;
268 sem_post(&top.sync.upd.sem);
274 //========================================================================
279 sem_post(&top.sync.upd.sem);
283 FILE *frc = find_rcfile(loc.path.rcfile);
285 while (fgets(top.parm.buff, sizeof(top.parm.buff), frc))
286 do_update(top.parm.buff, top.verbose ? stderr : 0);
291 // start audio processing
292 if (jack_activate(top.jack.client))
293 perror("cannot activate jack client"), exit(1);
295 // wait for threads to terminate
296 pthread_join(top.thrd.trx.id, 0);
297 pthread_join(top.thrd.upd.id, 0);
298 pthread_join(top.thrd.mon.id, 0);
300 // stop audio processing
301 jack_client_close(top.jack.client);
306 jack_ringbuffer_free(top.jack.ring.o.r);
307 jack_ringbuffer_free(top.jack.ring.o.l);
308 jack_ringbuffer_free(top.jack.ring.i.r);
309 jack_ringbuffer_free(top.jack.ring.i.l);
311 safefree((char *) top.hold.buf.r);
312 safefree((char *) top.hold.buf.l);
321 fprintf(stderr, "usage:\n");
322 fprintf(stderr, "jsdr [-flag [arg]] [file]\n");
323 fprintf(stderr, "flags:\n");
324 fprintf(stderr, " -v verbose commentary\n");
325 fprintf(stderr, " -m do metering\n");
326 fprintf(stderr, " -l file execute update commands in file at startup\n");
327 fprintf(stderr, " -P cmdpath path to command/update pipe\n");
328 fprintf(stderr, " -S s-mtrpath path to S-meter output channel\n");
329 fprintf(stderr, " -W wispath path to FFTW wisdom file\n");
330 fprintf(stderr, " -R rate sampling rate\n");
331 fprintf(stderr, " -B bufsize internal DSP buffer size\n");
332 fprintf(stderr, " -M mode start up in mode (SAM, USB, LCW, etc.)\n");
333 fprintf(stderr, " -G num use num as ringbuffer mult\n");
334 fprintf(stderr, " -E num use num as meter chan mult\n");
335 fprintf(stderr, "'file' arg unused, but available\n");
342 arg = fcntl(fd, F_GETFL);
344 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
345 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
346 fcntl(fd, F_SETFL, arg);
349 //........................................................................
352 setup_switching(void) {
353 top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
354 top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
358 setup_local_audio(void) {
359 top.hold.size.frames = uni.buflen;
360 top.hold.size.bytes = top.hold.size.frames * sizeof(float);
361 top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
362 "main hold buffer left");
363 top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
364 "main hold buffer right");
368 setup_updates(void) {
369 top.parm.path = loc.path.parm;
370 if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
371 perror(top.parm.path), exit(1);
372 if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
373 fprintf(stderr, "can't fdopen parm pipe %s\n", loc.path.parm);
379 jack_xrun(void *arg) {
381 sem_post(&top.sync.mon.sem);
385 jack_shutdown(void *arg) {}
388 setup_system_audio(void) {
389 if (loc.name[0]) strcpy(top.jack.name, loc.name);
390 else sprintf(top.jack.name, "sdr-%d", top.pid);
391 if (!(top.jack.client = jack_client_new(top.jack.name)))
392 perror("can't make client -- jack not running?"), exit(1);
394 jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
395 jack_on_shutdown(top.jack.client, (void *) jack_shutdown, 0);
396 jack_set_xrun_callback(top.jack.client, (void *) jack_xrun, 0);
397 top.jack.size = jack_get_buffer_size(top.jack.client);
398 memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow));
400 top.jack.port.i.l = jack_port_register(top.jack.client,
402 JACK_DEFAULT_AUDIO_TYPE,
405 top.jack.port.i.r = jack_port_register(top.jack.client,
407 JACK_DEFAULT_AUDIO_TYPE,
410 top.jack.port.o.l = jack_port_register(top.jack.client,
412 JACK_DEFAULT_AUDIO_TYPE,
415 top.jack.port.o.r = jack_port_register(top.jack.client,
417 JACK_DEFAULT_AUDIO_TYPE,
420 top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
421 top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
422 top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
423 top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * loc.mult.ring);
424 clear_jack_ringbuffer(top.jack.ring.o.l, top.jack.size * sizeof(float));
425 clear_jack_ringbuffer(top.jack.ring.o.r, top.jack.size * sizeof(float));
429 setup_threading(void) {
430 sem_init(&top.sync.upd.sem, 0, 0);
431 pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
432 sem_init(&top.sync.buf.sem, 0, 0);
433 pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
434 sem_init(&top.sync.mon.sem, 0, 0);
435 pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL);
438 //========================================================================
439 // hard defaults, then environment
442 setup_defaults(void) {
443 loc.name[0] = 0; // no default name for jack client
444 strcpy(loc.path.rcfile, RCBASE);
445 strcpy(loc.path.parm, PARMPATH);
446 strcpy(loc.path.meter, METERPATH);
447 strcpy(loc.path.wisdom, WISDOMPATH);
448 loc.def.rate = DEFRATE;
449 loc.def.size = DEFSIZE;
450 loc.def.mode = DEFMODE;
452 loc.mult.ring = RINGMULT;
453 loc.mult.meter = METERMULT;
457 if ((ep = getenv("SDR_NAME"))) strcpy(loc.name, ep);
458 if ((ep = getenv("SDR_RCBASE"))) strcpy(loc.path.rcfile, ep);
459 if ((ep = getenv("SDR_PARMPATH"))) strcpy(loc.path.parm, ep);
460 if ((ep = getenv("SDR_METERPATH"))) strcpy(loc.path.meter, ep);
461 if ((ep = getenv("SDR_WISDOMPATH"))) strcpy(loc.path.wisdom, ep);
462 if ((ep = getenv("SDR_RINGMULT"))) loc.mult.ring = atoi(ep);
463 if ((ep = getenv("SDR_METERMULT"))) loc.mult.meter = atoi(ep);
464 if ((ep = getenv("SDR_DEFRATE"))) loc.def.rate = atof(ep);
465 if ((ep = getenv("SDR_DEFSIZE"))) loc.def.size = atoi(ep);
466 if ((ep = getenv("SDR_DEFMODE"))) loc.def.mode = atoi(ep);
470 //========================================================================
472 setup(int argc, char **argv) {
477 top.start_tv = now_tv();
480 top.state = RUN_PLAY;
484 for (i = 1; i < argc; i++)
485 if (argv[i][0] == '-')
486 switch (argv[i][1]) {
491 strcpy(loc.path.rcfile, argv[++i]);
494 uni.meter.flag = TRUE;
497 strcpy(loc.path.parm, argv[++i]);
500 strcpy(loc.path.meter, argv[++i]);
503 strcpy(loc.path.wisdom, argv[++i]);
506 loc.def.rate = atof(argv[++i]);
509 loc.def.size = atoi(argv[++i]);
512 loc.def.mode = atoi(argv[++i]);
515 loc.mult.ring = atoi(argv[++i]);
518 loc.mult.meter = atoi(argv[++i]);
525 if (!freopen(argv[i], "r", stdin))
526 perror(argv[i]), exit(1);
534 setup_system_audio();
540 //========================================================================
543 main(int argc, char **argv) { setup(argc, argv), execute(), closeup(); }