All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
@ 2015-09-15  7:00 han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 1/7] BAT: Add initial functions han.lu
                   ` (9 more replies)
  0 siblings, 10 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

BAT (Basic Audio Tester) is a simple command line utility intended
to automate audio driver and sound server QA testing with no human
interaction.

BAT works by playing an audio stream and capturing the same stream
in either a digital or analog loopback. It then compares the captured
stream to the original to determine if the testcase passes or fails.

The main idea of frequency detecting is:
The analysis function reads data from wav file, run fft against the data
to get magnitude of frequency vectors, and then calculates the average
value and standard deviation of frequency vectors. After that, we define
a threshold:
    threshold = 3 * standard_deviation + average_value
Frequencies with amplitude larger than threshold will be recognized as
a peak, and the frequency with largest peak value will be recognized as
a detected frequency.
BAT then compares the detected frequency to target frequency, to decide
if the detecting passes or fails.

BAT supports 4 working modes:
  1. single line playback;
  2. single line capture and analysis;
  3. playback and capture in loop, and analyze captured data;
  4. local analyze without actual playback or capture.
BAT will check devices input by user to decide which mode to use.
BAT will create threads for playback and record, and will run spectrum
analysis for mode 2, 3 or 4.

Lu, Han (7):
  BAT: Add initial functions
  BAT: Add common definitions and functions
  BAT: Add playback and record functions
  BAT: Add signal generator
  BAT: Add converting functions
  BAT: Add spectrum analysis functions
  BAT: Add Makefile and configures

 Makefile.am     |   3 +
 bat/Makefile.am |  14 ++
 bat/alsa.c      | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bat/alsa.h      |  20 ++
 bat/analyze.c   | 314 ++++++++++++++++++++++++++++
 bat/analyze.h   |  16 ++
 bat/bat.c       | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 bat/common.c    | 198 ++++++++++++++++++
 bat/common.h    | 169 ++++++++++++++++
 bat/convert.c   | 113 +++++++++++
 bat/convert.h   |  23 +++
 bat/signal.c    |  88 ++++++++
 configure.ac    |  19 +-
 13 files changed, 2202 insertions(+), 1 deletion(-)
 create mode 100644 bat/Makefile.am
 create mode 100644 bat/alsa.c
 create mode 100644 bat/alsa.h
 create mode 100644 bat/analyze.c
 create mode 100644 bat/analyze.h
 create mode 100644 bat/bat.c
 create mode 100644 bat/common.c
 create mode 100644 bat/common.h
 create mode 100644 bat/convert.c
 create mode 100644 bat/convert.h
 create mode 100644 bat/signal.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 1/7] BAT: Add initial functions
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 2/7] BAT: Add common definitions and functions han.lu
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add main entrance, command line parsing, parameter initiating and
thread initiating functions for BAT.

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/bat/bat.c b/bat/bat.c
new file mode 100644
index 0000000..24c74e8
--- /dev/null
+++ b/bat/bat.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+#include <getopt.h>
+#include <math.h>
+#include <limits.h>
+
+#include "aconfig.h"
+#include "gettext.h"
+#include "version.h"
+
+#include "common.h"
+
+#include "alsa.h"
+#include "convert.h"
+#include "analyze.h"
+
+static int get_duration(struct bat *bat)
+{
+	float duration_f;
+	long duration_i;
+	char *ptrf, *ptri;
+
+	duration_f = strtof(bat->narg, &ptrf);
+	if (duration_f == HUGE_VALF || duration_f == -HUGE_VALF) {
+		fprintf(bat->err, _("duration float overflow: %f %d\n"),
+				duration_f, -errno);
+		return -errno;
+	} else if (duration_f == 0.0 && errno != 0) {
+		fprintf(bat->err, _("duration float underflow: %f %d\n"),
+				duration_f, -errno);
+		return -errno;
+	}
+
+	duration_i = strtol(bat->narg, &ptri, 10);
+	if (duration_i == LONG_MAX) {
+		fprintf(bat->err, _("duration long overflow: %ld %d\n"),
+				duration_i, -errno);
+		return -errno;
+	} else if (duration_i == LONG_MIN) {
+		fprintf(bat->err, _("duration long underflow: %ld %d\n"),
+				duration_i, -errno);
+		return -errno;
+	}
+
+	if (*ptrf == 's') {
+		bat->frames = duration_f * bat->rate;
+	} else if (*ptri == 0) {
+		bat->frames = duration_i;
+	} else {
+		fprintf(bat->err, _("invalid duration: %s\n"), bat->narg);
+		return -EINVAL;
+	}
+
+	if (bat->frames <= 0 || bat->frames > MAX_FRAMES) {
+		fprintf(bat->err, _("duration out of range: (0, %d(%ds))\n"),
+				MAX_FRAMES, (bat->frames / bat->rate));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void get_sine_frequencies(struct bat *bat, char *freq)
+{
+	char *tmp1;
+
+	tmp1 = strchr(freq, ',');
+	if (tmp1 == NULL) {
+		bat->target_freq[1] = bat->target_freq[0] = atof(optarg);
+	} else {
+		*tmp1 = '\0';
+		bat->target_freq[0] = atof(optarg);
+		bat->target_freq[1] = atof(tmp1 + 1);
+	}
+}
+
+static void get_format(struct bat *bat, char *optarg)
+{
+	if (strcasecmp(optarg, "cd") == 0) {
+		bat->format = SND_PCM_FORMAT_S16_LE;
+		bat->rate = 44100;
+		bat->channels = 2;
+	} else if (strcasecmp(optarg, "dat") == 0) {
+		bat->format = SND_PCM_FORMAT_S16_LE;
+		bat->rate = 48000;
+		bat->channels = 2;
+	} else {
+		bat->format = snd_pcm_format_value(optarg);
+		if (bat->format == SND_PCM_FORMAT_UNKNOWN) {
+			fprintf(bat->err, _("wrong extended format '%s'\n"),
+					optarg);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	switch (bat->format) {
+	case SND_PCM_FORMAT_U8:
+		bat->sample_size = 1;
+		break;
+	case SND_PCM_FORMAT_S16_LE:
+		bat->sample_size = 2;
+		break;
+	case SND_PCM_FORMAT_S24_3LE:
+		bat->sample_size = 3;
+		break;
+	case SND_PCM_FORMAT_S32_LE:
+		bat->sample_size = 4;
+		break;
+	default:
+		fprintf(bat->err, _("unsupported format: %d\n"), bat->format);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static inline int thread_wait_completion(struct bat *bat,
+		pthread_t id, int **val)
+{
+	int err;
+
+	err = pthread_join(id, (void **) val);
+	if (err)
+		pthread_cancel(id);
+
+	return err;
+}
+
+/* loopback test where we play sine wave and capture the same sine wave */
+static void test_loopback(struct bat *bat)
+{
+	pthread_t capture_id, playback_id;
+	int err;
+	int *thread_result_capture, *thread_result_playback;
+
+	/* start playback */
+	err = pthread_create(&playback_id, NULL,
+			(void *) bat->playback.fct, bat);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot create playback thread: %d\n"),
+				err);
+		exit(EXIT_FAILURE);
+	}
+
+	/* TODO: use a pipe to signal stream start etc - i.e. to sync threads */
+	/* Let some time for playing something before capturing */
+	usleep(CAPTURE_DELAY * 1000);
+
+	/* start capture */
+	err = pthread_create(&capture_id, NULL, (void *) bat->capture.fct, bat);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot create capture thread: %d\n"), err);
+		pthread_cancel(playback_id);
+		exit(EXIT_FAILURE);
+	}
+
+	/* wait for playback to complete */
+	err = thread_wait_completion(bat, playback_id, &thread_result_playback);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot join playback thread: %d\n"), err);
+		free(thread_result_playback);
+		pthread_cancel(capture_id);
+		exit(EXIT_FAILURE);
+	}
+
+	/* check playback status */
+	if (*thread_result_playback != 0) {
+		fprintf(bat->err, _("Exit playback thread fail: %d\n"),
+				*thread_result_playback);
+		pthread_cancel(capture_id);
+		exit(EXIT_FAILURE);
+	} else {
+		fprintf(bat->log, _("Playback completed.\n"));
+	}
+
+	/* now stop and wait for capture to finish */
+	pthread_cancel(capture_id);
+	err = thread_wait_completion(bat, capture_id, &thread_result_capture);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot join capture thread: %d\n"), err);
+		free(thread_result_capture);
+		exit(EXIT_FAILURE);
+	}
+
+	/* check capture status */
+	if (*thread_result_capture != 0) {
+		fprintf(bat->err, _("Exit capture thread fail: %d\n"),
+				*thread_result_capture);
+		exit(EXIT_FAILURE);
+	} else {
+		fprintf(bat->log, _("Capture completed.\n"));
+	}
+}
+
+/* single ended playback only test */
+static void test_playback(struct bat *bat)
+{
+	pthread_t playback_id;
+	int err;
+	int *thread_result;
+
+	/* start playback */
+	err = pthread_create(&playback_id, NULL,
+			(void *) bat->playback.fct, bat);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot create playback thread: %d\n"),
+				err);
+		exit(EXIT_FAILURE);
+	}
+
+	/* wait for playback to complete */
+	err = thread_wait_completion(bat, playback_id, &thread_result);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot join playback thread: %d\n"), err);
+		free(thread_result);
+		exit(EXIT_FAILURE);
+	}
+
+	/* check playback status */
+	if (*thread_result != 0) {
+		fprintf(bat->err, _("Exit playback thread fail: %d\n"),
+				*thread_result);
+		exit(EXIT_FAILURE);
+	} else {
+		fprintf(bat->log, _("Playback completed.\n"));
+	}
+}
+
+/* single ended capture only test */
+static void test_capture(struct bat *bat)
+{
+	pthread_t capture_id;
+	int err;
+	int *thread_result;
+
+	/* start capture */
+	err = pthread_create(&capture_id, NULL, (void *) bat->capture.fct, bat);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot create capture thread: %d\n"), err);
+		exit(EXIT_FAILURE);
+	}
+
+	/* TODO: stop capture */
+
+	/* wait for capture to complete */
+	err = thread_wait_completion(bat, capture_id, &thread_result);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot join capture thread: %d\n"), err);
+		free(thread_result);
+		exit(EXIT_FAILURE);
+	}
+
+	/* check playback status */
+	if (*thread_result != 0) {
+		fprintf(bat->err, _("Exit capture thread fail: %d\n"),
+				*thread_result);
+		exit(EXIT_FAILURE);
+	} else {
+		fprintf(bat->log, _("Capture completed.\n"));
+	}
+}
+
+static void usage(struct bat *bat, char *argv[])
+{
+	fprintf(bat->log,
+_("Usage:%s [Option]...\n"
+"\n"
+"-h, --help             help\n"
+"-D                     sound card\n"
+"-P                     playback pcm\n"
+"-C                     capture pcm\n"
+"-f                     sample size\n"
+"-c                     number of channels\n"
+"-r                     sampling rate\n"
+"-n                     frames to capture\n"
+"-k                     sigma k\n"
+"-F                     target frequency\n"
+"-p                     total number of periods to play/capture\n"
+"    --log=#            path of log file. if not set, logs be put to stdout,\n"
+"                       and errors be put to stderr.\n"
+"    --file=#           input file\n"
+"    --saveplay=#       save playback content to target file, for debug\n"
+"    --local            internal loop, bypass hardware\n"
+), argv[0]);
+	fprintf(bat->log, _("Recognized sample formats are: %s %s %s %s\n"),
+			snd_pcm_format_name(SND_PCM_FORMAT_U8),
+			snd_pcm_format_name(SND_PCM_FORMAT_S16_LE),
+			snd_pcm_format_name(SND_PCM_FORMAT_S24_3LE),
+			snd_pcm_format_name(SND_PCM_FORMAT_S32_LE));
+	fprintf(bat->log, _("The available format shotcuts are:\n"));
+	fprintf(bat->log, _("-f cd (16 bit little endian, 44100, stereo)\n"));
+	fprintf(bat->log, _("-f dat (16 bit little endian, 48000, stereo)\n"));
+}
+
+static void set_defaults(struct bat *bat)
+{
+	memset(bat, 0, sizeof(struct bat));
+
+	/* Set default values */
+	bat->rate = 44100;
+	bat->channels = 1;
+	bat->frame_size = 2;
+	bat->sample_size = 2;
+	bat->format = SND_PCM_FORMAT_S16_LE;
+	bat->convert_float_to_sample = convert_float_to_int16;
+	bat->convert_sample_to_double = convert_int16_to_double;
+	bat->sinf_func = sinf_sign;
+	bat->frames = bat->rate * 2;
+	bat->target_freq[0] = 997.0;
+	bat->target_freq[1] = 997.0;
+	bat->sigma_k = 3.0;
+	bat->playback.device = NULL;
+	bat->capture.device = NULL;
+	bat->buf = NULL;
+	bat->local = false;
+	bat->playback.fct = &playback_alsa;
+	bat->capture.fct = &record_alsa;
+	bat->playback.mode = MODE_LOOPBACK;
+	bat->capture.mode = MODE_LOOPBACK;
+	bat->period_is_limited = false;
+	bat->log = stdout;
+	bat->err = stderr;
+}
+
+static void parse_arguments(struct bat *bat, int argc, char *argv[])
+{
+	int c, option_index;
+	static const char short_options[] = "D:P:C:f:n:F:c:r:s:k:p:lth";
+	static const struct option long_options[] = {
+		{"help",     0, 0, 'h'},
+		{"log",      1, 0, OPT_LOG},
+		{"file",     1, 0, OPT_READFILE},
+		{"saveplay", 1, 0, OPT_SAVEPLAY},
+		{"local",    0, 0, OPT_LOCAL},
+		{0, 0, 0, 0}
+	};
+
+	while ((c = getopt_long(argc, argv, short_options, long_options,
+					&option_index)) != -1) {
+		switch (c) {
+		case OPT_LOG:
+			bat->logarg = optarg;
+			break;
+		case OPT_READFILE:
+			bat->playback.file = optarg;
+			break;
+		case OPT_SAVEPLAY:
+			bat->debugplay = optarg;
+			break;
+		case OPT_LOCAL:
+			bat->local = true;
+			break;
+		case 'D':
+			if (bat->playback.device == NULL)
+				bat->playback.device = optarg;
+			if (bat->capture.device == NULL)
+				bat->capture.device = optarg;
+			break;
+		case 'P':
+			if (bat->capture.mode == MODE_SINGLE)
+				bat->capture.mode = MODE_LOOPBACK;
+			else
+				bat->playback.mode = MODE_SINGLE;
+			bat->playback.device = optarg;
+			break;
+		case 'C':
+			if (bat->playback.mode == MODE_SINGLE)
+				bat->playback.mode = MODE_LOOPBACK;
+			else
+				bat->capture.mode = MODE_SINGLE;
+			bat->capture.device = optarg;
+			break;
+		case 'n':
+			bat->narg = optarg;
+			break;
+		case 'F':
+			get_sine_frequencies(bat, optarg);
+			break;
+		case 'c':
+			bat->channels = atoi(optarg);
+			break;
+		case 'r':
+			bat->rate = atoi(optarg);
+			break;
+		case 'f':
+			get_format(bat, optarg);
+			break;
+		case 'k':
+			bat->sigma_k = atof(optarg);
+			break;
+		case 'p':
+			bat->periods_total = atoi(optarg);
+			bat->period_is_limited = true;
+			break;
+		case 'h':
+		default:
+			usage(bat, argv);
+			exit(EXIT_SUCCESS);
+		}
+	}
+}
+
+static int validate_options(struct bat *bat)
+{
+	int c;
+	float freq_low, freq_high;
+
+	/* check we have an input file for local mode */
+	if ((bat->local == true) && (bat->capture.file == NULL)) {
+		fprintf(bat->err, _("no input file for local testing\n"));
+		return -EINVAL;
+	}
+
+	/* check supported channels */
+	if (bat->channels > MAX_CHANNELS || bat->channels < MIN_CHANNELS) {
+		fprintf(bat->err, _("%d channels not supported\n"),
+				bat->channels);
+		return -EINVAL;
+	}
+
+	/* check single ended is in either playback or capture - not both */
+	if ((bat->playback.mode == MODE_SINGLE)
+			&& (bat->capture.mode == MODE_SINGLE)) {
+		fprintf(bat->err, _("single ended mode is simplex\n"));
+		return -EINVAL;
+	}
+
+	/* check sine wave frequency range */
+	freq_low = DC_THRESHOLD;
+	freq_high = bat->rate * RATE_FACTOR;
+	for (c = 0; c < bat->channels; c++) {
+		if (bat->target_freq[c] < freq_low
+				|| bat->target_freq[c] > freq_high) {
+			fprintf(bat->err, _("sine wave frequency out of"));
+			fprintf(bat->err, _(" range: (%.1f, %.1f)\n"),
+				freq_low, freq_high);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int bat_init(struct bat *bat)
+{
+	int err = 0;
+
+	/* Determine logging to a file or stdout and stderr */
+	if (bat->logarg) {
+		bat->log = NULL;
+		bat->log = fopen(bat->logarg, "wb");
+		if (bat->log == NULL) {
+			fprintf(bat->err, _("Cannot open file for capture:"));
+			fprintf(bat->err, _(" %s %d\n"),
+					bat->logarg, -errno);
+			return -errno;
+		}
+		bat->err = bat->log;
+	}
+
+	/* Determine duration of playback and/or capture */
+	if (bat->narg) {
+		err = get_duration(bat);
+		if (err < 0)
+			return err;
+	}
+
+	/* Determine capture file */
+	if (bat->local)
+		bat->capture.file = bat->playback.file;
+	else
+		bat->capture.file = TEMP_RECORD_FILE_NAME;
+
+	/* Initial for playback */
+	if (bat->playback.file == NULL) {
+		/* No input file so we will generate our own sine wave */
+		if (bat->frames) {
+			if (bat->playback.mode == MODE_SINGLE) {
+				/* Play nb of frames given by -n argument */
+				bat->sinus_duration = bat->frames;
+			} else {
+				/* Play CAPTURE_DELAY msec +
+				 * 150% of the nb of frames to be analyzed */
+				bat->sinus_duration = bat->rate *
+						CAPTURE_DELAY / 1000;
+				bat->sinus_duration +=
+						(bat->frames + bat->frames / 2);
+			}
+		} else {
+			/* Special case where we want to generate a sine wave
+			 * endlessly without capturing */
+			bat->sinus_duration = 0;
+			bat->playback.mode = MODE_SINGLE;
+		}
+	} else {
+		bat->fp = fopen(bat->playback.file, "rb");
+		if (bat->fp == NULL) {
+			fprintf(bat->err, _("Cannot open file for playback:"));
+			fprintf(bat->err, _(" %s %d\n"),
+					bat->playback.file, -errno);
+			return -errno;
+		}
+		err = read_wav_header(bat, bat->playback.file, bat->fp, false);
+		fclose(bat->fp);
+		if (err != 0)
+			return err;
+	}
+
+	bat->frame_size = bat->sample_size * bat->channels;
+
+	/* Set conversion functions */
+	switch (bat->sample_size) {
+	case 1:
+		bat->convert_float_to_sample = convert_float_to_uint8;
+		bat->convert_sample_to_double = convert_uint8_to_double;
+		bat->sinf_func = sinf_unsign;
+		break;
+	case 2:
+		bat->convert_float_to_sample = convert_float_to_int16;
+		bat->convert_sample_to_double = convert_int16_to_double;
+		bat->sinf_func = sinf_sign;
+		break;
+	case 3:
+		bat->convert_float_to_sample = convert_float_to_int24;
+		bat->convert_sample_to_double = convert_int24_to_double;
+		bat->sinf_func = sinf_sign;
+		break;
+	case 4:
+		bat->convert_float_to_sample = convert_float_to_int32;
+		bat->convert_sample_to_double = convert_int32_to_double;
+		bat->sinf_func = sinf_sign;
+		break;
+	default:
+		fprintf(bat->err, _("Invalid PCM format: size=%d\n"),
+				bat->sample_size);
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+int main(int argc, char *argv[])
+{
+	struct bat bat;
+	int err = 0;
+
+	set_defaults(&bat);
+
+#ifdef ENABLE_NLS
+	setlocale(LC_ALL, "");
+	textdomain(PACKAGE);
+#endif
+
+	fprintf(bat.log, _("%s version %s\n\n"), PACKAGE_NAME, PACKAGE_VERSION);
+
+	parse_arguments(&bat, argc, argv);
+
+	err = bat_init(&bat);
+	if (err < 0)
+		goto out;
+
+	err = validate_options(&bat);
+	if (err < 0)
+		goto out;
+
+	/* single line playback thread: playback only, no capture */
+	if (bat.playback.mode == MODE_SINGLE) {
+		test_playback(&bat);
+		goto out;
+	}
+
+	/* single line capture thread: capture only, no playback */
+	if (bat.capture.mode == MODE_SINGLE) {
+		test_capture(&bat);
+		goto analyze;
+	}
+
+	/* loopback thread: playback and capture in a loop */
+	if (bat.local == false)
+		test_loopback(&bat);
+
+analyze:
+	err = analyze_capture(&bat);
+out:
+	fprintf(bat.log, _("\nReturn value is %d\n"), err);
+	if (bat.logarg)
+		fclose(bat.log);
+
+	return err;
+}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 2/7] BAT: Add common definitions and functions
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 1/7] BAT: Add initial functions han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 3/7] BAT: Add playback and record functions han.lu
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add common definitions of macros and data structures; Add functions
that used by multiple components, such as wav file reading and writing.

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/bat/common.c b/bat/common.c
new file mode 100644
index 0000000..798b00b
--- /dev/null
+++ b/bat/common.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include "aconfig.h"
+#include "gettext.h"
+
+#include "common.h"
+#include "alsa.h"
+
+int retval_play;
+int retval_record;
+
+/* update chunk_fmt data to bat */
+static int update_fmt_to_bat(struct bat *bat, struct chunk_fmt *fmt)
+{
+	bat->channels = fmt->channels;
+	bat->rate = fmt->sample_rate;
+	bat->sample_size = fmt->sample_length / 8;
+	if (bat->sample_size > 4) {
+		fprintf(bat->err, _("Invalid format: sample size=%d\n"),
+				bat->sample_size);
+		return -EINVAL;
+	}
+	bat->frame_size = fmt->blocks_align;
+
+	return 0;
+}
+
+/* calculate frames and update to bat */
+static int update_frames_to_bat(struct bat *bat,
+		struct wav_chunk_header *header, FILE *fp)
+{
+	/* The number of analyzed captured frames is arbitrarily set to half of
+	   the number of frames of the wav file or the number of frames of the
+	   wav file when doing direct analysis (--local) */
+	bat->frames = header->length / bat->frame_size;
+	if (!bat->local)
+		bat->frames /= 2;
+
+	return 0;
+}
+
+static int read_chunk_fmt(struct bat *bat, char *file, FILE *fp, bool skip,
+		struct wav_chunk_header *header)
+{
+	size_t err;
+	int header_skip;
+	struct chunk_fmt chunk_fmt;
+
+	err = fread(&chunk_fmt, sizeof(chunk_fmt), 1, fp);
+	if (err != 1) {
+		fprintf(bat->err, _("Read chunk fmt error: %s:%zd\n"),
+				file, err);
+		return -EIO;
+	}
+	/* If the format header is larger, skip the rest */
+	header_skip = header->length - sizeof(chunk_fmt);
+	if (header_skip > 0) {
+		err = fseek(fp, header_skip, SEEK_CUR);
+		if (err == -1) {
+			fprintf(bat->err, _("Seek fmt header error: %s:%zd\n"),
+					file, err);
+			return -EINVAL;
+		}
+	}
+	/* If the file is opened for playback, update BAT data;
+	   If the file is opened for analysis, no update */
+	if (skip == false) {
+		err = update_fmt_to_bat(bat, &chunk_fmt);
+		if (err != 0)
+			return err;
+	}
+
+	return 0;
+}
+
+int read_wav_header(struct bat *bat, char *file, FILE *fp, bool skip)
+{
+	struct wav_header riff_wave_header;
+	struct wav_chunk_header chunk_header;
+	int more_chunks = 1;
+	size_t err;
+
+	/* Read header of RIFF wav file */
+	err = fread(&riff_wave_header, sizeof(riff_wave_header), 1, fp);
+	if (err != 1) {
+		fprintf(bat->err, _("Read header error: %s:%zd\n"), file, err);
+		return -EIO;
+	}
+	if ((riff_wave_header.magic != WAV_RIFF)
+			|| (riff_wave_header.type != WAV_WAVE)) {
+		fprintf(bat->err, _("%s is not a riff/wave file\n"), file);
+		return -EINVAL;
+	}
+
+	/* Read chunks in RIFF wav file */
+	do {
+		err = fread(&chunk_header, sizeof(chunk_header), 1, fp);
+		if (err != 1) {
+			fprintf(bat->err, _("Read chunk header error: "));
+			fprintf(bat->err, _("%s:%zd\n"), file, err);
+			return -EIO;
+		}
+
+		switch (chunk_header.type) {
+		case WAV_FMT:
+			/* WAV_FMT chunk, read and analyze */
+			err = read_chunk_fmt(bat, file, fp, skip,
+					&chunk_header);
+			if (err != 0)
+				return err;
+			break;
+		case WAV_DATA:
+			/* WAV_DATA chunk, break looping */
+			/* If the file is opened for playback, update BAT data;
+			   If the file is opened for analysis, no update */
+			if (skip == false) {
+				err = update_frames_to_bat(bat, &chunk_header,
+						fp);
+				if (err != 0)
+					return err;
+			}
+			/* Stop looking for chunks */
+			more_chunks = 0;
+			break;
+		default:
+			/* Unknown chunk, skip bytes */
+			err = fseek(fp, chunk_header.length, SEEK_CUR);
+			if (err == -1) {
+				fprintf(bat->err, _("Fail to skip unknown"));
+				fprintf(bat->err, _(" chunk of %s:%zd\n"),
+						file, err);
+				return -EINVAL;
+			}
+		}
+	} while (more_chunks);
+
+	return 0;
+}
+
+void prepare_wav_info(struct wav_container *wav, struct bat *bat)
+{
+	wav->header.magic = WAV_RIFF;
+	wav->header.type = WAV_WAVE;
+	wav->format.magic = WAV_FMT;
+	wav->format.fmt_size = 16;
+	wav->format.format = WAV_FORMAT_PCM;
+	wav->format.channels = bat->channels;
+	wav->format.sample_rate = bat->rate;
+	wav->format.sample_length = bat->sample_size * 8;
+	wav->format.blocks_align = bat->channels * bat->sample_size;
+	wav->format.bytes_p_second = wav->format.blocks_align * bat->rate;
+	wav->chunk.length = bat->frames * bat->frame_size;
+	wav->chunk.type = WAV_DATA;
+	wav->header.length = (wav->chunk.length) + sizeof(wav->chunk)
+			+ sizeof(wav->format) + sizeof(wav->header) - 8;
+}
+
+int write_wav_header(FILE *fp, struct wav_container *wav, struct bat *bat)
+{
+	int err = 0;
+
+	err = fwrite(&wav->header, 1, sizeof(wav->header), fp);
+	if (err != sizeof(wav->header)) {
+		fprintf(bat->err, _("Write file error: header %d\n"), err);
+		return -EIO;
+	}
+	err = fwrite(&wav->format, 1, sizeof(wav->format), fp);
+	if (err != sizeof(wav->format)) {
+		fprintf(bat->err, _("Write file error: format %d\n"), err);
+		return -EIO;
+	}
+	err = fwrite(&wav->chunk, 1, sizeof(wav->chunk), fp);
+	if (err != sizeof(wav->chunk)) {
+		fprintf(bat->err, _("Write file error: chunk %d\n"), err);
+		return -EIO;
+	}
+
+	return 0;
+}
diff --git a/bat/common.h b/bat/common.h
new file mode 100644
index 0000000..4e773cc
--- /dev/null
+++ b/bat/common.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <alsa/asoundlib.h>
+
+#define TEMP_RECORD_FILE_NAME		"/tmp/bat.wav"
+
+#define OPT_BASE			300
+#define OPT_LOG				(OPT_BASE + 1)
+#define OPT_READFILE			(OPT_BASE + 2)
+#define OPT_SAVEPLAY			(OPT_BASE + 3)
+#define OPT_LOCAL			(OPT_BASE + 4)
+
+#define COMPOSE(a, b, c, d)		((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
+#define WAV_RIFF			COMPOSE('R', 'I', 'F', 'F')
+#define WAV_WAVE			COMPOSE('W', 'A', 'V', 'E')
+#define WAV_FMT				COMPOSE('f', 'm', 't', ' ')
+#define WAV_DATA			COMPOSE('d', 'a', 't', 'a')
+#define WAV_FORMAT_PCM			1	/* PCM WAVE file encoding */
+
+#define MAX_CHANNELS			2
+#define MIN_CHANNELS			1
+#define MAX_PEAKS			10
+#define MAX_FRAMES			(10 * 1024 * 1024)
+/* Given in ms */
+#define CAPTURE_DELAY			500
+/* signal frequency should be less than samplerate * RATE_FACTOR */
+#define RATE_FACTOR			0.4
+/* valid range of samplerate: (1 - RATE_RANGE, 1 + RATE_RANGE) * samplerate */
+#define RATE_RANGE			0.05
+/* Given in us */
+#define MAX_BUFFERTIME			500000
+/* devide factor, was 4, changed to 8 to remove reduce capture overrun */
+#define DIV_BUFFERTIME			8
+/* margin to avoid sign inversion when generate sine wav */
+#define RANGE_FACTOR			0.95
+
+#define EBATBASE			1000
+#define ENOPEAK				(EBATBASE + 1)
+#define EONLYDC				(EBATBASE + 2)
+#define EBADPEAK			(EBATBASE + 3)
+
+#define DC_THRESHOLD			7.01
+
+/* tolerance of detected peak = max (DELTA_HZ, DELTA_RATE * target_freq).
+ * If DELTA_RATE is too high, BAT may not be able to recognize negative result;
+ * if too low, BAT may be too sensitive and results in uncecessary failure. */
+#define DELTA_RATE			0.005
+#define DELTA_HZ			1
+
+#define FOUND_DC			(1<<1)
+#define FOUND_WRONG_PEAK		(1<<0)
+
+struct wav_header {
+	unsigned int magic; /* 'RIFF' */
+	unsigned int length; /* file len */
+	unsigned int type; /* 'WAVE' */
+};
+
+struct wav_chunk_header {
+	unsigned int type; /* 'data' */
+	unsigned int length; /* sample count */
+};
+
+struct wav_fmt {
+	unsigned int magic; /* 'FMT '*/
+	unsigned int fmt_size; /* 16 or 18 */
+	unsigned short format; /* see WAV_FMT_* */
+	unsigned short channels;
+	unsigned int sample_rate; /* Frequency of sample */
+	unsigned int bytes_p_second;
+	unsigned short blocks_align; /* sample size; 1 or 2 bytes */
+	unsigned short sample_length; /* 8, 12 or 16 bit */
+};
+
+struct chunk_fmt {
+	unsigned short format; /* see WAV_FMT_* */
+	unsigned short channels;
+	unsigned int sample_rate; /* Frequency of sample */
+	unsigned int bytes_p_second;
+	unsigned short blocks_align; /* sample size; 1 or 2 bytes */
+	unsigned short sample_length; /* 8, 12 or 16 bit */
+};
+
+struct wav_container {
+	struct wav_header header;
+	struct wav_fmt format;
+	struct wav_chunk_header chunk;
+};
+
+struct bat;
+
+enum _bat_op_mode {
+	MODE_UNKNOWN = -1,
+	MODE_SINGLE = 0,
+	MODE_LOOPBACK,
+	MODE_LAST
+};
+
+struct pcm {
+	char *device;
+	char *file;
+	enum _bat_op_mode mode;
+	void *(*fct)(struct bat *);
+};
+
+struct bat {
+	unsigned int rate;		/* sampling rate */
+	int channels;			/* nb of channels */
+	int frames;			/* nb of frames */
+	int frame_size;			/* size of frame */
+	int sample_size;		/* size of sample */
+	snd_pcm_format_t format;	/* PCM format */
+
+	float sigma_k;			/* threshold for peak detection */
+	float target_freq[MAX_CHANNELS];
+
+	int sinus_duration;		/* number of frames for playback */
+	char *narg;			/* argument string of duration */
+	char *logarg;			/* path name of log file */
+	char *debugplay;		/* path name to store playback signal */
+
+	struct pcm playback;
+	struct pcm capture;
+
+	unsigned int periods_played;
+	unsigned int periods_total;
+	bool period_is_limited;
+
+	FILE *fp;
+
+	FILE *log;
+	FILE *err;
+
+	void (*convert_sample_to_double)(void *, double *, int);
+	void (*convert_float_to_sample)(float *, void *, int, int);
+	void (*sinf_func)(struct bat *, float *, int, int, int *, float *);
+
+	void *buf;			/* PCM Buffer */
+
+	bool local;			/* true for internal test */
+};
+
+struct analyze {
+	void *buf;
+	double *in;
+	double *out;
+	double *mag;
+};
+
+void prepare_wav_info(struct wav_container *, struct bat *);
+int read_wav_header(struct bat *, char *, FILE *, bool);
+int write_wav_header(FILE *, struct wav_container *, struct bat *);
+
+void sinf_sign(struct bat *, float *, int, int, int *, float *);
+void sinf_unsign(struct bat *, float *, int, int, int *, float *);
+int generate_sine_wave(struct bat *, int, void *, int);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 3/7] BAT: Add playback and record functions
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 1/7] BAT: Add initial functions han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 2/7] BAT: Add common definitions and functions han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 4/7] BAT: Add signal generator han.lu
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add functions as main loop of playback thread and record thread.
The functions access pcm hardware through ALSA APIs.

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/bat/alsa.c b/bat/alsa.c
new file mode 100644
index 0000000..de543ed
--- /dev/null
+++ b/bat/alsa.c
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <math.h>
+#include <stdint.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#include "aconfig.h"
+#include "gettext.h"
+
+#include "common.h"
+#include "alsa.h"
+
+struct pcm_container {
+	snd_pcm_t *handle;
+	snd_pcm_uframes_t period_size;
+	snd_pcm_uframes_t buffer_size;
+	snd_pcm_format_t format;
+	unsigned short channels;
+	size_t period_bytes;
+	size_t sample_bits;
+	size_t frame_bits;
+	char *buffer;
+};
+
+static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
+{
+	snd_pcm_hw_params_t *params;
+	unsigned int buffer_time = 0;
+	unsigned int period_time = 0;
+	unsigned int rate;
+	int err;
+	const char *device_name = snd_pcm_name(sndpcm->handle);
+
+	/* Allocate a hardware parameters object. */
+	snd_pcm_hw_params_alloca(&params);
+
+	/* Fill it in with default values. */
+	err = snd_pcm_hw_params_any(sndpcm->handle, params);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("default params: %s: %s(%d)\n"),
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	/* Set access mode */
+	err = snd_pcm_hw_params_set_access(sndpcm->handle, params,
+			SND_PCM_ACCESS_RW_INTERLEAVED);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("access type: %s: %s(%d)\n"),
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	/* Set format */
+	err = snd_pcm_hw_params_set_format(sndpcm->handle, params, bat->format);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("PCM format: %d %s: %s(%d)\n"),
+				bat->format,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	/* Set channels */
+	err = snd_pcm_hw_params_set_channels(sndpcm->handle,
+			params, bat->channels);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("channel number: %d %s: %s(%d)\n"),
+				bat->channels,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	/* Set sampling rate */
+	rate = bat->rate;
+	err = snd_pcm_hw_params_set_rate_near(sndpcm->handle,
+			params, &bat->rate,
+			0);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("sample rate: %d %s: %s(%d)\n"),
+				bat->rate,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+	if ((float) rate * (1 + RATE_RANGE) < bat->rate
+			|| (float) rate * (1 - RATE_RANGE) > bat->rate) {
+		fprintf(bat->err, _("Invalid parameters: sample rate: "));
+		fprintf(bat->err, _("requested %dHz, got %dHz\n"),
+				rate, bat->rate);
+		return -EINVAL;
+	}
+
+	if (snd_pcm_hw_params_get_buffer_time_max(params,
+			&buffer_time, 0) < 0) {
+		fprintf(bat->err, _("Get parameter from device error: "));
+		fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
+				buffer_time,
+				device_name, snd_strerror(err), err);
+		return -EINVAL;
+	}
+
+	if (buffer_time > MAX_BUFFERTIME)
+		buffer_time = MAX_BUFFERTIME;
+
+	period_time = buffer_time / DIV_BUFFERTIME;
+
+	/* Set buffer time and period time */
+	err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle, params,
+			&buffer_time, 0);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
+				buffer_time,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle, params,
+			&period_time, 0);
+	if (err < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
+				period_time,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	/* Write the parameters to the driver */
+	if (snd_pcm_hw_params(sndpcm->handle, params) < 0) {
+		fprintf(bat->err, _("Set parameter to device error: "));
+		fprintf(bat->err, _("hw params: %s: %s(%d)\n"),
+				device_name, snd_strerror(err), err);
+		return -EINVAL;
+	}
+
+	err = snd_pcm_hw_params_get_period_size(params,
+			&sndpcm->period_size, 0);
+	if (err < 0) {
+		fprintf(bat->err, _("Get parameter from device error: "));
+		fprintf(bat->err, _("period size: %zd %s: %s(%d)\n"),
+				sndpcm->period_size,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	err = snd_pcm_hw_params_get_buffer_size(params, &sndpcm->buffer_size);
+	if (err < 0) {
+		fprintf(bat->err, _("Get parameter from device error: "));
+		fprintf(bat->err, _("buffer size: %zd %s: %s(%d)\n"),
+				sndpcm->buffer_size,
+				device_name, snd_strerror(err), err);
+		return err;
+	}
+
+	if (sndpcm->period_size == sndpcm->buffer_size) {
+		fprintf(bat->err, _("Invalid parameters: can't use period "));
+		fprintf(bat->err, _("equal to buffer size (%zd)\n"),
+				sndpcm->period_size);
+		return -EINVAL;
+	}
+
+	err = snd_pcm_format_physical_width(bat->format);
+	if (err < 0) {
+		fprintf(bat->err, _("Invalid parameters: "));
+		fprintf(bat->err, _("snd_pcm_format_physical_width: %d\n"),
+				err);
+		return err;
+	}
+	sndpcm->sample_bits = err;
+
+	sndpcm->frame_bits = sndpcm->sample_bits * bat->channels;
+
+	/* Calculate the period bytes */
+	sndpcm->period_bytes = sndpcm->period_size * sndpcm->frame_bits / 8;
+	sndpcm->buffer = (char *) malloc(sndpcm->period_bytes);
+	if (sndpcm->buffer == NULL) {
+		fprintf(bat->err, _("Not enough memory: size=%zd\n"),
+				sndpcm->period_bytes);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * Generate buffer to be played either from input file or from generated data
+ * Return value
+ * <0 error
+ * 0 ok
+ * >0 break
+ */
+static int generate_input_data(struct pcm_container *sndpcm, int bytes,
+		struct bat *bat)
+{
+	int err;
+	static int load;
+	void *buf;
+	int max, samples = bytes * 8 / sndpcm->frame_bits;
+
+	if (bat->playback.file != NULL) {
+		/* From input file */
+		load = 0;
+
+		while (1) {
+			err = fread(sndpcm->buffer + load, 1,
+					bytes - load, bat->fp);
+			if (0 == err) {
+				if (feof(bat->fp)) {
+					fprintf(bat->log,
+							_("End of playing.\n"));
+					return 1;
+				}
+			} else if (err < bytes - load) {
+				if (ferror(bat->fp)) {
+					fprintf(bat->err, _("Read file error"));
+					fprintf(bat->err, _(": %d\n"), err);
+					return -EIO;
+				}
+				load += err;
+			} else {
+				break;
+			}
+		}
+	} else {
+		/* Generate sine wave */
+		if ((bat->sinus_duration) && (load > bat->sinus_duration))
+			return 1;
+
+		switch (bat->sample_size) {
+		case 1:
+			buf = (uint8_t *) sndpcm->buffer;
+			max = UINT8_MAX;
+			break;
+		case 2:
+			buf = (int16_t *) sndpcm->buffer;
+			max = INT16_MAX;
+			break;
+		case 3:
+			buf = (int8_t *) sndpcm->buffer;
+			max = (1 << 23) - 1;
+			break;
+		case 4:
+			buf = (int32_t *) sndpcm->buffer;
+			max = INT32_MAX;
+			break;
+		default:
+			fprintf(bat->err, _("Invalid PCM format: size=%d\n"),
+					bat->sample_size);
+			return -EINVAL;
+		}
+
+		err = generate_sine_wave(bat, samples, buf, max);
+		if (err != 0)
+			return err;
+
+		load += samples;
+	}
+
+	bat->periods_played++;
+
+	return 0;
+}
+
+static int write_to_pcm(const struct pcm_container *sndpcm,
+		int frames, struct bat *bat)
+{
+	int err;
+	int offset = 0;
+	int remain = frames;
+
+	while (remain > 0) {
+		err = snd_pcm_writei(sndpcm->handle, sndpcm->buffer + offset,
+				remain);
+		if (err == -EAGAIN || (err >= 0 && err < frames)) {
+			snd_pcm_wait(sndpcm->handle, 500);
+		} else if (err == -EPIPE) {
+			fprintf(bat->err, _("Underrun: %s(%d)\n"),
+					snd_strerror(err), err);
+			snd_pcm_prepare(sndpcm->handle);
+		} else if (err < 0) {
+			fprintf(bat->err, _("Write PCM device error: %s(%d)\n"),
+					snd_strerror(err), err);
+			return err;
+		}
+
+		if (err > 0) {
+			remain -= err;
+			offset += err * sndpcm->frame_bits / 8;
+		}
+	}
+
+	return 0;
+}
+
+static int write_to_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
+{
+	int err;
+	int bytes = sndpcm->period_bytes; /* playback buffer size */
+	int frames = bytes * 8 / sndpcm->frame_bits; /* frame count */
+	FILE *fp = NULL;
+	struct wav_container wav;
+	int bytes_total = 0;
+
+	if (bat->debugplay) {
+		fp = fopen(bat->debugplay, "wb");
+		if (fp == NULL) {
+			fprintf(bat->err, _("Cannot open file for capture: "));
+			fprintf(bat->err, _("%s %d\n"), bat->debugplay, -errno);
+			return -errno;
+		}
+		/* leave space for wav header */
+		err = fseek(fp, sizeof(wav), SEEK_SET);
+		if (err != 0) {
+			fprintf(bat->err, _("Seek file error: %d %d\n"),
+					err, -errno);
+			return -errno;
+		}
+	}
+
+	while (1) {
+		err = generate_input_data(sndpcm, bytes, bat);
+		if (err < 0)
+			return err;
+		else if (err > 0)
+			break;
+
+		if (bat->debugplay) {
+			err = fwrite(sndpcm->buffer, 1, bytes, fp);
+			if (err != bytes) {
+				fprintf(bat->err, _("Write file error: "));
+				fprintf(bat->err, _("%s(%d)\n"),
+						snd_strerror(err), err);
+				return -EIO;
+			}
+			bytes_total += bytes;
+		}
+
+		if (bat->period_is_limited
+				&& bat->periods_played >= bat->periods_total)
+			break;
+
+		err = write_to_pcm(sndpcm, frames, bat);
+		if (err != 0)
+			return err;
+	}
+
+	if (bat->debugplay) {
+		/* update wav header */
+		prepare_wav_info(&wav, bat);
+		wav.chunk.length = bytes_total;
+		wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
+			+ sizeof(wav.format) + sizeof(wav.header) - 8;
+
+		rewind(fp);
+		err = write_wav_header(fp, &wav, bat);
+		if (err != 0) {
+			fprintf(bat->err, _("Write file error: %s %s(%d)\n"),
+					bat->debugplay, snd_strerror(err), err);
+			return err;
+		}
+		fclose(fp);
+	}
+
+	snd_pcm_drain(sndpcm->handle);
+
+	return 0;
+}
+
+/**
+ * Play
+ */
+void *playback_alsa(struct bat *bat)
+{
+	int err = 0;
+	struct pcm_container sndpcm;
+
+	fprintf(bat->log, _("Entering playback thread (ALSA).\n"));
+
+	retval_play = 0;
+	memset(&sndpcm, 0, sizeof(sndpcm));
+
+	if (bat->playback.device == NULL) {
+		fprintf(bat->err, _("No PCM device for playback: exit\n"));
+		retval_play = 1;
+		goto exit1;
+	}
+
+	err = snd_pcm_open(&sndpcm.handle, bat->playback.device,
+			SND_PCM_STREAM_PLAYBACK, 0);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot open PCM playback device: "));
+		fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
+		retval_play = 1;
+		goto exit1;
+	}
+
+	err = set_snd_pcm_params(bat, &sndpcm);
+	if (err != 0) {
+		retval_play = 1;
+		goto exit2;
+	}
+
+	if (bat->playback.file == NULL) {
+		fprintf(bat->log, _("Playing generated audio sine wave"));
+		bat->sinus_duration == 0 ?
+			fprintf(bat->log, _(" endlessly\n")) :
+			fprintf(bat->log, _("\n"));
+	} else {
+		fprintf(bat->log, _("Playing input audio file: %s\n"),
+				bat->playback.file);
+		bat->fp = fopen(bat->playback.file, "rb");
+		if (bat->fp == NULL) {
+			fprintf(bat->err, _("Cannot open file for capture: "));
+			fprintf(bat->err, _("%s %d\n"),
+					bat->playback.file, -errno);
+			retval_play = 1;
+			goto exit3;
+		}
+		/* Skip header */
+		err = read_wav_header(bat, bat->playback.file, bat->fp, true);
+		if (err != 0) {
+			retval_play = 1;
+			goto exit4;
+		}
+	}
+
+	err = write_to_pcm_loop(&sndpcm, bat);
+	if (err != 0) {
+		retval_play = 1;
+		goto exit4;
+	}
+
+exit4:
+	if (bat->playback.file)
+		fclose(bat->fp);
+exit3:
+	free(sndpcm.buffer);
+exit2:
+	snd_pcm_close(sndpcm.handle);
+exit1:
+	pthread_exit(&retval_play);
+}
+
+static int read_from_pcm(struct pcm_container *sndpcm,
+		int frames, struct bat *bat)
+{
+	int err = 0;
+	int offset = 0;
+	int remain = frames;
+
+	while (remain > 0) {
+		err = snd_pcm_readi(sndpcm->handle,
+				sndpcm->buffer + offset, remain);
+		if (err == -EAGAIN || (err >= 0 && err < remain)) {
+			snd_pcm_wait(sndpcm->handle, 500);
+		} else if (err == -EPIPE) {
+			snd_pcm_prepare(sndpcm->handle);
+			fprintf(bat->err, _("Overrun: %s(%d)\n"),
+					snd_strerror(err), err);
+		} else if (err < 0) {
+			fprintf(bat->err, _("Read PCM device error: %s(%d)\n"),
+					snd_strerror(err), err);
+			return err;
+		}
+
+		if (err > 0) {
+			remain -= err;
+			offset += err * sndpcm->frame_bits / 8;
+		}
+	}
+
+	return 0;
+}
+
+static int read_from_pcm_loop(FILE *fp, int count,
+		struct pcm_container *sndpcm, struct bat *bat)
+{
+	int err = 0;
+	int size, frames;
+	int remain = count;
+
+	while (remain > 0) {
+		size = (remain <= sndpcm->period_bytes) ?
+			remain : sndpcm->period_bytes;
+		frames = size * 8 / sndpcm->frame_bits;
+
+		/* read a chunk from pcm device */
+		err = read_from_pcm(sndpcm, frames, bat);
+		if (err != 0)
+			return err;
+
+		/* write the chunk to file */
+		err = fwrite(sndpcm->buffer, 1, size, fp);
+		if (err != size) {
+			fprintf(bat->err, _("Write file error: %s(%d)\n"),
+					snd_strerror(err), err);
+			return -EIO;
+		}
+		remain -= size;
+		bat->periods_played++;
+
+		if (bat->period_is_limited
+				&& bat->periods_played >= bat->periods_total)
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * Record
+ */
+void *record_alsa(struct bat *bat)
+{
+	int err = 0;
+	FILE *fp = NULL;
+	struct pcm_container sndpcm;
+	struct wav_container wav;
+	int count;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+
+	fprintf(bat->log, _("Entering capture thread (ALSA).\n"));
+
+	retval_record = 0;
+	memset(&sndpcm, 0, sizeof(sndpcm));
+
+	if (bat->capture.device == NULL) {
+		fprintf(bat->err, _("No PCM device for capture: exit\n"));
+		retval_record = 1;
+		goto exit1;
+	}
+
+	err = snd_pcm_open(&sndpcm.handle, bat->capture.device,
+			SND_PCM_STREAM_CAPTURE, 0);
+	if (err != 0) {
+		fprintf(bat->err, _("Cannot open PCM capture device: "));
+		fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
+		retval_record = 1;
+		goto exit1;
+	}
+
+	err = set_snd_pcm_params(bat, &sndpcm);
+	if (err != 0) {
+		retval_record = 1;
+		goto exit2;
+	}
+
+	remove(bat->capture.file);
+	fp = fopen(bat->capture.file, "w+");
+	if (fp == NULL) {
+		fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
+				bat->capture.file, -errno);
+		retval_record = 1;
+		goto exit3;
+	}
+
+	prepare_wav_info(&wav, bat);
+
+	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+	pthread_cleanup_push(snd_pcm_close, sndpcm.handle);
+	pthread_cleanup_push(free, sndpcm.buffer);
+	pthread_cleanup_push(fclose, fp);
+
+	err = write_wav_header(fp, &wav, bat);
+	if (err != 0) {
+		retval_record = 1;
+		goto exit4;
+	}
+
+	count = wav.chunk.length;
+	fprintf(bat->log, _("Recording ...\n"));
+	err = read_from_pcm_loop(fp, count, &sndpcm, bat);
+	if (err != 0) {
+		retval_record = 1;
+		goto exit4;
+	}
+
+	/* Normally we will never reach this part of code (before fail_exit) as
+	   this thread will be cancelled by end of play thread. */
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+	pthread_cleanup_pop(0);
+
+	snd_pcm_drain(sndpcm.handle);
+
+exit4:
+	fclose(fp);
+exit3:
+	free(sndpcm.buffer);
+exit2:
+	snd_pcm_close(sndpcm.handle);
+exit1:
+	pthread_exit(&retval_record);
+}
diff --git a/bat/alsa.h b/bat/alsa.h
new file mode 100644
index 0000000..d5c9972
--- /dev/null
+++ b/bat/alsa.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+extern int retval_play;
+extern int retval_record;
+
+void *playback_alsa(struct bat *);
+void *record_alsa(struct bat *);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 4/7] BAT: Add signal generator
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (2 preceding siblings ...)
  2015-09-15  7:00 ` [PATCH BAT V1 3/7] BAT: Add playback and record functions han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-18 16:50   ` Caleb Crome
  2015-09-15  7:00 ` [PATCH BAT V1 5/7] BAT: Add converting functions han.lu
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add function that generates sine waveform through math lib.
The waveform can be used as source for playback or analysis.

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/bat/signal.c b/bat/signal.c
new file mode 100644
index 0000000..1efb81e
--- /dev/null
+++ b/bat/signal.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdbool.h>
+
+#include "gettext.h"
+#include "common.h"
+
+void sinf_sign(struct bat *bat,
+		float *out, int length, int max, int *phase, float *val)
+{
+	int i = *phase, k, c, idx;
+	float factor = max * RANGE_FACTOR;
+
+	for (k = 0; k < length; k++) {
+		for (c = 0; c < bat->channels; c++) {
+			idx = k * bat->channels + c;
+			out[idx] = sinf(val[c] * i) * factor;
+		}
+		i++;
+		if (i == bat->rate)
+			i = 0; /* Restart from 0 after one sine wave period */
+	}
+	*phase = i;
+}
+
+void sinf_unsign(struct bat *bat,
+		float *out, int length, int max, int *phase, float *val)
+{
+	int i = *phase, k, c, idx;
+	float factor = max * RANGE_FACTOR / 2.0;
+	float offset = max * (1 - RANGE_FACTOR) / 2.0;
+
+	for (k = 0; k < length; k++) {
+		for (c = 0; c < bat->channels; c++) {
+			idx = k * bat->channels + c;
+			out[idx] = (sinf(val[c] * i) + 1.0) * factor
+				+ offset;
+		}
+		i++;
+		if (i == bat->rate)
+			i = 0; /* Restart from 0 after one sine wave period */
+	}
+	*phase = i;
+}
+
+int generate_sine_wave(struct bat *bat, int length, void *buf, int max)
+{
+	static int i;
+	int c, buf_bytes;
+	float sin_val[MAX_CHANNELS];
+	float *sinus_f = NULL;
+
+	buf_bytes = bat->channels * length;
+	sinus_f = (float *) malloc(buf_bytes * sizeof(float));
+	if (sinus_f == NULL) {
+		fprintf(bat->err, _("Not enough memory.\n"));
+		return -ENOMEM;
+	}
+
+	for (c = 0; c < bat->channels; c++)
+		sin_val[c] = 2.0 * M_PI * bat->target_freq[c]
+			/ (float) bat->rate;
+
+	bat->sinf_func(bat, sinus_f, length, max, &i, sin_val);
+
+	bat->convert_float_to_sample(sinus_f, buf, length, bat->channels);
+
+	free(sinus_f);
+
+	return 0;
+}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 5/7] BAT: Add converting functions
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (3 preceding siblings ...)
  2015-09-15  7:00 ` [PATCH BAT V1 4/7] BAT: Add signal generator han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 6/7] BAT: Add spectrum analysis functions han.lu
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add functions that converting audio samples to double data for analysis,
and functions that converting float data to audio samples for playback.

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/bat/convert.c b/bat/convert.c
new file mode 100644
index 0000000..dcbe912
--- /dev/null
+++ b/bat/convert.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+void convert_uint8_to_double(void *buf, double *val, int samples)
+{
+	int i;
+
+	for (i = 0; i < samples; i++)
+		val[i] = ((uint8_t *) buf)[i];
+}
+
+void convert_int16_to_double(void *buf, double *val, int samples)
+{
+	int i;
+
+	for (i = 0; i < samples; i++)
+		val[i] = ((int16_t *) buf)[i];
+}
+
+void convert_int24_to_double(void *buf, double *val, int samples)
+{
+	int i;
+	int32_t tmp;
+
+	for (i = 0; i < samples; i++) {
+		tmp = ((uint8_t *) buf)[i * 3 + 2] << 24;
+		tmp |= ((uint8_t *) buf)[i * 3 + 1] << 16;
+		tmp |= ((uint8_t *) buf)[i * 3] << 8;
+		tmp >>= 8;
+		val[i] = tmp;
+	}
+}
+
+void convert_int32_to_double(void *buf, double *val, int samples)
+{
+	int i;
+
+	for (i = 0; i < samples; i++)
+		val[i] = ((int32_t *) buf)[i];
+}
+
+void convert_float_to_uint8(float *val, void *buf, int samples, int channels)
+{
+	int i, c, idx;
+
+	for (i = 0; i < samples; i++) {
+		for (c = 0; c < channels; c++) {
+			idx = i * channels + c;
+			((uint8_t *) buf)[idx] = (uint8_t) val[idx];
+		}
+	}
+}
+
+void convert_float_to_int16(float *val, void *buf, int samples, int channels)
+{
+	int i, c, idx;
+
+	for (i = 0; i < samples; i++) {
+		for (c = 0; c < channels; c++) {
+			idx = i * channels + c;
+			((int16_t *) buf)[idx] = (int16_t) val[idx];
+		}
+	}
+}
+
+void convert_float_to_int24(float *val, void *buf, int samples, int channels)
+{
+	int i, c, idx_f, idx_i;
+	int32_t val_f_i;
+
+	for (i = 0; i < samples; i++) {
+		for (c = 0; c < channels; c++) {
+			idx_f = i * channels + c;
+			idx_i = 3 * idx_f;
+			val_f_i = (int32_t) val[idx_f];
+			((int8_t *) buf)[idx_i + 0] =
+				(int8_t) (val_f_i & 0xff);
+			((int8_t *) buf)[idx_i + 1] =
+				(int8_t) ((val_f_i >> 8) & 0xff);
+			((int8_t *) buf)[idx_i + 2] =
+				(int8_t) ((val_f_i >> 16) & 0xff);
+		}
+	}
+}
+
+void convert_float_to_int32(float *val, void *buf, int samples, int channels)
+{
+	int i, c, idx;
+
+	for (i = 0; i < samples; i++) {
+		for (c = 0; c < channels; c++) {
+			idx = i * channels + c;
+			((int32_t *) buf)[idx] = (int32_t) val[idx];
+		}
+	}
+}
diff --git a/bat/convert.h b/bat/convert.h
new file mode 100644
index 0000000..28828ba
--- /dev/null
+++ b/bat/convert.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+void convert_uint8_to_double(void *, double *, int);
+void convert_int16_to_double(void *, double *, int);
+void convert_int24_to_double(void *, double *, int);
+void convert_int32_to_double(void *, double *, int);
+void convert_float_to_uint8(float *, void *, int, int);
+void convert_float_to_int16(float *, void *, int, int);
+void convert_float_to_int24(float *, void *, int, int);
+void convert_float_to_int32(float *, void *, int, int);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 6/7] BAT: Add spectrum analysis functions
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (4 preceding siblings ...)
  2015-09-15  7:00 ` [PATCH BAT V1 5/7] BAT: Add converting functions han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-15  7:00 ` [PATCH BAT V1 7/7] BAT: Add Makefile and configures han.lu
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add functions that detecting signal frequency through spectrum
analyzing.

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/bat/analyze.c b/bat/analyze.c
new file mode 100644
index 0000000..ff04838
--- /dev/null
+++ b/bat/analyze.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <math.h>
+#include <fftw3.h>
+
+#include "aconfig.h"
+#include "gettext.h"
+
+#include "common.h"
+
+static void check_amplitude(struct bat *bat, double *buf)
+{
+	double sum, average, amplitude;
+	int i, percent;
+
+	/* calculate average value */
+	for (i = 0, sum = 0.0; i < bat->frames; i++)
+		sum += buf[i];
+	average = sum / bat->frames;
+
+	/* calculate peak-to-average amplitude */
+	for (i = 0, sum = 0.0; i < bat->frames; i++)
+		sum += abs(buf[i] - average);
+	amplitude = sum / bat->frames * M_PI / 2.0;
+
+	/* calculate amplitude percentage against full range */
+	percent = amplitude * 100 / ((1 << ((bat->sample_size << 3) - 1)) - 1);
+
+	fprintf(bat->log, _("Amplitude: %.1f; Percentage: [%d]\n"),
+			amplitude, percent);
+	if (percent < 0)
+		fprintf(bat->err, _("ERROR: Amplitude can't be negative!\n"));
+	else if (percent < 1)
+		fprintf(bat->err, _("WARNING: Signal too weak!\n"));
+	else if (percent > 100)
+		fprintf(bat->err, _("WARNING: Signal overflow!\n"));
+}
+
+/**
+ *
+ * @return 0 if peak detected at right frequency,
+ *         1 if peak detected somewhere else
+ *         2 if DC detected
+ */
+int check_peak(struct bat *bat, struct analyze *a, int end, int peak, float hz,
+		float mean, float p, int channel, int start)
+{
+	int err;
+	float hz_peak = (float) (peak) * hz;
+	float delta_rate = DELTA_RATE * bat->target_freq[channel];
+	float delta_HZ = DELTA_HZ;
+	float tolerance = (delta_rate > delta_HZ) ? delta_rate : delta_HZ;
+
+	fprintf(bat->log, _("Detected peak at %2.2f Hz of %2.2f dB\n"), hz_peak,
+			10.0 * log10(a->mag[peak] / mean));
+	fprintf(bat->log, _(" Total %3.1f dB from %2.2f to %2.2f Hz\n"),
+			10.0 * log10(p / mean), start * hz, end * hz);
+
+	if (hz_peak < DC_THRESHOLD) {
+		fprintf(bat->err, _(" WARNING: Found low peak %2.2f Hz,"),
+				hz_peak);
+		fprintf(bat->err, _(" very close to DC\n"));
+		err = FOUND_DC;
+	} else if (hz_peak < bat->target_freq[channel] - tolerance) {
+		fprintf(bat->err, _(" FAIL: Peak freq too low %2.2f Hz\n"),
+				hz_peak);
+		err = FOUND_WRONG_PEAK;
+	} else if (hz_peak > bat->target_freq[channel] + tolerance) {
+		fprintf(bat->err, _(" FAIL: Peak freq too high %2.2f Hz\n"),
+				hz_peak);
+		err = FOUND_WRONG_PEAK;
+	} else {
+		fprintf(bat->log, _(" PASS: Peak detected"));
+		fprintf(bat->log, _(" at target frequency\n"));
+		err = 0;
+	}
+
+	return err;
+}
+
+/**
+ * Search for main frequencies in fft results and compare it to target
+ */
+static int check(struct bat *bat, struct analyze *a, int channel)
+{
+	float hz = 1.0 / ((float) bat->frames / (float) bat->rate);
+	float mean = 0.0, t, sigma = 0.0, p = 0.0;
+	int i, start = -1, end = -1, peak = 0, signals = 0;
+	int err = 0, N = bat->frames / 2;
+
+	/* calculate mean */
+	for (i = 0; i < N; i++)
+		mean += a->mag[i];
+	mean /= (float) N;
+
+	/* calculate standard deviation */
+	for (i = 0; i < N; i++) {
+		t = a->mag[i] - mean;
+		t *= t;
+		sigma += t;
+	}
+	sigma /= (float) N;
+	sigma = sqrtf(sigma);
+
+	/* clip any data less than k sigma + mean */
+	for (i = 0; i < N; i++) {
+		if (a->mag[i] > mean + bat->sigma_k * sigma) {
+
+			/* find peak start points */
+			if (start == -1) {
+				start = peak = end = i;
+				signals++;
+			} else {
+				if (a->mag[i] > a->mag[peak])
+					peak = i;
+				end = i;
+			}
+			p += a->mag[i];
+		} else if (start != -1) {
+			/* Check if peak is as expected */
+			err |= check_peak(bat, a, end, peak, hz, mean,
+					p, channel, start);
+			end = start = -1;
+			if (signals == MAX_PEAKS)
+				break;
+		}
+	}
+	if (signals == 0)
+		err = -ENOPEAK; /* No peak detected */
+	else if ((err == FOUND_DC) && (signals == 1))
+		err = -EONLYDC; /* Only DC detected */
+	else if ((err & FOUND_WRONG_PEAK) == FOUND_WRONG_PEAK)
+		err = -EBADPEAK; /* Bad peak detected */
+	else
+		err = 0; /* Correct peak detected */
+
+	fprintf(bat->log, _("Detected at least %d signal(s) in total\n"),
+			signals);
+
+	return err;
+}
+
+static void calc_magnitude(struct bat *bat, struct analyze *a, int N)
+{
+	double r2, i2;
+	int i;
+
+	for (i = 1; i < N / 2; i++) {
+		r2 = a->out[i] * a->out[i];
+		i2 = a->out[N - i] * a->out[N - i];
+
+		a->mag[i] = sqrtf(r2 + i2);
+	}
+	a->mag[0] = 0.0;
+}
+
+static int find_and_check_harmonics(struct bat *bat, struct analyze *a,
+		int channel)
+{
+	fftw_plan p;
+	int err = -ENOMEM, N = bat->frames;
+
+	/* Allocate FFT buffers */
+	a->in = (double *) fftw_malloc(sizeof(double) * bat->frames);
+	if (a->in == NULL)
+		goto out1;
+
+	a->out = (double *) fftw_malloc(sizeof(double) * bat->frames);
+	if (a->out == NULL)
+		goto out2;
+
+	a->mag = (double *) fftw_malloc(sizeof(double) * bat->frames);
+	if (a->mag == NULL)
+		goto out3;
+
+	/* create FFT plan */
+	p = fftw_plan_r2r_1d(N, a->in, a->out, FFTW_R2HC,
+			FFTW_MEASURE | FFTW_PRESERVE_INPUT);
+	if (p == NULL)
+		goto out4;
+
+	/* convert source PCM to doubles */
+	bat->convert_sample_to_double(a->buf, a->in, bat->frames);
+
+	/* check amplitude */
+	check_amplitude(bat, a->in);
+
+	/* run FFT */
+	fftw_execute(p);
+
+	/* FFT out is real and imaginary numbers - calc magnitude for each */
+	calc_magnitude(bat, a, N);
+
+	/* check data */
+	err = check(bat, a, channel);
+
+	fftw_destroy_plan(p);
+
+out4:
+	fftw_free(a->mag);
+out3:
+	fftw_free(a->out);
+out2:
+	fftw_free(a->in);
+out1:
+	return err;
+}
+
+/**
+ * Convert interleaved samples from channels in samples from a single channel
+ */
+static int reorder_data(struct bat *bat)
+{
+	char *p, *new_bat_buf;
+	int ch, i, j;
+
+	if (bat->channels == 1)
+		return 0; /* No need for reordering */
+
+	p = malloc(bat->frames * bat->frame_size);
+	new_bat_buf = p;
+	if (p == NULL)
+		return -ENOMEM;
+
+	for (ch = 0; ch < bat->channels; ch++) {
+		for (j = 0; j < bat->frames; j++) {
+			for (i = 0; i < bat->sample_size; i++) {
+				*p++ = ((char *) (bat->buf))[j * bat->frame_size
+						+ ch * bat->sample_size + i];
+			}
+		}
+	}
+
+	free(bat->buf);
+	bat->buf = new_bat_buf;
+
+	return 0;
+}
+
+int analyze_capture(struct bat *bat)
+{
+	int err = 0;
+	size_t items;
+	int c;
+	struct analyze a;
+
+	fprintf(bat->log, _("\nBAT analyzes signal has %d frames at %d Hz,"),
+			bat->frames, bat->rate);
+	fprintf(bat->log, _(" %d channels, %d bytes per sample.\n"),
+			bat->channels, bat->sample_size);
+
+	bat->buf = malloc(bat->frames * bat->frame_size);
+	if (bat->buf == NULL)
+		return -ENOMEM;
+
+	bat->fp = fopen(bat->capture.file, "rb");
+	if (bat->fp == NULL) {
+		fprintf(bat->err, _("Cannot open file for capture: %s %d\n"),
+				bat->capture.file, -errno);
+		err = -errno;
+		goto exit1;
+	}
+
+	/* Skip header */
+	err = read_wav_header(bat, bat->capture.file, bat->fp, true);
+	if (err != 0)
+		goto exit2;
+
+	items = fread(bat->buf, bat->frame_size, bat->frames, bat->fp);
+	if (items != bat->frames) {
+		err = -EIO;
+		goto exit2;
+	}
+
+	err = reorder_data(bat);
+	if (err != 0)
+		goto exit2;
+
+	for (c = 0; c < bat->channels; c++) {
+		fprintf(bat->log, _("\nChannel %i - "), c + 1);
+		fprintf(bat->log, _("Checking for target frequency %2.2f Hz\n"),
+				bat->target_freq[c]);
+		a.buf = bat->buf +
+				c * bat->frames * bat->frame_size
+				/ bat->channels;
+		err = find_and_check_harmonics(bat, &a, c);
+	}
+
+exit2:
+	fclose(bat->fp);
+exit1:
+	free(bat->buf);
+
+	return err;
+}
diff --git a/bat/analyze.h b/bat/analyze.h
new file mode 100644
index 0000000..3fd03d4
--- /dev/null
+++ b/bat/analyze.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2013-2015 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+int analyze_capture(struct bat *);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH BAT V1 7/7] BAT: Add Makefile and configures
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (5 preceding siblings ...)
  2015-09-15  7:00 ` [PATCH BAT V1 6/7] BAT: Add spectrum analysis functions han.lu
