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 extern void process_samples(float *, float *, int);
38 extern void setup_workspace(void);
44 #define METERPATH NULL
47 #define PWSMULT (32768)
52 #define PARMPATH "./IPC/SDR-1000-0-commands.fifo"
54 #define METERPATH "./IPC/SDR-1000-0-meter.chan"
56 #define PWSPATH "./IPC/SDR-1000-0-pws.chan"
57 #define PWSMULT (32768)
60 //------------------------------------------------------------------------
64 process_updates_thread(void) {
67 while (newupdates()) {
68 tmp = ringb_read_space(uni.update.chan.c->rb);
69 getChan_nowait(uni.update.chan.c, top.parm.buff, tmp);
70 do_update(top.parm.buff);
76 //========================================================================
80 ringb_write(top.jack.ring.o.l, (char *) top.hold.buf.l, top.hold.size.bytes);
81 ringb_write(top.jack.ring.o.r, (char *) top.hold.buf.r, top.hold.size.bytes);
82 ringb_read(top.jack.ring.i.l, (char *) top.hold.buf.l, top.hold.size.bytes);
83 ringb_read(top.jack.ring.i.r, (char *) top.hold.buf.r, top.hold.size.bytes);
86 PRIVATE BOOLEAN canhold(void) {
87 return ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes;
93 //========================================================================
96 process_updates_thread(void) {
98 while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
99 do_update(top.parm.buff, top.verbose ? stderr : 0);
106 jack_ringbuffer_write(top.jack.ring.o.l, (char *) top.hold.buf.l, top.hold.size.bytes);
107 jack_ringbuffer_write(top.jack.ring.o.r, (char *) top.hold.buf.r, top.hold.size.bytes);
108 jack_ringbuffer_read(top.jack.ring.i.l, (char *) top.hold.buf.l, top.hold.size.bytes);
109 jack_ringbuffer_read(top.jack.ring.i.r, (char *) top.hold.buf.r, top.hold.size.bytes);
112 PRIVATE BOOLEAN canhold(void) {
113 return jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes;
120 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
121 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
129 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
132 // NB do not set RUN_SWCH directly via setRunState;
133 // use setSWCH instead
137 if (top.swch.bfct.have == 0) {
140 int i, m = top.swch.fade, n = top.swch.tail;
141 for (i = 0; i < m; i++) {
142 float w = (float) 1.0 - (float) i / m;
143 top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
145 memset((char *) (top.hold.buf.l + m), 0, n);
146 memset((char *) (top.hold.buf.r + m), 0, n);
147 top.swch.bfct.have++;
148 } else if (top.swch.bfct.have < top.swch.bfct.want) {
150 memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
151 memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
152 top.swch.bfct.have++;
156 int i, m = top.swch.fade, n = top.swch.tail;
157 for (i = 0; i < m; i++) {
158 float w = (float) i / m;
159 top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w;
161 uni.mode.trx = top.swch.trx.next;
162 top.state = top.swch.run.last;
163 top.swch.bfct.want = top.swch.bfct.have = 0;
166 process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
169 //========================================================================
173 static int exchptr = 0;
175 exchange_samples(float *input, float *output, int nframes) {
177 for (i = 0, j = 0; i < nframes; i++, j += 2) {
178 ringb_read(top.jack.ring.o.l, (char *) &output[j], sizeof(float));
179 ringb_read(top.jack.ring.o.r, (char *) &output[j + 1], sizeof(float));
180 ringb_write(top.jack.ring.i.l, (char *) &input[j], sizeof(float));
181 ringb_write(top.jack.ring.i.r, (char *) &input[j + 1], sizeof(float));
183 if (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
184 ReleaseSemaphore(top.sync.buf.sem, 1L, NULL);
188 audioreset(void) { exchptr = 0; }
191 process_samples_thread(void) {
192 while (top.running) {
193 WaitForSingleObject(top.sync.buf.sem, INFINITE);
194 WaitForSingleObject(top.sync.upd.sem, INFINITE);
212 ReleaseSemaphore(top.sync.upd.sem, 1L, NULL);
221 audio_callback(jack_nframes_t nframes, void *arg) {
223 int nbytes = nframes * sizeof(float);
225 // output: copy from ring to port
226 lp = (float *) jack_port_get_buffer(top.jack.port.o.l, nframes);
227 rp = (float *) jack_port_get_buffer(top.jack.port.o.r, nframes);
228 jack_ringbuffer_read(top.jack.ring.o.l, (char *) lp, nbytes);
229 jack_ringbuffer_read(top.jack.ring.o.r, (char *) rp, nbytes);
231 // input: copy from port to ring
232 lp = (float *) jack_port_get_buffer(top.jack.port.i.l, nframes);
233 rp = (float *) jack_port_get_buffer(top.jack.port.i.r, nframes);
234 jack_ringbuffer_write(top.jack.ring.i.l, (char *) lp, nbytes);
235 jack_ringbuffer_write(top.jack.ring.i.r, (char *) rp, nbytes);
237 // if enough accumulated in ring, fire dsp
238 if (jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
239 sem_post(&top.sync.buf.sem);
243 process_samples_thread(void) {
244 while (top.running) {
245 sem_wait(&top.sync.buf.sem);
248 sem_wait(&top.sync.upd.sem);
263 sem_post(&top.sync.upd.sem);
271 //========================================================================
276 ResumeThread(top.thrd.trx.thrd);
281 extern void destroy_workspace();
282 TerminateThread(top.thrd.trx.thrd, 0L);
283 CloseHandle(top.thrd.trx.thrd);
284 CloseHandle(top.thrd.upd.thrd);
285 CloseHandle(top.sync.buf.sem);
286 CloseHandle(top.sync.upd.sem);
294 sem_post(&top.sync.upd.sem);
296 // start audio processing
297 if (jack_activate(top.jack.client))
298 perror("cannot activate jack client"), exit(1);
300 // wait for threads to terminate
301 pthread_join(top.thrd.trx.id, 0);
302 pthread_join(top.thrd.upd.id, 0);
304 // stop audio processing
305 jack_client_close(top.jack.client);
309 closeup(void) { exit(0); }
313 fprintf(stderr, "usage:\n");
314 fprintf(stderr, "-m\n");
315 fprintf(stderr, "-v\n");
322 arg = fcntl(fd, F_GETFL);
324 /* if (fcntl(fd, F_GETFL, &arg) >= 0)
325 fcntl(fd, F_SETFL, arg | O_NONBLOCK); */
326 fcntl(fd, F_SETFL, arg);
331 //........................................................................
335 uni.meter.flag = TRUE;
336 uni.meter.chan.path = METERPATH;
337 uni.meter.chan.size = METERMULT * sizeof(REAL);
338 uni.meter.val = -200.0;
339 uni.meter.chan.c = openChan(uni.meter.chan.path, uni.meter.chan.size);
343 setup_powerspectrum(void) {
344 uni.powsp.flag = TRUE;
345 uni.powsp.chan.path = PWSPATH;
346 uni.powsp.chan.size = 8192 * sizeof(COMPLEX);
347 uni.powsp.buf = newCXB(4096, NULL, "PowerSpectrum Chan Buffer");
348 uni.powsp.fftsize = 4096;
349 uni.powsp.chan.c = openChan(uni.powsp.chan.path, uni.powsp.chan.size);
353 setup_switching(void) {
354 top.swch.fade = (int) (0.1 * uni.buflen + 0.5);
355 top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float);
359 setup_local_audio(void) {
360 top.hold.size.frames = uni.buflen;
361 top.hold.size.bytes = top.hold.size.frames * sizeof(float);
362 top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float),
363 "main hold buffer left");
364 top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float),
365 "main hold buffer right");
371 setup_updates(void) {
372 uni.update.flag = TRUE;
373 uni.update.chan.path = NULL;
374 uni.update.chan.size = 4096;
375 uni.update.chan.c = openChan(uni.update.chan.path, uni.update.chan.size);
378 setup_system_audio(void) {
379 sprintf(top.jack.name, "sdr-%d", top.pid);
380 top.jack.ring.i.l = ringb_create(top.hold.size.bytes * RINGMULT);
381 top.jack.ring.i.r = ringb_create(top.hold.size.bytes * RINGMULT);
382 top.jack.ring.o.l = ringb_create(top.hold.size.bytes * RINGMULT);
383 top.jack.ring.o.r = ringb_create(top.hold.size.bytes * RINGMULT);
387 for (i = 0; i < top.hold.size.bytes; i++) {
388 ringb_write(top.jack.ring.o.l, &zero, 1);
389 ringb_write(top.jack.ring.o.r, &zero, 1);
395 release_update(void) {}
398 suspend_sdr(void) { SuspendThread(top.thrd.trx.thrd); }
401 setup_threading(void) {
402 top.sync.upd.sem = CreateSemaphore(NULL, 1L, 1L, "UpdateSemaphore");
403 top.sync.buf.sem = CreateSemaphore(NULL, 0L, 1L, "Process Samples Semaphore");
404 top.thrd.trx.thrd = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0L, process_samples_thread,
405 NULL, CREATE_SUSPENDED, &top.thrd.trx.id);
406 SetThreadPriority(top.thrd.trx.thrd, THREAD_PRIORITY_HIGHEST);
411 top.pid = GetCurrentThreadId();
414 top.state = RUN_PLAY;
416 setup_powerspectrum();
418 uni.meter.flag = TRUE;
422 setup_system_audio();
430 setup_updates(void) {
431 top.parm.path = PARMPATH;
432 if ((top.parm.fd = open(top.parm.path, O_RDWR)) == -1)
433 perror(top.parm.path), exit(1);
434 if (!(top.parm.fp = fdopen(top.parm.fd, "r+"))) {
435 fprintf(stderr, "can't fdopen parm pipe %s\n", PARMPATH);
441 setup_system_audio(void) {
442 sprintf(top.jack.name, "sdr-%d", top.pid);
443 if (!(top.jack.client = jack_client_new(top.jack.name)))
444 perror("can't make client -- jack not running?"), exit(1);
445 jack_set_process_callback(top.jack.client, (void *) audio_callback, 0);
446 top.jack.port.i.l = jack_port_register(top.jack.client,
448 JACK_DEFAULT_AUDIO_TYPE,
451 top.jack.port.i.r = jack_port_register(top.jack.client,
453 JACK_DEFAULT_AUDIO_TYPE,
456 top.jack.port.o.l = jack_port_register(top.jack.client,
458 JACK_DEFAULT_AUDIO_TYPE,
461 top.jack.port.o.r = jack_port_register(top.jack.client,
463 JACK_DEFAULT_AUDIO_TYPE,
466 top.jack.ring.i.l = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT);
467 top.jack.ring.i.r = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT);
468 top.jack.ring.o.l = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT);
469 top.jack.ring.o.r = jack_ringbuffer_create(top.hold.size.bytes * RINGMULT);
473 for (i = 0; i < top.hold.size.bytes * RINGMULT / 2; i++) {
474 jack_ringbuffer_write(top.jack.ring.o.l, &zero, 1);
475 jack_ringbuffer_write(top.jack.ring.o.r, &zero, 1);
481 setup_threading(void) {
482 sem_init(&top.sync.upd.sem, 0, 0);
483 pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL);
484 sem_init(&top.sync.buf.sem, 0, 0);
485 pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL);
488 //========================================================================
491 setup(int argc, char **argv) {
495 top.start_tv = now_tv();
498 top.state = RUN_PLAY;
501 for (i = 1; i < argc; i++)
502 if (argv[i][0] == '-')
503 switch (argv[i][1]) {
505 uni.meter.flag = TRUE;
515 if (!freopen(argv[i], "r", stdin))
516 perror(argv[i]), exit(1);
521 setup_system_audio();
527 //========================================================================
530 main(int argc, char **argv) {
531 setup(argc, argv), execute(), closeup();