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, ramp = 5.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 // try to map char -> morse string
87 get_morse(int c) { return morse_table[c & 0x7F]; }
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
94 BOOLEAN b = TRUE; // we're coming from silence
98 // keep reading 1 char at a time
99 while ((c = getchar()) != EOF) {
101 // is char mapped to morse?
102 if (m = get_morse(c)) {
105 // for each element in morse string
106 // (dit/dah, doesn't matter)
108 // first segment is ramp up...
110 morsel.type = ME_RAMP, morsel.size = risesize;
111 morsel.curr = 0.0, morsel.incr = riseincr;
114 // ...then steady state...
115 // (choose dit/dah here)
117 morsel.type = ME_STDY;
118 morsel.size = e == '.' ? ditstdysize : dahstdysize;
121 // ...then ramp down...
123 morsel.type = ME_RAMP, morsel.size = fallsize;
124 morsel.curr = 1.0, morsel.incr = fallincr;
127 // ...finally, post-element pause
129 morsel.type = ME_ZERO;
130 morsel.size = ditspacesize;
134 // post-character pause
136 morsel.type = ME_ZERO;
137 // (we already emitted a dit-sized space)
138 morsel.size = charspacesize - ditspacesize;
141 // wherever we go next, it won't have been from silence
145 // anything else treated as interword space,
146 // which has only one segment (silence)
148 morsel.type = ME_ZERO;
149 // was previous output also interword space?
151 // yes, use full duration
152 morsel.size = wordspacesize;
154 // no, part of duration already played
155 morsel.size = wordspacesize - charspacesize;
161 // indicate EOF on input
163 morsel.type = ME_EOF;
171 double ofreq = freq * 2.0 * M_PI / SAMP_RATE,
173 scale = pow(10.0, gain / 20.0);
176 // keep looking for sub-element segments, one at a time
179 // pause for next sub-element segment
184 if (morsel.type == ME_EOF) break;
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));
194 // play out this segment
195 for (i = 0; i < morsel.size; i++) {
198 if (morsel.type == ME_ZERO) zout[k] = cxzero;
202 z = Cmul(z, delta_z);
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));
209 zout[k] = Cscl(z, scale);
212 // have we played enough to fill a jack buffer?
214 // yes, send to output
216 // wait until some audio has been drained
219 if (morsel.type != ME_ZERO) {
221 if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
222 z = Cmplx(cos(phase), sin(phase));
223 delta_z = Cmplx(cos(ofreq), sin(ofreq));
229 // anything left unsent?
230 if (k > 0) send_sound(zout, k);
235 //------------------------------------------------------------------------
238 jack_ringbuffer_clear(jack_ringbuffer_t *ring, int nbytes) {
241 for (i = 0; i < nbytes; i++)
242 jack_ringbuffer_write(ring, &zero, 1);
246 jack_ringbuffer_restart(jack_ringbuffer_t *ring, int nbytes) {
247 jack_ringbuffer_reset(ring);
248 jack_ringbuffer_clear(ring, nbytes);
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));
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));
268 jack_xrun(void *arg) {
269 char *str = "xrun!\n";
270 write(2, str, strlen(str));
274 jack_shutdown(void *arg) {}
277 jack_callback(jack_nframes_t nframes, void *arg) {
279 int nwant = nframes * sizeof(float),
280 nhave = jack_ringbuffer_read_space(lring);
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);
289 memset(lp, 0, nwant);
290 memset(rp, 0, nwant);
295 main(int argc, char **argv) {
298 for (i = 1; i < argc; i++)
299 if (argv[i][0] == '-')
300 switch (argv[i][1]) {
302 freq = atof(argv[++i]);
305 wpm = atof(argv[++i]);
308 ramp = atof(argv[++i]);
311 fprintf(stderr, "keyd [-w wpm] [-f freq] [-r ramp_ms] [infile]\n");
317 if (!freopen(argv[i], "r", stdin))
318 perror(argv[i]), exit(1);
322 //------------------------------------------------------------
325 morsel.rise = morsel.fall = ramp;
326 morsel.rate = SAMP_RATE;
328 ditspacesize = SAMP_RATE * 1.2 / morsel.wpm + 0.5;
329 dahspacesize = 3 * ditspacesize;
330 charspacesize = dahspacesize;
331 wordspacesize = 7 * ditspacesize;
333 risesize = SAMP_RATE * morsel.rise / 1e3 + 0.5;
335 riseincr = 1.0 / (risesize - 1);
339 fallsize = SAMP_RATE * morsel.fall / 1e3 + 0.5;
341 fallincr = -1.0 / (fallsize - 1);
345 ditstdysize = ditspacesize - risesize - fallsize;
346 dahstdysize = dahspacesize - risesize - fallsize;
348 //------------------------------------------------------------
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);
357 lport = jack_port_register(client,
359 JACK_DEFAULT_AUDIO_TYPE,
362 rport = jack_port_register(client,
364 JACK_DEFAULT_AUDIO_TYPE,
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));
372 //------------------------------------------------------------
374 zout = newvec_COMPLEX(size, "keyb sample buffer");
376 //------------------------------------------------------------
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);
384 //------------------------------------------------------------
386 jack_activate(client);
389 if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) {
390 fprintf(stderr, "can't find any physical playback ports\n");
393 if (jack_connect(client, jack_port_name(lport), ports[0])) {
394 fprintf(stderr, "can't connect left output\n");
397 if (jack_connect(client, jack_port_name(rport), ports[1])) {
398 fprintf(stderr, "can't connect right output\n");
404 pthread_join(input, 0);
405 pthread_join(play, 0);
406 jack_client_close(client);
408 //------------------------------------------------------------
410 delvec_COMPLEX(zout);
412 //------------------------------------------------------------
414 jack_ringbuffer_free(lring);
415 jack_ringbuffer_free(rring);
417 sem_destroy(&reader);
418 sem_destroy(&writer);
420 //------------------------------------------------------------
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,
435 /* 033 ! */ "...-.", // [SN]
436 /* 034 " */ 0, /* 035 # */ 0, /* 036 $ */ 0,
437 /* 037 % */ ".-...", // [AS]
438 /* 038 & */ 0, /* 039 ' */ 0,
439 /* 040 ( */ "-.--.", // [KN]
441 /* 042 * */ "...-.-", // [SK]
442 /* 043 + */ ".-.-.", // [AR]
443 /* 044 , */ "--..--",
444 /* 045 - */ "-....-",
445 /* 046 . */ ".-.-.-",
457 /* 058 : */ 0, /* 059 ; */ 0, /* 060 < */ 0,
458 /* 061 = */ "-...-", // [BT]
460 /* 063 ? */ "..__..", // [IMI]
461 /* 064 @ */ ".--.-.",
488 /* 091 [ */ 0, /* 092 \ */ 0, /* 093 ] */ 0, /* 094 ^ */ 0,
489 /* 095 _ */ 0, /* 096 ` */ 0,
516 /* 123 { */ 0, /* 124 | */ 0, /* 125 } */ 0, /* 126 ~ */ 0,