1 /* tinycap.c
2 **
3 ** Copyright 2011, The Android Open Source Project
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are met:
7 ** * Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
9 ** * Redistributions in binary form must reproduce the above copyright
10 ** notice, this list of conditions and the following disclaimer in the
11 ** documentation and/or other materials provided with the distribution.
12 ** * Neither the name of The Android Open Source Project nor the names of
13 ** its contributors may be used to endorse or promote products derived
14 ** from this software without specific prior written permission.
15 **
16 ** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 ** DAMAGE.
27 */
28
29 #include <tinyalsa/asoundlib.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <stdbool.h>
34 #include <signal.h>
35 #include <string.h>
36 #include <limits.h>
37
38 #define OPTPARSE_IMPLEMENTATION
39 #include "optparse.h"
40
41 #define ID_RIFF 0x46464952
42 #define ID_WAVE 0x45564157
43 #define ID_FMT 0x20746d66
44 #define ID_DATA 0x61746164
45
46 #define FORMAT_PCM 1
47
48 struct wav_header {
49 uint32_t riff_id;
50 uint32_t riff_sz;
51 uint32_t riff_fmt;
52 uint32_t fmt_id;
53 uint32_t fmt_sz;
54 uint16_t audio_format;
55 uint16_t num_channels;
56 uint32_t sample_rate;
57 uint32_t byte_rate;
58 uint16_t block_align;
59 uint16_t bits_per_sample;
60 uint32_t data_id;
61 uint32_t data_sz;
62 };
63
64 int capturing = 1;
65 int prinfo = 1;
66
67 unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
68 bool use_mmap, unsigned int channels, unsigned int rate,
69 enum pcm_format format, unsigned int period_size,
70 unsigned int period_count, unsigned int capture_time);
71
sigint_handler(int sig)72 void sigint_handler(int sig)
73 {
74 if (sig == SIGINT){
75 capturing = 0;
76 }
77 }
78
main(int argc,char ** argv)79 int main(int argc, char **argv)
80 {
81 FILE *file;
82 struct wav_header header;
83 unsigned int card = 0;
84 unsigned int device = 0;
85 unsigned int channels = 2;
86 unsigned int rate = 48000;
87 unsigned int bits = 16;
88 unsigned int frames;
89 unsigned int period_size = 1024;
90 unsigned int period_count = 4;
91 unsigned int capture_time = UINT_MAX;
92 bool use_mmap = false;
93 enum pcm_format format;
94 int no_header = 0, c;
95 struct optparse opts;
96
97 if (argc < 2) {
98 fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-M] [-c channels] "
99 "[-r rate] [-b bits] [-p period_size] [-n n_periods] [-t time_in_seconds]\n\n"
100 "Use -- for filename to send raw PCM to stdout\n", argv[0]);
101 return 1;
102 }
103
104 if (strcmp(argv[1],"--") == 0) {
105 file = stdout;
106 prinfo = 0;
107 no_header = 1;
108 } else {
109 file = fopen(argv[1], "wb");
110 if (!file) {
111 fprintf(stderr, "Unable to create file '%s'\n", argv[1]);
112 return 1;
113 }
114 }
115
116 /* parse command line arguments */
117 optparse_init(&opts, argv + 1);
118 while ((c = optparse(&opts, "D:d:c:r:b:p:n:t:M")) != -1) {
119 switch (c) {
120 case 'd':
121 device = atoi(opts.optarg);
122 break;
123 case 'c':
124 channels = atoi(opts.optarg);
125 break;
126 case 'r':
127 rate = atoi(opts.optarg);
128 break;
129 case 'b':
130 bits = atoi(opts.optarg);
131 break;
132 case 'D':
133 card = atoi(opts.optarg);
134 break;
135 case 'p':
136 period_size = atoi(opts.optarg);
137 break;
138 case 'n':
139 period_count = atoi(opts.optarg);
140 break;
141 case 't':
142 capture_time = atoi(opts.optarg);
143 break;
144 case 'M':
145 use_mmap = true;
146 break;
147 case '?':
148 fprintf(stderr, "%s\n", opts.errmsg);
149 return EXIT_FAILURE;
150 }
151 }
152
153 header.riff_id = ID_RIFF;
154 header.riff_sz = 0;
155 header.riff_fmt = ID_WAVE;
156 header.fmt_id = ID_FMT;
157 header.fmt_sz = 16;
158 header.audio_format = FORMAT_PCM;
159 header.num_channels = channels;
160 header.sample_rate = rate;
161
162 switch (bits) {
163 case 32:
164 format = PCM_FORMAT_S32_LE;
165 break;
166 case 24:
167 format = PCM_FORMAT_S24_LE;
168 break;
169 case 16:
170 format = PCM_FORMAT_S16_LE;
171 break;
172 default:
173 fprintf(stderr, "%u bits is not supported.\n", bits);
174 fclose(file);
175 return 1;
176 }
177
178 header.bits_per_sample = pcm_format_to_bits(format);
179 header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
180 header.block_align = channels * (header.bits_per_sample / 8);
181 header.data_id = ID_DATA;
182
183 /* leave enough room for header */
184 if (!no_header) {
185 fseek(file, sizeof(struct wav_header), SEEK_SET);
186 }
187
188 /* install signal handler and begin capturing */
189 signal(SIGINT, sigint_handler);
190 frames = capture_sample(file, card, device, use_mmap,
191 header.num_channels, header.sample_rate,
192 format, period_size, period_count, capture_time);
193 if (prinfo) {
194 printf("Captured %u frames\n", frames);
195 }
196
197 /* write header now all information is known */
198 if (!no_header) {
199 header.data_sz = frames * header.block_align;
200 header.riff_sz = header.data_sz + sizeof(header) - 8;
201 fseek(file, 0, SEEK_SET);
202 fwrite(&header, sizeof(struct wav_header), 1, file);
203 }
204
205 fclose(file);
206
207 return 0;
208 }
209
capture_sample(FILE * file,unsigned int card,unsigned int device,bool use_mmap,unsigned int channels,unsigned int rate,enum pcm_format format,unsigned int period_size,unsigned int period_count,unsigned int capture_time)210 unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
211 bool use_mmap, unsigned int channels, unsigned int rate,
212 enum pcm_format format, unsigned int period_size,
213 unsigned int period_count, unsigned int capture_time)
214 {
215 struct pcm_config config;
216 unsigned int pcm_open_flags;
217 struct pcm *pcm;
218 char *buffer;
219 unsigned int size;
220 unsigned int frames_read;
221 unsigned int total_frames_read;
222 unsigned int bytes_per_frame;
223
224 memset(&config, 0, sizeof(config));
225 config.channels = channels;
226 config.rate = rate;
227 config.period_size = period_size;
228 config.period_count = period_count;
229 config.format = format;
230 config.start_threshold = 0;
231 config.stop_threshold = 0;
232 config.silence_threshold = 0;
233
234 pcm_open_flags = PCM_IN;
235 if (use_mmap)
236 pcm_open_flags |= PCM_MMAP;
237
238 pcm = pcm_open(card, device, pcm_open_flags, &config);
239 if (!pcm || !pcm_is_ready(pcm)) {
240 fprintf(stderr, "Unable to open PCM device (%s)\n",
241 pcm_get_error(pcm));
242 return 0;
243 }
244
245 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
246 buffer = malloc(size);
247 if (!buffer) {
248 fprintf(stderr, "Unable to allocate %u bytes\n", size);
249 pcm_close(pcm);
250 return 0;
251 }
252
253 if (prinfo) {
254 printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
255 pcm_format_to_bits(format));
256 }
257
258 bytes_per_frame = pcm_frames_to_bytes(pcm, 1);
259 total_frames_read = 0;
260 frames_read = 0;
261 while (capturing) {
262 frames_read = pcm_readi(pcm, buffer, pcm_get_buffer_size(pcm));
263 total_frames_read += frames_read;
264 if ((total_frames_read / rate) >= capture_time) {
265 capturing = 0;
266 }
267 if (fwrite(buffer, bytes_per_frame, frames_read, file) != frames_read) {
268 fprintf(stderr,"Error capturing sample\n");
269 break;
270 }
271 }
272
273 free(buffer);
274 pcm_close(pcm);
275 return total_frames_read;
276 }
277
278