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 /metronome.c | |
Initial commit
Diffstat (limited to 'metronome.c')
| -rw-r--r-- | metronome.c | 165 |
1 files changed, 165 insertions, 0 deletions
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; +} |
