1c72fcc34Sopenharmony_ci/*
2c72fcc34Sopenharmony_ci * Copyright (C) 2013-2015 Intel Corporation
3c72fcc34Sopenharmony_ci *
4c72fcc34Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
5c72fcc34Sopenharmony_ci * it under the terms of the GNU General Public License as published by
6c72fcc34Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or
7c72fcc34Sopenharmony_ci * (at your option) any later version.
8c72fcc34Sopenharmony_ci *
9c72fcc34Sopenharmony_ci * This program is distributed in the hope that it will be useful,
10c72fcc34Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
11c72fcc34Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12c72fcc34Sopenharmony_ci * GNU General Public License for more details.
13c72fcc34Sopenharmony_ci *
14c72fcc34Sopenharmony_ci */
15c72fcc34Sopenharmony_ci
16c72fcc34Sopenharmony_ci#include "aconfig.h"
17c72fcc34Sopenharmony_ci
18c72fcc34Sopenharmony_ci#include <stdio.h>
19c72fcc34Sopenharmony_ci#include <stddef.h>
20c72fcc34Sopenharmony_ci#include <stdlib.h>
21c72fcc34Sopenharmony_ci#include <stdbool.h>
22c72fcc34Sopenharmony_ci#include <errno.h>
23c72fcc34Sopenharmony_ci
24c72fcc34Sopenharmony_ci#include "gettext.h"
25c72fcc34Sopenharmony_ci
26c72fcc34Sopenharmony_ci#include "common.h"
27c72fcc34Sopenharmony_ci#include "alsa.h"
28c72fcc34Sopenharmony_ci#include "bat-signal.h"
29c72fcc34Sopenharmony_ci
30c72fcc34Sopenharmony_ciint retval_play;
31c72fcc34Sopenharmony_ciint retval_record;
32c72fcc34Sopenharmony_ci
33c72fcc34Sopenharmony_ci/* update chunk_fmt data to bat */
34c72fcc34Sopenharmony_cistatic int update_fmt_to_bat(struct bat *bat, struct chunk_fmt *fmt)
35c72fcc34Sopenharmony_ci{
36c72fcc34Sopenharmony_ci	bat->channels = fmt->channels;
37c72fcc34Sopenharmony_ci	bat->rate = fmt->sample_rate;
38c72fcc34Sopenharmony_ci	bat->sample_size = fmt->sample_length / 8;
39c72fcc34Sopenharmony_ci	if (bat->sample_size > 4) {
40c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Invalid format: sample size=%d\n"),
41c72fcc34Sopenharmony_ci				bat->sample_size);
42c72fcc34Sopenharmony_ci		return -EINVAL;
43c72fcc34Sopenharmony_ci	}
44c72fcc34Sopenharmony_ci	bat->frame_size = fmt->blocks_align;
45c72fcc34Sopenharmony_ci
46c72fcc34Sopenharmony_ci	return 0;
47c72fcc34Sopenharmony_ci}
48c72fcc34Sopenharmony_ci
49c72fcc34Sopenharmony_ci/* calculate frames and update to bat */
50c72fcc34Sopenharmony_cistatic int update_frames_to_bat(struct bat *bat, struct wav_chunk_header *header,
51c72fcc34Sopenharmony_ci				FILE *file ATTRIBUTE_UNUSED)
52c72fcc34Sopenharmony_ci{
53c72fcc34Sopenharmony_ci	/* The number of analyzed captured frames is arbitrarily set to half of
54c72fcc34Sopenharmony_ci	   the number of frames of the wav file or the number of frames of the
55c72fcc34Sopenharmony_ci	   wav file when doing direct analysis (--local) */
56c72fcc34Sopenharmony_ci	bat->frames = header->length / bat->frame_size;
57c72fcc34Sopenharmony_ci	if (!bat->local)
58c72fcc34Sopenharmony_ci		bat->frames /= 2;
59c72fcc34Sopenharmony_ci
60c72fcc34Sopenharmony_ci	return 0;
61c72fcc34Sopenharmony_ci}
62c72fcc34Sopenharmony_ci
63c72fcc34Sopenharmony_cistatic int read_chunk_fmt(struct bat *bat, char *file, FILE *fp, bool skip,
64c72fcc34Sopenharmony_ci		struct wav_chunk_header *header)
65c72fcc34Sopenharmony_ci{
66c72fcc34Sopenharmony_ci	size_t err;
67c72fcc34Sopenharmony_ci	int ret, header_skip;
68c72fcc34Sopenharmony_ci	struct chunk_fmt chunk_fmt;
69c72fcc34Sopenharmony_ci
70c72fcc34Sopenharmony_ci	err = fread(&chunk_fmt, sizeof(chunk_fmt), 1, fp);
71c72fcc34Sopenharmony_ci	if (err != 1) {
72c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Read chunk fmt error: %s:%zd\n"),
73c72fcc34Sopenharmony_ci				file, err);
74c72fcc34Sopenharmony_ci		return -EIO;
75c72fcc34Sopenharmony_ci	}
76c72fcc34Sopenharmony_ci	/* If the format header is larger, skip the rest */
77c72fcc34Sopenharmony_ci	header_skip = header->length - sizeof(chunk_fmt);
78c72fcc34Sopenharmony_ci	if (header_skip > 0) {
79c72fcc34Sopenharmony_ci		ret = fseek(fp, header_skip, SEEK_CUR);
80c72fcc34Sopenharmony_ci		if (ret == -1) {
81c72fcc34Sopenharmony_ci			fprintf(bat->err, _("Seek fmt header error: %s:%d\n"),
82c72fcc34Sopenharmony_ci					file, ret);
83c72fcc34Sopenharmony_ci			return -EINVAL;
84c72fcc34Sopenharmony_ci		}
85c72fcc34Sopenharmony_ci	}
86c72fcc34Sopenharmony_ci	/* If the file is opened for playback, update BAT data;
87c72fcc34Sopenharmony_ci	   If the file is opened for analysis, no update */
88c72fcc34Sopenharmony_ci	if (skip == false) {
89c72fcc34Sopenharmony_ci		err = update_fmt_to_bat(bat, &chunk_fmt);
90c72fcc34Sopenharmony_ci		if (err != 0)
91c72fcc34Sopenharmony_ci			return err;
92c72fcc34Sopenharmony_ci	}
93c72fcc34Sopenharmony_ci
94c72fcc34Sopenharmony_ci	return 0;
95c72fcc34Sopenharmony_ci}
96c72fcc34Sopenharmony_ci
97c72fcc34Sopenharmony_ciint read_wav_header(struct bat *bat, char *file, FILE *fp, bool skip)
98c72fcc34Sopenharmony_ci{
99c72fcc34Sopenharmony_ci	struct wav_header riff_wave_header;
100c72fcc34Sopenharmony_ci	struct wav_chunk_header chunk_header;
101c72fcc34Sopenharmony_ci	int more_chunks = 1;
102c72fcc34Sopenharmony_ci	size_t err;
103c72fcc34Sopenharmony_ci	int ret;
104c72fcc34Sopenharmony_ci
105c72fcc34Sopenharmony_ci	/* Read header of RIFF wav file */
106c72fcc34Sopenharmony_ci	err = fread(&riff_wave_header, sizeof(riff_wave_header), 1, fp);
107c72fcc34Sopenharmony_ci	if (err != 1) {
108c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Read header error: %s:%zd\n"), file, err);
109c72fcc34Sopenharmony_ci		return -EIO;
110c72fcc34Sopenharmony_ci	}
111c72fcc34Sopenharmony_ci	if ((riff_wave_header.magic != WAV_RIFF)
112c72fcc34Sopenharmony_ci			|| (riff_wave_header.type != WAV_WAVE)) {
113c72fcc34Sopenharmony_ci		fprintf(bat->err, _("%s is not a riff/wave file\n"), file);
114c72fcc34Sopenharmony_ci		return -EINVAL;
115c72fcc34Sopenharmony_ci	}
116c72fcc34Sopenharmony_ci
117c72fcc34Sopenharmony_ci	/* Read chunks in RIFF wav file */
118c72fcc34Sopenharmony_ci	do {
119c72fcc34Sopenharmony_ci		err = fread(&chunk_header, sizeof(chunk_header), 1, fp);
120c72fcc34Sopenharmony_ci		if (err != 1) {
121c72fcc34Sopenharmony_ci			fprintf(bat->err, _("Read chunk header error: "));
122c72fcc34Sopenharmony_ci			fprintf(bat->err, _("%s:%zd\n"), file, err);
123c72fcc34Sopenharmony_ci			return -EIO;
124c72fcc34Sopenharmony_ci		}
125c72fcc34Sopenharmony_ci
126c72fcc34Sopenharmony_ci		switch (chunk_header.type) {
127c72fcc34Sopenharmony_ci		case WAV_FMT:
128c72fcc34Sopenharmony_ci			/* WAV_FMT chunk, read and analyze */
129c72fcc34Sopenharmony_ci			err = read_chunk_fmt(bat, file, fp, skip,
130c72fcc34Sopenharmony_ci					&chunk_header);
131c72fcc34Sopenharmony_ci			if (err != 0)
132c72fcc34Sopenharmony_ci				return err;
133c72fcc34Sopenharmony_ci			break;
134c72fcc34Sopenharmony_ci		case WAV_DATA:
135c72fcc34Sopenharmony_ci			/* WAV_DATA chunk, break looping */
136c72fcc34Sopenharmony_ci			/* If the file is opened for playback, update BAT data;
137c72fcc34Sopenharmony_ci			   If the file is opened for analysis, no update */
138c72fcc34Sopenharmony_ci			if (skip == false) {
139c72fcc34Sopenharmony_ci				err = update_frames_to_bat(bat, &chunk_header,
140c72fcc34Sopenharmony_ci						fp);
141c72fcc34Sopenharmony_ci				if (err != 0)
142c72fcc34Sopenharmony_ci					return err;
143c72fcc34Sopenharmony_ci			}
144c72fcc34Sopenharmony_ci			/* Stop looking for chunks */
145c72fcc34Sopenharmony_ci			more_chunks = 0;
146c72fcc34Sopenharmony_ci			break;
147c72fcc34Sopenharmony_ci		default:
148c72fcc34Sopenharmony_ci			/* Unknown chunk, skip bytes */
149c72fcc34Sopenharmony_ci			ret = fseek(fp, chunk_header.length, SEEK_CUR);
150c72fcc34Sopenharmony_ci			if (ret == -1) {
151c72fcc34Sopenharmony_ci				fprintf(bat->err, _("Fail to skip unknown"));
152c72fcc34Sopenharmony_ci				fprintf(bat->err, _(" chunk of %s:%d\n"),
153c72fcc34Sopenharmony_ci						file, ret);
154c72fcc34Sopenharmony_ci				return -EINVAL;
155c72fcc34Sopenharmony_ci			}
156c72fcc34Sopenharmony_ci		}
157c72fcc34Sopenharmony_ci	} while (more_chunks);
158c72fcc34Sopenharmony_ci
159c72fcc34Sopenharmony_ci	return 0;
160c72fcc34Sopenharmony_ci}
161c72fcc34Sopenharmony_ci
162c72fcc34Sopenharmony_civoid prepare_wav_info(struct wav_container *wav, struct bat *bat)
163c72fcc34Sopenharmony_ci{
164c72fcc34Sopenharmony_ci	wav->header.magic = WAV_RIFF;
165c72fcc34Sopenharmony_ci	wav->header.type = WAV_WAVE;
166c72fcc34Sopenharmony_ci	wav->format.magic = WAV_FMT;
167c72fcc34Sopenharmony_ci	wav->format.fmt_size = 16;
168c72fcc34Sopenharmony_ci	wav->format.format = WAV_FORMAT_PCM;
169c72fcc34Sopenharmony_ci	wav->format.channels = bat->channels;
170c72fcc34Sopenharmony_ci	wav->format.sample_rate = bat->rate;
171c72fcc34Sopenharmony_ci	wav->format.sample_length = bat->sample_size * 8;
172c72fcc34Sopenharmony_ci	wav->format.blocks_align = bat->channels * bat->sample_size;
173c72fcc34Sopenharmony_ci	wav->format.bytes_p_second = wav->format.blocks_align * bat->rate;
174c72fcc34Sopenharmony_ci	wav->chunk.length = bat->frames * bat->frame_size;
175c72fcc34Sopenharmony_ci	wav->chunk.type = WAV_DATA;
176c72fcc34Sopenharmony_ci	wav->header.length = (wav->chunk.length) + sizeof(wav->chunk)
177c72fcc34Sopenharmony_ci			+ sizeof(wav->format) + sizeof(wav->header) - 8;
178c72fcc34Sopenharmony_ci}
179c72fcc34Sopenharmony_ci
180c72fcc34Sopenharmony_ciint write_wav_header(FILE *fp, struct wav_container *wav, struct bat *bat)
181c72fcc34Sopenharmony_ci{
182c72fcc34Sopenharmony_ci	int err = 0;
183c72fcc34Sopenharmony_ci
184c72fcc34Sopenharmony_ci	err = fwrite(&wav->header, 1, sizeof(wav->header), fp);
185c72fcc34Sopenharmony_ci	if (err != sizeof(wav->header)) {
186c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Write file error: header %d\n"), err);
187c72fcc34Sopenharmony_ci		return -EIO;
188c72fcc34Sopenharmony_ci	}
189c72fcc34Sopenharmony_ci	err = fwrite(&wav->format, 1, sizeof(wav->format), fp);
190c72fcc34Sopenharmony_ci	if (err != sizeof(wav->format)) {
191c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Write file error: format %d\n"), err);
192c72fcc34Sopenharmony_ci		return -EIO;
193c72fcc34Sopenharmony_ci	}
194c72fcc34Sopenharmony_ci	err = fwrite(&wav->chunk, 1, sizeof(wav->chunk), fp);
195c72fcc34Sopenharmony_ci	if (err != sizeof(wav->chunk)) {
196c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Write file error: chunk %d\n"), err);
197c72fcc34Sopenharmony_ci		return -EIO;
198c72fcc34Sopenharmony_ci	}
199c72fcc34Sopenharmony_ci
200c72fcc34Sopenharmony_ci	return 0;
201c72fcc34Sopenharmony_ci}
202c72fcc34Sopenharmony_ci
203c72fcc34Sopenharmony_ci/* update wav header when data size changed */
204c72fcc34Sopenharmony_ciint update_wav_header(struct bat *bat, FILE *fp, int bytes)
205c72fcc34Sopenharmony_ci{
206c72fcc34Sopenharmony_ci	int err = 0;
207c72fcc34Sopenharmony_ci	struct wav_container wav;
208c72fcc34Sopenharmony_ci
209c72fcc34Sopenharmony_ci	prepare_wav_info(&wav, bat);
210c72fcc34Sopenharmony_ci	wav.chunk.length = bytes;
211c72fcc34Sopenharmony_ci	wav.header.length = (wav.chunk.length) + sizeof(wav.chunk)
212c72fcc34Sopenharmony_ci		+ sizeof(wav.format) + sizeof(wav.header) - 8;
213c72fcc34Sopenharmony_ci	rewind(fp);
214c72fcc34Sopenharmony_ci	err = write_wav_header(fp, &wav, bat);
215c72fcc34Sopenharmony_ci
216c72fcc34Sopenharmony_ci	return err;
217c72fcc34Sopenharmony_ci}
218c72fcc34Sopenharmony_ci
219c72fcc34Sopenharmony_ci/*
220c72fcc34Sopenharmony_ci * Generate buffer to be played either from input file or from generated data
221c72fcc34Sopenharmony_ci * Return value
222c72fcc34Sopenharmony_ci * <0 error
223c72fcc34Sopenharmony_ci * 0 ok
224c72fcc34Sopenharmony_ci * >0 break
225c72fcc34Sopenharmony_ci */
226c72fcc34Sopenharmony_ciint generate_input_data(struct bat *bat, void *buffer, int bytes, int frames)
227c72fcc34Sopenharmony_ci{
228c72fcc34Sopenharmony_ci	int err;
229c72fcc34Sopenharmony_ci	static int load;
230c72fcc34Sopenharmony_ci
231c72fcc34Sopenharmony_ci	if (bat->playback.file != NULL) {
232c72fcc34Sopenharmony_ci		/* From input file */
233c72fcc34Sopenharmony_ci		load = 0;
234c72fcc34Sopenharmony_ci
235c72fcc34Sopenharmony_ci		while (1) {
236c72fcc34Sopenharmony_ci			err = fread((char *)buffer + load, 1, bytes - load, bat->fp);
237c72fcc34Sopenharmony_ci			if (0 == err) {
238c72fcc34Sopenharmony_ci				if (feof(bat->fp)) {
239c72fcc34Sopenharmony_ci					fprintf(bat->log,
240c72fcc34Sopenharmony_ci							_("End of playing.\n"));
241c72fcc34Sopenharmony_ci					return 1;
242c72fcc34Sopenharmony_ci				}
243c72fcc34Sopenharmony_ci			} else if (err < bytes - load) {
244c72fcc34Sopenharmony_ci				if (ferror(bat->fp)) {
245c72fcc34Sopenharmony_ci					fprintf(bat->err, _("Read file error"));
246c72fcc34Sopenharmony_ci					fprintf(bat->err, _(": %d\n"), err);
247c72fcc34Sopenharmony_ci					return -EIO;
248c72fcc34Sopenharmony_ci				}
249c72fcc34Sopenharmony_ci				load += err;
250c72fcc34Sopenharmony_ci			} else {
251c72fcc34Sopenharmony_ci				break;
252c72fcc34Sopenharmony_ci			}
253c72fcc34Sopenharmony_ci		}
254c72fcc34Sopenharmony_ci	} else {
255c72fcc34Sopenharmony_ci		/* Generate sine wave */
256c72fcc34Sopenharmony_ci		if ((bat->sinus_duration) && (load > bat->sinus_duration))
257c72fcc34Sopenharmony_ci			return 1;
258c72fcc34Sopenharmony_ci
259c72fcc34Sopenharmony_ci		err = generate_sine_wave(bat, frames, buffer);
260c72fcc34Sopenharmony_ci		if (err != 0)
261c72fcc34Sopenharmony_ci			return err;
262c72fcc34Sopenharmony_ci
263c72fcc34Sopenharmony_ci		load += frames;
264c72fcc34Sopenharmony_ci	}
265c72fcc34Sopenharmony_ci
266c72fcc34Sopenharmony_ci	return 0;
267c72fcc34Sopenharmony_ci}
268