@ 2015-09-15  7:00 ` han.lu
  2015-09-16  3:23 ` [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool Caleb Crome
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 26+ messages in thread
From: han.lu @ 2015-09-15  7:00 UTC (permalink / raw)
  To: tiwai, liam.r.girdwood, bernard.gautier, alsa-devel; +Cc: Lu, Han

From: "Lu, Han" <han.lu@intel.com>

Add Makefile and configures that enable BAT on alsa-utils

Signed-off-by: Lu, Han <han.lu@intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood@intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier@intel.com>

diff --git a/Makefile.am b/Makefile.am
index 613f62d..3d24b87 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,6 +18,9 @@ SUBDIRS += aplay iecset speaker-test
 if ALSALOOP
 SUBDIRS += alsaloop
 endif
+if BAT
+SUBDIRS += bat
+endif
 endif
 if HAVE_SEQ
 SUBDIRS += seq
diff --git a/bat/Makefile.am b/bat/Makefile.am
new file mode 100644
index 0000000..0849d0d
--- /dev/null
+++ b/bat/Makefile.am
@@ -0,0 +1,14 @@
+bin_PROGRAMS = bat
+
+bat_SOURCES = \
+	bat.c \
+	common.c \
+	analyze.c \
+	signal.c \
+	convert.c \
+	alsa.c
+
+AM_CPPFLAGS = \
+	      -Wall -I$(top_srcdir)/include
+
+bat_LDADD = -lasound
diff --git a/configure.ac b/configure.ac
index 4c279a9..8c2d1a5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,6 +63,23 @@ AM_CONDITIONAL(HAVE_UCM, test "$have_ucm" = "yes")
 AM_CONDITIONAL(HAVE_TOPOLOGY, test "$have_topology" = "yes")
 AM_CONDITIONAL(HAVE_SAMPLERATE, test "$have_samplerate" = "yes")
 
+AC_CHECK_LIB([fftw3], [fftw_malloc], , [AC_MSG_ERROR([Error: need FFTW3 library])])
+AC_CHECK_LIB([m], [sqrtf], , [AC_MSG_ERROR([Error: Need sqrtf])])
+AC_CHECK_LIB([pthread], [pthread_create], , [AC_MSG_ERROR([Error: need PTHREAD library])])
+
+dnl Disable bat
+bat=
+if test "$have_pcm" = "yes"; then
+AC_ARG_ENABLE(bat,
+     AS_HELP_STRING([--disable-bat], [Disable bat compilation]),
+     [case "${enableval}" in
+       yes) bat=true ;;
+       no)  bat=false ;;
+       *) AC_MSG_ERROR(bad value ${enableval} for --enable-bat) ;;
+     esac],[bat=true])
+fi
+AM_CONDITIONAL(BAT, test x$bat = xtrue)
+
 dnl Check for librt
 LIBRT=""
 AC_MSG_CHECKING(for librt)
