]> git.alsa-project.org Git - alsa-utils.git/commitdiff
BAT: Add common definitions and functions
authorLu, Han <han.lu@intel.com>
Wed, 23 Sep 2015 07:48:50 +0000 (15:48 +0800)
committerTakashi Iwai <tiwai@suse.de>
Fri, 2 Oct 2015 10:42:01 +0000 (12:42 +0200)
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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
bat/common.c [new file with mode: 0644]
bat/common.h [new file with mode: 0644]

diff --git a/bat/common.c b/bat/common.c
new file mode 100644 (file)
index 0000000..798b00b
--- /dev/null
@@ -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 (file)
index 0000000..e6ff7f1
--- /dev/null
@@ -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 *);