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 @@ -1,5 +1,5 @@ .\"- -.\" Copyright (c) 2021 Hans Petter Selasky +.\" Copyright (c) 2021-2022 Hans Petter Selasky .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -23,7 +23,7 @@ .\" .\" $FreeBSD$ .\" -.Dd November 4, 2021 +.Dd July 11, 2022 .Dt beep 1 .Os .Sh NAME @@ -36,6 +36,10 @@ .Op Fl r Ar sample_rate_hz .Op Fl d Ar oss_device .Op Fl g Ar gain +.Op Fl t Ar UTF-8-text +.Op Fl b Ar 8-bit-number +.Op Fl s Ar 16-bit-number +.Op Fl i Ar 32-bit-number .Op Fl B .Op Fl h .Sh DESCRIPTION @@ -60,6 +64,20 @@ .It Fl g Sets the waveform gain, between 0 and 100 inclusively. The default is 75. +.It Fl t +Render the given UTF-8 text starting at the least significant bit, similarly to Braille. +Each full character is output separately, meaning either 8-, 12-, 17- or 22-bits will be output. +A small pause separate each character. +This option can also be combined with other options, like duration and frequency. +.It Fl b +Render the given number as a 8-bit number, starting at the least significant bit. +This option can also be combined with other options, like duration and frequency. +.It Fl s +Render the given number as a 16-bit number, starting at the least significant bit. +This option can also be combined with other options, like duration and frequency. +.It Fl i +Render the given number as a 32-bit number, starting at the least significant bit. +This option can also be combined with other options, like duration and frequency. .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 @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2021 Hans Petter Selasky + * Copyright (c) 2021-2022 Hans Petter Selasky * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,6 +36,7 @@ #include #include #include +#include #define SAMPLE_RATE_DEF 48000 /* hz */ #define SAMPLE_RATE_MAX 48000 /* hz */ @@ -61,6 +62,10 @@ static int gain = GAIN_DEF; static const char *oss_dev = DEFAULT_DEVICE; static bool background; +static const uint8_t *text; +static const uint8_t *number; +static uint32_t number_parsed; +static uint8_t number_bits; /* * wave_function_16 @@ -129,6 +134,135 @@ 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 uint32_t +get_unicode_char(const uint8_t **pptr, uint8_t *bits) +{ + const uint8_t *p8; + uint32_t ch; + + p8 = *pptr; + + if ((*p8 & 0xc0) == 0xc0) { + p8++; + while ((*p8 & 0xc0) == 0x80) + p8++; + switch (p8 - *pptr) { + case 1: /* 12-bit */ + ch = (*pptr)[0] & 0x3f; + ch <<= 6; + ch |= (*pptr)[1] & 0x3f; + + *pptr = p8; + *bits = 12; + goto done; + case 2: /* 17-bit */ + ch = (*pptr)[0] & 0x1f; + ch <<= 6; + ch |= (*pptr)[1] & 0x3f; + ch <<= 6; + ch |= (*pptr)[2] & 0x3f; + + *pptr = p8; + *bits = 17; + goto done; + case 3: /* 22-bit */ + ch = (*pptr)[0] & 0x0f; + ch <<= 6; + ch |= (*pptr)[1] & 0x3f; + ch <<= 6; + ch |= (*pptr)[2] & 0x3f; + ch <<= 6; + ch |= (*pptr)[3] & 0x3f; + + *pptr = p8; + *bits = 22; + goto done; + default: + break; + } + } + /* not UTF-8 */ + ch = **pptr; + (*pptr) ++; + *bits = 8; +done: + return (ch); +} + +static size_t +text_length_in_units(const uint8_t *ptr) +{ + size_t units = 0; + uint32_t ch; + uint8_t bits; + + while (*ptr) { + ch = get_unicode_char(&ptr, &bits); + + for (uint8_t x = 0; x != bits; x++) { + if ((ch >> x) & 1) + units += 2; + else + units += 4; + } + + units += 4; + } + return (units); +} + +static size_t +number_length_in_units(uint32_t num, uint8_t max) +{ + size_t units = 0; + + for (uint8_t x = 0; x != max; x++) { + if ((num >> x) & 1) + units += 2; + else + units += 4; + } + return (units); +} + static void usage(void) { @@ -138,6 +272,10 @@ "\t" "-r \n" "\t" "-d \n" "\t" "-g \n" + "\t" "-t \n" + "\t" "-b \n" + "\t" "-s \n" + "\t" "-i \n" "\t" "-B Run in background\n" "\t" "-h Show usage\n", getprogname(), @@ -153,16 +291,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:b:s:i:t:g:d:h")) != -1) { switch (c) { case 'F': frequency = strtol(optarg, NULL, 10); @@ -191,12 +329,30 @@ case 'B': background = true; break; + case 'b': + number = optarg; + number_bits = 8; + break; + case 's': + number = optarg; + number_bits = 16; + break; + case 'i': + number = optarg; + number_bits = 32; + break; + case 't': + text = optarg; + break; default: usage(); break; } } + if (text != NULL && number != NULL) + errx(1, "-t and -s options are mutually exclusive"); + if (background && daemon(0, 0) != 0) errx(1, "daemon(0,0) failed"); @@ -224,38 +380,84 @@ if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0) errx(1, "ioctl SNDCTL_DSP_GETODELAY failed"); + if (number != NULL) { + number_parsed = strtoul(number, NULL, 0); + units = number_length_in_units(number_parsed, number_bits); + } else 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; - - p = p - floorf(p); - sample = a * 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; + if (text != NULL || number != 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); + } else { + buffer_short = NULL; + buffer_long = NULL; + } - buffer[off] = sample * 0x7fffff00; + if (number != NULL) { + off = 0; + + for (uint8_t x = 0; x != number_bits; x++) { + if ((number_parsed >> 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; + } + } + assert(off == (size * units)); + } else if (text != NULL) { + for (off = 0; *text != 0; ) { + uint8_t bits; + uint32_t ch; + + ch = get_unicode_char(&text, &bits); + + for (uint8_t x = 0; x != bits; 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; + } + assert(off == (size * units)); + } else { + render_beep(buffer, size, a); } - if (write(f, buffer, size * sizeof(buffer[0])) != - (ssize_t)(size * sizeof(buffer[0]))) + free(buffer_short); + free(buffer_long); + + 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);