@@ -361,7 +378,7 @@ AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
 	  m4/Makefile po/Makefile.in \
 	  alsaconf/alsaconf alsaconf/Makefile \
 	  alsaconf/po/Makefile \
-	  alsaucm/Makefile topology/Makefile \
+	  alsaucm/Makefile topology/Makefile bat/Makefile \
 	  aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \
 	  utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \
 	  seq/aplaymidi/Makefile seq/aseqdump/Makefile seq/aseqnet/Makefile \
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (6 preceding siblings ...)
  2015-09-15  7:00 ` [PATCH BAT V1 7/7] BAT: Add Makefile and configures han.lu
@ 2015-09-16  3:23 ` Caleb Crome
  2015-09-16 11:40   ` Liam Girdwood
  2015-09-16 16:37 ` Mark Brown
  2015-09-19 16:40 ` Takashi Iwai
  9 siblings, 1 reply; 26+ messages in thread
From: Caleb Crome @ 2015-09-16  3:23 UTC (permalink / raw)
  To: han.lu; +Cc: bernard.gautier, alsa-devel, tiwai, liam.r.girdwood

Hi Han,
  This is great.  Audio testing is something near and dear to my heart
:-)  Well, if not heart, at least my profession.  One test that we've
been using a long time (that I got from the Audio Anecdotes books) is
to do a ramp test.   In fact, we strongly suggest our customers
(almost require them) to complete the test successfully before trying
to integrate our software (high end mic array and acoustic echo
cancellation software).

So, the test is pretty simple:  Set up a hardware  (digital) loopback
between output and input.  Then send out a 'ramp', i.e. just keep a
counter that increments each frame, and send out the counter value out
to the audio port and watch the values that come back.  In our work
it's quite important that every sample come back bit-perfect with no
samples added or dropped.

Once this test is up and running, you then bang on the system to
ensure the audio stream doesn't break even under heavy load.

After the test has been up and running for some days without a bit
lost, then you can be pretty sure it's safe to integrate the AEC
software.

The benefit of the ramp test is that it's a 100% bit-perfect test.  No
analysis required, simple pass or fail The drawback is that it doesn't
work with analog loopback, which your test will.

For analog testing there may be other tests that could be useful too,
like an audio PLL plus comparator to test in the time domain or a
million other possibilities, but it depends on what failure modes
actually happen.

What kind of failure modes do we see in linux?    Do all failures end
up with underrun or overrun, or are there more subtle errors, like
frames lost or gained without the software knowing about it?  Or
perhaps whole blocks that get duplicated or deleted?

i.e. is the user level always notified of an error?

>From one of our customers that runs on Windows, it seems that there
are million ways Windows messes with audio and can wreck the audio
without telling you, but I haven't seen too many (or any) like that
with Linux?






On Tue, Sep 15, 2015 at 12:00 AM,  <han.lu@intel.com> wrote:
> From: "Lu, Han" <han.lu@intel.com>
>
> BAT (Basic Audio Tester) is a simple command line utility intended
> to automate audio driver and sound server QA testing with no human
> interaction.
>
> BAT works by playing an audio stream and capturing the same stream
> in either a digital or analog loopback. It then compares the captured
> stream to the original to determine if the testcase passes or fails.
>
> The main idea of frequency detecting is:
> The analysis function reads data from wav file, run fft against the data
> to get magnitude of frequency vectors, and then calculates the average
> value and standard deviation of frequency vectors. After that, we define
> a threshold:
>     threshold = 3 * standard_deviation + average_value
> Frequencies with amplitude larger than threshold will be recognized as
> a peak, and the frequency with largest peak value will be recognized as
> a detected frequency.
> BAT then compares the detected frequency to target frequency, to decide
> if the detecting passes or fails.
>
> BAT supports 4 working modes:
>   1. single line playback;
>   2. single line capture and analysis;
>   3. playback and capture in loop, and analyze captured data;
>   4. local analyze without actual playback or capture.
> BAT will check devices input by user to decide which mode to use.
> BAT will create threads for playback and record, and will run spectrum
> analysis for mode 2, 3 or 4.
>
> Lu, Han (7):
>   BAT: Add initial functions
>   BAT: Add common definitions and functions
>   BAT: Add playback and record functions
>   BAT: Add signal generator
>   BAT: Add converting functions
>   BAT: Add spectrum analysis functions
>   BAT: Add Makefile and configures
>
>  Makefile.am     |   3 +
>  bat/Makefile.am |  14 ++
>  bat/alsa.c      | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  bat/alsa.h      |  20 ++
>  bat/analyze.c   | 314 ++++++++++++++++++++++++++++
>  bat/analyze.h   |  16 ++
>  bat/bat.c       | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  bat/common.c    | 198 ++++++++++++++++++
>  bat/common.h    | 169 ++++++++++++++++
>  bat/convert.c   | 113 +++++++++++
>  bat/convert.h   |  23 +++
>  bat/signal.c    |  88 ++++++++
>  configure.ac    |  19 +-
>  13 files changed, 2202 insertions(+), 1 deletion(-)
>  create mode 100644 bat/Makefile.am
>  create mode 100644 bat/alsa.c
>  create mode 100644 bat/alsa.h
>  create mode 100644 bat/analyze.c
>  create mode 100644 bat/analyze.h
>  create mode 100644 bat/bat.c
>  create mode 100644 bat/common.c
>  create mode 100644 bat/common.h
>  create mode 100644 bat/convert.c
>  create mode 100644 bat/convert.h
>  create mode 100644 bat/signal.c
>
> --
> 1.9.1
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-16  3:23 ` [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool Caleb Crome
@ 2015-09-16 11:40   ` Liam Girdwood
  2015-09-16 16:43     ` Caleb Crome
  0 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-09-16 11:40 UTC (permalink / raw)
  To: Caleb Crome; +Cc: Takashi Iwai, han.lu, alsa-devel, Mark Brown, bernard.gautier

+ Mark and fix Takashi's email address

On Tue, 2015-09-15 at 20:23 -0700, Caleb Crome wrote:
> Hi Han,
>   This is great.  Audio testing is something near and dear to my heart
> :-)  Well, if not heart, at least my profession.  One test that we've
> been using a long time (that I got from the Audio Anecdotes books) is
> to do a ramp test.   In fact, we strongly suggest our customers
> (almost require them) to complete the test successfully before trying
> to integrate our software (high end mic array and acoustic echo
> cancellation software).
> 
> So, the test is pretty simple:  Set up a hardware  (digital) loopback
> between output and input.  Then send out a 'ramp', i.e. just keep a
> counter that increments each frame, and send out the counter value out
> to the audio port and watch the values that come back.  In our work
> it's quite important that every sample come back bit-perfect with no
> samples added or dropped.
> 
> Once this test is up and running, you then bang on the system to
> ensure the audio stream doesn't break even under heavy load.
> 
> After the test has been up and running for some days without a bit
> lost, then you can be pretty sure it's safe to integrate the AEC
> software.
> 
> The benefit of the ramp test is that it's a 100% bit-perfect test.  No
> analysis required, simple pass or fail The drawback is that it doesn't
> work with analog loopback, which your test will.
> 

