]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/win/main.c
Initial revision
[dttsp.git] / jDttSP / win / main.c
1 /* main.c
2
3 This file is part of a program that implements a Software-Defined Radio.
4
5 Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY
6
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.
11
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.
16
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
20
21 The authors can be reached by email at
22
23 ab2kt@arrl.net
24 or
25 rwmcgwier@comcast.net
26
27 or by paper mail at
28
29 The DTTS Microwave Society
30 6 Kathleen Place
31 Bridgewater, NJ 08807
32 */  
33   
34 #include <common.h>
35   
36 /////////////////////////////////////////////////////////////////////////
37 extern void process_samples(float *, float *, int);
38 extern void setup_workspace(void);
39
40 #ifdef _WINDOWS
41   
42 #define PARMPATH NULL
43 #define RINGMULT (4)
44 #define METERPATH NULL
45 #define METERMULT (2)
46 #define PWSPATH NULL
47 #define PWSMULT (32768)
48 #define TEST
49
50 #else
51   
52 #define PARMPATH "./IPC/SDR-1000-0-commands.fifo"
53 #define RINGMULT (4)
54 #define METERPATH "./IPC/SDR-1000-0-meter.chan"
55 #define METERMULT (2)
56 #define PWSPATH "./IPC/SDR-1000-0-pws.chan"
57 #define PWSMULT (32768)
58   
59 #endif
60 //------------------------------------------------------------------------
61   
62 #ifdef _WINDOWS
63 PRIVATE DWORD WINAPI
64 process_updates_thread(void) {
65   size_t tmp;
66   while (top.running) {
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);
71     }
72   }
73   ExitThread(0);
74 }
75
76 //========================================================================
77
78 PRIVATE void 
79 gethold(void) {
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);
84 }
85
86 PRIVATE BOOLEAN canhold(void) {
87   return ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes;
88 }
89
90
91 #else
92
93 //========================================================================
94
95 PRIVATE void 
96 process_updates_thread(void) {
97   while (top.running) {
98     while (fgets(top.parm.buff, sizeof(top.parm.buff), top.parm.fp))
99       do_update(top.parm.buff, top.verbose ? stderr : 0);
100   }
101   pthread_exit(0);
102 }
103
104 PRIVATE void 
105 gethold(void) {
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);
110 }
111
112 PRIVATE BOOLEAN canhold(void) {
113   return jack_ringbuffer_read_space(top.jack.ring.i.l) >= top.hold.size.bytes;
114 }
115
116 #endif
117
118 PRIVATE void 
119 run_mute(void) {
120   memset((char *) top.hold.buf.l, 0, top.hold.size.bytes);
121   memset((char *) top.hold.buf.r, 0, top.hold.size.bytes);
122 }
123
124 PRIVATE void 
125 run_pass(void) {}
126
127 PRIVATE void 
128 run_play(void) {
129   process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
130
131
132 // NB do not set RUN_SWCH directly via setRunState;
133 // use setSWCH instead
134
135 PRIVATE void 
136 run_swch(void) {
137   if (top.swch.bfct.have == 0) {
138     // first time
139     // apply ramp down
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;
144     }
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) {
149     // in medias res
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++;
153   } else {
154     // last time
155     // apply ramp up
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;
160     }
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;
164   }
165
166   process_samples(top.hold.buf.l, top.hold.buf.r, top.hold.size.frames);
167
168
169 //========================================================================
170
171 #ifdef _WINDOWS
172
173 static int exchptr = 0;
174 DttSP_EXP void 
175 exchange_samples(float *input, float *output, int nframes) {
176   int i, j;
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));
182   }
183   if (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes)
184     ReleaseSemaphore(top.sync.buf.sem, 1L, NULL);
185 }
186
187 DttSP_EXP void
188 audioreset(void) { exchptr = 0; }
189
190 PRIVATE DWORD WINAPI
191 process_samples_thread(void) {
192   while (top.running) {
193     WaitForSingleObject(top.sync.buf.sem, INFINITE);
194     WaitForSingleObject(top.sync.upd.sem, INFINITE);
195     do {
196       gethold();
197       switch (top.state) {
198       case RUN_MUTE:
199         run_mute();
200         break;
201       case RUN_PASS:
202         run_pass();
203         break;
204       case RUN_PLAY:
205         run_play();
206         break;
207       case RUN_SWCH:
208         run_swch();
209         break;
210       }
211     } while (canhold());
212     ReleaseSemaphore(top.sync.upd.sem, 1L, NULL);
213   }
214   ExitThread(0);
215   return 1;
216 }
217   
218 #else   /*  */
219
220 PRIVATE void 
221 audio_callback(jack_nframes_t nframes, void *arg) {
222   float *lp, *rp;
223   int nbytes = nframes * sizeof(float);
224   
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);
230   
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);
236   
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);
240 }
241
242 PRIVATE void 
243 process_samples_thread(void) {
244   while (top.running) {
245     sem_wait(&top.sync.buf.sem);
246     do {
247       gethold();
248       sem_wait(&top.sync.upd.sem);
249       switch (top.state) {
250       case RUN_MUTE:
251         run_mute();
252         break;
253       case RUN_PASS:
254         run_pass();
255         break;
256       case RUN_PLAY:
257         run_play();
258         break;
259       case RUN_SWCH:
260         run_swch();
261         break;
262       }
263       sem_post(&top.sync.upd.sem);
264     } while (canhold());
265   }
266   pthread_exit(0);
267 }
268
269 #endif
270
271 //========================================================================
272
273 #ifdef _WINDOWS
274 DttSP_EXP  void 
275 execute(void) {
276   ResumeThread(top.thrd.trx.thrd);
277 }
278
279 DttSP_EXP void
280 closeup(void) {
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);
287   destroy_workspace();
288
289
290 #else
291
292 PRIVATE void 
293 execute(void) {
294   sem_post(&top.sync.upd.sem);
295   
296   // start audio processing
297   if (jack_activate(top.jack.client))
298     perror("cannot activate jack client"), exit(1);
299   
300   // wait for threads to terminate
301   pthread_join(top.thrd.trx.id, 0);
302   pthread_join(top.thrd.upd.id, 0);
303   
304   // stop audio processing
305   jack_client_close(top.jack.client);
306 }
307
308 PRIVATE void 
309 closeup(void) { exit(0); }
310
311 PRIVATE void 
312 usage(void) {
313   fprintf(stderr, "usage:\n");
314   fprintf(stderr, "-m\n");
315   fprintf(stderr, "-v\n");
316   exit(1);
317 }
318
319 PRIVATE void 
320 nonblock(int fd) {
321   long arg;
322   arg = fcntl(fd, F_GETFL);
323   arg |= O_NONBLOCK;
324 /*  if (fcntl(fd, F_GETFL, &arg) >= 0)
325     fcntl(fd, F_SETFL, arg | O_NONBLOCK); */ 
326     fcntl(fd, F_SETFL, arg);
327
328
329 #endif
330
331 //........................................................................
332
333 PRIVATE void 
334 setup_meter(void) {
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);
340 }
341
342 PRIVATE void 
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);
350 }
351
352 PRIVATE void 
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);
356 }
357
358 PRIVATE void 
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");
366
367
368 #ifdef _WINDOWS
369
370 PRIVATE void 
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);
376 } PRIVATE void 
377
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);
384   {
385     unsigned int i;
386     char zero = 0;
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);
390     }
391   }
392 }
393
394 DttSP_EXP void
395 release_update(void) {}
396
397 DttSP_EXP void
398 suspend_sdr(void) { SuspendThread(top.thrd.trx.thrd); }
399
400 PRIVATE void 
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);
407 }
408
409 DttSP_EXP void
410 setup_sdr(void) {
411   top.pid = GetCurrentThreadId();
412   top.uid = 0L;
413   top.running = TRUE;
414   top.state = RUN_PLAY;
415   setup_meter();
416   setup_powerspectrum();
417   setup_updates();
418   uni.meter.flag = TRUE;
419   top.verbose = FALSE;
420   setup_workspace();
421   setup_local_audio();
422   setup_system_audio();
423   setup_threading();
424   setup_switching();
425 }
426
427 #else
428
429 PRIVATE void 
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);
436     exit(1);
437   }
438 }
439
440 PRIVATE void 
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,
447                                          "il",
448                                          JACK_DEFAULT_AUDIO_TYPE,
449                                          JackPortIsInput,
450                                          0);
451   top.jack.port.i.r = jack_port_register(top.jack.client,
452                                          "ir",
453                                          JACK_DEFAULT_AUDIO_TYPE,
454                                          JackPortIsInput,
455                                          0);
456   top.jack.port.o.l = jack_port_register(top.jack.client,
457                                          "ol",
458                                          JACK_DEFAULT_AUDIO_TYPE,
459                                          JackPortIsOutput,
460                                          0);
461   top.jack.port.o.r = jack_port_register(top.jack.client,
462                                          "or",
463                                          JACK_DEFAULT_AUDIO_TYPE,
464                                          JackPortIsOutput,
465                                          0);
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);
470   {
471     int i;
472     char zero = 0;
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);
476     }
477   }
478 }
479
480 PRIVATE void 
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);
486
487
488 //========================================================================
489
490 PRIVATE void 
491 setup(int argc, char **argv) {
492   int i;
493   top.pid = getpid();
494   top.uid = getuid();
495   top.start_tv = now_tv();
496   top.running = TRUE;
497   top.verbose = FALSE;
498   top.state = RUN_PLAY;
499   setup_meter();
500   setup_updates();
501   for (i = 1; i < argc; i++)
502     if (argv[i][0] == '-')
503       switch (argv[i][1]) {
504       case 'm':
505         uni.meter.flag = TRUE;
506         break;
507       case 'v':
508         top.verbose = TRUE;
509         break;
510       default:
511         usage();
512       }
513     else break;
514   if (i < argc) {
515     if (!freopen(argv[i], "r", stdin))
516       perror(argv[i]), exit(1);
517     i++;
518   }
519   setup_workspace();
520   setup_local_audio();
521   setup_system_audio();
522   setup_threading();
523   setup_switching();
524 }
525
526
527 //========================================================================
528
529 int 
530 main(int argc, char **argv) {
531   setup(argc, argv), execute(), closeup();
532
533
534 #endif