#include <cxops.h>
#define SAMP_RATE (48000)
-#define HUGE_PHASE 1256637061.43593
+#define HUGE_PHASE (1256637061.43593)
#define RING_SIZE (01 << 020)
jack_nframes_t size;
BOOLEAN playing = FALSE;
-double wpm = 18.0, freq = 750.0, gain = -6.0;
+double wpm = 18.0, freq = 750.0, gain = -6.0, ramp = 5.0;
COMPLEX *zout = 0;
//------------------------------------------------------------
-// map char -> morse string
+// try to map char -> morse string
char *
get_morse(int c) { return morse_table[c & 0x7F]; }
+// translate text input to timed, sub-morse-element
+// audio segment specs; parcel the segments out
+// one at a time to the sound player
void
reader_thread(void) {
BOOLEAN b = TRUE; // we're coming from silence
if (m = get_morse(c)) {
// yup
- // for each sub-element (dit/dah)
+ // for each element in morse string
+ // (dit/dah, doesn't matter)
while (e = *m++) {
- // first segment is slew in...
+ // first segment is ramp up...
sem_wait(&reader);
morsel.type = ME_RAMP, morsel.size = risesize;
morsel.curr = 0.0, morsel.incr = riseincr;
sem_post(&writer);
// ...then steady state...
+ // (choose dit/dah here)
sem_wait(&reader);
morsel.type = ME_STDY;
morsel.size = e == '.' ? ditstdysize : dahstdysize;
sem_post(&writer);
- // ...then slew out...
+ // ...then ramp down...
sem_wait(&reader);
morsel.type = ME_RAMP, morsel.size = fallsize;
morsel.curr = 1.0, morsel.incr = fallincr;
b = FALSE;
} else {
- // anything else treated as interword space
+ // anything else treated as interword space,
+ // which has only one segment (silence)
sem_wait(&reader);
morsel.type = ME_ZERO;
// was previous output also interword space?
scale = pow(10.0, gain / 20.0);
COMPLEX z, delta_z;
- // as long as there's been no EOF on the input...
+ // keep looking for sub-element segments, one at a time
for (;;) {
- // pause for next sub-element
+ // pause for next sub-element segment
sem_post(&reader);
sem_wait(&writer);
// no more data?
if (morsel.type == ME_EOF) break;
- // interword space == silence?
+ // requires playing some tone?
if (morsel.type != ME_ZERO) {
- // no, set up CORDIC tone generation
+ // yes, set up CORDIC tone generation
if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
z = Cmplx(cos(phase), sin(phase));
delta_z = Cmplx(cos(ofreq), sin(ofreq));
}
- // play out this sub-segment
+ // play out this segment
for (i = 0; i < morsel.size; i++) {
// make silence
else {
z = Cmul(z, delta_z);
phase += ofreq;
- // slewing segment?
+ // is this a ramping segment?
if (morsel.type == ME_RAMP) {
morsel.curr += morsel.incr;
zout[k] = Cscl(z, scale * sin(morsel.curr * M_PI / 2.0));
zout[k] = Cscl(z, scale);
}
- // one jack bufferful yet?
+ // have we played enough to fill a jack buffer?
if (++k >= size) {
// yes, send to output
send_sound(zout, k);
- // wait until it's been taken away
+ // wait until some audio has been drained
sem_wait(&ready);
k = 0;
- // reset CORDIC
if (morsel.type != ME_ZERO) {
+ // reset CORDIC
if (phase > HUGE_PHASE) phase -= HUGE_PHASE;
z = Cmplx(cos(phase), sin(phase));
delta_z = Cmplx(cos(ofreq), sin(ofreq));
}
}
- // anything left unsent in buffer?
+ // anything left unsent?
if (k > 0) send_sound(zout, k);
pthread_exit(0);
case 'w':
wpm = atof(argv[++i]);
break;
+ case 'r':
+ ramp = atof(argv[++i]);
+ break;
default:
- fprintf(stderr, "keyd [-w wpm] [-f freq] [infile]\n");
+ fprintf(stderr, "keyd [-w wpm] [-f freq] [-r ramp_ms] [infile]\n");
exit(1);
}
else break;
//------------------------------------------------------------
morsel.wpm = wpm;
- morsel.rise = morsel.fall = 5.0; // ms
+ morsel.rise = morsel.fall = ramp;
morsel.rate = SAMP_RATE;
ditspacesize = SAMP_RATE * 1.2 / morsel.wpm + 0.5;
exit(1);
}
if (jack_connect(client, jack_port_name(rport), ports[1])) {
- fprintf(stderr, "can't connect left output\n");
+ fprintf(stderr, "can't connect right output\n");
exit(1);
}
free(ports);