[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