diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 9b38cc2512b7..6126e93b28a8 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -1,294 +1,295 @@ # From: @(#)Makefile 8.3 (Berkeley) 1/7/94 # $FreeBSD$ .include SUBDIR= alias \ apply \ asa \ awk \ backlight \ banner \ basename \ + beep \ brandelf \ bsdcat \ bsdiff \ bzip2 \ bzip2recover \ cap_mkdb \ chat \ chpass \ cksum \ cmp \ col \ colrm \ column \ comm \ compress \ cpuset \ csplit \ ctlstat \ cut \ diff \ dirname \ du \ elfctl \ elfdump \ enigma \ env \ etdump \ expand \ false \ fetch \ find \ fmt \ fold \ fstat \ fsync \ gcore \ gencat \ getaddrinfo \ getconf \ getent \ getopt \ grep \ gzip \ head \ hexdump \ id \ ident \ ipcrm \ ipcs \ join \ jot \ keylogin \ keylogout \ killall \ ktrace \ ktrdump \ lam \ ldd \ leave \ less \ lessecho \ lesskey \ limits \ locale \ localedef \ lock \ lockf \ logger \ login \ logins \ logname \ look \ lorder \ lsvfs \ lzmainfo \ m4 \ mandoc \ mesg \ minigzip \ ministat \ mkdep \ mkfifo \ mkimg \ mktemp \ mkuzip \ mt \ ncal \ netstat \ newgrp \ nfsstat \ nice \ nl \ nohup \ opieinfo \ opiekey \ opiepasswd \ pagesize \ passwd \ paste \ patch \ pathchk \ perror \ posixshmcontrol \ pr \ printenv \ printf \ proccontrol \ procstat \ protect \ rctl \ renice \ resizewin \ rev \ revoke \ rpcinfo \ rs \ rup \ ruptime \ rusers \ rwall \ rwho \ script \ sdiff \ sed \ seq \ shar \ showmount \ sockstat \ soelim \ sort \ split \ stat \ stdbuf \ strings \ su \ systat \ tabs \ tail \ tar \ tcopy \ tee \ time \ timeout \ tip \ top \ touch \ tput \ tr \ true \ truncate \ tset \ tsort \ tty \ uname \ unexpand \ uniq \ unzip \ units \ unvis \ uudecode \ uuencode \ vis \ vmstat \ w \ wall \ wc \ what \ whereis \ which \ whois \ write \ xargs \ xinstall \ xo \ xz \ xzdec \ yes \ zstd # NB: keep these sorted by MK_* knobs SUBDIR.${MK_ACCT}+= lastcomm SUBDIR.${MK_AT}+= at SUBDIR.${MK_ATM}+= atm SUBDIR.${MK_BLUETOOTH}+= bluetooth SUBDIR.${MK_BSD_CPIO}+= cpio SUBDIR.${MK_CALENDAR}+= calendar SUBDIR.${MK_CLANG}+= clang SUBDIR.${MK_DIALOG}+= dpv SUBDIR.${MK_EE}+= ee SUBDIR.${MK_FILE}+= file SUBDIR.${MK_FINGER}+= finger SUBDIR.${MK_FTP}+= ftp SUBDIR.${MK_GAMES}+= caesar SUBDIR.${MK_GAMES}+= factor SUBDIR.${MK_GAMES}+= fortune SUBDIR.${MK_GAMES}+= grdc SUBDIR.${MK_GAMES}+= morse SUBDIR.${MK_GAMES}+= number SUBDIR.${MK_GAMES}+= pom SUBDIR.${MK_GAMES}+= primes SUBDIR.${MK_GAMES}+= random SUBDIR.${MK_CXX}+= dtc .if ${MK_GH_BC} == "yes" SUBDIR+= gh-bc .else SUBDIR.${MK_OPENSSL}+= bc SUBDIR.${MK_OPENSSL}+= dc .endif SUBDIR.${MK_HESIOD}+= hesinfo SUBDIR.${MK_ICONV}+= iconv SUBDIR.${MK_ICONV}+= mkcsmapper SUBDIR.${MK_ICONV}+= mkesdb SUBDIR.${MK_ISCSI}+= iscsictl SUBDIR.${MK_KDUMP}+= kdump SUBDIR.${MK_KDUMP}+= truss SUBDIR.${MK_KERBEROS_SUPPORT}+= compile_et SUBDIR.${MK_LDNS_UTILS}+= drill SUBDIR.${MK_LDNS_UTILS}+= host SUBDIR.${MK_LIB32}+= ldd32 SUBDIR.${MK_LOCATE}+= locate # XXX msgs? SUBDIR.${MK_MAIL}+= biff SUBDIR.${MK_MAIL}+= from SUBDIR.${MK_MAIL}+= mail SUBDIR.${MK_MAIL}+= msgs SUBDIR.${MK_MAKE}+= bmake SUBDIR.${MK_MAN_UTILS}+= man SUBDIR.${MK_NETCAT}+= nc SUBDIR.${MK_NIS}+= ypcat SUBDIR.${MK_NIS}+= ypmatch SUBDIR.${MK_NIS}+= ypwhich SUBDIR.${MK_OPENSSH}+= ssh-copy-id SUBDIR.${MK_OPENSSL}+= chkey SUBDIR.${MK_OPENSSL}+= newkey SUBDIR.${MK_QUOTAS}+= quota SUBDIR.${MK_SENDMAIL}+= vacation SUBDIR.${MK_TALK}+= talk SUBDIR.${MK_TELNET}+= telnet SUBDIR.${MK_TESTS_SUPPORT}.${MK_CXX}+= kyua SUBDIR.${MK_TESTS}+= tests SUBDIR.${MK_TEXTPROC}+= ul SUBDIR.${MK_TFTP}+= tftp SUBDIR.${MK_TOOLCHAIN}+= addr2line SUBDIR.${MK_TOOLCHAIN}+= ar SUBDIR.${MK_TOOLCHAIN}+= c89 SUBDIR.${MK_TOOLCHAIN}+= c99 SUBDIR.${MK_TOOLCHAIN}+= ctags .if ${MK_LLVM_CXXFILT} == "no" SUBDIR.${MK_TOOLCHAIN}+= cxxfilt .endif SUBDIR.${MK_TOOLCHAIN}+= objcopy SUBDIR.${MK_TOOLCHAIN}+= file2c SUBDIR.${MK_TOOLCHAIN}+= gprof SUBDIR.${MK_TOOLCHAIN}+= indent SUBDIR.${MK_TOOLCHAIN}+= lex SUBDIR.${MK_TOOLCHAIN}+= mkstr SUBDIR.${MK_TOOLCHAIN}+= nm SUBDIR.${MK_TOOLCHAIN}+= readelf SUBDIR.${MK_TOOLCHAIN}+= rpcgen SUBDIR.${MK_TOOLCHAIN}+= unifdef SUBDIR.${MK_TOOLCHAIN}+= size SUBDIR.${MK_TOOLCHAIN}+= xstr SUBDIR.${MK_TOOLCHAIN}+= yacc SUBDIR.${MK_VI}+= vi SUBDIR.${MK_VT}+= vtfontcvt SUBDIR.${MK_USB}+= usbhidaction SUBDIR.${MK_USB}+= usbhidctl SUBDIR.${MK_UTMPX}+= last .if ${MK_CXX} != "no" SUBDIR.${MK_UTMPX}+= users .endif SUBDIR.${MK_UTMPX}+= who SUBDIR.${MK_SVN}+= svn SUBDIR.${MK_SVNLITE}+= svn SUBDIR.${MK_OFED}+= ofed .include SUBDIR_PARALLEL= .include diff --git a/usr.bin/beep/Makefile b/usr.bin/beep/Makefile new file mode 100644 index 000000000000..754656ef059b --- /dev/null +++ b/usr.bin/beep/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= beep +MAN= beep.1 + +LDFLAGS= -lm + +.include diff --git a/usr.bin/beep/beep.1 b/usr.bin/beep/beep.1 new file mode 100644 index 000000000000..55fe0173272a --- /dev/null +++ b/usr.bin/beep/beep.1 @@ -0,0 +1,84 @@ +.\"- +.\" Copyright (c) 2021 Hans Petter Selasky +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 4, 2021 +.Dt beep 1 +.Os +.Sh NAME +.Nm beep +.Nd play a beep sound +.Sh SYNOPSIS +.Nm +.Op Fl F Ar frequency +.Op Fl D Ar duration_ms +.Op Fl r Ar sample_rate_hz +.Op Fl d Ar oss_device +.Op Fl g Ar gain +.Op Fl B +.Op Fl h +.Sh DESCRIPTION +The +.Nm +utility is used to playback a beep on the soundcard. +.Pp +The options are as follows: +.Bl -tag -width "-f device" +.It Fl F +Sets the center frequency of the beep in Hz. +The default is 440 Hz . +.It Fl D +Sets the duration of the beep in milliseconds. +The default is 150 ms . +.It Fl d +Sets the soundcard to use. +The default is /dev/dsp . +.It Fl r +Sets the soundcard samplerate in Hz. +The default is 48000 Hz. +.It Fl g +Sets the waveform gain, between 0 and 100 inclusively. +The default is 75. +.It Fl B +Runs the +.Nm +utility in the background. +.It Fl h +Display summary of options. +.El +.Sh EXAMPLES +.Pp +Playback default beep sound using /dev/dsp . +.Bl -tag -width Ds -offset indent +.It $ beep +.El +.Sh SEE ALSO +.Xr mixer 3 , +.Xr sound 4 , +.Sh HISTORY +The +.Nm +utility first appeared in FreeBSD 14.0. +.Sh AUTHORS +.An Hans Petter Selasky Aq Mt hselasky@FreeBSD.org diff --git a/usr.bin/beep/beep.c b/usr.bin/beep/beep.c new file mode 100644 index 000000000000..151236b4825b --- /dev/null +++ b/usr.bin/beep/beep.c @@ -0,0 +1,275 @@ +/*- + * Copyright (c) 2021 Hans Petter Selasky + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAMPLE_RATE_DEF 48000 /* hz */ +#define SAMPLE_RATE_MAX 48000 /* hz */ +#define SAMPLE_RATE_MIN 8000 /* hz */ + +#define DURATION_DEF 150 /* ms */ +#define DURATION_MAX 2000 /* ms */ +#define DURATION_MIN 50 /* ms */ + +#define GAIN_DEF 75 +#define GAIN_MAX 100 +#define GAIN_MIN 0 + +#define WAVE_POWER 1.25f + +#define DEFAULT_HZ 440 + +#define DEFAULT_DEVICE _PATH_DEV "dsp" + +static int frequency = DEFAULT_HZ; +static int duration_ms = DURATION_DEF; +static int sample_rate = SAMPLE_RATE_DEF; +static int gain = GAIN_DEF; +static const char *oss_dev = DEFAULT_DEVICE; +static bool background; + +/* + * wave_function_16 + * + * "phase" should be in the range [0.0f .. 1.0f> + * "power" should be in the range <0.0f .. 2.0f> + * + * The return value is in the range [-1.0f .. 1.0f] + */ +static float +wave_function_16(float phase, float power) +{ + uint16_t x = phase * (1U << 16); + float retval; + uint8_t num; + + /* Handle special cases, if any */ + switch (x) { + case 0xffff: + case 0x0000: + return (1.0f); + case 0x3fff: + case 0x4000: + case 0xBfff: + case 0xC000: + return (0.0f); + case 0x7FFF: + case 0x8000: + return (-1.0f); + default: + break; + } + + /* Apply Gray coding */ + for (uint16_t mask = 1U << 15; mask != 1; mask /= 2) { + if (x & mask) + x ^= (mask - 1); + } + + /* Find first set bit */ + for (num = 0; num != 14; num++) { + if (x & (1U << num)) { + num++; + break; + } + } + + /* Initialize return value */ + retval = 0.0; + + /* Compute the rest of the power series */ + for (; num != 14; num++) { + if (x & (1U << num)) { + retval = (1.0f - retval) / 2.0f; + retval = powf(retval, power); + } else { + retval = (1.0f + retval) / 2.0f; + retval = powf(retval, power); + } + } + + /* Check if halfway */ + if (x & (1ULL << 14)) + retval = -retval; + + return (retval); +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: %s [parameters]\n" + "\t" "-F \n" + "\t" "-D \n" + "\t" "-r \n" + "\t" "-d \n" + "\t" "-g \n" + "\t" "-B Run in background\n" + "\t" "-h Show usage\n", + getprogname(), + DEFAULT_HZ, + DURATION_MIN, DURATION_MAX, DURATION_DEF, + SAMPLE_RATE_MIN, SAMPLE_RATE_MAX, SAMPLE_RATE_DEF, + DEFAULT_DEVICE, + GAIN_MIN, GAIN_MAX, GAIN_DEF); + exit(1); +} + +int +main(int argc, char **argv) +{ + int32_t *buffer; + size_t slope; + size_t size; + 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) { + switch (c) { + case 'F': + frequency = strtol(optarg, NULL, 10); + break; + case 'D': + duration_ms = strtol(optarg, NULL, 10); + if (duration_ms < DURATION_MIN || + duration_ms > DURATION_MAX) + usage(); + break; + case 'r': + sample_rate = strtol(optarg, NULL, 10); + if (sample_rate < SAMPLE_RATE_MIN || + sample_rate > SAMPLE_RATE_MAX) + usage(); + break; + case 'g': + gain = strtol(optarg, NULL, 10); + if (gain < GAIN_MIN || + gain > GAIN_MAX) + usage(); + break; + case 'd': + oss_dev = optarg; + break; + case 'B': + background = true; + break; + default: + usage(); + break; + } + } + + if (background && daemon(0, 0) != 0) + errx(1, "daemon(0,0) failed"); + + f = open(oss_dev, O_WRONLY); + if (f < 0) + errx(1, "Failed to open '%s'", oss_dev); + + c = 1; /* mono */ + if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0) + errx(1, "ioctl SOUND_PCM_WRITE_CHANNELS(1) failed"); + + c = AFMT_S32_NE; + if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0) + errx(1, "ioctl SNDCTL_DSP_SETFMT(AFMT_S32_NE) failed"); + + if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0) + errx(1, "ioctl SNDCTL_DSP_SPEED(%d) failed", sample_rate); + + c = (2 << 16); + while ((1ULL << (c & 63)) < (size_t)(4 * sample_rate / 50)) + c++; + if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c)) + errx(1, "ioctl SNDCTL_DSP_SETFRAGMENT(0x%x) failed", c); + + if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0) + errx(1, "ioctl SNDCTL_DSP_GETODELAY failed"); + + size = ((sample_rate * duration_ms) + 999) / 1000; + buffer = malloc(sizeof(buffer[0]) * size); + 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; + + buffer[off] = sample * 0x7fffff00; + } + + if (write(f, buffer, size * sizeof(buffer[0])) != + (ssize_t)(size * sizeof(buffer[0]))) + errx(1, "failed writing to DSP device(%s)", oss_dev); + + free(buffer); + + /* wait for data to be written */ + while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) { + if (c == 0) + break; + usleep(10000); + } + + /* wait for audio to go out */ + usleep(50000); + close(f); + + return (0); +}