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