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