But this would be a good test to add into BAT too :) There are many DSPs
and codecs that can do digital loopback so we should look at adding this
to BAT too.

> For analog testing there may be other tests that could be useful too,
> like an audio PLL plus comparator to test in the time domain or a
> million other possibilities, but it depends on what failure modes
> actually happen.
> 

We also have some plans for testing gain changes and channel mappings.
Fwiw, during our testing it did pick up some driver bugs with incorrect
BIAS setting (signal went to DC after 10 hours of testing) and filter
settings (test failed at certain sine wave frequencies). We have also
used BAT for stress testing audio over D0 -> D3 -> D0 transitions.

> What kind of failure modes do we see in linux?    Do all failures end
> up with underrun or overrun, or are there more subtle errors, like
> frames lost or gained without the software knowing about it?  Or
> perhaps whole blocks that get duplicated or deleted?
> 
> i.e. is the user level always notified of an error?
> 

No, not for every error. Userspace usually does not know about FIFO
underruns/overrruns. It does know about DMA overrruns/underruns though.
Userspace mainly has some difficulty with errors caused in the analog
domain.


> From one of our customers that runs on Windows, it seems that there
> are million ways Windows messes with audio and can wreck the audio
> without telling you, but I haven't seen too many (or any) like that
> with Linux?
> 

