summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--LICENSE2
-rw-r--r--Makefile6
-rw-r--r--common.h2
-rw-r--r--config.mk6
-rw-r--r--counter.c184
-rw-r--r--dogwhistle.c19
-rw-r--r--timer.c4
-rw-r--r--tuner.c203
9 files changed, 341 insertions, 86 deletions
diff --git a/.gitignore b/.gitignore
index 0a85b5e..0020899 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@
/dogwhistle
/stopwatch
/timer
+/tuner
diff --git a/LICENSE b/LICENSE
index c44b2d9..09fb02a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
ISC License
-© 2021 Mattias Andrée <maandree@kth.se>
+© 2021 Mattias Andrée <m@maandree.se>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
index 6a087e0..c291ef6 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,8 @@ BIN =\
dice\
dogwhistle\
stopwatch\
- timer
+ timer\
+ tuner
HDR =\
common.h\
@@ -94,6 +95,9 @@ stopwatch: stopwatch.o
timer: timer.o large-digits.o large-colon.o large-dot.o large-minus.o
$(CC) -o $@ $@.o large-digits.o large-colon.o large-dot.o large-minus.o $(LDFLAGS)
+tuner: tuner.o
+ $(CC) -o $@ $@.o $(LDFLAGS)
+
install-common:
mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1"
cp -- $(MAN1) "$(DESTDIR)$(MANPREFIX)/man1"
diff --git a/common.h b/common.h
index 9357ea9..32f364a 100644
--- a/common.h
+++ b/common.h
@@ -5,11 +5,11 @@
#include <libsimple.h>
#include <libsimple-arg.h>
+#include <libterminput.h>
#include <sys/random.h>
#include <sys/timerfd.h>
#include <sys/timex.h>
-#include <stdint.h>
#include <termios.h>
#include "large.h"
diff --git a/config.mk b/config.mk
index 6379f10..45403a8 100644
--- a/config.mk
+++ b/config.mk
@@ -3,8 +3,8 @@ MANPREFIX = $(PREFIX)/share/man
VIDEO_GROUP = video
-CC = cc
+CC = c99
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
-CFLAGS = -std=c99 -Wall -Wextra -Os
-LDFLAGS = -lsimple -lm -lasound -s
+CFLAGS =
+LDFLAGS = -lsimple -lterminput -lm -lasound -s
diff --git a/counter.c b/counter.c
index 2ebb5d1..f28ed05 100644
--- a/counter.c
+++ b/counter.c
@@ -1,15 +1,12 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
-/* TODO add buttons */
-
USAGE("[-2s]");
static volatile sig_atomic_t caught_sigterm = 0;
static volatile sig_atomic_t caught_sigwinch = 1;
-static volatile sig_atomic_t caught_sigio = 1;
static int with_small = 0;
static int with_quad = 0;
@@ -39,13 +36,6 @@ sigwinch(int signo)
(void) signo;
}
-static void
-sigio(int signo)
-{
- caught_sigio = 1;
- (void) signo;
-}
-
static void
print_number(const char ***str, int left, size_t height)
@@ -69,13 +59,16 @@ print_number(const char ***str, int left, size_t height)
static int
display_counter(void)
{
+ union libterminput_input input;
+ struct libterminput_state termctx;
char buf[sizeof(intmax_t) * 3 + 2];
const char **digits[sizeof(intmax_t) * 3 + 2];
intmax_t counter = {0};
int r, i, x = 0, y = 0, was_quad = 0, len, width;
+ int redraw = 1;
struct winsize winsize;
- char c;
- ssize_t rd;
+
+ memset(&termctx, 0, sizeof(termctx));
while (!caught_sigterm) {
if (caught_sigwinch) {
@@ -92,37 +85,83 @@ display_counter(void)
for (r = 0; r < y; r++)
printf("%s\033#5", r ? "\n" : "\033[H");
}
- }
-
- if (caught_sigio) {
- caught_sigio = 0;
- for (;;) {
- rd = read(STDIN_FILENO, &c, 1);
- if (rd <= 0) {
- if (!rd)
- goto out;
- if (errno == EAGAIN)
- break;
- if (errno == EINTR)
- continue;
- goto fail;
- }
- if (c == 'q')
+ redraw = 1;
+ } else {
+ r = libterminput_read(STDIN_FILENO, &input, &termctx);
+ if (r <= 0) {
+ if (!r)
goto out;
- else if (c == 'r')
- counter = 0;
- else if (c == '+' || c == 'C')
+ if (errno == EAGAIN)
+ break;
+ if (errno == EINTR)
+ continue;
+ goto fail;
+ }
+ if (input.type == LIBTERMINPUT_KEYPRESS) {
+ if (input.keypress.key == LIBTERMINPUT_UP ||
+ input.keypress.key == LIBTERMINPUT_RIGHT) {
counter += 1;
- else if (c == '-' || c == 'D')
+ redraw = 1;
+ } else if (input.keypress.key == LIBTERMINPUT_DOWN ||
+ input.keypress.key == LIBTERMINPUT_LEFT) {
counter -= 1;
+ redraw = 1;
+ } else if (input.keypress.key == LIBTERMINPUT_SYMBOL) {
+ if (!strcmp(input.keypress.symbol, "q") ||
+ !strcmp(input.keypress.symbol, "Q")) {
+ goto out;
+ } else if (!strcmp(input.keypress.symbol, "r") ||
+ !strcmp(input.keypress.symbol, "R")) {
+ counter = 0;
+ redraw = 1;
+ } else if (!strcmp(input.keypress.symbol, "+")) {
+ counter += 1;
+ redraw = 1;
+ } else if (!strcmp(input.keypress.symbol, "-")) {
+ counter -= 1;
+ redraw = 1;
+ } else if (!strcmp(input.keypress.symbol, "L") &&
+ (input.keypress.mods & LIBTERMINPUT_CTRL)) {
+ redraw = 1;
+ }
+ }
+ } else if (input.type == LIBTERMINPUT_MOUSEEVENT &&
+ input.mouseevent.event == LIBTERMINPUT_PRESS &&
+ input.mouseevent.button == LIBTERMINPUT_BUTTON1) {
+ if (input.mouseevent.y > (size_t)y - 3) {
+ if (input.mouseevent.x <= 10) {
+ goto out;
+ } else if (input.mouseevent.x > (size_t)x - 11) {
+ counter = 0;
+ redraw = 1;
+ }
+ }
+ if (!redraw) {
+ if (input.mouseevent.y - 1 < (size_t)(y - 1) / 2) {
+ counter += 1;
+ redraw = 1;
+ } else {
+ counter -= 1;
+ redraw = 1;
+ }
+ }
}
}
+ if (!redraw)
+ continue;
+ redraw = 0;
+
len = sprintf(buf, "%ji", counter);
printf("\033[H");
- if (x >= (width = len * LARGE_X + (counter < 0 ? LARGE_XM - LARGE_X : 0)) && y >= LARGE_Y) {
+ if (x >= (int)sizeof("Click on lower half on screen to increase value by 1") - 1) {
+ printf("%*.sClick on upper half on screen to increase value by 1",
+ (x - ((int)sizeof("Click on upper half on screen to increase value by 1") - 1)) / 2, "");
+ }
+
+ if (x >= (width = len * LARGE_X + (counter < 0 ? LARGE_XM - LARGE_X : 0)) && y >= LARGE_Y + 5) {
for (r = 0; r < (y - LARGE_Y) / 2; r++)
printf("\033[K\n");
for (i = 0; i < len; i++) {
@@ -133,7 +172,8 @@ display_counter(void)
}
digits[i] = NULL;
print_number(digits, (x - width) / 2, LARGE_Y);
- } else if (with_small && x >= (width = len * SMALL_X + (counter < 0 ? SMALL_XM - SMALL_X : 0)) && y > SMALL_Y + 1) {
+ r += LARGE_Y;
+ } else if (with_small && x >= (width = len * SMALL_X + (counter < 0 ? SMALL_XM - SMALL_X : 0)) && y > SMALL_Y + 6) {
for (r = 0; r < (y - SMALL_Y) / 2; r++)
printf("\033[K\n");
for (i = 0; i < len; i++) {
@@ -144,19 +184,50 @@ display_counter(void)
}
digits[i] = NULL;
print_number(digits, (x - width) / 2, SMALL_Y);
- } else if (with_quad && x > len * 2 && y > 2) {
+ r += SMALL_Y;
+ } else if (with_quad && x > len * 2 && y > 3) {
printf("\033#3%*.s%s\033[K\n", (x / 2 - len) / 2, "", buf);
printf("\033#4%*.s%s\033[K\n", (x / 2 - len) / 2, "", buf);
printf("\033#5");
was_quad = 1;
+ r = 2;
} else {
printf("%*.s%s\033[K", (x - len) / 2, "", buf);
+ r = 1;
}
- printf("\033[J");
- fflush(stdout);
+ if (x >= 21 + (int)sizeof("Click on lower half on screen to decrease value by 1") - 1 && r + 3 <= y) {
+ width = 21 + ((int)sizeof("Click on lower half on screen to decrease value by 1") - 1);
+ for (; r <= y - 3; r++)
+ printf("\033[K\n");
+ printf("┌────────┐%*.s┌─────────┐\033[K\n", x - 21, "");
+ printf("│ \033[1mQ\033[muit │%*.s│ \033[1mR\033[meset │\033[K\n", x - 21, "");
+ printf("└────────┘%*.s%s%*.s└─────────┘\033[K",
+ (x - width) / 2, "",
+ "Click on lower half on screen to decrease value by 1",
+ x - (x - width) / 2 - width, "");
+ } else if (x >= (int)sizeof("Click on lower half on screen to decrease value by 1") - 1 && r + 4 <= y) {
+ for (; r <= y - 4; r++)
+ printf("\033[K\n");
+ printf("%*.sClick on lower half on screen to decrease value by 1\033[K",
+ (x - ((int)sizeof("Click on lower half on screen to decrease value by 1") - 1)) / 2, "");
+ printf("\n┌────────┐%*.s┌─────────┐\033[K\n", x - 21, "");
+ printf("│ \033[1mQ\033[muit │%*.s│ \033[1mR\033[meset │\033[K\n", x - 21, "");
+ printf("└────────┘%*.s└─────────┘\033[K", x - 21, "");
+ } else if (x >= 21 && r + 3 <= y) {
+ for (; r <= y - 3; r++)
+ printf("\033[K\n");
+ printf("┌────────┐%*.s┌─────────┐\033[K\n", x - 21, "");
+ printf("│ \033[1mQ\033[muit │%*.s│ \033[1mR\033[meset │\033[K\n", x - 21, "");
+ printf("└────────┘%*.s└─────────┘\033[K", x - 21, "");
+ } else {
+ printf("\033[J");
+ fflush(stdout);
+ continue;
+ }
- pause();
+ printf("\033[D\033[J─┘");
+ fflush(stdout);
}
out:
@@ -170,11 +241,9 @@ fail:
int
main(int argc, char *argv[])
{
- int tcset = 0, old_flags = -1, old_sig = 0, owner_set = 0;
- int saved_errno;
+ int tcset = 0, saved_errno;
struct sigaction sigact;
struct termios stty, saved_stty;
- struct f_owner_ex old_owner, new_owner;
ARGBEGIN {
case '2':
@@ -190,7 +259,7 @@ main(int argc, char *argv[])
if (argc)
usage();
- fprintf(stdout, "\033[?1049h\033[?25l");
+ fprintf(stdout, "\033[?1049h\033[?25l\033[?1049;1003;1006h");
memset(&sigact, 0, sizeof(sigact));
@@ -202,24 +271,6 @@ main(int argc, char *argv[])
sigact.sa_handler = sigwinch;
sigaction(SIGWINCH, &sigact, NULL);
- sigact.sa_handler = sigio;
- sigaction(SIGIO, &sigact, NULL);
- sigaction(SIGURG, &sigact, NULL);
-
- if (fcntl(STDIN_FILENO, F_GETOWN_EX, &old_owner))
- goto fail;
- memset(&new_owner, 0, sizeof(new_owner));
- new_owner.type = F_OWNER_PID;
- new_owner.pid = getpid();
- if (fcntl(STDIN_FILENO, F_SETOWN_EX, &new_owner))
- goto fail;
- owner_set = 1;
- old_flags = fcntl(STDIN_FILENO, F_GETFL);
- fcntl(STDIN_FILENO, F_SETFL, old_flags | FASYNC | O_NONBLOCK);
- fcntl(STDIN_FILENO, F_GETSIG, &old_sig);
- if (old_sig)
- fcntl(STDIN_FILENO, F_SETSIG, 0);
-
if (!tcgetattr(STDIN_FILENO, &stty)) {
saved_stty = stty;
stty.c_lflag &= (tcflag_t)~(ECHO | ICANON);
@@ -230,26 +281,17 @@ main(int argc, char *argv[])
if (display_counter())
goto fail;
- fprintf(stdout, "\033[?25h\n\033[?1049l");
+ fprintf(stdout, "\033[?1049;1003;1006l\033[?25h\n\033[?1049l");
fflush(stdout);
- fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner);
- fcntl(STDIN_FILENO, F_SETFL, old_flags);
- fcntl(STDIN_FILENO, F_SETSIG, old_sig);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty);
return 0;
fail:
saved_errno = errno;
- fprintf(stdout, "\033[?25h\n\033[?1049l");
+ fprintf(stdout, "\033[?1049;1003;1006l\033[?25h\n\033[?1049l");
fflush(stdout);
errno = saved_errno;
perror(argv0 ? argv0 : "counter");
- if (owner_set)
- fcntl(STDIN_FILENO, F_SETOWN_EX, &old_owner);
- if (old_flags != -1)
- fcntl(STDIN_FILENO, F_SETFL, old_flags);
- if (old_sig)
- fcntl(STDIN_FILENO, F_SETSIG, old_sig);
if (tcset)
tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_stty);
return 1;
diff --git a/dogwhistle.c b/dogwhistle.c
index 2d48b82..894d7e3 100644
--- a/dogwhistle.c
+++ b/dogwhistle.c
@@ -7,9 +7,10 @@
/* TODO add support for selecting output device and backend */
/* TODO add support for selecting volume */
/* TODO add support for pausing and exiting */
+/* TODO prefer loudspeakers over headphones */
+/* TODO use bus to listen for incoming phone calls and pause when arriving */
#define UTYPE uint32_t
-#define SMIN INT32_MIN
#define SMAX INT32_MAX
#define FORMAT SND_PCM_FORMAT_U32
@@ -23,10 +24,10 @@ int
main(int argc, char *argv[])
{
UTYPE *buffer;
- snd_pcm_uframes_t i, buflen = SAMPLE_RATE;
+ snd_pcm_uframes_t i, buflen, wavelen;
snd_pcm_sframes_t n;
snd_pcm_t *sound_handle = NULL;
- unsigned long int freq = 16000;
+ double freq = 16000;
double volume = (double)0.1f, imul, omul;
int r;
@@ -46,19 +47,23 @@ main(int argc, char *argv[])
eprintf("snd_pcm_set_params: %s\n", snd_strerror(r));
/* Generate tone */
+ buflen = wavelen = (snd_pcm_uframes_t)((double)SAMPLE_RATE / freq);
+ if ((snd_pcm_uframes_t)freq > 1)
+ buflen *= (snd_pcm_uframes_t)freq;
buffer = emallocn((size_t)buflen, sizeof(*buffer), 0);
omul = volume * SMAX * 2;
- imul = 2 * (double)freq / (double)SAMPLE_RATE;
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunsuffixed-float-constants"
#endif
- imul *= M_PI;
+ imul = 2 * M_PI * freq / (double)SAMPLE_RATE;
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
- for (i = 0; i < buflen; i++)
- buffer[i] = (UTYPE)(sin(imul * (double)i) * omul - SMIN);
+ for (i = 0; i < wavelen; i++)
+ buffer[i] = (UTYPE)(sin(imul * (double)i) * omul + SMAX);
+ for (i = wavelen; i < buflen; i += wavelen)
+ memcpy(&buffer[i], &buffer[0], wavelen * sizeof(UTYPE));
/* Play tone */
for (;;) {
diff --git a/timer.c b/timer.c
index a906e65..efc06cd 100644
--- a/timer.c
+++ b/timer.c
@@ -1,7 +1,7 @@
/* See LICENSE file for copyright and license details. */
#include "common.h"
-/* TODO add support for in application entering of time */
+/* TODO add support for in-application entering of time */
/* TODO add support for rat input */
/* TODO add support for audible alert */
/* TODO add support for vibration alert */
@@ -9,7 +9,7 @@
/* TODO add support for messages */
/* TODO add support for multiple timers */
/* TODO add support for CLOCK_BOOTTIME_ALARM */
-/* TODO add support for ptuting timer in the background */
+/* TODO add support for putting timer in the background */
USAGE("[-2s] [[hours:]minutes:]second[.deciseconds]");
diff --git a/tuner.c b/tuner.c
new file mode 100644
index 0000000..f8f5a3a
--- /dev/null
+++ b/tuner.c
@@ -0,0 +1,203 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#include <alsa/asoundlib.h>
+#include <math.h>
+
+/* TODO add support for online configuration */
+/* TODO add support for displaying input note */
+/* TODO add support for selecting output device and backend */
+/* TODO add support for selecting volume */
+/* TODO use bus to listen for incoming phone calls and pause when arriving */
+
+#define UTYPE uint32_t
+#define SMAX INT32_MAX
+#define FORMAT SND_PCM_FORMAT_U32
+
+#define SAMPLE_RATE 52000 /* Hz */
+#define LATENCY 100000 /* µs */
+
+USAGE("[-a | -u] [-n note] [-o octave]");
+
+
+static int
+check_n_lined(const char *s, const char *a, const char *b)
+{
+ if (!strncmp(s, a, strlen(a)))
+ s = &s[strlen(a)];
+ else if (!strncmp(s, b, strlen(b)))
+ s = &s[strlen(b)];
+ else
+ return 0;
+
+ if (*s == '-' || *s == ' ')
+ s = &s[1];
+ else if (!strncmp(s, " ", strlen(" ")))
+ s = &s[strlen(" ")];
+ else
+ return 0;
+
+ return !strcasecmp(s, "lined");
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ UTYPE *buffer;
+ snd_pcm_uframes_t i, buflen, wavelen;
+ snd_pcm_sframes_t n;
+ snd_pcm_t *sound_handle = NULL;
+ double freq = 440;
+ double volume = (double)0.1f, imul, omul, osub;
+ char *env, *note_str = NULL, *octave_str = NULL, *end;
+ int r, ascii, note, octave;
+
+ env = getenv("TERM");
+ ascii = (env && !strcmp(env, "linux"));
+
+ ARGBEGIN {
+ case 'a':
+ ascii = 1;
+ break;
+ case 'n':
+ if (note_str)
+ usage();
+ note_str = ARG();
+ break;
+ case 'o':
+ if (octave_str)
+ usage();
+ octave_str = ARG();
+ break;
+ case 'u':
+ ascii = 0;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc)
+ usage();
+
+ if (!note_str) {
+ note = 9;
+ } else {
+ if (note_str[0] == 'C' || note_str[0] == 'c')
+ note = 0;
+ else if (note_str[0] == 'D' || note_str[0] == 'd')
+ note = 2;
+ else if (note_str[0] == 'E' || note_str[0] == 'e')
+ note = 4;
+ else if (note_str[0] == 'F' || note_str[0] == 'f')
+ note = 5;
+ else if (note_str[0] == 'G' || note_str[0] == 'g')
+ note = 7;
+ else if (note_str[0] == 'A' || note_str[0] == 'a')
+ note = 9;
+ else if (note_str[0] == 'B' || note_str[0] == 'b' || note_str[0] == 'H' || note_str[0] == 'h')
+ note = 11;
+ else
+ eprintf("%s is not a recognised note\n", note_str);
+ if (!strcmp(&note_str[1], "#") || !strcmp(&note_str[1], "♯"))
+ note += 1;
+ else if (!strcmp(&note_str[1], "b") || !strcmp(&note_str[1], "♭"))
+ note -= 1;
+ else if (note_str[1])
+ eprintf("%s is not a recognised note\n", note_str);
+ }
+
+ if (!octave_str) {
+ octave = 4;
+ } else if (!strcasecmp(octave_str, "subsubcontra")) {
+ octave = -1;
+ } else if (!strcasecmp(octave_str, "subcontra")) {
+ octave = 0;
+ } else if (!strcasecmp(octave_str, "contra")) {
+ octave = 1;
+ } else if (!strcasecmp(octave_str, "great")) {
+ octave = 2;
+ } else if (!strcasecmp(octave_str, "small")) {
+ octave = 3;
+ } else if (check_n_lined(octave_str, "one", "1")) {
+ octave = 4;
+ } else if (check_n_lined(octave_str, "two", "2")) {
+ octave = 5;
+ } else if (check_n_lined(octave_str, "three", "3")) {
+ octave = 6;
+ } else if (check_n_lined(octave_str, "four", "4")) {
+ octave = 7;
+ } else if (check_n_lined(octave_str, "five", "5")) {
+ octave = 8;
+ } else if (check_n_lined(octave_str, "six", "6")) {
+ octave = 9;
+ } else if (check_n_lined(octave_str, "seven", "7")) {
+ octave = 10;
+ } else if (!isdigit(octave_str[0]) && octave_str[0] != '-') {
+ eprintf("%s is not a recognised octave\n", note_str);
+ } else {
+ long int tmp;
+ errno = 0;
+ tmp = strtol(octave_str, &end, 10);
+ if (errno)
+ eprintf("strol %s:", octave_str);
+ if (*end)
+ eprintf("%s is not a recognised octave\n", note_str);
+ if (tmp < ((long int)INT_MIN + 1 + 9) / 12 + 4 ||
+ tmp > ((long int)INT_MAX - 12 + 9) / 12 + 4)
+ eprintf("octave %s is out side the supported domain\n", note_str);
+ octave = (int)tmp;
+ }
+
+ fprintf(stderr, "Note: %i\n", note);
+ fprintf(stderr, "Octave: %i\n", octave);
+ note += 12 * (octave - 4);
+ freq = 440 * pow(2, (double)(note - 9) / 12);
+ fprintf(stderr, "Frequency: %g Hz\n", freq);
+ fprintf(stderr, "Wavelength: %g mm\n", 343000 / freq);
+
+ /* Set up audio */
+ r = snd_pcm_open(&sound_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
+ if (r < 0)
+ eprintf("snd_pcm_open: %s\n", snd_strerror(r));
+ if (!sound_handle)
+ eprintf("snd_pcm_open:");
+
+ /* Configure audio */
+ r = snd_pcm_set_params(sound_handle, FORMAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1 /* channels */,
+ SAMPLE_RATE, 1 /* allow resampling? */, LATENCY);
+ if (r < 0)
+ eprintf("snd_pcm_set_params: %s\n", snd_strerror(r));
+
+ /* Generate tone */
+ buflen = wavelen = (snd_pcm_uframes_t)((double)SAMPLE_RATE / freq);
+ if ((snd_pcm_uframes_t)freq > 1)
+ buflen *= (snd_pcm_uframes_t)freq;
+ buffer = emallocn((size_t)buflen, sizeof(*buffer), 0);
+ omul = volume * SMAX * 2;
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunsuffixed-float-constants"
+#endif
+ imul = 2 * M_PI * freq / (double)SAMPLE_RATE;
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+ for (i = 0; i < wavelen; i++)
+ buffer[i] = (UTYPE)(sin(imul * (double)i) * omul - osub);
+ for (i = wavelen; i < buflen; i += wavelen)
+ memcpy(&buffer[i], &buffer[0], wavelen * sizeof(UTYPE));
+
+ /* Play tone */
+ for (;;) {
+ n = snd_pcm_writei(sound_handle, buffer, buflen);
+ if (n < 0)
+ n = snd_pcm_recover(sound_handle, (int)n, 0 /* do not print error reason? */);
+ if (n < 0)
+ eprintf("snd_pcm_writei: %s\n", snd_strerror((int)n));
+ if (n > 0 && (snd_pcm_uframes_t)n < buflen)
+ eprintf("short write from snd_pcm_writei\n");
+ }
+
+ snd_pcm_close(sound_handle);
+ return 0;
+}