Blame bat/common.c

Packit Service a9274b
/*
Packit Service a9274b
 * Copyright (C) 2013-2015 Intel Corporation
Packit Service a9274b
 *
Packit Service a9274b
 * This program is free software; you can redistribute it and/or modify
Packit Service a9274b
 * it under the terms of the GNU General Public License as published by
Packit Service a9274b
 * the Free Software Foundation; either version 2 of the License, or
Packit Service a9274b
 * (at your option) any later version.
Packit Service a9274b
 *
Packit Service a9274b
 * This program is distributed in the hope that it will be useful,
Packit Service a9274b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a9274b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a9274b
 * GNU General Public License for more details.
Packit Service a9274b
 *
Packit Service a9274b
 */
Packit Service a9274b
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
#include <stddef.h>
Packit Service a9274b
#include <stdlib.h>
Packit Service a9274b
#include <stdbool.h>
Packit Service a9274b
#include <errno.h>
Packit Service a9274b
Packit Service a9274b
#include "aconfig.h"
Packit Service a9274b
#include "gettext.h"
Packit Service a9274b
Packit Service a9274b
#include "common.h"
Packit Service a9274b
#include "alsa.h"
Packit Service a9274b
#include "bat-signal.h"
Packit Service a9274b
Packit Service a9274b
int retval_play;
Packit Service a9274b
int retval_record;
Packit Service a9274b
Packit Service a9274b
/* update chunk_fmt data to bat */
Packit Service a9274b
static int update_fmt_to_bat(struct bat *bat, struct chunk_fmt *fmt)
Packit Service a9274b
{
Packit Service a9274b
	bat->channels = fmt->channels;
Packit Service a9274b
	bat->rate = fmt->sample_rate;
Packit Service a9274b
	bat->sample_size = fmt->sample_length / 8;
Packit Service a9274b
	if (bat->sample_size > 4) {
Packit Service a9274b
		fprintf(bat->err, _("Invalid format: sample size=%d\n"),
Packit Service a9274b
				bat->sample_size);
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
	bat->frame_size = fmt->blocks_align;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/* calculate frames and update to bat */
Packit Service a9274b
static int update_frames_to_bat(struct bat *bat,
Packit Service a9274b
		struct wav_chunk_header *header, FILE *fp)
Packit Service a9274b
{
Packit Service a9274b
	/* The number of analyzed captured frames is arbitrarily set to half of
Packit Service a9274b
	   the number of frames of the wav file or the number of frames of the
Packit Service a9274b
	   wav file when doing direct analysis (--local) */
Packit Service a9274b
	bat->frames = header->length / bat->frame_size;
Packit Service a9274b
	if (!bat->local)
Packit Service a9274b
		bat->frames /= 2;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int read_chunk_fmt(struct bat *bat, char *file, FILE *fp, bool skip,
Packit Service a9274b
		struct wav_chunk_header *header)
Packit Service a9274b
{
Packit Service a9274b
	size_t err;
Packit Service a9274b
	int header_skip;
Packit Service a9274b
	struct chunk_fmt chunk_fmt;
Packit Service a9274b
Packit Service a9274b
	err = fread(&chunk_fmt, sizeof(chunk_fmt), 1, fp);
Packit Service a9274b
	if (err != 1) {
Packit Service a9274b
		fprintf(bat->err, _("Read chunk fmt error: %s:%zd\n"),
Packit Service a9274b
				file, err);
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	}
Packit Service a9274b
	/* If the format header is larger, skip the rest */
Packit Service a9274b
	header_skip = header->length - sizeof(chunk_fmt);
Packit Service a9274b
	if (header_skip > 0) {
Packit Service a9274b
		err = fseek(fp, header_skip, SEEK_CUR);
Packit Service a9274b
		if (err == -1) {
Packit Service a9274b
			fprintf(bat->err, _("Seek fmt header error: %s:%zd\n"),
Packit Service a9274b
					file, err);
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
	/* If the file is opened for playback, update BAT data;
Packit Service a9274b
	   If the file is opened for analysis, no update */
Packit Service a9274b
	if (skip == false) {
Packit Service a9274b
		err = update_fmt_to_bat(bat, &chunk_fmt);
Packit Service a9274b
		if (err != 0)
Packit Service a9274b
			return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int read_wav_header(struct bat *bat, char *file, FILE *fp, bool skip)
Packit Service a9274b
{
Packit Service a9274b
	struct wav_header riff_wave_header;
Packit Service a9274b
	struct wav_chunk_header chunk_header;
Packit Service a9274b
	int more_chunks = 1;
Packit Service a9274b
	size_t err;
Packit Service a9274b
Packit Service a9274b
	/* Read header of RIFF wav file */
Packit Service a9274b
	err = fread(&riff_wave_header, sizeof(riff_wave_header), 1, fp);
Packit Service a9274b
	if (err != 1) {
Packit Service a9274b
		fprintf(bat->err, _("Read header error: %s:%zd\n"), file, err);
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	}
Packit Service a9274b
	if ((riff_wave_header.magic != WAV_RIFF)
Packit Service a9274b
			|| (riff_wave_header.type != WAV_WAVE)) {
Packit Service a9274b
		fprintf(bat->err, _("%s is not a riff/wave file\n"), file);
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	/* Read chunks in RIFF wav file */
Packit Service a9274b
	do {
Packit Service a9274b
		err = fread(&chunk_header, sizeof(chunk_header), 1, fp);
Packit Service a9274b
		if (err != 1) {
Packit Service a9274b
			fprintf(bat->err, _("Read chunk header error: "));
Packit Service a9274b
			fprintf(bat->err, _("%s:%zd\n"), file, err);
Packit Service a9274b
			return -EIO;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		switch (chunk_header.type) {
Packit Service a9274b
		case WAV_FMT:
Packit Service a9274b
			/* WAV_FMT chunk, read and analyze */
Packit Service a9274b
			err = read_chunk_fmt(bat, file, fp, skip,
Packit Service a9274b
					&chunk_header);
Packit Service a9274b
			if (err != 0)
Packit Service a9274b
				return err;
Packit Service a9274b
			break;
Packit Service a9274b
		case WAV_DATA:
Packit Service a9274b
			/* WAV_DATA chunk, break looping */
Packit Service a9274b
			/* If the file is opened for playback, update BAT data;
Packit Service a9274b
			   If the file is opened for analysis, no update */
Packit Service a9274b
			if (skip == false) {
Packit Service a9274b
				err = update_frames_to_bat(bat, &chunk_header,
Packit Service a9274b
						fp);
Packit Service a9274b
				if (err != 0)
Packit Service a9274b
					return err;
Packit Service a9274b
			}
Packit Service a9274b
			/* Stop looking for chunks */
Packit Service a9274b
			more_chunks = 0;
Packit Service a9274b
			break;
Packit Service a9274b
		default:
Packit Service a9274b
			/* Unknown chunk, skip bytes */
Packit Service a9274b
			err = fseek(fp, chunk_header.length, SEEK_CUR);
Packit Service a9274b
			if (err == -1) {
Packit Service a9274b
				fprintf(bat->err, _("Fail to skip unknown"));
Packit Service a9274b
				fprintf(bat->err, _(" chunk of %s:%zd\n"),
Packit Service a9274b
						file, err);
Packit Service a9274b
				return -EINVAL;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	} while (more_chunks);
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void prepare_wav_info(struct wav_container *wav, struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	wav->header.magic = WAV_RIFF;
Packit Service a9274b
	wav->header.type = WAV_WAVE;
Packit Service a9274b
	wav->format.magic = WAV_FMT;
Packit Service a9274b
	wav->format.fmt_size = 16;
Packit Service a9274b
	wav->format.format = WAV_FORMAT_PCM;
Packit Service a9274b
	wav->format.channels = bat->channels;
Packit Service a9274b
	wav->format.sample_rate = bat->rate;
Packit Service a9274b
	wav->format.sample_length = bat->sample_size * 8;
Packit Service a9274b
	wav->format.blocks_align = bat->channels * bat->sample_size;
Packit Service a9274b
	wav->format.bytes_p_second = wav->format.blocks_align * bat->rate;
Packit Service a9274b
	wav->chunk.length = bat->frames * bat->frame_size;
Packit Service a9274b
	wav->chunk.type = WAV_DATA;
Packit Service a9274b
	wav->header.length = (wav->chunk.length) + sizeof(wav->chunk)
Packit Service a9274b
			+ sizeof(wav->format) + sizeof(wav->header) - 8;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int write_wav_header(FILE *fp, struct wav_container *wav, struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	err = fwrite(&wav->header, 1, sizeof(wav->header), fp);
Packit Service a9274b
	if (err != sizeof(wav->header)) {
Packit Service a9274b
		fprintf(bat->err, _("Write file error: header %d\n"), err);
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	}
Packit Service a9274b
	err = fwrite(&wav->format, 1, sizeof(wav->format), fp);
Packit Service a9274b
	if (err != sizeof(wav->format)) {
Packit Service a9274b
		fprintf(bat->err, _("Write file error: format %d\n"), err);
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	}
Packit Service a9274b
	err = fwrite(&wav->chunk, 1, sizeof(wav->chunk), fp);
Packit Service a9274b
	if (err != sizeof(wav->chunk)) {
Packit Service a9274b
		fprintf(bat->err, _("Write file error: chunk %d\n"), err);
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/* update wav header when data size changed */
Packit Service a9274b
int update_wav_header(struct bat *bat, FILE *fp, int bytes)
Packit Service a9274b
{
Packit Service a9274b
	int err = 0;
Packit Service a9274b
	struct wav_container wav;
Packit Service a9274b
Packit Service a9274b
	prepare_wav_info(&wav, bat);
Packit Service a9274b
	wav.chunk.length = bytes;
Packit Service a9274b
	wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
Packit Service a9274b
		+ sizeof(wav.format) + sizeof(wav.header) - 8;
Packit Service a9274b
	rewind(fp);
Packit Service a9274b
	err = write_wav_header(fp, &wav, bat);
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
/*
Packit Service a9274b
 * Generate buffer to be played either from input file or from generated data
Packit Service a9274b
 * Return value
Packit Service a9274b
 * <0 error
Packit Service a9274b
 * 0 ok
Packit Service a9274b
 * >0 break
Packit Service a9274b
 */
Packit Service a9274b
int generate_input_data(struct bat *bat, void *buffer, int bytes, int frames)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
	static int load;
Packit Service a9274b
Packit Service a9274b
	if (bat->playback.file != NULL) {
Packit Service a9274b
		/* From input file */
Packit Service a9274b
		load = 0;
Packit Service a9274b
Packit Service a9274b
		while (1) {
Packit Service a9274b
			err = fread((char *)buffer + load, 1, bytes - load, bat->fp);
Packit Service a9274b
			if (0 == err) {
Packit Service a9274b
				if (feof(bat->fp)) {
Packit Service a9274b
					fprintf(bat->log,
Packit Service a9274b
							_("End of playing.\n"));
Packit Service a9274b
					return 1;
Packit Service a9274b
				}
Packit Service a9274b
			} else if (err < bytes - load) {
Packit Service a9274b
				if (ferror(bat->fp)) {
Packit Service a9274b
					fprintf(bat->err, _("Read file error"));
Packit Service a9274b
					fprintf(bat->err, _(": %d\n"), err);
Packit Service a9274b
					return -EIO;
Packit Service a9274b
				}
Packit Service a9274b
				load += err;
Packit Service a9274b
			} else {
Packit Service a9274b
				break;
Packit Service a9274b
			}
Packit Service a9274b
		}
Packit Service a9274b
	} else {
Packit Service a9274b
		/* Generate sine wave */
Packit Service a9274b
		if ((bat->sinus_duration) && (load > bat->sinus_duration))
Packit Service a9274b
			return 1;
Packit Service a9274b
Packit Service a9274b
		err = generate_sine_wave(bat, frames, buffer);
Packit Service a9274b
		if (err != 0)
Packit Service a9274b
			return err;
Packit Service a9274b
Packit Service a9274b
		load += frames;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}