I'm sure there are ways for us to do this in Linux too ;)

Liam

> 
> 
> 
> 
> On Tue, Sep 15, 2015 at 12:00 AM,  <han.lu@intel.com> wrote:
> > From: "Lu, Han" <han.lu@intel.com>
> >
> > BAT (Basic Audio Tester) is a simple command line utility intended
> > to automate audio driver and sound server QA testing with no human
> > interaction.
> >
> > BAT works by playing an audio stream and capturing the same stream
> > in either a digital or analog loopback. It then compares the captured
> > stream to the original to determine if the testcase passes or fails.
> >
> > The main idea of frequency detecting is:
> > The analysis function reads data from wav file, run fft against the data
> > to get magnitude of frequency vectors, and then calculates the average
> > value and standard deviation of frequency vectors. After that, we define
> > a threshold:
> >     threshold = 3 * standard_deviation + average_value
> > Frequencies with amplitude larger than threshold will be recognized as
> > a peak, and the frequency with largest peak value will be recognized as
> > a detected frequency.
> > BAT then compares the detected frequency to target frequency, to decide
> > if the detecting passes or fails.
> >
> > BAT supports 4 working modes:
> >   1. single line playback;
> >   2. single line capture and analysis;
> >   3. playback and capture in loop, and analyze captured data;
> >   4. local analyze without actual playback or capture.
> > BAT will check devices input by user to decide which mode to use.
> > BAT will create threads for playback and record, and will run spectrum
> > analysis for mode 2, 3 or 4.
> >
> > Lu, Han (7):
> >   BAT: Add initial functions
> >   BAT: Add common definitions and functions
> >   BAT: Add playback and record functions
> >   BAT: Add signal generator
> >   BAT: Add converting functions
> >   BAT: Add spectrum analysis functions
> >   BAT: Add Makefile and configures
> >
> >  Makefile.am     |   3 +
> >  bat/Makefile.am |  14 ++
> >  bat/alsa.c      | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  bat/alsa.h      |  20 ++
> >  bat/analyze.c   | 314 ++++++++++++++++++++++++++++
> >  bat/analyze.h   |  16 ++
> >  bat/bat.c       | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  bat/common.c    | 198 ++++++++++++++++++
> >  bat/common.h    | 169 ++++++++++++++++
> >  bat/convert.c   | 113 +++++++++++
> >  bat/convert.h   |  23 +++
> >  bat/signal.c    |  88 ++++++++
> >  configure.ac    |  19 +-
> >  13 files changed, 2202 insertions(+), 1 deletion(-)
> >  create mode 100644 bat/Makefile.am
> >  create mode 100644 bat/alsa.c
> >  create mode 100644 bat/alsa.h
> >  create mode 100644 bat/analyze.c
> >  create mode 100644 bat/analyze.h
> >  create mode 100644 bat/bat.c
> >  create mode 100644 bat/common.c
> >  create mode 100644 bat/common.h
> >  create mode 100644 bat/convert.c
> >  create mode 100644 bat/convert.h
> >  create mode 100644 bat/signal.c
> >
> > --
> > 1.9.1
> >
> > _______________________________________________
> > Alsa-devel mailing list
> > Alsa-devel@alsa-project.org
> > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (7 preceding siblings ...)
  2015-09-16  3:23 ` [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool Caleb Crome
@ 2015-09-16 16:37 ` Mark Brown
  2015-09-16 16:57   ` Liam Girdwood
  2015-09-19 16:40 ` Takashi Iwai
  9 siblings, 1 reply; 26+ messages in thread
From: Mark Brown @ 2015-09-16 16:37 UTC (permalink / raw)
  To: han.lu; +Cc: bernard.gautier, alsa-devel, tiwai, liam.r.girdwood


[-- Attachment #1.1: Type: text/plain, Size: 758 bytes --]

On Tue, Sep 15, 2015 at 03:00:16PM +0800, han.lu@intel.com wrote:

> BAT works by playing an audio stream and capturing the same stream
> in either a digital or analog loopback. It then compares the captured
> stream to the original to determine if the testcase passes or fails.

This is really cool, sounds like there's some overlap with the tool
David wrote to verify TLV information?  Actually in that vein one thing
I started looking at but parked until this got posted is something that
goes through and tries to stress test control value settings (trying to
set out of bounds values, make sure all valid settings can be used and
so on).  That's probably better kept as a separate tool I think given
that it doesn't really overlap with audio streaming.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-16 11:40   ` Liam Girdwood
@ 2015-09-16 16:43     ` Caleb Crome
  2015-09-17 10:40       ` Liam Girdwood
  0 siblings, 1 reply; 26+ messages in thread
From: Caleb Crome @ 2015-09-16 16:43 UTC (permalink / raw)
  To: Liam Girdwood
  Cc: Takashi Iwai, han.lu, alsa-devel, Mark Brown, bernard.gautier

;; This buffer is for notes you don't want to save, and for Lisp evaluation.
;; If you want to create a file, visit that file with C-x C-f,
;; then enter the text in that file's own buffer.

>> So, the test is pretty simple:  Set up a hardware  (digital) loopback
>> between output and input.  Then send out a 'ramp', i.e. just keep a
>> counter that increments each frame, and send out the counter value out
>> to the audio port and watch the values that come back.  In our work
>> it's quite important that every sample come back bit-perfect with no
>> samples added or dropped.
>>
>> Once this test is up and running, you then bang on the system to
>> ensure the audio stream doesn't break even under heavy load.
>>
>>
>> The benefit of the ramp test is that it's a 100% bit-perfect test.
> But this would be a good test to add into BAT too :) There are many DSPs
> and codecs that can do digital loopback so we should look at adding this
> to BAT too.

