2 * Copyright (c) 2021 Hans Petter Selasky <hselasky@freebsd.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 #include <sys/soundcard.h>
40 #define SAMPLE_RATE_DEF 48000 /* hz */
41 #define SAMPLE_RATE_MAX 48000 /* hz */
42 #define SAMPLE_RATE_MIN 8000 /* hz */
44 #define DURATION_DEF 150 /* ms */
45 #define DURATION_MAX 2000 /* ms */
46 #define DURATION_MIN 50 /* ms */
52 #define WAVE_POWER 1.25f
54 #define DEFAULT_HZ 440
56 #define DEFAULT_DEVICE _PATH_DEV "dsp"
58 static int frequency = DEFAULT_HZ;
59 static int duration_ms = DURATION_DEF;
60 static int sample_rate = SAMPLE_RATE_DEF;
61 static int gain = GAIN_DEF;
62 static const char *oss_dev = DEFAULT_DEVICE;
63 static bool background;
68 * "phase" should be in the range [0.0f .. 1.0f>
69 * "power" should be in the range <0.0f .. 2.0f>
71 * The return value is in the range [-1.0f .. 1.0f]
74 wave_function_16(float phase, float power)
76 uint16_t x = phase * (1U << 16);
80 /* Handle special cases, if any */
97 /* Apply Gray coding */
98 for (uint16_t mask = 1U << 15; mask != 1; mask /= 2) {
103 /* Find first set bit */
104 for (num = 0; num != 14; num++) {
105 if (x & (1U << num)) {
111 /* Initialize return value */
114 /* Compute the rest of the power series */
115 for (; num != 14; num++) {
116 if (x & (1U << num)) {
117 retval = (1.0f - retval) / 2.0f;
118 retval = powf(retval, power);
120 retval = (1.0f + retval) / 2.0f;
121 retval = powf(retval, power);
125 /* Check if halfway */
126 if (x & (1ULL << 14))
135 fprintf(stderr, "Usage: %s [parameters]\n"
136 "\t" "-F <frequency in HZ, default %d Hz>\n"
137 "\t" "-D <duration in ms, from %d ms to %d ms, default %d ms>\n"
138 "\t" "-r <sample rate in HZ, from %d Hz to %d Hz, default %d Hz>\n"
139 "\t" "-d <OSS device (default %s)>\n"
140 "\t" "-g <gain from %d to %d, default %d>\n"
141 "\t" "-B Run in background\n"
142 "\t" "-h Show usage\n",
145 DURATION_MIN, DURATION_MAX, DURATION_DEF,
146 SAMPLE_RATE_MIN, SAMPLE_RATE_MAX, SAMPLE_RATE_DEF,
148 GAIN_MIN, GAIN_MAX, GAIN_DEF);
153 main(int argc, char **argv)
165 while ((c = getopt(argc, argv, "BF:D:r:g:d:h")) != -1) {
168 frequency = strtol(optarg, NULL, 10);
171 duration_ms = strtol(optarg, NULL, 10);
172 if (duration_ms < DURATION_MIN ||
173 duration_ms > DURATION_MAX)
177 sample_rate = strtol(optarg, NULL, 10);
178 if (sample_rate < SAMPLE_RATE_MIN ||
179 sample_rate > SAMPLE_RATE_MAX)
183 gain = strtol(optarg, NULL, 10);
184 if (gain < GAIN_MIN ||
200 if (background && daemon(0, 0) != 0)
201 errx(1, "daemon(0,0) failed");
203 f = open(oss_dev, O_WRONLY);
205 errx(1, "Failed to open '%s'", oss_dev);
208 if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0)
209 errx(1, "ioctl SOUND_PCM_WRITE_CHANNELS(1) failed");
212 if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0)
213 errx(1, "ioctl SNDCTL_DSP_SETFMT(AFMT_S32_NE) failed");
215 if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0)
216 errx(1, "ioctl SNDCTL_DSP_SPEED(%d) failed", sample_rate);
219 while ((1ULL << (c & 63)) < (size_t)(4 * sample_rate / 50))
221 if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c))
222 errx(1, "ioctl SNDCTL_DSP_SETFRAGMENT(0x%x) failed", c);
224 if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0)
225 errx(1, "ioctl SNDCTL_DSP_GETODELAY failed");
227 size = ((sample_rate * duration_ms) + 999) / 1000;
228 buffer = malloc(sizeof(buffer[0]) * size);
230 errx(1, "out of memory");
232 /* compute slope duration in samples */
233 slope = (DURATION_MIN * sample_rate) / 2000;
235 /* compute base gain */
236 a = powf(65536.0f, (float)gain / (float)GAIN_MAX) / 65536.0f;
238 /* set initial phase and delta */
240 d = (float)frequency / (float)sample_rate;
243 for (p = off = 0; off != size; off++, p += d) {
247 sample = a * wave_function_16(p, WAVE_POWER);
250 sample = sample * off / (float)slope;
251 else if (off > (size - slope))
252 sample = sample * (size - off - 1) / (float)slope;
254 buffer[off] = sample * 0x7fffff00;
257 if (write(f, buffer, size * sizeof(buffer[0])) !=
258 (ssize_t)(size * sizeof(buffer[0])))
259 errx(1, "failed writing to DSP device(%s)", oss_dev);
263 /* wait for data to be written */
264 while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) {
270 /* wait for audio to go out */