[alsa-devel] [PATCH V2 2/7] BAT: Add common definitions and functions

han.lu at intel.com han.lu at intel.com
Wed Sep 23 09:48:50 CEST 2015


From: "Lu, Han" <han.lu at 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 at intel.com>
Signed-off-by: Liam Girdwood <liam.r.girdwood at intel.com>
Signed-off-by: Bernard Gautier <bernard.gautier at 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..e6ff7f1
--- /dev/null
+++ b/bat/common.h
@@ -0,0 +1,176 @@
+/*
+ * 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 sin_generator;
+
+struct sin_generator {
+	double state_real;
+	double state_imag;
+	double phasor_real;
+	double phasor_imag;
+	float frequency;
+	float sample_rate;
+	float magnitude;
+};
+
+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 *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 *);
-- 
1.9.1



More information about the Alsa-devel mailing list