commit b232461a63350252e93337c1c0c30e53f2a8cb0a from: Sven M. Hallberg date: Thu Nov 09 18:44:05 2023 UTC try to figure out proper timing This is an intermediate commit of some experimental changes: - playback in synchronous mode (par.xrun = SIO_SYNC) - tracking of play position via sio_onmove() and total frames written - (broken/ignored) reporting of "time remaining" from sound() and friends - debug instrumentation (#ifdef DEBUGTIME) commit - ed992560fbc706d9790080ef4361e6a48790dd69 commit + b232461a63350252e93337c1c0c30e53f2a8cb0a blob - 32d3db82039f22d66de13c59bd13e50f1cb25cd7 blob + 0d4c17d99a4faa43760f441d3787183fa9d67c9e --- cw.c +++ cw.c @@ -22,6 +22,7 @@ static struct sio_hdl *hdl; 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] */ @@ -185,20 +186,46 @@ sample_out(int f, int x, int n, int total, int nramp) sio_write(hdl, buf, n * framesz); } + +#ifdef DEBUGTIME +#include +#include + +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) { @@ -208,29 +235,53 @@ beep(int f, int len) } 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 @@ -246,6 +297,7 @@ cwinit(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)) @@ -265,16 +317,22 @@ cwinit(void) 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 */ @@ -294,11 +352,49 @@ cwstart() 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 @@ -8,8 +8,8 @@ rampfun_t ramp_lin, ramp_sqr, ramp_sin, ramp_cos, ramp 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 @@ -130,7 +130,7 @@ int lookup(char *); 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; @@ -206,8 +206,6 @@ main(int argc, char *argv[]) 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) @@ -239,8 +237,6 @@ main(int argc, char *argv[]) morse(ch); show("...-.-"); /* SK */ } - if (aflag || tflag) - cwstop(); return 0; } @@ -376,34 +372,43 @@ show(char *s) 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 @@ -35,6 +35,7 @@ main(int argc, char **argv) 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 */ @@ -64,37 +65,48 @@ main(int argc, char **argv) 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 */ @@ -103,7 +115,6 @@ main(int argc, char **argv) if (echo) putchar(ch); } - cwstop(); return 0; } blob - e554dc61f8bb868917fd0a06b18d214ef45cd015 blob + 8987ab4d704c00e4f4d165076a2480df5dc50380 --- morseteach.c +++ morseteach.c @@ -10,11 +10,12 @@ void teach(void); /* from teach.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 @@ -61,7 +61,7 @@ struct timespec tpause; /* time spent paused */ /* 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) @@ -164,6 +164,7 @@ trial(int i, int ch) double result; int input; int tries; + int latency; /* return latency of sound() */ /* * Emphasize new/unknown symbols by a short pause and automatically @@ -171,7 +172,8 @@ trial(int i, int ch) */ 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); @@ -189,6 +191,7 @@ trial(int i, int 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 */ @@ -197,7 +200,7 @@ trial(int i, int ch) return -1; /* otherwise resound, reset timer, and restart */ - sound(ch); + latency = sound(ch); clock_gettime(CLOCK_MONOTONIC, &t0); continue; } @@ -211,6 +214,10 @@ trial(int i, int ch) return -1; } + /* ignore premature answers */ + if (taken < 0) + continue; + tries++; /* if space pressed, resound and show hint */