aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReiner Herrmann <reiner@reiner-h.de>2017-04-02 22:02:53 +0200
committerReiner Herrmann <reiner@reiner-h.de>2017-04-02 22:02:53 +0200
commitf253fb860d4970df567d795af215bd68ff1acda9 (patch)
tree19edd9e9f317c18c035f09982c601f87055c7c81
Initial commit
-rw-r--r--COPYING21
-rw-r--r--Makefile13
-rw-r--r--metronome.c165
3 files changed, 199 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5adb574
--- /dev/null
+++ b/COPYING
@@ -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;
+}