risesize, fallsize;
double riseincr, fallincr;
+#define MAX_ESC (512)
+#define ESC_L '<'
+#define ESC_R '>'
+
+void inlinecmd(char *, int);
+
void jack_ringbuffer_clear(jack_ringbuffer_t *, int);
void jack_ringbuffer_restart(jack_ringbuffer_t *, int);
void send_sound(COMPLEX *, int);
// keep reading 1 char at a time
while ((c = getchar()) != EOF) {
+ // inline command?
+ if (c == ESC_L) {
+ int i = 0;
+ char buf[MAX_ESC];
+ while ((c = getchar()) != EOF) {
+ if (c == ESC_R) break;
+ buf[i] = c;
+ if (++i >= (MAX_ESC - 1)) break;
+ }
+ if (c == EOF) goto finish;
+ buf[i] = 0;
+ inlinecmd(buf, i);
+ continue;
+ }
+
// is char mapped to morse?
if (m = get_morse(c)) {
}
}
+ finish:
// indicate EOF on input
sem_wait(&reader);
morsel.type = ME_EOF;
void
sound_thread(void) {
int i, k = 0;
- double ofreq = freq * 2.0 * M_PI / SAMP_RATE,
- phase = 0.0,
- scale = pow(10.0, gain / 20.0);
+ double ofreq, scale, phase = 0.0;
COMPLEX z, delta_z;
// keep looking for sub-element segments, one at a time
// requires playing some tone?
if (morsel.type != ME_ZERO) {
- // yes, set up CORDIC tone generation
+ // yes, reset params and
+ // set up CORDIC tone generation
+ ofreq = freq * 2.0 * M_PI / SAMP_RATE;
+ scale = pow(10.0, gain / 20.0);
if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
z = Cmplx(cos(phase), sin(phase));
delta_z = Cmplx(cos(ofreq), sin(ofreq));
}
}
+void
+resetparam(void) {
+ morsel.wpm = wpm;
+ morsel.rise = morsel.fall = ramp;
+ morsel.rate = SAMP_RATE;
+
+ ditspacesize = SAMP_RATE * 1.2 / morsel.wpm + 0.5;
+ dahspacesize = 3 * ditspacesize;
+ charspacesize = dahspacesize;
+ wordspacesize = 7 * ditspacesize;
+
+ risesize = SAMP_RATE * morsel.rise / 1e3 + 0.5;
+ if (risesize > 1)
+ riseincr = 1.0 / (risesize - 1);
+ else
+ riseincr = 1.0;
+
+ fallsize = SAMP_RATE * morsel.fall / 1e3 + 0.5;
+ if (fallsize > 1)
+ fallincr = -1.0 / (fallsize - 1);
+ else
+ fallincr = -1.0;
+
+ ditstdysize = ditspacesize - risesize - fallsize;
+ dahstdysize = dahspacesize - risesize - fallsize;
+}
+
int
main(int argc, char **argv) {
int i;
//------------------------------------------------------------
- morsel.wpm = wpm;
- morsel.rise = morsel.fall = ramp;
- morsel.rate = SAMP_RATE;
-
- ditspacesize = SAMP_RATE * 1.2 / morsel.wpm + 0.5;
- dahspacesize = 3 * ditspacesize;
- charspacesize = dahspacesize;
- wordspacesize = 7 * ditspacesize;
-
- risesize = SAMP_RATE * morsel.rise / 1e3 + 0.5;
- if (risesize > 1)
- riseincr = 1.0 / (risesize - 1);
- else
- riseincr = 1.0;
-
- fallsize = SAMP_RATE * morsel.fall / 1e3 + 0.5;
- if (fallsize > 1)
- fallincr = -1.0 / (fallsize - 1);
- else
- fallincr = -1.0;
-
- ditstdysize = ditspacesize - risesize - fallsize;
- dahstdysize = dahspacesize - risesize - fallsize;
+ resetparam();
//------------------------------------------------------------
/* 028 FS */ 0, /* 029 GS */ 0, /* 030 RS */ 0, /* 031 US */ 0,
/* 032 SP */ 0,
/* 033 ! */ "...-.", // [SN]
- /* 034 " */ 0, /* 035 # */ 0, /* 036 $ */ 0,
+ /* 034 " */ 0,
+ /* 035 # */ 0,
+ /* 036 $ */ 0,
/* 037 % */ ".-...", // [AS]
- /* 038 & */ 0, /* 039 ' */ 0,
+ /* 038 & */ 0,
+ /* 039 ' */ 0,
/* 040 ( */ "-.--.", // [KN]
/* 041 ) */ 0,
/* 042 * */ "...-.-", // [SK]
/* 055 7 */ "--...",
/* 056 8 */ "---..",
/* 057 9 */ "----.",
- /* 058 : */ 0, /* 059 ; */ 0, /* 060 < */ 0,
+ /* 058 : */ 0,
+ /* 059 ; */ 0,
+ /* 060 < */ 0,
/* 061 = */ "-...-", // [BT]
/* 062 > */ 0,
/* 063 ? */ "..__..", // [IMI]
/* 088 X */ "-..-",
/* 089 Y */ "-.--",
/* 090 Z */ "--..",
- /* 091 [ */ 0, /* 092 \ */ 0, /* 093 ] */ 0, /* 094 ^ */ 0,
- /* 095 _ */ 0, /* 096 ` */ 0,
+ /* 091 [ */ 0,
+ /* 092 \ */ 0,
+ /* 093 ] */ 0,
+ /* 094 ^ */ 0,
+ /* 095 _ */ 0,
+ /* 096 ` */ 0,
/* 097 a */ ".-",
/* 098 b */ "-...",
/* 099 c */ "-.-.",
/* 120 x */ "-..-",
/* 121 y */ "-.--",
/* 122 z */ "--..",
- /* 123 { */ 0, /* 124 | */ 0, /* 125 } */ 0, /* 126 ~ */ 0,
+ /* 123 { */ 0,
+ /* 124 | */ 0,
+ /* 125 } */ 0,
+ /* 126 ~ */ 0,
/* 127 DEL */ 0
};
+
+void
+inlinecmd(char *buf, int len) {
+ if (!buf || len < 1) return;
+ if (!strncmp(buf, "wpm", 3)) {
+ wpm = atof(buf + 3);
+ resetparam();
+ } else if (!strncmp(buf, "ramp", 4)) {
+ ramp = atof(buf + 4);
+ resetparam();
+ } else if (!strncmp(buf, "freq", 4))
+ freq = atof(buf + 4);
+ else if (!strncmp(buf, "gain", 4))
+ gain = atof(buf + 4);
+}
KeyerState ks;
KeyerLogic kl;
-pthread_t play, key;
-sem_t clock_fired, keyer_started;
+pthread_t play, key, update;
+sem_t clock_fired, keyer_started, update_ok;
int fdser, fdrtc;
CWToneGen gen;
BOOLEAN playing = FALSE, iambic = FALSE;
-double wpm = 18.0, freq = 750.0;
+double wpm = 18.0, freq = 750.0, ramp = 5.0, gain = -3.0;
//------------------------------------------------------------
}
}
+//------------------------------------------------------------------------
+
// sound/silence generation
// tone turned on/off asynchronously
+
void
sound_thread(void) {
for (;;) {
// returns FALSE when it's actually done.
playing = CWTone(gen);
send_tone();
- } else
+ } else {
send_silence();
+ // only let updates run when we've just generated silence
+ sem_post(&update_ok);
+ }
}
pthread_exit(0);
}
+//------------------------------------------------------------------------
+
// basic heartbeat
// returns actual dur in msec since last tick;
// uses Linux rtc interrupts.
// other strategies will work too, so long as they
// provide a measurable delay in msec.
+
double
timed_delay(void) {
double del, std = 1000 / (double) RTC_RATE;
}
// key down? (real or via keyer logic)
+
BOOLEAN
read_key(double del) {
if (iambic)
return read_straight_key_serial(ks, fdser);
}
+//------------------------------------------------------------------------
+
// main keyer loop
-//
+
void
key_thread(void) {
pthread_exit(0);
}
+//------------------------------------------------------------------------
+
+// update keyer parameters via text input from stdin
+// <wpm xxx> -> set keyer speed to xxx
+// <gain xxx> -> set gain to xxx (dB)
+// <freq xxx> -> set freq to xxx
+// <ramp xxx> -> set attack/decay times to xxx ms
+
+#define MAX_ESC (512)
+#define ESC_L '<'
+#define ESC_R '>'
+
+void
+updater(void) {
+ for (;;) {
+ int c;
+
+ // get or wait for next input char
+ if ((c = getchar()) == EOF) goto finish;
+
+ // if we see the beginning of a command,
+ if (c == ESC_L) {
+ int i = 0;
+ char buf[MAX_ESC];
+
+ // gather up the remainder
+ while ((c = getchar()) != EOF) {
+ if (c == ESC_R) break;
+ buf[i] = c;
+ if (++i >= (MAX_ESC - 1)) break;
+ }
+ if (c == EOF) goto finish;
+ buf[i] = 0;
+
+ // wait until changes are safe
+ sem_wait(&update_ok);
+
+ if (!strncmp(buf, "wpm", 3))
+ ks->wpm = wpm = atof(buf + 3);
+ else if (!strncmp(buf, "ramp", 4)) {
+ ramp = atof(buf + 4);
+ setCWToneGenVals(gen, gain, freq, ramp, ramp);
+ } else if (!strncmp(buf, "freq", 4)) {
+ freq = atof(buf + 4);
+ setCWToneGenVals(gen, gain, freq, ramp, ramp);
+ } else if (!strncmp(buf, "gain", 4)) {
+ gain = atof(buf + 4);
+ setCWToneGenVals(gen, gain, freq, ramp, ramp);
+ } else if (!strncmp(buf, "quit", 4))
+ goto finish;
+
+ } // otherwise go around again
+ }
+
+ // we saw an EOF or quit; kill other threads and exit neatly
+
+ finish:
+ pthread_cancel(play);
+ pthread_cancel(key);
+ pthread_exit(0);
+}
+
+//------------------------------------------------------------------------
+
PRIVATE void
jack_xrun(void *arg) {
char *str = "xrun";
case 'i':
iambic = TRUE;
break;
+ case 'g':
+ gain = atof(argv[++i]);
+ break;
+ case 'r':
+ ramp = atof(argv[++i]);
+ break;
case 'w':
wpm = atof(argv[++i]);
break;
default:
- fprintf(stderr, "keyd [-i] [-w wpm]\n");
+ fprintf(stderr,
+ "keyd [-i] [-w wpm] [-g gain_dB] [-r ramp_ms]\n");
exit(1);
}
else break;
+ if (i < argc) {
+ if (!freopen(argv[i], "r", stdin))
+ perror(argv[i]), exit(1);
+ i++;
+ }
+
//------------------------------------------------------------
- gen = newCWToneGen(-3.0, freq, 5.0, 5.0, TONE_SIZE, 48000.0);
+ gen = newCWToneGen(gain, freq, ramp, ramp, TONE_SIZE, 48000.0);
//------------------------------------------------------------
sem_init(&clock_fired, 0, 0);
sem_init(&keyer_started, 0, 0);
+ sem_init(&update_ok, 0, 0);
pthread_create(&play, 0, (void *) sound_thread, 0);
pthread_create(&key, 0, (void *) key_thread, 0);
+ pthread_create(&update, 0, (void *) updater, 0);
//------------------------------------------------------------
pthread_join(play, 0);
pthread_join(key, 0);
+ pthread_join(update, 0);
jack_client_close(client);
//------------------------------------------------------------