Yep, many don't as well.  In those cases we recommend that there is a
resistor stuffing option on the PCB to allow for digital loopback.

>
> We also have some plans for testing gain changes and channel mappings.
> Fwiw, during our testing it did pick up some driver bugs with incorrect
> BIAS setting (signal went to DC after 10 hours of testing)

Ah Ha!  So I'm not crazy asking our customers to run for days!  :-)  I
knew there was good reason for that.

> We have also used BAT for stress testing audio over D0 -> D3 -> D0 transitions.

Sorry, I'm not familiar with that terminology.  Are those the
different power states?

>
>> What kind of failure modes do we see in linux?    Do all failures end
>> up with underrun or overrun, or are there more subtle errors, like
>> frames lost or gained without the software knowing about it?  Or
>> perhaps whole blocks that get duplicated or deleted?
>>
>> i.e. is the user level always notified of an error?
>>
>
> No, not for every error. Userspace usually does not know about FIFO
> underruns/overrruns. It does know about DMA overrruns/underruns though.
> Userspace mainly has some difficulty with errors caused in the analog
> domain.


So, it sounds to me like there are definitely several tests that would
be useful, both for digital loopback testing and analog loopback
testing.

Since we run microphone arrays, it's imperative that all the ADC
channels are perfectly time-aligned.  It's definitely non-trivial to
do that on some codecs, and impossible on others.  It's definitely not
good enough to just 'start the codecs at the same time' because that's
not well defined on some codecs.

Here are some of the tests I can think of:

Digital Loopback
---------------------------------
Setup:  Either configure the serial port or PCB for digital loopback
        form out->in.

Test:  Ramp test.   This will do bit-perfect, very simple, very
reliable test that when passed will guarantee:
* No samples lost/added/duplicated.
* Show latency from out -> in.  This should be the SAME latency at
  every startup under all load conditions.
* This is essentially the gold standard test for Linux+Driver+serial
  port configuration.
* Does not test codec startup/phase alignment.

Analog Loopback
--------------------------------
Setup: Loop back one output to all inputs (up to 32 on my board!) in
the analog domain.

Test:  Impulse response.  There are many ways to measure impulse
       responses.  I can help with this :-)  It's something I do quite
       often.
The impulse response test will let us gather:
   * Frequency response of analog system, and compare channel-to-channel
     component matching.
   * Phase response of the analog system, which will let us see any
     component matching issues, but more importantly, codec
startup/phase issues.
   * Can also show latency from out->in
   * does test codec phase alignment

Test: Time domain testy thingy.
   * This is one that we don't normally do because its not needed if you
     do the digital ramp test, but would be useful for finding
     single/few sample defects in systems that can't do digital
     loopback.
   * Run a sine wave out
   * use a (software) PLL to lock on to the received sine wave.  This
     PLL will lock on to frequency, phase, and magnitude (essentially
     a 2 tap adaptive phasor).  Then you simply use the PLL output
     to subtract from the input signal, and you should end up with no
     signal, but just noise.  If you get any pops or samples higher
     than the expected noise floor, then you know you have a problem.
   * Definitely an imperfect test, especially if the Fs/2 is much
     higher than the analog bandwidth. (i.e. 20kHz analog bandwidth,
     but 96kHz sampling), but it's not terrible.


If you're unfamiliar with the codec time alignment problem, the issue
is that codecs don't use the WCLK as the sample-time source, but
rather some number of MCLKs from when they turn on.  In my system I
have to turn on and configure up to 16 stereo codecs, but if MCLK is
running during configuration, there is literally no way to get all the
codecs to start at once because they are turned on via I2C.  So, on
the TLV320AIC3x series of parts the way to do it is:  Make sure MCLK
is turned off, then configure and enable all the codecs.  Then, and
only then, do you turn the MCLK clock on.  That way all codecs start
in synchrony and are cycle-locked together.

If you wish to use a PLL on the AIC3x, the problem gets even trickier
-- you can't use the PLL for multiple codecs unless you run the source
clock in on GPIO2, and the PLL output (MCLK) out on GPIO1, then to all
the codecs in the system (include the PLL).  It takes a bit of time
staring at the datasheet to work that out :-/

On the CS53L30 (4 channel ADC), there is a dedicated signal to
synchronize multiple codec chips, so the startup is much easier, but
still requires thought.

I've seen at least one codec with 3 stereo ADCs on-chip with literally
no way to synchronize them.  The vendor's response was... "use another
part".  Unfortunately, I can't remember which part it was that had the
problem.  Just beware if you're doing multi-channel arrays!

Anyway, in summary, the tests I'm thinking of are:
* Ramp test:  the gold standard for driver/SoC testing.
* Impulse response test:  the gold standard for phase/alignment
  issues.
* Time domain testy thingy:  a less good ramp test if you can't manage
  digital loopback in your system.

In addition, maybe there should be a 'load' parameter in the test tool
to add system load until things start breaking.

I wonder, will these tests capture all failure modes?

-Caleb

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-16 16:37 ` Mark Brown
@ 2015-09-16 16:57   ` Liam Girdwood
  2015-09-16 20:34     ` Mark Brown
  0 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-09-16 16:57 UTC (permalink / raw)
  To: Mark Brown; +Cc: han.lu, alsa-devel, tiwai, bernard.gautier

On Wed, 2015-09-16 at 17:37 +0100, Mark Brown wrote:
> On Tue, Sep 15, 2015 at 03:00:16PM +0800, han.lu@intel.com wrote:
> 
> > BAT works by playing an audio stream and capturing the same stream
> > in either a digital or analog loopback. It then compares the captured
> > stream to the original to determine if the testcase passes or fails.
> 
> This is really cool, sounds like there's some overlap with the tool
> David wrote to verify TLV information?  Actually in that vein one thing
> I started looking at but parked until this got posted is something that
> goes through and tries to stress test control value settings (trying to
> set out of bounds values, make sure all valid settings can be used and
> so on).  That's probably better kept as a separate tool I think given
> that it doesn't really overlap with audio streaming.

I didn't think of bounds testing the controls :) Especially useful for
DSPs where we could overflow or crash FW if certain combinations of
enumerated controls are used.

We were looking at testing the gains too, to make sure that a 6dB
control gain really does mean a 6dB gain in signal. It's in the TODO
list. Most of the code is already there to do this, but we were looking
to upstream first and then incrementally add features. I think Han plans
to upstream the tinyalsa support after the initial code is upstream
though.

Liam

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-16 16:57   ` Liam Girdwood
@ 2015-09-16 20:34     ` Mark Brown
  0 siblings, 0 replies; 26+ messages in thread
From: Mark Brown @ 2015-09-16 20:34 UTC (permalink / raw)
  To: Liam Girdwood; +Cc: han.lu, alsa-devel, tiwai, bernard.gautier


[-- Attachment #1.1: Type: text/plain, Size: 728 bytes --]

On Wed, Sep 16, 2015 at 05:57:00PM +0100, Liam Girdwood wrote:

> I didn't think of bounds testing the controls :) Especially useful for
> DSPs where we could overflow or crash FW if certain combinations of
> enumerated controls are used.

I'll try to get onto it again in my copious free time.  :/

> We were looking at testing the gains too, to make sure that a 6dB
> control gain really does mean a 6dB gain in signal. It's in the TODO
> list. Most of the code is already there to do this, but we were looking
> to upstream first and then incrementally add features. I think Han plans
> to upstream the tinyalsa support after the initial code is upstream
> though.

Yeah, definitely not something needed for initial release.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-16 16:43     ` Caleb Crome
@ 2015-09-17 10:40       ` Liam Girdwood
  2015-09-17 13:29         ` Caleb Crome
  0 siblings, 1 reply; 26+ messages in thread
From: Liam Girdwood @ 2015-09-17 10:40 UTC (permalink / raw)
  To: Caleb Crome; +Cc: Takashi Iwai, han.lu, alsa-devel, Mark Brown, bernard.gautier


On Wed, 2015-09-16 at 09:43 -0700, Caleb Crome wrote:
> ;; This buffer is for notes you don't want to save, and for Lisp evaluation.
> ;; If you want to create a file, visit that file with C-x C-f,
> ;; then enter the text in that file's own buffer.
> 
> >> So, the test is pretty simple:  Set up a hardware  (digital) loopback
> >> between output and input.  Then send out a 'ramp', i.e. just keep a
> >> counter that increments each frame, and send out the counter value out
> >> to the audio port and watch the values that come back.  In our work
> >> it's quite important that every sample come back bit-perfect with no
> >> samples added or dropped.
> >>
> >> Once this test is up and running, you then bang on the system to
> >> ensure the audio stream doesn't break even under heavy load.
> >>
> >>
> >> The benefit of the ramp test is that it's a 100% bit-perfect test.
> > But this would be a good test to add into BAT too :) There are many DSPs
> > and codecs that can do digital loopback so we should look at adding this
> > to BAT too.
> 
> Yep, many don't as well.  In those cases we recommend that there is a
> resistor stuffing option on the PCB to allow for digital loopback.
> 
> >
> > We also have some plans for testing gain changes and channel mappings.
> > Fwiw, during our testing it did pick up some driver bugs with incorrect
> > BIAS setting (signal went to DC after 10 hours of testing)
> 
> Ah Ha!  So I'm not crazy asking our customers to run for days!  :-)  I
> knew there was good reason for that.
> 
> > We have also used BAT for stress testing audio over D0 -> D3 -> D0 transitions.
> 
> Sorry, I'm not familiar with that terminology.  Are those the
> different power states?

Yes, PM suspend and resume of device. We have some scripts (to be
upstreamed later) that would play audio, suspend the device, resume the
device and then retest the audio.

> 
> >
> >> What kind of failure modes do we see in linux?    Do all failures end
> >> up with underrun or overrun, or are there more subtle errors, like
> >> frames lost or gained without the software knowing about it?  Or
> >> perhaps whole blocks that get duplicated or deleted?
> >>
> >> i.e. is the user level always notified of an error?
> >>
> >
> > No, not for every error. Userspace usually does not know about FIFO
> > underruns/overrruns. It does know about DMA overrruns/underruns though.
> > Userspace mainly has some difficulty with errors caused in the analog
> > domain.
> 
> 
> So, it sounds to me like there are definitely several tests that would
> be useful, both for digital loopback testing and analog loopback
> testing.
> 
> Since we run microphone arrays, it's imperative that all the ADC
> channels are perfectly time-aligned.  It's definitely non-trivial to
> do that on some codecs, and impossible on others.  It's definitely not
> good enough to just 'start the codecs at the same time' because that's
> not well defined on some codecs.
> 
> Here are some of the tests I can think of:
> 
> Digital Loopback
> ---------------------------------
> Setup:  Either configure the serial port or PCB for digital loopback
>         form out->in.
> 
> Test:  Ramp test.   This will do bit-perfect, very simple, very
> reliable test that when passed will guarantee:
> * No samples lost/added/duplicated.
> * Show latency from out -> in.  This should be the SAME latency at
>   every startup under all load conditions.
> * This is essentially the gold standard test for Linux+Driver+serial
>   port configuration.
> * Does not test codec startup/phase alignment.
> 
> Analog Loopback
> --------------------------------
> Setup: Loop back one output to all inputs (up to 32 on my board!) in
> the analog domain.
> 
> Test:  Impulse response.  There are many ways to measure impulse
>        responses.  I can help with this :-)  It's something I do quite
>        often.

Thanks, any help to add new tests is appreciated !

> The impulse response test will let us gather:
>    * Frequency response of analog system, and compare channel-to-channel
>      component matching.
>    * Phase response of the analog system, which will let us see any
>      component matching issues, but more importantly, codec
> startup/phase issues.
>    * Can also show latency from out->in
>    * does test codec phase alignment
> 
> Test: Time domain testy thingy.
>    * This is one that we don't normally do because its not needed if you
>      do the digital ramp test, but would be useful for finding
>      single/few sample defects in systems that can't do digital
>      loopback.
>    * Run a sine wave out
>    * use a (software) PLL to lock on to the received sine wave.  This
>      PLL will lock on to frequency, phase, and magnitude (essentially
>      a 2 tap adaptive phasor).  Then you simply use the PLL output
>      to subtract from the input signal, and you should end up with no
>      signal, but just noise.  If you get any pops or samples higher
>      than the expected noise floor, then you know you have a problem.
>    * Definitely an imperfect test, especially if the Fs/2 is much
>      higher than the analog bandwidth. (i.e. 20kHz analog bandwidth,
>      but 96kHz sampling), but it's not terrible.
> 
> 
> If you're unfamiliar with the codec time alignment problem, the issue
> is that codecs don't use the WCLK as the sample-time source, but
> rather some number of MCLKs from when they turn on.  In my system I
> have to turn on and configure up to 16 stereo codecs, but if MCLK is
> running during configuration, there is literally no way to get all the
> codecs to start at once because they are turned on via I2C.  So, on
> the TLV320AIC3x series of parts the way to do it is:  Make sure MCLK
> is turned off, then configure and enable all the codecs.  Then, and
> only then, do you turn the MCLK clock on.  That way all codecs start
> in synchrony and are cycle-locked together.
> 
> If you wish to use a PLL on the AIC3x, the problem gets even trickier
> -- you can't use the PLL for multiple codecs unless you run the source
> clock in on GPIO2, and the PLL output (MCLK) out on GPIO1, then to all
> the codecs in the system (include the PLL).  It takes a bit of time
> staring at the datasheet to work that out :-/
> 
> On the CS53L30 (4 channel ADC), there is a dedicated signal to
> synchronize multiple codec chips, so the startup is much easier, but
> still requires thought.
> 
> I've seen at least one codec with 3 stereo ADCs on-chip with literally
> no way to synchronize them.  The vendor's response was... "use another
> part".  Unfortunately, I can't remember which part it was that had the
> problem.  Just beware if you're doing multi-channel arrays!
> 
> Anyway, in summary, the tests I'm thinking of are:
> * Ramp test:  the gold standard for driver/SoC testing.
> * Impulse response test:  the gold standard for phase/alignment
>   issues.
> * Time domain testy thingy:  a less good ramp test if you can't manage
>   digital loopback in your system.

This sounds great. Lets get the first part upstream and then we can
incrementally add the other tests. Btw, are you going to the audio mini
conf at ELCE in Dublin ? It would be good to discuss this in person !

> 
> In addition, maybe there should be a 'load' parameter in the test tool
> to add system load until things start breaking.
> 

That's a good idea too. Ideally we would want to stress both IO load and
CPU load independently and together. 

> I wonder, will these tests capture all failure modes?
> 

There may always be a few corner cases that are missed, but I would
imagine that most cases would be covered.

Liam

> -Caleb
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 10:40       ` Liam Girdwood
@ 2015-09-17 13:29         ` Caleb Crome
  2015-09-17 17:42           ` Mark Brown
  2015-09-18  9:09           ` Liam Girdwood
  0 siblings, 2 replies; 26+ messages in thread
From: Caleb Crome @ 2015-09-17 13:29 UTC (permalink / raw)
  To: Liam Girdwood
  Cc: Takashi Iwai, han.lu, alsa-devel, Mark Brown, bernard.gautier

>> > We have also used BAT for stress testing audio over D0 -> D3 -> D0 transitions.
>>
>> Sorry, I'm not familiar with that terminology.  Are those the
>> different power states?
>
> Yes, PM suspend and resume of device. We have some scripts (to be
> upstreamed later) that would play audio, suspend the device, resume the
> device and then retest the audio.

That seems particularly likely to result in multiple codecs getting
out of phase alignment.  i.e. when restarting, you want to be sure to
follow the same startup procedure as the first time to make sure all
codecs are aligned.

>>
>> Anyway, in summary, the tests I'm thinking of are:
>> * Ramp test:  the gold standard for driver/SoC testing.
>> * Impulse response test:  the gold standard for phase/alignment
>>   issues.
>> * Time domain testy thingy:  a less good ramp test if you can't manage
>>   digital loopback in your system.
>
> This sounds great. Lets get the first part upstream and then we can
> incrementally add the other tests. Btw, are you going to the audio mini
> conf at ELCE in Dublin ? It would be good to discuss this in person !
>

