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 #include <splitfields.h>
37 #include <datatypes.h>
41 #define SAMP_RATE (48000)
42 #define HUGE_PHASE 1256637061.43593
44 #define RING_SIZE (01 << 020)
46 pthread_t input, play;
47 sem_t ready, reader, writer;
49 jack_client_t *client;
50 jack_port_t *lport, *rport;
51 jack_ringbuffer_t *lring, *rring;
54 BOOLEAN playing = FALSE;
55 double wpm = 18.0, freq = 750.0, gain = -6.0;
59 // basic mapping, chars -> morse strings
60 char *morse_table[128];
69 double wpm, rise, fall, curr, incr, rate;
73 int ditspacesize, dahspacesize,
74 ditstdysize, dahstdysize,
75 charspacesize, wordspacesize,
77 double riseincr, fallincr;
79 void jack_ringbuffer_clear(jack_ringbuffer_t *, int);
80 void jack_ringbuffer_restart(jack_ringbuffer_t *, int);
81 void send_sound(COMPLEX *, int);
83 //------------------------------------------------------------
85 // map char -> morse string
87 get_morse(int c) { return morse_table[c & 0x7F]; }
91 BOOLEAN b = TRUE; // we're coming from silence
95 // keep reading 1 char at a time
96 while ((c = getchar()) != EOF) {
98 // is char mapped to morse?
99 if (m = get_morse(c)) {
102 // for each sub-element (dit/dah)
104 // first segment is slew in...
106 morsel.type = ME_RAMP, morsel.size = risesize;
107 morsel.curr = 0.0, morsel.incr = riseincr;
110 // ...then steady state...
112 morsel.type = ME_STDY;
113 morsel.size = e == '.' ? ditstdysize : dahstdysize;
116 // ...then slew out...
118 morsel.type = ME_RAMP, morsel.size = fallsize;
119 morsel.curr = 1.0, morsel.incr = fallincr;
122 // ...finally, post-element pause
124 morsel.type = ME_ZERO;
125 morsel.size = ditspacesize;
129 // post-character pause
131 morsel.type = ME_ZERO;
132 // (we already emitted a dit-sized space)
133 morsel.size = charspacesize - ditspacesize;
136 // wherever we go next, it won't have been from silence
140 // anything else treated as interword space
142 morsel.type = ME_ZERO;
143 // was previous output also interword space?
145 // yes, use full duration
146 morsel.size = wordspacesize;
148 // no, part of duration already played
149 morsel.size = wordspacesize - charspacesize;
155 // indicate EOF on input
157 morsel.type = ME_EOF;
165 double ofreq = freq * 2.0 * M_PI / SAMP_RATE,
167 scale = pow(10.0, gain / 20.0);
170 // as long as there's been no EOF on the input...
173 // pause for next sub-element
178 if (morsel.type == ME_EOF) break;
180 // interword space == silence?
181 if (morsel.type != ME_ZERO) {
182 // no, set up CORDIC tone generation
183 if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
184 z = Cmplx(cos(phase), sin(phase));
185 delta_z = Cmplx(cos(ofreq), sin(ofreq));
188 // play out this sub-segment
189 for (i = 0; i < morsel.size; i++) {
192 if (morsel.type == ME_ZERO) zout[k] = cxzero;
196 z = Cmul(z, delta_z);
199 if (morsel.type == ME_RAMP) {
200 morsel.curr += morsel.incr;
201 zout[k] = Cscl(z, scale * sin(morsel.curr * M_PI / 2.0));
203 zout[k] = Cscl(z, scale);
206 // one jack bufferful yet?
208 // yes, send to output
210 // wait until it's been taken away
214 if (morsel.type != ME_ZERO) {
215 if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
216 z = Cmplx(cos(phase), sin(phase));
217 delta_z = Cmplx(cos(ofreq), sin(ofreq));
223 // anything left unsent in buffer?
224 if (k > 0) send_sound(zout, k);
229 //------------------------------------------------------------------------
232 jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) {
235 for (i = 0; i < nbytes; i++)
236 jack_ringbuffer_write(ring, &zero, 1);
240 jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) {
241 jack_ringbuffer_reset(ring);
242 jack_ringbuffer_clear(ring, nbytes);
246 send_sound(COMPLEX *buff, int len) {
247 if (jack_ringbuffer_write_space(lring) < len * sizeof(float)) {
248 write(2, "overrun\n", 8);
249 jack_ringbuffer_restart(lring, size * sizeof(float));
250 jack_ringbuffer_restart(rring, size * sizeof(float));
253 for (i = 0; i < len; i++) {
254 float l = buff[i].re, r = buff[i].im;
255 jack_ringbuffer_write(lring, (char *) &l, sizeof(float));
256 jack_ringbuffer_write(rring, (char *) &r, sizeof(float));
262 jack_xrun(void *arg) {
263 char *str = "xrun!\n";
264 write(2, str, strlen(str));
268 jack_shutdown(void *arg) {}
271 jack_callback(jack_nframes_t nframes, void *arg) {
273 int nwant = nframes * sizeof(float),
274 nhave = jack_ringbuffer_read_space(lring);
276 lp = jack_port_get_buffer(lport, nframes);
277 rp = jack_port_get_buffer(rport, nframes);
278 if (nhave >= nwant) {
279 jack_ringbuffer_read(lring, lp, nwant);
280 jack_ringbuffer_read(rring, rp, nwant);
283 memset(lp, 0, nwant);
284 memset(rp, 0, nwant);
289 main(int argc, char **argv) {
292 for (i = 1; i < argc; i++)
293 if (argv[i][0] == '-')
294 switch (argv[i][1]) {
296 freq = atof(argv[++i]);
299 wpm = atof(argv[++i]);
302 fprintf(stderr, "keyd [-w wpm] [-f freq] [infile]\n");
308 if (!freopen(argv[i], "r", stdin))
309 perror(argv[i]), exit(1);
313 //------------------------------------------------------------
316 morsel.rise = morsel.fall = 5.0; // ms
317 morsel.rate = SAMP_RATE;
319 ditspacesize = SAMP_RATE * 1.2 / morsel.wpm + 0.5;
320 dahspacesize = 3 * ditspacesize;
321 charspacesize = dahspacesize;
322 wordspacesize = 7 * ditspacesize;
324 risesize = SAMP_RATE * morsel.rise / 1e3 + 0.5;
326 riseincr = 1.0 / (risesize - 1);
330 fallsize = SAMP_RATE * morsel.fall / 1e3 + 0.5;
332 fallincr = -1.0 / (fallsize - 1);
336 ditstdysize = ditspacesize - risesize - fallsize;
337 dahstdysize = dahspacesize - risesize - fallsize;
339 //------------------------------------------------------------
341 if (!(client = jack_client_new("keyb")))
342 fprintf(stderr, "can't make client -- jack not running?\n"), exit(1);
343 jack_set_process_callback(client, (void *) jack_callback, 0);
344 jack_on_shutdown(client, (void *) jack_shutdown, 0);
345 jack_set_xrun_callback(client, (void *) jack_xrun, 0);
346 size = jack_get_buffer_size(client);
348 lport = jack_port_register(client,
350 JACK_DEFAULT_AUDIO_TYPE,
353 rport = jack_port_register(client,
355 JACK_DEFAULT_AUDIO_TYPE,
358 lring = jack_ringbuffer_create(RING_SIZE);
359 rring = jack_ringbuffer_create(RING_SIZE);
360 jack_ringbuffer_clear(lring, size * sizeof(float));
361 jack_ringbuffer_clear(rring, size * sizeof(float));
363 //------------------------------------------------------------
365 zout = newvec_COMPLEX(size, "keyb sample buffer");
367 //------------------------------------------------------------
369 sem_init(&ready, 0, 0);
370 sem_init(&reader, 0, 0);
371 sem_init(&writer, 0, 0);
372 pthread_create(&input, 0, (void *) reader_thread, 0);
373 pthread_create(&play, 0, (void *) sound_thread, 0);
375 //------------------------------------------------------------
377 jack_activate(client);
380 if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
381 fprintf(stderr, "can't find any physical playback ports\n");
384 if (jack_connect(client, jack_port_name(lport), ports[0])) {
385 fprintf(stderr, "can't connect left output\n");
388 if (jack_connect(client, jack_port_name(rport), ports[1])) {
389 fprintf(stderr, "can't connect left output\n");
395 pthread_join(input, 0);
396 pthread_join(play, 0);
397 jack_client_close(client);
399 //------------------------------------------------------------
401 delvec_COMPLEX(zout);
403 //------------------------------------------------------------
405 jack_ringbuffer_free(lring);
406 jack_ringbuffer_free(rring);
408 sem_destroy(&reader);
409 sem_destroy(&writer);
411 //------------------------------------------------------------
416 char *morse_table[128] = {
417 /* 000 NUL */ 0, /* 001 SOH */ 0, /* 002 STX */ 0, /* 003 ETX */ 0,
418 /* 004 EOT */ 0, /* 005 ENQ */ 0, /* 006 ACK */ 0, /* 007 BEL */ 0,
419 /* 008 BS */ 0, /* 009 HT */ 0, /* 010 LF */ 0, /* 011 VT */ 0,
420 /* 012 FF */ 0, /* 013 CR */ 0, /* 014 SO */ 0, /* 015 SI */ 0,
421 /* 016 DLE */ 0, /* 017 DC1 */ 0, /* 018 DC2 */ 0, /* 019 DC3 */ 0,
422 /* 020 DC4 */ 0, /* 021 NAK */ 0, /* 022 SYN */ 0, /* 023 ETB */ 0,
423 /* 024 CAN */ 0, /* 025 EM */ 0, /* 026 SUB */ 0, /* 027 ESC */ 0,
424 /* 028 FS */ 0, /* 029 GS */ 0, /* 030 RS */ 0, /* 031 US */ 0,
426 /* 033 ! */ "...-.", // [SN]
427 /* 034 " */ 0, /* 035 # */ 0, /* 036 $ */ 0,
428 /* 037 % */ ".-...", // [AS]
429 /* 038 & */ 0, /* 039 ' */ 0,
430 /* 040 ( */ "-.--.", // [KN]
432 /* 042 * */ "...-.-", // [SK]
433 /* 043 + */ ".-.-.", // [AR]
434 /* 044 , */ "--..--",
435 /* 045 - */ "-....-",
436 /* 046 . */ ".-.-.-",
448 /* 058 : */ 0, /* 059 ; */ 0, /* 060 < */ 0,
449 /* 061 = */ "-...-", // [BT]
451 /* 063 ? */ "..__..", // [IMI]
452 /* 064 @ */ ".--.-.",
479 /* 091 [ */ 0, /* 092 \ */ 0, /* 093 ] */ 0, /* 094 ^ */ 0,
480 /* 095 _ */ 0, /* 096 ` */ 0,
507 /* 123 { */ 0, /* 124 | */ 0, /* 125 } */ 0, /* 126 ~ */ 0,