diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | LICENSE | 2 | ||||
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | common.h | 2 | ||||
| -rw-r--r-- | config.mk | 6 | ||||
| -rw-r--r-- | counter.c | 184 | ||||
| -rw-r--r-- | dogwhistle.c | 19 | ||||
| -rw-r--r-- | timer.c | 4 | ||||
| -rw-r--r-- | tuner.c | 203 |
9 files changed, 341 insertions, 86 deletions
@@ -19,3 +19,4 @@ /dogwhistle /stopwatch /timer +/tuner @@ -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 @@ -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" @@ -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" @@ -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 @@ -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 (;;) { @@ -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]"); @@ -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(¬e_str[1], "#") || !strcmp(¬e_str[1], "♯")) + note += 1; + else if (!strcmp(¬e_str[1], "b") || !strcmp(¬e_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; +} |
