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