18abfbe5aSHans Petter Selasky /*- 28abfbe5aSHans Petter Selasky * Copyright (c) 2021 Hans Petter Selasky <hselasky@freebsd.org> 38abfbe5aSHans Petter Selasky * 48abfbe5aSHans Petter Selasky * Redistribution and use in source and binary forms, with or without 58abfbe5aSHans Petter Selasky * modification, are permitted provided that the following conditions 68abfbe5aSHans Petter Selasky * are met: 78abfbe5aSHans Petter Selasky * 1. Redistributions of source code must retain the above copyright 88abfbe5aSHans Petter Selasky * notice, this list of conditions and the following disclaimer. 98abfbe5aSHans Petter Selasky * 2. Redistributions in binary form must reproduce the above copyright 108abfbe5aSHans Petter Selasky * notice, this list of conditions and the following disclaimer in the 118abfbe5aSHans Petter Selasky * documentation and/or other materials provided with the distribution. 128abfbe5aSHans Petter Selasky * 138abfbe5aSHans Petter Selasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 148abfbe5aSHans Petter Selasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 158abfbe5aSHans Petter Selasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 168abfbe5aSHans Petter Selasky * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 178abfbe5aSHans Petter Selasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 188abfbe5aSHans Petter Selasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 198abfbe5aSHans Petter Selasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 208abfbe5aSHans Petter Selasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 218abfbe5aSHans Petter Selasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 228abfbe5aSHans Petter Selasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 238abfbe5aSHans Petter Selasky * SUCH DAMAGE. 248abfbe5aSHans Petter Selasky */ 258abfbe5aSHans Petter Selasky 268abfbe5aSHans Petter Selasky #include <sys/soundcard.h> 278abfbe5aSHans Petter Selasky 288abfbe5aSHans Petter Selasky #include <err.h> 298abfbe5aSHans Petter Selasky #include <errno.h> 308abfbe5aSHans Petter Selasky #include <fcntl.h> 318abfbe5aSHans Petter Selasky #include <math.h> 328abfbe5aSHans Petter Selasky #include <paths.h> 338abfbe5aSHans Petter Selasky #include <stdbool.h> 348abfbe5aSHans Petter Selasky #include <stdint.h> 358abfbe5aSHans Petter Selasky #include <stdio.h> 368abfbe5aSHans Petter Selasky #include <stdlib.h> 378abfbe5aSHans Petter Selasky #include <string.h> 388abfbe5aSHans Petter Selasky #include <unistd.h> 398abfbe5aSHans Petter Selasky 408abfbe5aSHans Petter Selasky #define SAMPLE_RATE_DEF 48000 /* hz */ 418abfbe5aSHans Petter Selasky #define SAMPLE_RATE_MAX 48000 /* hz */ 428abfbe5aSHans Petter Selasky #define SAMPLE_RATE_MIN 8000 /* hz */ 438abfbe5aSHans Petter Selasky 448abfbe5aSHans Petter Selasky #define DURATION_DEF 150 /* ms */ 458abfbe5aSHans Petter Selasky #define DURATION_MAX 2000 /* ms */ 468abfbe5aSHans Petter Selasky #define DURATION_MIN 50 /* ms */ 478abfbe5aSHans Petter Selasky 488abfbe5aSHans Petter Selasky #define GAIN_DEF 75 498abfbe5aSHans Petter Selasky #define GAIN_MAX 100 508abfbe5aSHans Petter Selasky #define GAIN_MIN 0 518abfbe5aSHans Petter Selasky 528abfbe5aSHans Petter Selasky #define WAVE_POWER 1.25f 538abfbe5aSHans Petter Selasky 548abfbe5aSHans Petter Selasky #define DEFAULT_HZ 440 558abfbe5aSHans Petter Selasky 568abfbe5aSHans Petter Selasky #define DEFAULT_DEVICE _PATH_DEV "dsp" 578abfbe5aSHans Petter Selasky 588abfbe5aSHans Petter Selasky static int frequency = DEFAULT_HZ; 598abfbe5aSHans Petter Selasky static int duration_ms = DURATION_DEF; 608abfbe5aSHans Petter Selasky static int sample_rate = SAMPLE_RATE_DEF; 618abfbe5aSHans Petter Selasky static int gain = GAIN_DEF; 628abfbe5aSHans Petter Selasky static const char *oss_dev = DEFAULT_DEVICE; 638abfbe5aSHans Petter Selasky static bool background; 648abfbe5aSHans Petter Selasky 658abfbe5aSHans Petter Selasky /* 668abfbe5aSHans Petter Selasky * wave_function_16 678abfbe5aSHans Petter Selasky * 688abfbe5aSHans Petter Selasky * "phase" should be in the range [0.0f .. 1.0f> 698abfbe5aSHans Petter Selasky * "power" should be in the range <0.0f .. 2.0f> 708abfbe5aSHans Petter Selasky * 718abfbe5aSHans Petter Selasky * The return value is in the range [-1.0f .. 1.0f] 728abfbe5aSHans Petter Selasky */ 738abfbe5aSHans Petter Selasky static float 748abfbe5aSHans Petter Selasky wave_function_16(float phase, float power) 758abfbe5aSHans Petter Selasky { 768abfbe5aSHans Petter Selasky uint16_t x = phase * (1U << 16); 778abfbe5aSHans Petter Selasky float retval; 788abfbe5aSHans Petter Selasky uint8_t num; 798abfbe5aSHans Petter Selasky 808abfbe5aSHans Petter Selasky /* Handle special cases, if any */ 818abfbe5aSHans Petter Selasky switch (x) { 828abfbe5aSHans Petter Selasky case 0xffff: 838abfbe5aSHans Petter Selasky case 0x0000: 848abfbe5aSHans Petter Selasky return (1.0f); 858abfbe5aSHans Petter Selasky case 0x3fff: 868abfbe5aSHans Petter Selasky case 0x4000: 878abfbe5aSHans Petter Selasky case 0xBfff: 888abfbe5aSHans Petter Selasky case 0xC000: 898abfbe5aSHans Petter Selasky return (0.0f); 908abfbe5aSHans Petter Selasky case 0x7FFF: 918abfbe5aSHans Petter Selasky case 0x8000: 928abfbe5aSHans Petter Selasky return (-1.0f); 938abfbe5aSHans Petter Selasky default: 948abfbe5aSHans Petter Selasky break; 958abfbe5aSHans Petter Selasky } 968abfbe5aSHans Petter Selasky 978abfbe5aSHans Petter Selasky /* Apply Gray coding */ 988abfbe5aSHans Petter Selasky for (uint16_t mask = 1U << 15; mask != 1; mask /= 2) { 998abfbe5aSHans Petter Selasky if (x & mask) 1008abfbe5aSHans Petter Selasky x ^= (mask - 1); 1018abfbe5aSHans Petter Selasky } 1028abfbe5aSHans Petter Selasky 1038abfbe5aSHans Petter Selasky /* Find first set bit */ 1048abfbe5aSHans Petter Selasky for (num = 0; num != 14; num++) { 1058abfbe5aSHans Petter Selasky if (x & (1U << num)) { 1068abfbe5aSHans Petter Selasky num++; 1078abfbe5aSHans Petter Selasky break; 1088abfbe5aSHans Petter Selasky } 1098abfbe5aSHans Petter Selasky } 1108abfbe5aSHans Petter Selasky 1118abfbe5aSHans Petter Selasky /* Initialize return value */ 1128abfbe5aSHans Petter Selasky retval = 0.0; 1138abfbe5aSHans Petter Selasky 1148abfbe5aSHans Petter Selasky /* Compute the rest of the power series */ 1158abfbe5aSHans Petter Selasky for (; num != 14; num++) { 1168abfbe5aSHans Petter Selasky if (x & (1U << num)) { 1178abfbe5aSHans Petter Selasky retval = (1.0f - retval) / 2.0f; 1188abfbe5aSHans Petter Selasky retval = powf(retval, power); 1198abfbe5aSHans Petter Selasky } else { 1208abfbe5aSHans Petter Selasky retval = (1.0f + retval) / 2.0f; 1218abfbe5aSHans Petter Selasky retval = powf(retval, power); 1228abfbe5aSHans Petter Selasky } 1238abfbe5aSHans Petter Selasky } 1248abfbe5aSHans Petter Selasky 1258abfbe5aSHans Petter Selasky /* Check if halfway */ 1268abfbe5aSHans Petter Selasky if (x & (1ULL << 14)) 1278abfbe5aSHans Petter Selasky retval = -retval; 1288abfbe5aSHans Petter Selasky 1298abfbe5aSHans Petter Selasky return (retval); 1308abfbe5aSHans Petter Selasky } 1318abfbe5aSHans Petter Selasky 1328abfbe5aSHans Petter Selasky static void 1338abfbe5aSHans Petter Selasky usage(void) 1348abfbe5aSHans Petter Selasky { 1358abfbe5aSHans Petter Selasky fprintf(stderr, "Usage: %s [parameters]\n" 1368abfbe5aSHans Petter Selasky "\t" "-F <frequency in HZ, default %d Hz>\n" 1378abfbe5aSHans Petter Selasky "\t" "-D <duration in ms, from %d ms to %d ms, default %d ms>\n" 1388abfbe5aSHans Petter Selasky "\t" "-r <sample rate in HZ, from %d Hz to %d Hz, default %d Hz>\n" 1398abfbe5aSHans Petter Selasky "\t" "-d <OSS device (default %s)>\n" 1408abfbe5aSHans Petter Selasky "\t" "-g <gain from %d to %d, default %d>\n" 1418abfbe5aSHans Petter Selasky "\t" "-B Run in background\n" 1428abfbe5aSHans Petter Selasky "\t" "-h Show usage\n", 1438abfbe5aSHans Petter Selasky getprogname(), 1448abfbe5aSHans Petter Selasky DEFAULT_HZ, 1458abfbe5aSHans Petter Selasky DURATION_MIN, DURATION_MAX, DURATION_DEF, 1468abfbe5aSHans Petter Selasky SAMPLE_RATE_MIN, SAMPLE_RATE_MAX, SAMPLE_RATE_DEF, 1478abfbe5aSHans Petter Selasky DEFAULT_DEVICE, 1488abfbe5aSHans Petter Selasky GAIN_MIN, GAIN_MAX, GAIN_DEF); 1498abfbe5aSHans Petter Selasky exit(1); 1508abfbe5aSHans Petter Selasky } 1518abfbe5aSHans Petter Selasky 1528abfbe5aSHans Petter Selasky int 1538abfbe5aSHans Petter Selasky main(int argc, char **argv) 1548abfbe5aSHans Petter Selasky { 1558abfbe5aSHans Petter Selasky int32_t *buffer; 1568abfbe5aSHans Petter Selasky size_t slope; 1578abfbe5aSHans Petter Selasky size_t size; 1588abfbe5aSHans Petter Selasky size_t off; 1598abfbe5aSHans Petter Selasky float a; 1608abfbe5aSHans Petter Selasky float d; 1618abfbe5aSHans Petter Selasky float p; 1628abfbe5aSHans Petter Selasky int c; 1638abfbe5aSHans Petter Selasky int f; 1648abfbe5aSHans Petter Selasky 1658abfbe5aSHans Petter Selasky while ((c = getopt(argc, argv, "BF:D:r:g:d:h")) != -1) { 1668abfbe5aSHans Petter Selasky switch (c) { 1678abfbe5aSHans Petter Selasky case 'F': 1688abfbe5aSHans Petter Selasky frequency = strtol(optarg, NULL, 10); 1698abfbe5aSHans Petter Selasky break; 1708abfbe5aSHans Petter Selasky case 'D': 1718abfbe5aSHans Petter Selasky duration_ms = strtol(optarg, NULL, 10); 1728abfbe5aSHans Petter Selasky if (duration_ms < DURATION_MIN || 1738abfbe5aSHans Petter Selasky duration_ms > DURATION_MAX) 1748abfbe5aSHans Petter Selasky usage(); 1758abfbe5aSHans Petter Selasky break; 1768abfbe5aSHans Petter Selasky case 'r': 1778abfbe5aSHans Petter Selasky sample_rate = strtol(optarg, NULL, 10); 1788abfbe5aSHans Petter Selasky if (sample_rate < SAMPLE_RATE_MIN || 1798abfbe5aSHans Petter Selasky sample_rate > SAMPLE_RATE_MAX) 1808abfbe5aSHans Petter Selasky usage(); 1818abfbe5aSHans Petter Selasky break; 1828abfbe5aSHans Petter Selasky case 'g': 1838abfbe5aSHans Petter Selasky gain = strtol(optarg, NULL, 10); 1848abfbe5aSHans Petter Selasky if (gain < GAIN_MIN || 1858abfbe5aSHans Petter Selasky gain > GAIN_MAX) 1868abfbe5aSHans Petter Selasky usage(); 1878abfbe5aSHans Petter Selasky break; 1888abfbe5aSHans Petter Selasky case 'd': 1898abfbe5aSHans Petter Selasky oss_dev = optarg; 1908abfbe5aSHans Petter Selasky break; 1918abfbe5aSHans Petter Selasky case 'B': 1928abfbe5aSHans Petter Selasky background = true; 1938abfbe5aSHans Petter Selasky break; 1948abfbe5aSHans Petter Selasky default: 1958abfbe5aSHans Petter Selasky usage(); 1968abfbe5aSHans Petter Selasky break; 1978abfbe5aSHans Petter Selasky } 1988abfbe5aSHans Petter Selasky } 1998abfbe5aSHans Petter Selasky 2008abfbe5aSHans Petter Selasky if (background && daemon(0, 0) != 0) 2018abfbe5aSHans Petter Selasky errx(1, "daemon(0,0) failed"); 2028abfbe5aSHans Petter Selasky 2038abfbe5aSHans Petter Selasky f = open(oss_dev, O_WRONLY); 2048abfbe5aSHans Petter Selasky if (f < 0) 205*83325e7bSEd Maste err(1, "Failed to open '%s'", oss_dev); 2068abfbe5aSHans Petter Selasky 2078abfbe5aSHans Petter Selasky c = 1; /* mono */ 2088abfbe5aSHans Petter Selasky if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0) 2098abfbe5aSHans Petter Selasky errx(1, "ioctl SOUND_PCM_WRITE_CHANNELS(1) failed"); 2108abfbe5aSHans Petter Selasky 2118abfbe5aSHans Petter Selasky c = AFMT_S32_NE; 2128abfbe5aSHans Petter Selasky if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0) 2138abfbe5aSHans Petter Selasky errx(1, "ioctl SNDCTL_DSP_SETFMT(AFMT_S32_NE) failed"); 2148abfbe5aSHans Petter Selasky 2158abfbe5aSHans Petter Selasky if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0) 2168abfbe5aSHans Petter Selasky errx(1, "ioctl SNDCTL_DSP_SPEED(%d) failed", sample_rate); 2178abfbe5aSHans Petter Selasky 2188abfbe5aSHans Petter Selasky c = (2 << 16); 2198abfbe5aSHans Petter Selasky while ((1ULL << (c & 63)) < (size_t)(4 * sample_rate / 50)) 2208abfbe5aSHans Petter Selasky c++; 2218abfbe5aSHans Petter Selasky if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c)) 2228abfbe5aSHans Petter Selasky errx(1, "ioctl SNDCTL_DSP_SETFRAGMENT(0x%x) failed", c); 2238abfbe5aSHans Petter Selasky 2248abfbe5aSHans Petter Selasky if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0) 2258abfbe5aSHans Petter Selasky errx(1, "ioctl SNDCTL_DSP_GETODELAY failed"); 2268abfbe5aSHans Petter Selasky 2278abfbe5aSHans Petter Selasky size = ((sample_rate * duration_ms) + 999) / 1000; 2288abfbe5aSHans Petter Selasky buffer = malloc(sizeof(buffer[0]) * size); 2298abfbe5aSHans Petter Selasky if (buffer == NULL) 2308abfbe5aSHans Petter Selasky errx(1, "out of memory"); 2318abfbe5aSHans Petter Selasky 2328abfbe5aSHans Petter Selasky /* compute slope duration in samples */ 2338abfbe5aSHans Petter Selasky slope = (DURATION_MIN * sample_rate) / 2000; 2348abfbe5aSHans Petter Selasky 2358abfbe5aSHans Petter Selasky /* compute base gain */ 2368abfbe5aSHans Petter Selasky a = powf(65536.0f, (float)gain / (float)GAIN_MAX) / 65536.0f; 2378abfbe5aSHans Petter Selasky 2388abfbe5aSHans Petter Selasky /* set initial phase and delta */ 2398abfbe5aSHans Petter Selasky p = 0; 2408abfbe5aSHans Petter Selasky d = (float)frequency / (float)sample_rate; 2418abfbe5aSHans Petter Selasky 2428abfbe5aSHans Petter Selasky /* compute wave */ 2438abfbe5aSHans Petter Selasky for (p = off = 0; off != size; off++, p += d) { 2448abfbe5aSHans Petter Selasky float sample; 2458abfbe5aSHans Petter Selasky 2468abfbe5aSHans Petter Selasky p = p - floorf(p); 2478abfbe5aSHans Petter Selasky sample = a * wave_function_16(p, WAVE_POWER); 2488abfbe5aSHans Petter Selasky 2498abfbe5aSHans Petter Selasky if (off < slope) 2508abfbe5aSHans Petter Selasky sample = sample * off / (float)slope; 2518abfbe5aSHans Petter Selasky else if (off > (size - slope)) 2528abfbe5aSHans Petter Selasky sample = sample * (size - off - 1) / (float)slope; 2538abfbe5aSHans Petter Selasky 2548abfbe5aSHans Petter Selasky buffer[off] = sample * 0x7fffff00; 2558abfbe5aSHans Petter Selasky } 2568abfbe5aSHans Petter Selasky 2578abfbe5aSHans Petter Selasky if (write(f, buffer, size * sizeof(buffer[0])) != 2588abfbe5aSHans Petter Selasky (ssize_t)(size * sizeof(buffer[0]))) 2598abfbe5aSHans Petter Selasky errx(1, "failed writing to DSP device(%s)", oss_dev); 2608abfbe5aSHans Petter Selasky 2618abfbe5aSHans Petter Selasky free(buffer); 2628abfbe5aSHans Petter Selasky 2638abfbe5aSHans Petter Selasky /* wait for data to be written */ 2648abfbe5aSHans Petter Selasky while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) { 2658abfbe5aSHans Petter Selasky if (c == 0) 2668abfbe5aSHans Petter Selasky break; 2678abfbe5aSHans Petter Selasky usleep(10000); 2688abfbe5aSHans Petter Selasky } 2698abfbe5aSHans Petter Selasky 2708abfbe5aSHans Petter Selasky /* wait for audio to go out */ 2718abfbe5aSHans Petter Selasky usleep(50000); 2728abfbe5aSHans Petter Selasky close(f); 2738abfbe5aSHans Petter Selasky 2748abfbe5aSHans Petter Selasky return (0); 2758abfbe5aSHans Petter Selasky } 276