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