diff options
| author | Reiner Herrmann <reiner@reiner-h.de> | 2017-04-02 22:02:53 +0200 |
|---|---|---|
| committer | Reiner Herrmann <reiner@reiner-h.de> | 2017-04-02 22:02:53 +0200 |
| commit | f253fb860d4970df567d795af215bd68ff1acda9 (patch) | |
| tree | 19edd9e9f317c18c035f09982c601f87055c7c81 | |
Initial commit
| -rw-r--r-- | COPYING | 21 | ||||
| -rw-r--r-- | Makefile | 13 | ||||
| -rw-r--r-- | metronome.c | 165 |
3 files changed, 199 insertions, 0 deletions
@@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Reiner Herrmann <reiner@reiner-h.de> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b24625d --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CPPFLAGS = -D_GNU_SOURCE +CFLAGS = -Wall -Wextra -Werror -ggdb3 -O3 +LDFLAGS = -lasound -lm -lrt + +OBJS = metronome.o +BIN = metronome + +$(BIN): $(OBJS) + +all: $(BIN) + +clean: + rm -f $(OBJS) $(BIN) diff --git a/metronome.c b/metronome.c new file mode 100644 index 0000000..d30b61e --- /dev/null +++ b/metronome.c @@ -0,0 +1,165 @@ +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <alsa/asoundlib.h> + +#define SAMPLE_RATE 8000 + +#define BUFFER_SIZE 512 + +/* 2 bytes per sample; 0.1 seconds long */ +#define TONE_SIZE (2*SAMPLE_RATE/10) + +#define TONE1_FREQ 800 +#define TONE2_FREQ 440 + +static uint8_t tone1[TONE_SIZE]; +static uint8_t tone2[TONE_SIZE]; +static const uint8_t silence[BUFFER_SIZE]; + +static int set_params(snd_pcm_t *pcm_handle) +{ + snd_pcm_hw_params_t *hwparams; + unsigned int rate = SAMPLE_RATE; + snd_pcm_uframes_t bufsize = BUFFER_SIZE; + + snd_pcm_hw_params_alloca(&hwparams); + + if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { + printf("Failed configuring device\n"); + return -1; + } + if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { + printf("Failed setting access mode\n"); + return -1; + } + if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) { + printf("Failed setting format\n"); + return -1; + } + if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0) < 0) { + printf("Failed setting sample rate\n"); + return -1; + } + if (snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &bufsize) < 0) { + printf("Failed setting sample rate\n"); + return -1; + } + if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 1) < 0) { + printf("Failed setting channel number\n"); + return -1; + } + if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) { + printf("Failed applying hardware parameters\n"); + return -1; + } + + return 0; +} + +static void prepare_tones() +{ + int i, amplitude = 20; + uint16_t sample; + + for(i=0; i<TONE_SIZE-1; i++) { + sample = amplitude * sin(2*M_PI*TONE1_FREQ*i/SAMPLE_RATE); + tone1[i] = sample % 256; + tone1[i+1] = sample >> 8; + sample = amplitude * sin(2*M_PI*TONE2_FREQ*i/SAMPLE_RATE); + tone2[i] = sample % 256; + tone2[i+1] = sample >> 8; + } +} + +static long timediff_us(struct timespec *ts1, struct timespec *ts2) +{ + struct timeval tv1, tv2, diff; + + TIMESPEC_TO_TIMEVAL(&tv1, ts1); + TIMESPEC_TO_TIMEVAL(&tv2, ts2); + + timersub(&tv1, &tv2, &diff); + + return diff.tv_sec * 1000000 + diff.tv_usec; +} + +static void play_tone(snd_pcm_t *pcm_handle, char tone) +{ + const uint8_t *tonebuf; + int tonebuf_size; + + switch(tone) { + case '1': + tonebuf = tone1; + tonebuf_size = sizeof(tone1); + break; + case '2': + tonebuf = tone2; + tonebuf_size = sizeof(tone2); + break; + case 's': + default: + tonebuf = silence; + tonebuf_size = sizeof(silence); + break; + } + while (snd_pcm_writei(pcm_handle, tonebuf, tonebuf_size / 2) < 0) { + snd_pcm_prepare(pcm_handle); + printf("underrun\n"); + } +} + +static int play(snd_pcm_t *pcm_handle, const char *pattern, int bpm) +{ + int i = 0; + double period_us; + struct timespec cur_t, old_t = {}; + + period_us = 1000000 * 60.0 / bpm; + + /* fill buffer with silence */ + snd_pcm_prepare(pcm_handle); + play_tone(pcm_handle, 's'); + + while(1) { + if (clock_gettime(CLOCK_MONOTONIC, &cur_t) == -1) { + printf("Failed getting time: %s\n", strerror(errno)); + return -1; + } + + if (timediff_us(&cur_t, &old_t) >= period_us) { + memcpy(&old_t, &cur_t, sizeof(struct timespec)); + play_tone(pcm_handle, pattern[i++ % strlen(pattern)]); + } else { + play_tone(pcm_handle, 's'); + } + + usleep(500); + } +} + +int main() +{ + snd_pcm_t *pcm_handle; + + if (snd_pcm_open(&pcm_handle, /*"default"*/ "plughw:1,0", SND_PCM_STREAM_PLAYBACK, 0) < 0) { + printf("Failed opening device\n"); + return 1; + } + + if (set_params(pcm_handle) < 0) { + return 1; + } + + prepare_tones(); + play(pcm_handle, "2111", 120); + + snd_pcm_close(pcm_handle); + + return 0; +} |
