diff --git a/usr.bin/beep/beep.1 b/usr.bin/beep/beep.1 --- a/usr.bin/beep/beep.1 +++ b/usr.bin/beep/beep.1 @@ -23,7 +23,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 4, 2021 +.Dd July 11, 2022 .Dt beep 1 .Os .Sh NAME @@ -36,6 +36,7 @@ .Op Fl r Ar sample_rate_hz .Op Fl d Ar oss_device .Op Fl g Ar gain +.Op Fl t Ar text .Op Fl B .Op Fl h .Sh DESCRIPTION @@ -60,6 +61,9 @@ .It Fl g Sets the waveform gain, between 0 and 100 inclusively. The default is 75. +.It Fl t +Render the given text as 8-bit audio codes starting at the least significant bit, similarly to Braille. +This option can also be combined with the duration option to either speed up or slow down the audio. .It Fl B Runs the .Nm diff --git a/usr.bin/beep/beep.c b/usr.bin/beep/beep.c --- a/usr.bin/beep/beep.c +++ b/usr.bin/beep/beep.c @@ -36,6 +36,7 @@ #include #include #include +#include #define SAMPLE_RATE_DEF 48000 /* hz */ #define SAMPLE_RATE_MAX 48000 /* hz */ @@ -61,6 +62,7 @@ static int gain = GAIN_DEF; static const char *oss_dev = DEFAULT_DEVICE; static bool background; +static const uint8_t *text; /* * wave_function_16 @@ -129,6 +131,65 @@ return (retval); } +static void +render_beep(int32_t *buffer, size_t size, float amp) +{ + size_t slope; + size_t off; /* buffer offset */ + float p; /* phase */ + float d; /* delta phase */ + + /* compute slope duration in samples */ + slope = (DURATION_MIN * sample_rate) / 2000; + + /* set initial phase and delta */ + p = 0; + d = (float)frequency / (float)sample_rate; + + /* compute wave */ + for (p = off = 0; off != size; off++, p += d) { + float sample; + + p = p - floorf(p); + sample = amp * wave_function_16(p, WAVE_POWER); + + if (off < slope) + sample = sample * off / (float)slope; + else if (off > (size - slope)) + sample = sample * (size - off - 1) / (float)slope; + + buffer[off] = sample * 0x7fffff00; + } +} + +static void +render_silence(int32_t *buffer, size_t size) +{ + for (size_t x = 0; x != size; x++) + buffer[x] = 0; +} + +static size_t +text_length_in_units(const uint8_t *ptr) +{ + size_t units = 0; + + while (*ptr) { + uint8_t ch = *ptr; + + for (uint8_t x = 0; x != 8; x++) { + if ((ch >> x) & 1) + units += 2; + else + units += 4; + } + + units += 4; + ptr ++; + } + return (units); +} + static void usage(void) { @@ -138,6 +199,7 @@ "\t" "-r \n" "\t" "-d \n" "\t" "-g \n" + "\t" "-t \n" "\t" "-B Run in background\n" "\t" "-h Show usage\n", getprogname(), @@ -153,16 +215,16 @@ main(int argc, char **argv) { int32_t *buffer; - size_t slope; + int32_t *buffer_short; + int32_t *buffer_long; size_t size; + size_t units; size_t off; float a; - float d; - float p; int c; int f; - while ((c = getopt(argc, argv, "BF:D:r:g:d:h")) != -1) { + while ((c = getopt(argc, argv, "BF:D:r:t:g:d:h")) != -1) { switch (c) { case 'F': frequency = strtol(optarg, NULL, 10); @@ -191,6 +253,9 @@ case 'B': background = true; break; + case 't': + text = optarg; + break; default: usage(); break; @@ -224,38 +289,60 @@ if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0) errx(1, "ioctl SNDCTL_DSP_GETODELAY failed"); + if (text != NULL) + units = text_length_in_units(text); + else + units = 1; + size = ((sample_rate * duration_ms) + 999) / 1000; - buffer = malloc(sizeof(buffer[0]) * size); + buffer = malloc(sizeof(buffer[0]) * size * units); if (buffer == NULL) errx(1, "out of memory"); - /* compute slope duration in samples */ - slope = (DURATION_MIN * sample_rate) / 2000; - /* compute base gain */ a = powf(65536.0f, (float)gain / (float)GAIN_MAX) / 65536.0f; - /* set initial phase and delta */ - p = 0; - d = (float)frequency / (float)sample_rate; - - /* compute wave */ - for (p = off = 0; off != size; off++, p += d) { - float sample; + if (text != NULL) { + buffer_short = malloc(sizeof(buffer[0]) * size * 2); + if (buffer_short == NULL) + errx(1, "out of memory"); + buffer_long = malloc(sizeof(buffer[0]) * size * 4); + if (buffer_long == NULL) + errx(1, "out of memory"); + + render_beep(buffer_short, size, a); + render_silence(buffer_short + size, size); + + render_beep(buffer_long, 3 * size, a); + render_silence(buffer_long + 3 * size, size); + + for (off = 0; *text != 0; text++) { + uint8_t ch = *text; + + for (uint8_t x = 0; x != 8; x++) { + if ((ch >> x) & 1) { + memcpy(buffer + off, buffer_short, 2 * size * sizeof(buffer[0])); + off += 2 * size; + } else { + memcpy(buffer + off, buffer_long, 4 * size * sizeof(buffer[0])); + off += 4 * size; + } + } + + render_silence(buffer + off, 4 * size); + off += 4 * size; + } - p = p - floorf(p); - sample = a * wave_function_16(p, WAVE_POWER); + free(buffer_short); + free(buffer_long); - if (off < slope) - sample = sample * off / (float)slope; - else if (off > (size - slope)) - sample = sample * (size - off - 1) / (float)slope; - - buffer[off] = sample * 0x7fffff00; + assert(off == (size * units)); + } else { + render_beep(buffer, size, a); } - - if (write(f, buffer, size * sizeof(buffer[0])) != - (ssize_t)(size * sizeof(buffer[0]))) + + if (write(f, buffer, size * units * sizeof(buffer[0])) != + (ssize_t)(size * units * sizeof(buffer[0]))) errx(1, "failed writing to DSP device(%s)", oss_dev); free(buffer);