mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-22 23:36:30 +01:00
BAT: Add common definitions and functions
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>
This commit is contained in:
parent
c7e0c35661
commit
cba9050edf
2 changed files with 374 additions and 0 deletions
198
bat/common.c
Normal file
198
bat/common.c
Normal file
|
@ -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;
|
||||
}
|
176
bat/common.h
Normal file
176
bat/common.h
Normal file
|
@ -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 *);
|
Loading…
Reference in a new issue