OSDN Git Service

alsabat: fix a possible memory leak
[android-x86/external-alsa-utils.git] / bat / common.c
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 */
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 */
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
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
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
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
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 */
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  */
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(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 }