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