No, not planning to go to Dublin, though it's not a bad idea.  I'm not
sure I can justify the trip there (I'm in California).  My day job is
to be a DSP/Acoustical/Electrical engineer, and I'm only hacking on
the kernel because we need to get it working :-).  Which I guess is
also my day job now too.  Make that DSP/Acoustical/Electrical/Hack
engineer.

We can always chat via Skype or phone, though I agree, there's nothing
like talking face to face.

> Liam

-Caleb

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 13:29         ` Caleb Crome
@ 2015-09-17 17:42           ` Mark Brown
  2015-09-17 18:50             ` Caleb Crome
  2015-09-18  9:09           ` Liam Girdwood
  1 sibling, 1 reply; 26+ messages in thread
From: Mark Brown @ 2015-09-17 17:42 UTC (permalink / raw)
  To: Caleb Crome
  Cc: Liam Girdwood, Takashi Iwai, alsa-devel, bernard.gautier, han.lu


[-- Attachment #1.1: Type: text/plain, Size: 548 bytes --]

On Thu, Sep 17, 2015 at 06:29:58AM -0700, Caleb Crome wrote:

> > Yes, PM suspend and resume of device. We have some scripts (to be
> > upstreamed later) that would play audio, suspend the device, resume the
> > device and then retest the audio.

> That seems particularly likely to result in multiple codecs getting
> out of phase alignment.  i.e. when restarting, you want to be sure to
> follow the same startup procedure as the first time to make sure all
> codecs are aligned.

Why should resume be any different from initial system power up?

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 17:42           ` Mark Brown
@ 2015-09-17 18:50             ` Caleb Crome
  2015-09-17 22:40               ` Caleb Crome
  0 siblings, 1 reply; 26+ messages in thread
From: Caleb Crome @ 2015-09-17 18:50 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Takashi Iwai, alsa-devel, bernard.gautier, han.lu

On Thu, Sep 17, 2015 at 10:42 AM, Mark Brown <broonie@kernel.org> wrote:
> On Thu, Sep 17, 2015 at 06:29:58AM -0700, Caleb Crome wrote:
>
>> > Yes, PM suspend and resume of device. We have some scripts (to be
>> > upstreamed later) that would play audio, suspend the device, resume the
>> > device and then retest the audio.
>
>> That seems particularly likely to result in multiple codecs getting
>> out of phase alignment.  i.e. when restarting, you want to be sure to
>> follow the same startup procedure as the first time to make sure all
>> codecs are aligned.
>
> Why should resume be any different from initial system power up?

Heh, perhaps only because I don't understand how it all works :-)

The example I was thinking of is if the card gets partially powered
down.  For example, start recording on 8 channels (4 codecs, called 0,
1, 2, 3 in this example).  At this point, all's well, and all 4 codecs
are in perfect phase sync.

Then switch to playing 2 channel mode with codec 0 only.  Since codecs
1, 2, 3 are no longer needed, then they could get powered down.

Then, switch back to 8 channel mode -- codec 0 is up and running, and
now you want to turn on codecs 1, 2, 3.  At this point it's literally
impossible to resync them with codec 0 without powering down codec 0
and starting from scratch.  This is true of the TLV320AIC seris of
codecs.  The CS53L30 has a dedicated sync pin on the other hand, so
could perhaps recover gracefully in this situation.

-Caleb

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 18:50             ` Caleb Crome
@ 2015-09-17 22:40               ` Caleb Crome
  2015-09-17 23:00                 ` James Cameron
  0 siblings, 1 reply; 26+ messages in thread
From: Caleb Crome @ 2015-09-17 22:40 UTC (permalink / raw)
  To: Mark Brown
  Cc: Liam Girdwood, Takashi Iwai, alsa-devel, bernard.gautier, han.lu

The more I think of this channel synchronization issue, the thorinier it gets.

Basically, with the TLV320AIC series of codecs, whenever any of the
DAC or ADC channels are powered up on any codec, the MCLK needs to be
stopped, all DACs/ADCs turned off, then configuration can happen, then
all DACs/ADCs can be turned back on, then MCLK starts again.  That's
the only way to ensure phase/delay accuracy among several codecs.

So, going from play-only to play+record requires MCLK be stopped, then
restarted.

Maybe it's not too terrible.  Any thoughts?

-Caleb



On Thu, Sep 17, 2015 at 11:50 AM, Caleb Crome <caleb@crome.org> wrote:
> On Thu, Sep 17, 2015 at 10:42 AM, Mark Brown <broonie@kernel.org> wrote:
>> On Thu, Sep 17, 2015 at 06:29:58AM -0700, Caleb Crome wrote:
>>
>>> > Yes, PM suspend and resume of device. We have some scripts (to be
>>> > upstreamed later) that would play audio, suspend the device, resume the
>>> > device and then retest the audio.
>>
>>> That seems particularly likely to result in multiple codecs getting
>>> out of phase alignment.  i.e. when restarting, you want to be sure to
>>> follow the same startup procedure as the first time to make sure all
>>> codecs are aligned.
>>
>> Why should resume be any different from initial system power up?
>
> Heh, perhaps only because I don't understand how it all works :-)
>
> The example I was thinking of is if the card gets partially powered
> down.  For example, start recording on 8 channels (4 codecs, called 0,
> 1, 2, 3 in this example).  At this point, all's well, and all 4 codecs
> are in perfect phase sync.
>
> Then switch to playing 2 channel mode with codec 0 only.  Since codecs
> 1, 2, 3 are no longer needed, then they could get powered down.
>
> Then, switch back to 8 channel mode -- codec 0 is up and running, and
> now you want to turn on codecs 1, 2, 3.  At this point it's literally
> impossible to resync them with codec 0 without powering down codec 0
> and starting from scratch.  This is true of the TLV320AIC seris of
> codecs.  The CS53L30 has a dedicated sync pin on the other hand, so
> could perhaps recover gracefully in this situation.
>
> -Caleb

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 22:40               ` Caleb Crome
@ 2015-09-17 23:00                 ` James Cameron
  2015-09-17 23:30                   ` Caleb Crome
  0 siblings, 1 reply; 26+ messages in thread
From: James Cameron @ 2015-09-17 23:00 UTC (permalink / raw)
  To: Caleb Crome
  Cc: bernard.gautier, alsa-devel, Takashi Iwai, Liam Girdwood,
	Mark Brown, han.lu

On Thu, Sep 17, 2015 at 03:40:33PM -0700, Caleb Crome wrote:
> The more I think of this channel synchronization issue, the
> thorinier it gets.
> 
> Basically, with the TLV320AIC series of codecs, whenever any of the
> DAC or ADC channels are powered up on any codec, the MCLK needs to be
> stopped, all DACs/ADCs turned off, then configuration can happen, then
> all DACs/ADCs can be turned back on, then MCLK starts again.  That's
> the only way to ensure phase/delay accuracy among several codecs.
> 
> So, going from play-only to play+record requires MCLK be stopped, then
> restarted.

Aye.  Lots of clocks in lots of domains.  Also pay penalty of any
delays needed to stabilise ADCs.

Same general issue with many single codecs though, if capture vs
playback synchronisation required.

> Maybe it's not too terrible.  Any thoughts?

In this scenario my preference would be to always configure the codecs
and DMA for playback and capture, and do any gating higher in the
stack; e.g. send zero samples if there is no playback stream, and
discard samples if there is no capture stream.

-- 
James Cameron
http://quozl.linux.org.au/

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 23:00                 ` James Cameron
@ 2015-09-17 23:30                   ` Caleb Crome
  0 siblings, 0 replies; 26+ messages in thread
From: Caleb Crome @ 2015-09-17 23:30 UTC (permalink / raw)
  To: James Cameron
  Cc: bernard.gautier, alsa-devel, Takashi Iwai, Liam Girdwood,
	Mark Brown, han.lu

On Thu, Sep 17, 2015 at 4:00 PM, James Cameron <quozl@laptop.org> wrote:
> On Thu, Sep 17, 2015 at 03:40:33PM -0700, Caleb Crome wrote:
>> The more I think of this channel synchronization issue, the
>> thorinier it gets.
>>
>> Basically, with the TLV320AIC series of codecs, whenever any of the
>> DAC or ADC channels are powered up on any codec, the MCLK needs to be
>> stopped, all DACs/ADCs turned off, then configuration can happen, then
>> all DACs/ADCs can be turned back on, then MCLK starts again.  That's
>> the only way to ensure phase/delay accuracy among several codecs.
>>
>> So, going from play-only to play+record requires MCLK be stopped, then
>> restarted.
>
> Aye.  Lots of clocks in lots of domains.  Also pay penalty of any
> delays needed to stabilise ADCs.
>
> Same general issue with many single codecs though, if capture vs
> playback synchronisation required.
>
>> Maybe it's not too terrible.  Any thoughts?
>
> In this scenario my preference would be to always configure the codecs
> and DMA for playback and capture, and do any gating higher in the
> stack; e.g. send zero samples if there is no playback stream, and
> discard samples if there is no capture stream.

Yes, this makes sense to me.  This is for this sort of special case
that I have where we have many synchronized codecs on the same bus.
It's really just 1 clock that needs to be gated -- the MCLK.

Is there any way for the machine driver to handle this?  Currently,
the AIC3x driver uses a dapm control to turn the ADCs and DACs on and
off.  Can the machine driver currently get some sort of pre and post
callback whenever particular controls are enabled?

I think to get through the day, I need to remove the
SND_SOC_DAPM_ADC("Left ADC", ...)
SND_SOC_DAPM_ADC("Right ADC", ...)
SND_SOC_DAPM_DAC("Left ...")
SND_SOC_DAPM_DAC("Right"
lines in tlv320aic3x, and just hard-wire them somewhere so they are
all always turned on.  Then I can make sure to turn on the clocks only
after all the codecs have been powered up and alsa thinks they are
configured.  I guess in the machine driver's trigger callback?

-Caleb








>
> --
> James Cameron
> http://quozl.linux.org.au/

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-17 13:29         ` Caleb Crome
  2015-09-17 17:42           ` Mark Brown
@ 2015-09-18  9:09           ` Liam Girdwood
  1 sibling, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-09-18  9:09 UTC (permalink / raw)
  To: Caleb Crome; +Cc: Takashi Iwai, han.lu, alsa-devel, Mark Brown, bernard.gautier

On Thu, 2015-09-17 at 06:29 -0700, Caleb Crome wrote:
> >> > We have also used BAT for stress testing audio over D0 -> D3 -> D0 transitions.
> >>
> >> Sorry, I'm not familiar with that terminology.  Are those the
> >> different power states?
> >
> > Yes, PM suspend and resume of device. We have some scripts (to be
> > upstreamed later) that would play audio, suspend the device, resume the
> > device and then retest the audio.
> 
> That seems particularly likely to result in multiple codecs getting
> out of phase alignment.  i.e. when restarting, you want to be sure to
> follow the same startup procedure as the first time to make sure all
> codecs are aligned.
> 

This was more for testing our DSP and codec context was properly
saved/restored and also our codec was properly powered down/up during PM
state changes.

Liam

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 4/7] BAT: Add signal generator
  2015-09-15  7:00 ` [PATCH BAT V1 4/7] BAT: Add signal generator han.lu
@ 2015-09-18 16:50   ` Caleb Crome
  2015-09-21  6:44     ` Liam Girdwood
  0 siblings, 1 reply; 26+ messages in thread
From: Caleb Crome @ 2015-09-18 16:50 UTC (permalink / raw)
  To: han.lu; +Cc: bernard.gautier, alsa-devel, tiwai, liam.r.girdwood

Hi Han, Liam, all,

It looks like you have run into the old problem that the math library
actually stinks at generating sine waves right :-)

> +       for (k = 0; k < length; k++) {
> +               for (c = 0; c < bat->channels; c++) {
> +                       idx = k * bat->channels + c;
> +                       out[idx] = sinf(val[c] * i) * factor;
> +               }
> +               i++;
> +               if (i == bat->rate)
> +                       i = 0; /* Restart from 0 after one sine wave period */
> +       }

If I'm reading this right, you're resetting the argument to the sinf
function.  This can generate unwanted distortion in the sine wave if
the frequency isn't an exact fraction of the sample rate.

Here's a little library (along with a test program) I just whipped up
using a phasor as the sine wave generator.
The test program runs it for a simulated 3 days, then prints a python
script to stdout that you can then run and plot to see the generated
wave.

This will work for any frequency sine wave, and as far as I know, it's
the best way to generate sine waves. (Not for calculating the sin of a
value though!)

Here is the generator and test program.  Sorry If this isn't the right
way to submit a file/patch, but I have no idea how to do it properly
:-/

BTW, this isn't actually integrated into the bat program, it's just
the little library for generating the sine waves.

Author: Caleb Crome <caleb@signalessence.com>
Date:   Fri Sep 18 09:45:43 2015 -0700

    added sin_generator

diff --git a/bat/sin_generator.c b/bat/sin_generator.c
new file mode 100644
index 0000000..e67f376
--- /dev/null
+++ b/bat/sin_generator.c
@@ -0,0 +1,120 @@
+#include "math.h"
+#include "sin_generator.h"
+/*
+ * Copyright (C) 2015 Caleb Crome
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * This is a general purpose sine wave generator that will stay stable
+ * for a long time, and with a little renormalization, could stay stay
+ * stable indefinitely
+ */
+
+
+/* Initialize the sine wave generator.
+ * sin_generator:  gets initialized by this call.
+ * frequency:      the frequency for the sine wave.  must be < 0.5*sample_rate
+ * sample_rate:    the the sample rate...
+ * returns 0 on success, -1 on error.
+ */
+int sin_generator_init(struct sin_generator *sg, float magnitude,
float frequency, float sample_rate)
+{
+    // angular frequency:  cycles/sec / (samp/sec) * rad/cycle = rad/samp
+    float w = frequency/sample_rate*2*M_PI;
+    if (frequency >= sample_rate/2)
+        return -1;
+    sg->phasor_real = cos(w);
+    sg->phasor_imag = sin(w);
+    sg->magnitude   = magnitude;
+    sg->state_real  = 0.0;
+    sg->state_imag  = magnitude;
+    sg->frequency = frequency;
+    sg->sample_rate = sample_rate;
+    return 0;
+}
+
+/*
+ * Generates the next sample in the sine wave.
+ * should be much faster than calling a sin function
+ * if it's inlined and optimized.
+ *
+ * returns the next value.  no possibility of error.
+*/
+float sin_generator_next_sample(struct sin_generator *sg)
+{
+    // get shorthand to pointers
+    const double pr = sg->phasor_real;
+    const double pi = sg->phasor_imag;
+    const double sr = sg->state_real;
+    const double si = sg->state_imag;
+    // step the phasor -- complex multiply
+    sg->state_real = sr * pr - si * pi;
+    sg->state_imag = sr * pi + pr * si;
+    return sr;  // return the input value so sine wave starts at
+            // exactly 0.0
+}
+/* fills a vector with a sine wave */
+void sin_generator_vfill(struct sin_generator *sg, float *buf, int n)
+{
+    int i;
+    for (i = 0; i < n; i++) {
+        *buf++ = sin_generator_next_sample(sg);
+    }
+}
+
+#ifdef TEST_PHASOR
+#define TESTSIZE (48000/1000)
+// compile with this command to create executable that will
+// auto-generate a self plotting plot :-)
+
+// gcc -g sin_generator.c -o sin_generator -lm -DTEST_PHASOR
+
+#include <stdio.h>
+
+void dumpbuffer(const char *name, float *buffer, int n)
+{
+    int i;
+    printf("%s = [\n", name);
+    for (i = 0; i < n; i++) {
+        printf("   %f,\n", buffer[i]);
+    }
+    printf("]\n");
+    printf("plot(%s)\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+    float buffer[TESTSIZE];
+    long int  i;
+    struct sin_generator sg;
+
+    // test the first few.
+    sin_generator_init (&sg, 3.0, 1000, 48000);
+    sin_generator_vfill(&sg, buffer, TESTSIZE);
+    printf("from pylab import *\n");
+    dumpbuffer("s", buffer, TESTSIZE);
+
+    // now wind the phasor for 3 days worth of samples to see
+    // if magnitude drifts.
+    for (i = 0; i < (3L*24*60*60*48000); i++)
+        sin_generator_next_sample(&sg);
+
+    sin_generator_vfill(&sg, buffer, TESTSIZE);
+    // and let's see what that one looks like
+    dumpbuffer("o", buffer, TESTSIZE);
+    printf("show()\n");
+
+    return 0;
+}
+#endif
diff --git a/bat/sin_generator.h b/bat/sin_generator.h
new file mode 100644
index 0000000..2ab7561
--- /dev/null
+++ b/bat/sin_generator.h
@@ -0,0 +1,23 @@
+/*
+ * Here's a generic sine wave generator that will work indefinitely
for any frequency.
+ *
+ * Note:  the state & phasor are stored as doubles (and updated as
+ * doubles) because after a million samples the magnitude drifts a
+ * bit.  If we really need floats, it can be done with periodic
+ * renormalization of the state_real+state_imag magnitudes.  I was
+ */
+
+struct sin_generator;
+struct sin_generator {
+    double state_real;
+    double state_imag;
+    double phasor_real;
+    double phasor_imag;
+    float frequency;
+    float sample_rate;
+    float magnitude;
+};
+
+int   sin_generator_init(struct sin_generator *sg, float magnitude,
float frequency, float sample_rate);
+float sin_generator_next_sample(struct sin_generator *sg);
+void  sin_generator_vfill(struct sin_generator *sg, float *buf, int n);

^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
                   ` (8 preceding siblings ...)
  2015-09-16 16:37 ` Mark Brown
@ 2015-09-19 16:40 ` Takashi Iwai
  2015-09-21  6:33   ` Liam Girdwood
  9 siblings, 1 reply; 26+ messages in thread
From: Takashi Iwai @ 2015-09-19 16:40 UTC (permalink / raw)
  To: han.lu; +Cc: bernard.gautier, alsa-devel, liam.r.girdwood

Hi,

I'm currently traveling on vacation and have little time to check
through the patchset and join the discussion.  I'll take a closer look
in the next week.


thanks,

Takashi


On Tue, 15 Sep 2015 09:00:16 +0200,
<han.lu@intel.com> wrote:
> 
> From: "Lu, Han" <han.lu@intel.com>
> 
> BAT (Basic Audio Tester) is a simple command line utility intended
> to automate audio driver and sound server QA testing with no human
> interaction.
> 
> BAT works by playing an audio stream and capturing the same stream
> in either a digital or analog loopback. It then compares the captured
> stream to the original to determine if the testcase passes or fails.
> 
> The main idea of frequency detecting is:
> The analysis function reads data from wav file, run fft against the data
> to get magnitude of frequency vectors, and then calculates the average
> value and standard deviation of frequency vectors. After that, we define
> a threshold:
>     threshold = 3 * standard_deviation + average_value
> Frequencies with amplitude larger than threshold will be recognized as
> a peak, and the frequency with largest peak value will be recognized as
> a detected frequency.
> BAT then compares the detected frequency to target frequency, to decide
> if the detecting passes or fails.
> 
> BAT supports 4 working modes:
>   1. single line playback;
>   2. single line capture and analysis;
>   3. playback and capture in loop, and analyze captured data;
>   4. local analyze without actual playback or capture.
> BAT will check devices input by user to decide which mode to use.
> BAT will create threads for playback and record, and will run spectrum
> analysis for mode 2, 3 or 4.
> 
> Lu, Han (7):
>   BAT: Add initial functions
>   BAT: Add common definitions and functions
>   BAT: Add playback and record functions
>   BAT: Add signal generator
>   BAT: Add converting functions
>   BAT: Add spectrum analysis functions
>   BAT: Add Makefile and configures
> 
>  Makefile.am     |   3 +
>  bat/Makefile.am |  14 ++
>  bat/alsa.c      | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  bat/alsa.h      |  20 ++
>  bat/analyze.c   | 314 ++++++++++++++++++++++++++++
>  bat/analyze.h   |  16 ++
>  bat/bat.c       | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  bat/common.c    | 198 ++++++++++++++++++
>  bat/common.h    | 169 ++++++++++++++++
>  bat/convert.c   | 113 +++++++++++
>  bat/convert.h   |  23 +++
>  bat/signal.c    |  88 ++++++++
>  configure.ac    |  19 +-
>  13 files changed, 2202 insertions(+), 1 deletion(-)
>  create mode 100644 bat/Makefile.am
>  create mode 100644 bat/alsa.c
>  create mode 100644 bat/alsa.h
>  create mode 100644 bat/analyze.c
>  create mode 100644 bat/analyze.h
>  create mode 100644 bat/bat.c
>  create mode 100644 bat/common.c
>  create mode 100644 bat/common.h
>  create mode 100644 bat/convert.c
>  create mode 100644 bat/convert.h
>  create mode 100644 bat/signal.c
> 
> -- 
> 1.9.1
> 
> 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool
  2015-09-19 16:40 ` Takashi Iwai
