Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.bin/beep/beep.c
34677 views
1
/*-
2
* Copyright (c) 2021 Hans Petter Selasky <[email protected]>
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
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.
12
*
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
23
* SUCH DAMAGE.
24
*/
25
26
#include <sys/soundcard.h>
27
28
#include <capsicum_helpers.h>
29
#include <err.h>
30
#include <errno.h>
31
#include <fcntl.h>
32
#include <math.h>
33
#include <paths.h>
34
#include <stdbool.h>
35
#include <stdint.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
#define SAMPLE_RATE_DEF 48000 /* hz */
42
#define SAMPLE_RATE_MAX 48000 /* hz */
43
#define SAMPLE_RATE_MIN 8000 /* hz */
44
45
#define DURATION_DEF 150 /* ms */
46
#define DURATION_MAX 2000 /* ms */
47
#define DURATION_MIN 50 /* ms */
48
49
#define GAIN_DEF 75
50
#define GAIN_MAX 100
51
#define GAIN_MIN 0
52
53
#define WAVE_POWER 1.25f
54
55
#define DEFAULT_HZ 440
56
57
#define DEFAULT_DEVICE _PATH_DEV "dsp"
58
59
static int frequency = DEFAULT_HZ;
60
static int duration_ms = DURATION_DEF;
61
static int sample_rate = SAMPLE_RATE_DEF;
62
static int gain = GAIN_DEF;
63
static const char *oss_dev = DEFAULT_DEVICE;
64
static bool background;
65
66
/*
67
* wave_function_16
68
*
69
* "phase" should be in the range [0.0f .. 1.0f>
70
* "power" should be in the range <0.0f .. 2.0f>
71
*
72
* The return value is in the range [-1.0f .. 1.0f]
73
*/
74
static float
75
wave_function_16(float phase, float power)
76
{
77
uint16_t x = phase * (1U << 16);
78
float retval;
79
uint8_t num;
80
81
/* Handle special cases, if any */
82
switch (x) {
83
case 0xffff:
84
case 0x0000:
85
return (1.0f);
86
case 0x3fff:
87
case 0x4000:
88
case 0xBfff:
89
case 0xC000:
90
return (0.0f);
91
case 0x7FFF:
92
case 0x8000:
93
return (-1.0f);
94
default:
95
break;
96
}
97
98
/* Apply Gray coding */
99
for (uint16_t mask = 1U << 15; mask != 1; mask /= 2) {
100
if (x & mask)
101
x ^= (mask - 1);
102
}
103
104
/* Find first set bit */
105
for (num = 0; num != 14; num++) {
106
if (x & (1U << num)) {
107
num++;
108
break;
109
}
110
}
111
112
/* Initialize return value */
113
retval = 0.0;
114
115
/* Compute the rest of the power series */
116
for (; num != 14; num++) {
117
if (x & (1U << num)) {
118
retval = (1.0f - retval) / 2.0f;
119
retval = powf(retval, power);
120
} else {
121
retval = (1.0f + retval) / 2.0f;
122
retval = powf(retval, power);
123
}
124
}
125
126
/* Check if halfway */
127
if (x & (1ULL << 14))
128
retval = -retval;
129
130
return (retval);
131
}
132
133
static void
134
usage(void)
135
{
136
fprintf(stderr, "Usage: %s [parameters]\n"
137
"\t" "-F <frequency in HZ, default %d Hz>\n"
138
"\t" "-D <duration in ms, from %d ms to %d ms, default %d ms>\n"
139
"\t" "-r <sample rate in HZ, from %d Hz to %d Hz, default %d Hz>\n"
140
"\t" "-d <OSS device (default %s)>\n"
141
"\t" "-g <gain from %d to %d, default %d>\n"
142
"\t" "-B Run in background\n"
143
"\t" "-h Show usage\n",
144
getprogname(),
145
DEFAULT_HZ,
146
DURATION_MIN, DURATION_MAX, DURATION_DEF,
147
SAMPLE_RATE_MIN, SAMPLE_RATE_MAX, SAMPLE_RATE_DEF,
148
DEFAULT_DEVICE,
149
GAIN_MIN, GAIN_MAX, GAIN_DEF);
150
exit(1);
151
}
152
153
int
154
main(int argc, char **argv)
155
{
156
float *buffer;
157
size_t slope;
158
size_t size;
159
size_t off;
160
float a;
161
float d;
162
float p;
163
int c;
164
int f;
165
166
while ((c = getopt(argc, argv, "BF:D:r:g:d:h")) != -1) {
167
switch (c) {
168
case 'F':
169
frequency = strtol(optarg, NULL, 10);
170
break;
171
case 'D':
172
duration_ms = strtol(optarg, NULL, 10);
173
if (duration_ms < DURATION_MIN ||
174
duration_ms > DURATION_MAX)
175
usage();
176
break;
177
case 'r':
178
sample_rate = strtol(optarg, NULL, 10);
179
if (sample_rate < SAMPLE_RATE_MIN ||
180
sample_rate > SAMPLE_RATE_MAX)
181
usage();
182
break;
183
case 'g':
184
gain = strtol(optarg, NULL, 10);
185
if (gain < GAIN_MIN ||
186
gain > GAIN_MAX)
187
usage();
188
break;
189
case 'd':
190
oss_dev = optarg;
191
break;
192
case 'B':
193
background = true;
194
break;
195
default:
196
usage();
197
break;
198
}
199
}
200
201
if (background && daemon(0, 0) != 0)
202
errx(1, "daemon(0,0) failed");
203
204
f = open(oss_dev, O_WRONLY);
205
if (f < 0)
206
err(1, "Failed to open '%s'", oss_dev);
207
208
if (caph_enter() == -1)
209
err(1, "Failed to enter capability mode");
210
211
c = 1; /* mono */
212
if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &c) != 0)
213
errx(1, "ioctl SOUND_PCM_WRITE_CHANNELS(1) failed");
214
215
c = AFMT_FLOAT;
216
if (ioctl(f, SNDCTL_DSP_SETFMT, &c) != 0)
217
errx(1, "ioctl SNDCTL_DSP_SETFMT(AFMT_FLOAT) failed");
218
219
if (ioctl(f, SNDCTL_DSP_SPEED, &sample_rate) != 0)
220
errx(1, "ioctl SNDCTL_DSP_SPEED(%d) failed", sample_rate);
221
222
c = (2 << 16);
223
while ((1ULL << (c & 63)) < (size_t)(4 * sample_rate / 50))
224
c++;
225
if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &c))
226
errx(1, "ioctl SNDCTL_DSP_SETFRAGMENT(0x%x) failed", c);
227
228
if (ioctl(f, SNDCTL_DSP_GETODELAY, &c) != 0)
229
errx(1, "ioctl SNDCTL_DSP_GETODELAY failed");
230
231
size = ((sample_rate * duration_ms) + 999) / 1000;
232
buffer = malloc(sizeof(buffer[0]) * size);
233
if (buffer == NULL)
234
errx(1, "out of memory");
235
236
/* compute slope duration in samples */
237
slope = (DURATION_MIN * sample_rate) / 2000;
238
239
/* compute base gain */
240
a = powf(65536.0f, (float)gain / (float)GAIN_MAX) / 65536.0f;
241
242
/* set initial phase and delta */
243
p = 0;
244
d = (float)frequency / (float)sample_rate;
245
246
/* compute wave */
247
for (p = off = 0; off != size; off++, p += d) {
248
float sample;
249
250
p = p - floorf(p);
251
sample = a * wave_function_16(p, WAVE_POWER);
252
253
if (off < slope)
254
sample = sample * off / (float)slope;
255
else if (off > (size - slope))
256
sample = sample * (size - off - 1) / (float)slope;
257
258
buffer[off] = sample;
259
}
260
261
if (write(f, buffer, size * sizeof(buffer[0])) !=
262
(ssize_t)(size * sizeof(buffer[0])))
263
errx(1, "failed writing to DSP device(%s)", oss_dev);
264
265
free(buffer);
266
267
/* wait for data to be written */
268
while (ioctl(f, SNDCTL_DSP_GETODELAY, &c) == 0) {
269
if (c == 0)
270
break;
271
usleep(10000);
272
}
273
274
/* wait for audio to go out */
275
usleep(50000);
276
close(f);
277
278
return (0);
279
}
280
281