commit - ed992560fbc706d9790080ef4361e6a48790dd69
commit + b232461a63350252e93337c1c0c30e53f2a8cb0a
blob - 32d3db82039f22d66de13c59bd13e50f1cb25cd7
blob + 0d4c17d99a4faa43760f441d3787183fa9d67c9e
--- cw.c
+++ cw.c
static struct sio_par par;
static size_t framesz, bufsz;
static sample_t *buf;
+static int writ, pos; /* total frames and current play pos. */
/* envelope parameters */
static int rise = 4; /* ramp up (attack) time [ms] */
sio_write(hdl, buf, n * framesz);
}
+
+#ifdef DEBUGTIME
+#include <stdio.h>
+#include <sys/time.h>
+
+static struct timespec t0;
+
+static void
+timestamp(const char *str)
+{
+ struct timespec t, dt;
+ double ms;
+
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ timespecsub(&t, &t0, &dt);
+ ms = dt.tv_sec * 1000 + dt.tv_nsec / 1000000.0;
+ fprintf(stderr, "[%7.3f ms] %s", ms, str);
+}
+#else
+#define timestamp(S)
+#endif
+
/* play a beep of the desired frequency and length */
-void
+int
beep(int f, int len)
{
int n; /* samples left to play */
int x; /* sample position */
int total, nrise, nfall;
- len -= hightime; /* audible time in the ramps */
+ len -= hightime; /* audible time in the ramps */
total = par.rate * len / 1000;
nrise = par.rate * rise / 1000;
nfall = par.rate * fall / 1000;
+#ifdef DEBUGTIME
+ timestamp("beep");
+ fprintf(stderr, "(%d ms): begin writing, %d frames\n", len, total);
+#endif
n = total;
x = 0;
while (n >= bufsz) {
}
if (n > 0)
sample_out(f, x, n, total, nfall);
+ writ += total;
+ timestamp("beep(): done writing\n");
inbeep = 1;
+ return (writ - pos) * 1000 / par.rate; /* remaining play time [ms] */
}
/* play a pause of the desired length */
-void
+int
silence(int len)
{
- int n;
+ int n, total;
memset(buf, 0, bufsz * framesz);
if (inbeep)
- len -= lowtime; /* inaudible time in the ramps */
- n = par.rate * len / 1000;
+ len -= lowtime; /* inaudible time in ramps */
+
+ total = par.rate * len / 1000;
+
+#ifdef DEBUGTIME
+ timestamp("silence");
+ fprintf(stderr, "(%d ms): begin writing, %d frames\n", len, total);
+#endif
+ n = total;
while (n >= bufsz) {
sio_write(hdl, buf, bufsz * framesz);
n -= bufsz;
}
if (n > 0)
sio_write(hdl, buf, n * framesz);
+ writ += total;
+ timestamp("silence(): done writing\n");
inbeep = 0;
+ return (writ - pos) * 1000 / par.rate; /* remaining play time [ms] */
+}
+
+static void
+cb_onmove(void *arg, int delta)
+{
+ pos += delta;
+#ifdef DEBUGTIME
+ timestamp("onmove: ");
+ fprintf(stderr, "delta = %d frames (%d ms), pos = %d/%d\n", delta,
+ delta * 1000 / par.rate, pos, writ);
+#endif
}
void
par.le = SIO_LE_NATIVE; /* native byte order */
par.msb = 0; /* pad the higher bits */
par.pchan = 1; /* one playback channel */
+ par.xrun = SIO_SYNC; /* keep exact timing during underrun */
if (!sio_setpar(hdl, &par))
errx(1, "sio_setpar failed");
if (!sio_getpar(hdl, &par))
errx(1, "got MSB-aligned samples instead of LSB");
sample_max = (1 << (par.bits - 1)) - 1;
+#ifdef DEBUGTIME
+ fprintf(stderr, "par.rate = %6u Hz\n", par.rate);
+ fprintf(stderr, "par.bufsz = %6u (%3u ms)\n", par.bufsz,
+ par.bufsz * 1000 / par.rate);
+ fprintf(stderr, "par.appbufsz = %6u (%3u ms)\n", par.appbufsz,
+ par.appbufsz * 1000 / par.rate);
+#endif
+
/* allocate our buffer */
framesz = par.bps * par.pchan;
bufsz = (par.appbufsz + par.round - 1) / par.round * par.round;
if ((buf = malloc(bufsz * framesz)) == NULL)
err(1, "malloc");
-}
-void
-cwstart()
-{
+
+ /* get config from the environment */
getenvpct("CW_AMPLITUDE", &);
getenvnum("CW_ATTACK", "ms", &rise);
fall = rise; /* default */
lowtime = (rise + fall) / 2;
hightime = rise + fall - lowtime;
- sio_start(hdl);
+ /* register callback to track our position in the output */
+ sio_onmove(hdl, cb_onmove, NULL);
}
void
+cwstart()
+{
+#ifdef DEBUGTIME
+ struct timespec t1, delta;
+ double ms;
+
+ clock_gettime(CLOCK_MONOTONIC, &t0); /* resets the clock */
+ timestamp("cwstart()\n");
+#endif
+ writ = pos = 0;
+ if (!sio_start(hdl))
+ errx(1, "sio_start failed");
+#ifdef DEBUGTIME
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+ timespecsub(&t1, &t0, &delta);
+ ms = delta.tv_sec * 1000 + delta.tv_nsec / 1000000.0;
+ timestamp("sio_start() ");
+ fprintf(stderr, "took %f ms\n", ms);
+#endif
+}
+
+void
cwstop(void)
{
- sio_stop(hdl);
+#ifdef DEBUGTIME
+ struct timespec t0, t1, delta;
+ double ms;
+
+ timestamp("cwstop()\n");
+ clock_gettime(CLOCK_MONOTONIC, &t0);
+#endif
+ if (!sio_stop(hdl))
+ errx(1, "sio_stop failed");
+#ifdef DEBUGTIME
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+ timespecsub(&t1, &t0, &delta);
+ ms = delta.tv_sec * 1000 + delta.tv_nsec / 1000000.0;
+ timestamp("sio_stop() ");
+ fprintf(stderr, "took %f ms\n", ms);
+#endif
}
blob - 75927a85de175288c360be5e27058fdec27214e9
blob + 613e77647e70605af48b71e73f79cb6e6cb6fcf1
--- cw.h
+++ cw.h
void cwinit(void);
void cwstart(void);
void cwstop(void);
-void beep(int, int);
-void silence(int);
+int beep(int, int);
+int silence(int);
/* getenv helpers */
int getenvnum(const char *, const char *, int *);
blob - 7be34dc5fec95edd82a8c8011e032459258be59d
blob + 748852b89b60b9fe5bfaf97e7e733f96d617b36e
--- morse.c
+++ morse.c
void decode(char *);
char *encode(int);
void show(char *);
-void play(char *);
+int play(char *);
void teach(void); /* from teach.c */
static int sflag = 0;
if (!tflag)
if (pledge("stdio audio", NULL) == -1)
err(1, "pledge");
-
- cwstart();
} else {
/* no audio, no teacher - drop access to all but stdio */
if (pledge("stdio", NULL) == -1)
morse(ch);
show("...-.-"); /* SK */
}
- if (aflag || tflag)
- cwstop();
return 0;
}
putchar('t');
}
if (aflag)
- play(s);
+ play(s); // XXX wait until sound has finished playing
printf("\n");
}
-void
+int
play(char *s)
{
+ int pause, rem = 0;
+
if (*s == '\0')
- silence(7 * pditlen - ditlen); /* inter-word pause */
+ pause = 7 * pditlen - ditlen; /* inter-word pause */
else
- silence(3 * pditlen - ditlen); /* intra-word pause */
+ pause = 3 * pditlen - ditlen; /* intra-word pause */
+ cwstart();
+ rem = silence(pause);
while (*s) {
if (*s == '.')
beep(freq, ditlen);
else if (*s == '-')
beep(freq, 3 * ditlen);
s++;
- silence(ditlen);
+ rem = silence(ditlen);
}
+ cwstop();
+
+ return rem; /* remaining play time [ms] */
}
/* the audio hook for teacher mode */
-void
+int
sound(char c)
{
char *s;
if ((s = encode(c)) != NULL)
- play(s);
+ return play(s);
+ else
+ return 0;
}
blob - b1f2eaa242c81607932d3d6d87a5f9b6bd2a56ed
blob + 33debd4fe6388ec3a1e1a5a79545781ccafd84fb
--- morseplay.c
+++ morseplay.c
int nl = 1; /* new line of input? */
int ws = 0; /* whitespace seen? */
int dd = 0; /* dot or dash seen? */
+ int pl = 0; /* playback started (cwstart called)? */
setvbuf(stdout, NULL, _IONBF, 0); /* disable output buffering */
cwinit();
/* main loop */
- cwstart();
while ((ch = getchar()) != EOF) {
switch(ch) {
case '.':
+ if (!pl)
+ cwstart();
if (dd)
silence(len);
beep(freq, len);
- dd = 1;
+ pl = dd = 1;
nl = ws = 0;
break;
case '-':
+ if (!pl)
+ cwstart();
if (dd)
silence(len);
beep(freq, 3 * len);
- dd = 1;
+ pl = dd = 1;
nl = ws = 0;
break;
case ' ':
case '\t':
- if (!ws)
+ if (!ws) {
+ if (!pl)
+ cwstart();
silence(3 * plen);
+ pl = 1;
+ }
ws = 1;
dd = 0;
break;
case '\n':
+ if (!pl)
+ cwstart();
if (nl)
silence(7 * plen);
else if (!ws)
silence(3 * plen);
+ cwstop();
+ // XXX wait until sound has finished playing
nl = ws = 1;
- dd = 0;
+ pl = dd = 0;
break;
default:
continue; /* do not echo */
if (echo)
putchar(ch);
}
- cwstop();
return 0;
}
blob - e554dc61f8bb868917fd0a06b18d214ef45cd015
blob + 8987ab4d704c00e4f4d165076a2480df5dc50380
--- morseteach.c
+++ morseteach.c
FILE *fout, *fin; /* our pipes to 'morse -a' */
int Dflag = 0; /* level of diagnostic output */
-void
+int
sound(char c)
{
fputc(c, fout);
while (fgetc(fin) != '\n') ; /* wait for sound to finish */
+ return 0;
}
/*
blob - 577086ff4ac12eb079c791252762ac8c120c4d48
blob + c135883985cb742fd1e51598dafc674b826609ab
--- teach.c
+++ teach.c
/* externally supplied */
extern int Dflag; /* level of diagnostic output */
-void sound(char); /* sound output routine */
+int sound(char); /* sound output routine */
double
square(double x)
double result;
int input;
int tries;
+ int latency; /* return latency of sound() */
/*
* Emphasize new/unknown symbols by a short pause and automatically
*/
if (failprob[i] > 0.9)
sound(' '); /* inter-word pause */
- sound(ch);
+ latency = sound(ch);
+ //assert(latency == 0); // XXX
clock_gettime(CLOCK_MONOTONIC, &t0);
if (failprob[i] > 0.9)
printf("%c\b", ch);
clock_gettime(CLOCK_MONOTONIC, &t1);
timespecsub(&t1, &t0, &delta);
taken = delta.tv_sec + delta.tv_nsec / 1000000000.0; /* s */
+ //taken += latency / 1000.0;
sigma = sqrt(vartime);
/* if return pressed, pause and show scores */
return -1;
/* otherwise resound, reset timer, and restart */
- sound(ch);
+ latency = sound(ch);
clock_gettime(CLOCK_MONOTONIC, &t0);
continue;
}
return -1;
}
+ /* ignore premature answers */
+ if (taken < 0)
+ continue;
+
tries++;
/* if space pressed, resound and show hint */