Blame bat/common.c

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