]> git.rkrishnan.org Git - dttsp.git/blob - jDttSP/win/keyb.c
Upgraded windows alternatives
[dttsp.git] / jDttSP / win / keyb.c
1 /* keyb.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 <fromsys.h>
35 #include <banal.h>
36 #include <splitfields.h>
37 #include <datatypes.h>
38 #include <bufvec.h>
39 #include <cxops.h>
40 #include <pthread.h>
41 #include <semaphore.h>
42 #include <ringb.h>
43
44 #define SAMP_RATE (48000)
45 #define HUGE_PHASE (1256637061.43593)
46
47 #define RING_SIZE (01 << 020)
48
49 static pthread_t input, play;
50 static sem_t ready, reader, writer;
51
52 ringb_t *lring, *rring;
53 int size;
54
55 static BOOLEAN playing = FALSE;
56 static double wpm = 18.0, freq = 750.0, gain = -6.0, ramp = 5.0;
57
58 COMPLEX *zout = 0;
59
60 // basic mapping, chars -> morse strings
61 char *morse_table[128];
62
63 // CW tone segments
64 #define ME_EOF (-1)
65 #define ME_ZERO (0)
66 #define ME_RAMP (1)
67 #define ME_STDY (2)
68
69 struct {
70   double wpm, rise, fall, curr, incr, rate;
71   int type, size;
72 } morsel;
73
74 int ditspacesize, dahspacesize,
75     ditstdysize, dahstdysize,
76     charspacesize, wordspacesize,
77     risesize, fallsize;
78 double riseincr, fallincr;
79
80 #define MAX_ESC (512)
81 #define ESC_L '<'
82 #define ESC_R '>'
83
84 void inlinecmd(char *, int);
85
86 void send_sound(COMPLEX *, int);
87
88 //------------------------------------------------------------
89
90 // try to map char -> morse string
91 char *
92 get_morse(int c) { return morse_table[c & 0x7F]; }
93
94 // translate text input to timed, sub-morse-element
95 // audio segment specs; parcel the segments out
96 // one at a time to the sound player
97 void
98 reader_thread(void) {
99   BOOLEAN b = TRUE; // we're coming from silence
100   int c, e;
101   char *m;
102   
103   // keep reading 1 char at a time
104   while ((c = getchar()) != EOF) {
105     
106     // inline command?
107     if (c == ESC_L) {
108       int i = 0;
109       char buf[MAX_ESC];
110       while ((c = getchar()) != EOF) {
111         if (c == ESC_R) break;
112         buf[i] = c;
113         if (++i >= (MAX_ESC - 1)) break;
114       }
115       if (c == EOF) goto finish;
116       buf[i] = 0;
117       inlinecmd(buf, i);
118       continue;
119     }
120
121     // is char mapped to morse?
122     if (m = get_morse(c)) {
123       
124       // yup
125       // for each element in morse string
126       // (dit/dah, doesn't matter)
127       while (e = *m++) {
128         // first segment is ramp up...
129         sem_wait(&reader);
130         morsel.type = ME_RAMP, morsel.size = risesize;
131         morsel.curr = 0.0, morsel.incr = riseincr;
132         sem_post(&writer);
133         
134         // ...then steady state...
135         // (choose dit/dah here)
136         sem_wait(&reader);
137         morsel.type = ME_STDY;
138         morsel.size = e == '.' ? ditstdysize : dahstdysize;
139         sem_post(&writer);
140         
141         // ...then ramp down...
142         sem_wait(&reader);
143         morsel.type = ME_RAMP, morsel.size = fallsize;
144         morsel.curr = 1.0, morsel.incr = fallincr;
145         sem_post(&writer);
146         
147         // ...finally, post-element pause
148         sem_wait(&reader);
149         morsel.type = ME_ZERO;
150         morsel.size = ditspacesize;
151         sem_post(&writer);
152       }
153       
154       // post-character pause
155       sem_wait(&reader);
156       morsel.type = ME_ZERO;
157       // (we already emitted a dit-sized space)
158       morsel.size = charspacesize - ditspacesize;
159       sem_post(&writer);
160       
161       // wherever we go next, it won't have been from silence
162       b = FALSE;
163
164     } else {
165       // anything else treated as interword space,
166       // which has only one segment (silence)
167       sem_wait(&reader);
168       morsel.type = ME_ZERO;
169       // was previous output also interword space?
170       if (b)
171         // yes, use full duration
172         morsel.size = wordspacesize;
173       else
174         // no, part of duration already played
175         morsel.size = wordspacesize - charspacesize;
176       b = TRUE;
177       sem_post(&writer);
178     }
179   }
180   
181  finish:
182   // indicate EOF on input
183   sem_wait(&reader);
184   morsel.type = ME_EOF;
185   sem_post(&writer);
186   pthread_exit(0);
187 }
188
189 void
190 sound_thread_keyb(void) {
191   int i, k = 0;
192   double ofreq, scale, phase = 0.0;
193   COMPLEX z, delta_z;
194
195   // keep looking for sub-element segments, one at a time
196   for (;;) {
197
198     // pause for next sub-element segment
199     sem_post(&reader);
200     sem_wait(&writer);
201
202     // no more data?
203     if (morsel.type == ME_EOF) break;
204
205     // requires playing some tone?
206     if (morsel.type != ME_ZERO) {
207       // yes, reset params and
208       // set up CORDIC tone generation
209       ofreq = freq * 2.0 * M_PI / SAMP_RATE;
210       scale = pow(10.0, gain / 20.0);
211       if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
212       z = Cmplx(cos(phase), sin(phase));
213       delta_z = Cmplx(cos(ofreq), sin(ofreq));
214     }
215
216     // play out this segment
217     for (i = 0; i < morsel.size; i++) {
218
219       // make silence
220       if (morsel.type == ME_ZERO) zout[k] = cxzero;
221       
222       // make tone
223       else {
224         z = Cmul(z, delta_z);
225         phase += ofreq;
226         // is this a ramping segment?
227         if (morsel.type == ME_RAMP) {
228           morsel.curr += morsel.incr;
229           zout[k] = Cscl(z, scale * sin(morsel.curr * M_PI / 2.0));
230         } else
231           zout[k] = Cscl(z, scale);
232       }
233
234       // have we played enough to fill a jack buffer?
235       if (++k >= size) {
236         // yes, send to output
237         send_sound(zout, k);
238         // wait until some audio has been drained
239         sem_wait(&ready);
240         k = 0;
241         if (morsel.type != ME_ZERO) {
242           // reset CORDIC
243           if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
244           z = Cmplx(cos(phase), sin(phase));
245           delta_z = Cmplx(cos(ofreq), sin(ofreq));
246         }
247       }
248     }
249   }
250
251   // anything left unsent?
252   if (k > 0) send_sound(zout, k);
253
254   pthread_exit(0);
255 }
256
257 //------------------------------------------------------------------------
258 void
259 send_sound(COMPLEX *buff, int len) {
260   if (ringb_write_space(lring) < len * sizeof(float)) {
261     //write(2, "overrun\n", 8);
262     ringb_restart(lring, size * sizeof(float));
263     ringb_restart(rring, size * sizeof(float));
264   } else {
265     int i;
266     for (i = 0; i < len; i++) {
267       float l = (float)buff[i].re, r = (float)buff[i].im;
268       ringb_write(lring, (char *) &l, sizeof(float));
269       ringb_write(rring, (char *) &r, sizeof(float));
270     }
271   }
272 }
273 #ifndef _WINDOWS
274 PRIVATE void
275 jack_xrun(void *arg) {
276   char *str = "xrun!\n";
277   write(2, str, strlen(str));
278 }
279
280 PRIVATE void
281 jack_shutdown(void *arg) {}
282
283 PRIVATE void
284 jack_callback(jack_nframes_t nframes, void *arg) {
285   char *lp, *rp;
286   int nwant = nframes * sizeof(float),
287       nhave = ringb_read_space(lring);
288
289   lp = jack_port_get_buffer(lport, nframes);
290   rp = jack_port_get_buffer(rport, nframes);
291   if (nhave >= nwant) {
292     ringb_read(lring, lp, nwant);
293     ringb_read(rring, rp, nwant);
294     sem_post(&ready);
295   } else {
296     memset(lp, 0, nwant);
297     memset(rp, 0, nwant);
298   }
299 }
300 #endif
301 void
302 resetparam(void) {
303   morsel.wpm = wpm;
304   morsel.rise = morsel.fall = ramp;
305   morsel.rate = SAMP_RATE;
306
307   ditspacesize = (int)(SAMP_RATE * 1.2 / morsel.wpm + 0.5);
308   dahspacesize = (int)(3 * ditspacesize);
309   charspacesize = dahspacesize;
310   wordspacesize = 7 * ditspacesize;
311
312   risesize = (int)(SAMP_RATE * morsel.rise / 1e3 + 0.5);
313   if (risesize > 1)
314     riseincr = 1.0 / (risesize - 1);
315   else
316     riseincr = 1.0;
317
318   fallsize = (int)(SAMP_RATE * morsel.fall / 1e3 + 0.5);
319   if (fallsize > 1)
320     fallincr = -1.0 / (fallsize - 1);
321   else
322     fallincr = -1.0;
323
324   ditstdysize = ditspacesize - risesize - fallsize;
325   dahstdysize = dahspacesize - risesize - fallsize;
326 }
327
328 #ifndef _WINDOWS
329 int
330 main(int argc, char **argv) {
331   int i;
332
333   for (i = 1; i < argc; i++)
334     if (argv[i][0] == '-')
335       switch (argv[i][1]) {
336       case 'f':
337         freq = atof(argv[++i]);
338         break;
339       case 'w':
340         wpm = atof(argv[++i]);
341         break;
342       case 'r':
343         ramp = atof(argv[++i]);
344         break;
345       default:
346         fprintf(stderr, "keyd [-w wpm] [-f freq] [-r ramp_ms] [infile]\n");
347         exit(1);
348       }
349     else break;
350
351   if (i < argc) {
352     if (!freopen(argv[i], "r", stdin))
353       perror(argv[i]), exit(1);
354     i++;
355   }
356
357   //------------------------------------------------------------
358
359   resetparam();
360
361   //------------------------------------------------------------
362
363   if (!(client = jack_client_new("keyb")))
364     fprintf(stderr, "can't make client -- jack not running?\n"), exit(1);
365   jack_set_process_callback(client, (void *) jack_callback, 0);
366   jack_on_shutdown(client, (void *) jack_shutdown, 0);
367   jack_set_xrun_callback(client, (void *) jack_xrun, 0);
368   size = jack_get_buffer_size(client);
369
370   lport = jack_port_register(client,
371                              "ol",
372                              JACK_DEFAULT_AUDIO_TYPE,
373                              JackPortIsOutput,
374                              0);
375   rport = jack_port_register(client,
376                              "or",
377                              JACK_DEFAULT_AUDIO_TYPE,
378                              JackPortIsOutput,
379                              0);
380   lring = ringb_create(RING_SIZE);
381   rring = ringb_create(RING_SIZE);
382   ringb_clear(lring, size * sizeof(float));
383   ringb_clear(rring, size * sizeof(float));
384   
385   //------------------------------------------------------------
386
387   zout = newvec_COMPLEX(size, "keyb sample buffer");
388
389   //------------------------------------------------------------
390
391   sem_init(&ready, 0, 0);
392   sem_init(&reader, 0, 0);
393   sem_init(&writer, 0, 0);
394   pthread_create(&input, 0, (void *) reader_thread, 0);
395   pthread_create(&play, 0, (void *) sound_thread, 0);
396
397   //------------------------------------------------------------
398
399   jack_activate(client);
400   {
401     const char **ports;
402     if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
403       fprintf(stderr, "can't find any physical playback ports\n");
404       exit(1);
405     }
406     if (jack_connect(client, jack_port_name(lport), ports[0])) {
407       fprintf(stderr, "can't connect left output\n");
408       exit(1);
409     }
410     if (jack_connect(client, jack_port_name(rport), ports[1])) {
411       fprintf(stderr, "can't connect right output\n");
412       exit(1);
413     }
414     free(ports);
415   }
416
417   pthread_join(input, 0);
418   pthread_join(play, 0);
419   jack_client_close(client);
420
421   //------------------------------------------------------------
422
423   delvec_COMPLEX(zout);
424
425   //------------------------------------------------------------
426
427   ringb_free(lring);
428   ringb_free(rring);
429   sem_destroy(&ready);
430   sem_destroy(&reader);
431   sem_destroy(&writer);
432
433   //------------------------------------------------------------
434
435   exit(0);
436 }
437 #endif
438 char *morse_table[128] = {
439   /* 000 NUL */ 0, /* 001 SOH */ 0, /* 002 STX */ 0, /* 003 ETX */ 0,
440   /* 004 EOT */ 0, /* 005 ENQ */ 0, /* 006 ACK */ 0, /* 007 BEL */ 0,
441   /* 008  BS */ 0, /* 009  HT */ 0, /* 010  LF */ 0, /* 011  VT */ 0,
442   /* 012  FF */ 0, /* 013  CR */ 0, /* 014  SO */ 0, /* 015  SI */ 0,
443   /* 016 DLE */ 0, /* 017 DC1 */ 0, /* 018 DC2 */ 0, /* 019 DC3 */ 0,
444   /* 020 DC4 */ 0, /* 021 NAK */ 0, /* 022 SYN */ 0, /* 023 ETB */ 0,
445   /* 024 CAN */ 0, /* 025  EM */ 0, /* 026 SUB */ 0, /* 027 ESC */ 0,
446   /* 028  FS */ 0, /* 029  GS */ 0, /* 030  RS */ 0, /* 031  US */ 0,
447   /* 032  SP */ 0,
448   /* 033   ! */ "...-.",        // [SN]
449   /* 034   " */ 0,
450   /* 035   # */ 0,
451   /* 036   $ */ 0,
452   /* 037   % */ ".-...",        // [AS]
453   /* 038   & */ 0,
454   /* 039   ' */ 0,
455   /* 040   ( */ "-.--.",        // [KN]
456   /* 041   ) */ 0,
457   /* 042   * */ "...-.-",       // [SK]
458   /* 043   + */ ".-.-.",        // [AR]
459   /* 044   , */ "--..--",
460   /* 045   - */ "-....-",
461   /* 046   . */ ".-.-.-",
462   /* 047   / */ "-..-.",
463   /* 048   0 */ "-----",
464   /* 049   1 */ ".----",
465   /* 050   2 */ "..---",
466   /* 051   3 */ "...--",
467   /* 052   4 */ "....-",
468   /* 053   5 */ ".....",
469   /* 054   6 */ "-....",
470   /* 055   7 */ "--...",
471   /* 056   8 */ "---..",
472   /* 057   9 */ "----.",
473   /* 058   : */ 0,
474   /* 059   ; */ 0,
475   /* 060   < */ 0,
476   /* 061   = */ "-...-",        // [BT]
477   /* 062   > */ 0,
478   /* 063   ? */ "..__..",       // [IMI]
479   /* 064   @ */ ".--.-.",
480   /* 065   A */ ".-",
481   /* 066   B */ "-...",
482   /* 067   C */ "-.-.",
483   /* 068   D */ "-..",
484   /* 069   E */ ".",
485   /* 070   F */ "..-.",
486   /* 071   G */ "--.",
487   /* 072   H */ "....",
488   /* 073   I */ "..",
489   /* 074   J */ ".---",
490   /* 075   K */ "-.-",
491   /* 076   L */ ".-..",
492   /* 077   M */ "--",
493   /* 078   N */ "-.",
494   /* 079   O */ "---",
495   /* 080   P */ ".--.",
496   /* 081   Q */ "--.-",
497   /* 082   R */ ".-.",
498   /* 083   S */ "...",
499   /* 084   T */ "-",
500   /* 085   U */ "..-",
501   /* 086   V */ "...-",
502   /* 087   W */ ".--",
503   /* 088   X */ "-..-",
504   /* 089   Y */ "-.--",
505   /* 090   Z */ "--..",
506   /* 091   [ */ 0,
507   /* 092   \ */ 0,
508   /* 093   ] */ 0,
509   /* 094   ^ */ 0,
510   /* 095   _ */ 0,
511   /* 096   ` */ 0,
512   /* 097   a */ ".-",
513   /* 098   b */ "-...",
514   /* 099   c */ "-.-.",
515   /* 100   d */ "-..",
516   /* 101   e */ ".",
517   /* 102   f */ "..-.",
518   /* 103   g */ "--.",
519   /* 104   h */ "....",
520   /* 105   i */ "..",
521   /* 106   j */ ".---",
522   /* 107   k */ "-.-",
523   /* 108   l */ ".-..",
524   /* 109   m */ "--",
525   /* 110   n */ "-.",
526   /* 111   o */ "---",
527   /* 112   p */ ".--.",
528   /* 113   q */ "--.-",
529   /* 114   r */ ".-.",
530   /* 115   s */ "...",
531   /* 116   t */ "-",
532   /* 117   u */ "..-",
533   /* 118   v */ "...-",
534   /* 119   w */ ".--",
535   /* 120   x */ "-..-",
536   /* 121   y */ "-.--",
537   /* 122   z */ "--..",
538   /* 123   { */ 0,
539   /* 124   | */ 0,
540   /* 125   } */ 0,
541   /* 126   ~ */ 0,
542   /* 127 DEL */ 0
543 };
544
545 void
546 inlinecmd(char *buf, int len) {
547   if (!buf || len < 1) return;
548   if (!strncmp(buf, "wpm", 3)) {
549     wpm = atof(buf + 3);
550     resetparam();
551   } else if (!strncmp(buf, "ramp", 4)) {
552     ramp = atof(buf + 4);
553     resetparam();
554   } else if (!strncmp(buf, "freq", 4))
555     freq = atof(buf + 4);
556   else if (!strncmp(buf, "gain", 4))
557     gain = atof(buf + 4);
558 }