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