summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dbverify.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/dbverify.c b/dbverify.c
new file mode 100644
index 0000000..6b7121b
--- /dev/null
+++ b/dbverify.c
@@ -0,0 +1,349 @@
+/*-*- Mode: C; c-file-style: "linux"; indent-tabs-mode: nil; c-basic-offset: 8 -*-*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <asoundlib.h>
+
+static double linear_to_dB(double v) {
+ return 20.0 * log10(v);
+}
+
+static double dB_to_linear(double v) {
+ return pow(10.0, v / 20.0);
+}
+
+static float *generate_signal(unsigned n_samples, double amplitude, double frequency) {
+ float *r;
+ unsigned i;
+
+ if (!(r = malloc(n_samples * sizeof(float))))
+ return NULL;
+
+ for (i = 0; i < n_samples; i++)
+ r[i] = amplitude * sin(((double) i*frequency*M_PI*2)/(double) n_samples);
+
+ return r;
+}
+
+static snd_pcm_t *open_pcm(const char *name, unsigned *rate) {
+ snd_pcm_t *d = NULL;
+ int r;
+ snd_pcm_hw_params_t *hw;
+ snd_pcm_uframes_t t;
+ snd_output_t *output = NULL;
+ int dir = 1;
+
+ snd_pcm_hw_params_alloca(&hw);
+
+ printf("Opening %s for PCM.\n", name);
+
+ if ((r = snd_pcm_open(&d, name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ fprintf(stderr, "Cannot open audio device %s: %s\n", name, snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_hw_params_any(d, hw)) < 0) {
+ fprintf(stderr, "Cannot initialize hardware parameters: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_hw_params_set_access(d, hw, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ fprintf(stderr, "Cannot set access type: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_hw_params_set_format(d, hw, SND_PCM_FORMAT_FLOAT_LE)) < 0) {
+ fprintf(stderr, "Cannot set sample format: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_hw_params_set_rate_near(d, hw, rate, &dir)) < 0) {
+ fprintf(stderr, "Cannot set sample rate: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_hw_params_set_channels(d, hw, 1)) < 0) {
+ fprintf(stderr, "Cannot set channel count: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ t = *rate;
+ if ((r = snd_pcm_hw_params_set_buffer_size_near(d, hw, &t)) < 0) {
+ fprintf(stderr, "Cannot set buffer size: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_hw_params(d, hw)) < 0) {
+ fprintf(stderr, "Cannot set parameters: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_output_stdio_attach(&output, stderr, 0)) < 0) {
+ fprintf(stderr, "Cannot attach to stderr: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_pcm_dump(d, output)) < 0) {
+ fprintf(stderr, "Cannot dump status: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ snd_output_close(output);
+
+ if ((r = snd_pcm_prepare(d)) < 0) {
+ fprintf(stderr, "Preparing failed: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ return d;
+
+finish:
+ if (d)
+ snd_pcm_close(d);
+
+ if (output)
+ snd_output_close(output);
+
+ return NULL;
+}
+
+static int play_pcm(snd_pcm_t *pcm, float *samples, unsigned n_samples) {
+ snd_pcm_sframes_t t;
+
+ while (n_samples > 0) {
+
+ if ((t = snd_pcm_writei(pcm, samples, n_samples)) <= 0) {
+ int r;
+
+ if ((r = snd_pcm_recover(pcm, t, 0)) >= 0)
+ continue;
+
+ fprintf(stderr, "Failed to write samples: %s\n", snd_strerror(r));
+ return r;
+ }
+
+ n_samples -= t;
+ samples += t;
+ }
+
+ /* snd_pcm_drain(pcm); */
+ return 0;
+}
+
+static snd_mixer_t* open_mixer(const char *name) {
+ snd_mixer_t *d;
+ int r;
+
+ printf("Opening %s for control.\n", name);
+
+ if ((r = snd_mixer_open(&d, 0)) < 0) {
+ fprintf(stderr, "Cannot open mixer device %s: %s\n", name, snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_mixer_attach(d, name)) < 0) {
+ fprintf(stderr, "Unable to attach to mixer %s: %s\n", name, snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_mixer_selem_register(d, NULL, NULL)) < 0) {
+ fprintf(stderr, "Unable to register mixer: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if ((r = snd_mixer_load(d)) < 0) {
+ fprintf(stderr, "Unable to load mixer: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ return d;
+
+finish:
+ if (d)
+ snd_mixer_close(d);
+
+ return NULL;
+}
+
+static snd_mixer_elem_t* find_element(
+ snd_mixer_t *m,
+ const char *name,
+ long *min_discrete,
+ long *max_discrete) {
+
+ snd_mixer_elem_t *selem;
+ snd_mixer_selem_id_t *sid;
+ long min_dB, max_dB;
+ int r;
+
+ snd_mixer_selem_id_alloca(&sid);
+ snd_mixer_selem_id_set_name(sid, name);
+ snd_mixer_selem_id_set_index(sid, 0);
+
+ if (!(selem = snd_mixer_find_selem(m, sid))) {
+ fprintf(stderr, "Unable to find element %s.\n", name);
+ return NULL;
+ }
+
+ if (!snd_mixer_selem_has_playback_volume(selem)) {
+ fprintf(stderr, "Element %s does not control playback volume.\n", name);
+ return NULL;
+ }
+
+ if ((r = snd_mixer_selem_get_playback_volume_range(selem, min_discrete, max_discrete)) < 0) {
+ fprintf(stderr, "Failed to read element range data from %s: %s\n", name, snd_strerror(r));
+ return NULL;
+ }
+
+ if ((r = snd_mixer_selem_get_playback_dB_range(selem, &min_dB, &max_dB)) < 0) {
+ fprintf(stderr, "Failed to read element dB data from %s: %s\n", name, snd_strerror(r));
+ return NULL;
+ }
+
+ printf("Using element '%s'.\n"
+ "Element volume range is %li..%li (%0.2f..%0.2f dB).\n",
+ snd_mixer_selem_get_name(selem),
+ *min_discrete,
+ *max_discrete,
+ (double) min_dB / 100.0,
+ (double) max_dB / 100.0);
+
+ return selem;
+}
+
+int main(int argc, char *argv[]) {
+ snd_pcm_t *pcm = NULL;
+ snd_mixer_t *mixer = NULL;
+ snd_mixer_elem_t *selem = NULL;
+ char pcm_name[64], ctl_name[64];
+ int ret = 1, r;
+ unsigned rate;
+ long min_discrete, max_discrete, step1, step2, step1_alsa_dB, step2_alsa_dB;
+ double step1_dB, step2_dB, attenuation;
+ float *signal1 = NULL, *signal2 = NULL;
+
+ if (argc < 3 || argc > 5) {
+ fprintf(stderr,
+ "Wrong number of arguments:\n"
+ "%s CARD ELEMENT [VOLUME1] [VOLUME2]\n", argv[0]);
+ return 0;
+ }
+
+ snprintf(pcm_name, sizeof(pcm_name)-1, "plughw:%s", argv[1]);
+ snprintf(ctl_name, sizeof(ctl_name)-1, "hw:%s", argv[1]);
+ pcm_name[sizeof(pcm_name)-1] = ctl_name[sizeof(ctl_name)-1] = 0;
+
+ rate = 44100;
+ if (!(pcm = open_pcm(pcm_name, &rate)))
+ goto finish;
+
+ if (!(mixer = open_mixer(ctl_name)))
+ goto finish;
+
+ if (!(selem = find_element(mixer, argv[2], &min_discrete, &max_discrete)))
+ goto finish;
+
+ if (argc >= 4) {
+ char *e = NULL;
+
+ errno = 0;
+ step1 = strtol(argv[3], &e, 0);
+
+ if (errno != 0 || !e || *e) {
+ fprintf(stderr, "Failed to parse volume step #1: %s\n", argv[3]);
+ goto finish;
+ }
+ } else
+ step1 = min_discrete;
+
+ if (argc >= 5) {
+ char *e = NULL;
+
+ errno = 0;
+ step2 = strtol(argv[4], &e, 0);
+
+ if (errno != 0 || !e || *e) {
+ fprintf(stderr, "Failed to parse volume step #2: %s\n", argv[4]);
+ goto finish;
+ }
+ } else
+ step2 = max_discrete;
+
+ if (step1 > step2) {
+ long u = step1;
+ u = step2;
+ step2 = step1;
+ step1 = u;
+ }
+
+ if (step1 < min_discrete)
+ step1 = min_discrete;
+ if (step1 > max_discrete)
+ step1 = max_discrete;
+
+ if (step2 < min_discrete)
+ step2 = min_discrete;
+ if (step2 > max_discrete)
+ step2 = max_discrete;
+
+ printf("Testing volume steps %li vs. %li.\n", step1, step2);
+
+ if ((r = snd_mixer_selem_ask_playback_vol_dB(selem, step1, &step1_alsa_dB)) < 0 ||
+ (r = snd_mixer_selem_ask_playback_vol_dB(selem, step2, &step2_alsa_dB)) < 0) {
+ fprintf(stderr, "Failed to query dB data for volume steps: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ step1_dB = (double) step1_alsa_dB / 100.0;
+ step2_dB = (double) step2_alsa_dB / 100.0;
+
+ printf("Testing dB steps %0.2f vs. %0.2f.\n", step1_dB, step2_dB);
+
+ attenuation = dB_to_linear(step1_dB - step2_dB);
+
+ printf("Attenuation factor is %0.4f (%0.2f dB).\n", attenuation, linear_to_dB(attenuation));
+
+ if (!(signal1 = generate_signal(rate, 1.0, 440)) |
+ !(signal2 = generate_signal(rate, attenuation, 440))) {
+ fprintf(stderr, "Failed to generate test signal.\n");
+ goto finish;
+ }
+
+ for (;;) {
+ printf("Playing 1s of audio at amplitude %0.4f, mixer set to %li (%0.2f dB).\n", 1.0, step1, step1_dB);
+
+ if ((r = snd_mixer_selem_set_playback_volume_all(selem, step1)) < 0) {
+ fprintf(stderr, "Failed to set element volume step: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if (play_pcm(pcm, signal1, rate) < 0)
+ goto finish;
+
+ printf("Playing 1s of audio at amplitude %0.4f, mixer set to %li (%0.2f dB).\n", attenuation, step2, step2_dB);
+
+ if ((r = snd_mixer_selem_set_playback_volume_all(selem, step2)) < 0) {
+ fprintf(stderr, "Failed to set element volume step: %s\n", snd_strerror(r));
+ goto finish;
+ }
+
+ if (play_pcm(pcm, signal2, rate) < 0)
+ goto finish;
+
+ printf("Looping. Press C-c to quit.\n");
+ }
+
+ ret = 0;
+
+finish:
+ free(signal1);
+ free(signal2);
+
+ if (pcm)
+ snd_pcm_close(pcm);
+
+ if (mixer)
+ snd_mixer_close(mixer);
+
+ return ret;
+}