@ 2015-09-21  6:33   ` Liam Girdwood
  0 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-09-21  6:33 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: han.lu, alsa-devel, Mark Brown, Caleb Crome, bernard.gautier

On Sat, 2015-09-19 at 18:40 +0200, Takashi Iwai wrote:
> Hi,
> 
> I'm currently traveling on vacation and have little time to check
> through the patchset and join the discussion.  I'll take a closer look
> in the next week.
> 

No worries. This gives us time to merge in Caleb's sine wave generator
patch and we can then send a V2 series.

Thanks

Liam

> 
> thanks,
> 
> Takashi
> 
> 
> On Tue, 15 Sep 2015 09:00:16 +0200,
> <han.lu@intel.com> wrote:
> > 
> > From: "Lu, Han" <han.lu@intel.com>
> > 
> > BAT (Basic Audio Tester) is a simple command line utility intended
> > to automate audio driver and sound server QA testing with no human
> > interaction.
> > 
> > BAT works by playing an audio stream and capturing the same stream
> > in either a digital or analog loopback. It then compares the captured
> > stream to the original to determine if the testcase passes or fails.
> > 
> > The main idea of frequency detecting is:
> > The analysis function reads data from wav file, run fft against the data
> > to get magnitude of frequency vectors, and then calculates the average
> > value and standard deviation of frequency vectors. After that, we define
> > a threshold:
> >     threshold = 3 * standard_deviation + average_value
> > Frequencies with amplitude larger than threshold will be recognized as
> > a peak, and the frequency with largest peak value will be recognized as
> > a detected frequency.
> > BAT then compares the detected frequency to target frequency, to decide
> > if the detecting passes or fails.
> > 
> > BAT supports 4 working modes:
> >   1. single line playback;
> >   2. single line capture and analysis;
> >   3. playback and capture in loop, and analyze captured data;
> >   4. local analyze without actual playback or capture.
> > BAT will check devices input by user to decide which mode to use.
> > BAT will create threads for playback and record, and will run spectrum
> > analysis for mode 2, 3 or 4.
> > 
> > Lu, Han (7):
> >   BAT: Add initial functions
> >   BAT: Add common definitions and functions
> >   BAT: Add playback and record functions
> >   BAT: Add signal generator
> >   BAT: Add converting functions
> >   BAT: Add spectrum analysis functions
> >   BAT: Add Makefile and configures
> > 
> >  Makefile.am     |   3 +
> >  bat/Makefile.am |  14 ++
> >  bat/alsa.c      | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  bat/alsa.h      |  20 ++
> >  bat/analyze.c   | 314 ++++++++++++++++++++++++++++
> >  bat/analyze.h   |  16 ++
> >  bat/bat.c       | 608 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  bat/common.c    | 198 ++++++++++++++++++
> >  bat/common.h    | 169 ++++++++++++++++
> >  bat/convert.c   | 113 +++++++++++
> >  bat/convert.h   |  23 +++
> >  bat/signal.c    |  88 ++++++++
> >  configure.ac    |  19 +-
> >  13 files changed, 2202 insertions(+), 1 deletion(-)
> >  create mode 100644 bat/Makefile.am
> >  create mode 100644 bat/alsa.c
> >  create mode 100644 bat/alsa.h
> >  create mode 100644 bat/analyze.c
> >  create mode 100644 bat/analyze.h
> >  create mode 100644 bat/bat.c
> >  create mode 100644 bat/common.c
> >  create mode 100644 bat/common.h
> >  create mode 100644 bat/convert.c
> >  create mode 100644 bat/convert.h
> >  create mode 100644 bat/signal.c
> > 
> > -- 
> > 1.9.1
> > 
> > 


---------------------------------------------------------------------
Intel Corporation (UK) Limited
Registered No. 1134945 (England)
Registered Office: Pipers Way, Swindon SN3 1RJ
VAT No: 860 2173 47

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH BAT V1 4/7] BAT: Add signal generator
  2015-09-18 16:50   ` Caleb Crome
@ 2015-09-21  6:44     ` Liam Girdwood
  0 siblings, 0 replies; 26+ messages in thread
From: Liam Girdwood @ 2015-09-21  6:44 UTC (permalink / raw)
  To: Caleb Crome; +Cc: han.lu, alsa-devel, tiwai, bernard.gautier

On Fri, 2015-09-18 at 09:50 -0700, Caleb Crome wrote:
> Hi Han, Liam, all,
> 
> It looks like you have run into the old problem that the math library
> actually stinks at generating sine waves right :-)
> 
> > +       for (k = 0; k < length; k++) {
> > +               for (c = 0; c < bat->channels; c++) {
> > +                       idx = k * bat->channels + c;
> > +                       out[idx] = sinf(val[c] * i) * factor;
> > +               }
> > +               i++;
> > +               if (i == bat->rate)
> > +                       i = 0; /* Restart from 0 after one sine wave period */
> > +       }
> 
> If I'm reading this right, you're resetting the argument to the sinf
> function.  This can generate unwanted distortion in the sine wave if
> the frequency isn't an exact fraction of the sample rate.
> 
> Here's a little library (along with a test program) I just whipped up
> using a phasor as the sine wave generator.
> The test program runs it for a simulated 3 days, then prints a python
> script to stdout that you can then run and plot to see the generated
> wave.
> 
> This will work for any frequency sine wave, and as far as I know, it's
> the best way to generate sine waves. (Not for calculating the sin of a
> value though!)
> 
> Here is the generator and test program.  Sorry If this isn't the right
> way to submit a file/patch, but I have no idea how to do it properly
> :-/
> 

No worries, I dont think Han has put a link to his public git repo so it
will be a bit more difficult to create proper patches. 

> BTW, this isn't actually integrated into the bat program, it's just
> the little library for generating the sine waves.
> 

Thanks, we can integrate and add to the codebase for resending V2
patches. :)

Liam

> Author: Caleb Crome <caleb@signalessence.com>
> Date:   Fri Sep 18 09:45:43 2015 -0700
> 
>     added sin_generator
> 
> diff --git a/bat/sin_generator.c b/bat/sin_generator.c
> new file mode 100644
> index 0000000..e67f376
> --- /dev/null
> +++ b/bat/sin_generator.c
> @@ -0,0 +1,120 @@
> +#include "math.h"
> +#include "sin_generator.h"
> +/*
> + * Copyright (C) 2015 Caleb Crome
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +/*
> + * This is a general purpose sine wave generator that will stay stable
> + * for a long time, and with a little renormalization, could stay stay
> + * stable indefinitely
> + */
> +
> +
> +/* Initialize the sine wave generator.
> + * sin_generator:  gets initialized by this call.
> + * frequency:      the frequency for the sine wave.  must be < 0.5*sample_rate
> + * sample_rate:    the the sample rate...
> + * returns 0 on success, -1 on error.
> + */
> +int sin_generator_init(struct sin_generator *sg, float magnitude,
> float frequency, float sample_rate)
> +{
> +    // angular frequency:  cycles/sec / (samp/sec) * rad/cycle = rad/samp
> +    float w = frequency/sample_rate*2*M_PI;
> +    if (frequency >= sample_rate/2)
> +        return -1;
> +    sg->phasor_real = cos(w);
> +    sg->phasor_imag = sin(w);
> +    sg->magnitude   = magnitude;
> +    sg->state_real  = 0.0;
> +    sg->state_imag  = magnitude;
> +    sg->frequency = frequency;
> +    sg->sample_rate = sample_rate;
> +    return 0;
> +}
> +
> +/*
> + * Generates the next sample in the sine wave.
> + * should be much faster than calling a sin function
> + * if it's inlined and optimized.
> + *
> + * returns the next value.  no possibility of error.
> +*/
> +float sin_generator_next_sample(struct sin_generator *sg)
> +{
> +    // get shorthand to pointers
> +    const double pr = sg->phasor_real;
> +    const double pi = sg->phasor_imag;
> +    const double sr = sg->state_real;
> +    const double si = sg->state_imag;
> +    // step the phasor -- complex multiply
> +    sg->state_real = sr * pr - si * pi;
> +    sg->state_imag = sr * pi + pr * si;
> +    return sr;  // return the input value so sine wave starts at
> +            // exactly 0.0
> +}
> +/* fills a vector with a sine wave */
> +void sin_generator_vfill(struct sin_generator *sg, float *buf, int n)
> +{
> +    int i;
> +    for (i = 0; i < n; i++) {
> +        *buf++ = sin_generator_next_sample(sg);
> +    }
> +}
> +
> +#ifdef TEST_PHASOR
> +#define TESTSIZE (48000/1000)
> +// compile with this command to create executable that will
> +// auto-generate a self plotting plot :-)
> +
> +// gcc -g sin_generator.c -o sin_generator -lm -DTEST_PHASOR
> +
> +#include <stdio.h>
> +
> +void dumpbuffer(const char *name, float *buffer, int n)
> +{
> +    int i;
> +    printf("%s = [\n", name);
> +    for (i = 0; i < n; i++) {
> +        printf("   %f,\n", buffer[i]);
> +    }
> +    printf("]\n");
> +    printf("plot(%s)\n", name);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +    float buffer[TESTSIZE];
> +    long int  i;
> +    struct sin_generator sg;
> +
> +    // test the first few.
> +    sin_generator_init (&sg, 3.0, 1000, 48000);
> +    sin_generator_vfill(&sg, buffer, TESTSIZE);
> +    printf("from pylab import *\n");
> +    dumpbuffer("s", buffer, TESTSIZE);
> +
> +    // now wind the phasor for 3 days worth of samples to see
> +    // if magnitude drifts.
> +    for (i = 0; i < (3L*24*60*60*48000); i++)
> +        sin_generator_next_sample(&sg);
> +
> +    sin_generator_vfill(&sg, buffer, TESTSIZE);
> +    // and let's see what that one looks like
> +    dumpbuffer("o", buffer, TESTSIZE);
> +    printf("show()\n");
> +
> +    return 0;
> +}
> +#endif
> diff --git a/bat/sin_generator.h b/bat/sin_generator.h
> new file mode 100644
> index 0000000..2ab7561
> --- /dev/null
> +++ b/bat/sin_generator.h
> @@ -0,0 +1,23 @@
> +/*
> + * Here's a generic sine wave generator that will work indefinitely
> for any frequency.
> + *
> + * Note:  the state & phasor are stored as doubles (and updated as
> + * doubles) because after a million samples the magnitude drifts a
> + * bit.  If we really need floats, it can be done with periodic
> + * renormalization of the state_real+state_imag magnitudes.  I was
> + */
> +
> +struct sin_generator;
> +struct sin_generator {
> +    double state_real;
> +    double state_imag;
> +    double phasor_real;
> +    double phasor_imag;
> +    float frequency;
> +    float sample_rate;
> +    float magnitude;
> +};
> +
> +int   sin_generator_init(struct sin_generator *sg, float magnitude,
> float frequency, float sample_rate);
> +float sin_generator_next_sample(struct sin_generator *sg);
> +void  sin_generator_vfill(struct sin_generator *sg, float *buf, int n);

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2015-09-21  6:44 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-15  7:00 [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool han.lu
2015-09-15  7:00 ` [PATCH BAT V1 1/7] BAT: Add initial functions han.lu
2015-09-15  7:00 ` [PATCH BAT V1 2/7] BAT: Add common definitions and functions han.lu
2015-09-15  7:00 ` [PATCH BAT V1 3/7] BAT: Add playback and record functions han.lu
2015-09-15  7:00 ` [PATCH BAT V1 4/7] BAT: Add signal generator han.lu
2015-09-18 16:50   ` Caleb Crome
2015-09-21  6:44     ` Liam Girdwood
2015-09-15  7:00 ` [PATCH BAT V1 5/7] BAT: Add converting functions han.lu
2015-09-15  7:00 ` [PATCH BAT V1 6/7] BAT: Add spectrum analysis functions han.lu
2015-09-15  7:00 ` [PATCH BAT V1 7/7] BAT: Add Makefile and configures han.lu
2015-09-16  3:23 ` [PATCH BAT V1 0/7] BAT: Add Basic Audio Tester command line tool Caleb Crome
2015-09-16 11:40   ` Liam Girdwood
2015-09-16 16:43     ` Caleb Crome
2015-09-17 10:40       ` Liam Girdwood
2015-09-17 13:29         ` Caleb Crome
2015-09-17 17:42           ` Mark Brown
2015-09-17 18:50             ` Caleb Crome
2015-09-17 22:40               ` Caleb Crome
2015-09-17 23:00                 ` James Cameron
2015-09-17 23:30                   ` Caleb Crome
2015-09-18  9:09           ` Liam Girdwood
2015-09-16 16:37 ` Mark Brown
2015-09-16 16:57   ` Liam Girdwood
2015-09-16 20:34     ` Mark Brown
2015-09-19 16:40 ` Takashi Iwai
2015-09-21  6:33   ` Liam Girdwood

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.