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 <signal.h>
34 #include <string.h>
35 #include <limits.h>
36
37 #define OPTPARSE_IMPLEMENTATION
38 #include "optparse.h"
39
40 #define ID_RIFF 0x46464952
41 #define ID_WAVE 0x45564157
42 #define ID_FMT 0x20746d66
43 #define ID_DATA 0x61746164
44
45 #define FORMAT_PCM 1
46
47 struct wav_header {
48 uint32_t riff_id;
49 uint32_t riff_sz;
50 uint32_t riff_fmt;
51 uint32_t fmt_id;
52 uint32_t fmt_sz;
53 uint16_t audio_format;
54 uint16_t num_channels;
55 uint32_t sample_rate;
56 uint32_t byte_rate;
57 uint16_t block_align;
58 uint16_t bits_per_sample;
59 uint32_t data_id;
60 uint32_t data_sz;
61 };
62
63 int capturing = 1;
64 int prinfo = 1;
65
66 unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
67 unsigned int channels, unsigned int rate,
68 enum pcm_format format, unsigned int period_size,
69 unsigned int period_count, unsigned int capture_time);
70
sigint_handler(int sig)71 void sigint_handler(int sig)
72 {
73 if (sig == SIGINT){
74 capturing = 0;
75 }
76 }
77
main(int argc,char ** argv)78 int main(int argc, char **argv)
79 {
80 FILE *file;
81 struct wav_header header;
82 unsigned int card = 0;
83 unsigned int device = 0;
84 unsigned int channels = 2;
85 unsigned int rate = 48000;
86 unsigned int bits = 16;
87 unsigned int frames;
88 unsigned int period_size = 1024;
89 unsigned int period_count = 4;
90 unsigned int capture_time = UINT_MAX;
91 enum pcm_format format;
92 int no_header = 0, c;
93 struct optparse opts;
94
95 if (argc < 2) {
96 fprintf(stderr, "Usage: %s {file.wav | --} [-D card] [-d device] [-c channels] "
97 "[-r rate] [-b bits] [-p period_size] [-n n_periods] [-t time_in_seconds]\n\n"
98 "Use -- for filename to send raw PCM to stdout\n", argv[0]);
99 return 1;
100 }
101
102 if (strcmp(argv[1],"--") == 0) {
103 file = stdout;
104 prinfo = 0;
105 no_header = 1;
106 } else {
107 file = fopen(argv[1], "wb");
108 if (!file) {
109 fprintf(stderr, "Unable to create file '%s'\n", argv[1]);
110 return 1;
111 }
112 }
113
114 /* parse command line arguments */
115 optparse_init(&opts, argv + 1);
116 while ((c = optparse(&opts, "D:d:c:r:b:p:n:t:")) != -1) {
117 switch (c) {
118 case 'd':
119 device = atoi(opts.optarg);
120 break;
121 case 'c':
122 channels = atoi(opts.optarg);
123 break;
124 case 'r':
125 rate = atoi(opts.optarg);
126 break;
127 case 'b':
128 bits = atoi(opts.optarg);
129 break;
130 case 'D':
131 card = atoi(opts.optarg);
132 break;
133 case 'p':
134 period_size = atoi(opts.optarg);
135 break;
136 case 'n':
137 period_count = atoi(opts.optarg);
138 break;
139 case 't':
140 capture_time = atoi(opts.optarg);
141 break;
142 case '?':
143 fprintf(stderr, "%s\n", opts.errmsg);
144 return EXIT_FAILURE;
145 }
146 }
147
148 header.riff_id = ID_RIFF;
149 header.riff_sz = 0;
150 header.riff_fmt = ID_WAVE;
151 header.fmt_id = ID_FMT;
152 header.fmt_sz = 16;
153 header.audio_format = FORMAT_PCM;
154 header.num_channels = channels;
155 header.sample_rate = rate;
156
157 switch (bits) {
158 case 32:
159 format = PCM_FORMAT_S32_LE;
160 break;
161 case 24:
162 format = PCM_FORMAT_S24_LE;
163 break;
164 case 16:
165 format = PCM_FORMAT_S16_LE;
166 break;
167 default:
168 fprintf(stderr, "%u bits is not supported.\n", bits);
169 fclose(file);
170 return 1;
171 }
172
173 header.bits_per_sample = pcm_format_to_bits(format);
174 header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
175 header.block_align = channels * (header.bits_per_sample / 8);
176 header.data_id = ID_DATA;
177
178 /* leave enough room for header */
179 if (!no_header) {
180 fseek(file, sizeof(struct wav_header), SEEK_SET);
181 }
182
183 /* install signal handler and begin capturing */
184 signal(SIGINT, sigint_handler);
185 frames = capture_sample(file, card, device, header.num_channels,
186 header.sample_rate, format,
187 period_size, period_count, capture_time);
188 if (prinfo) {
189 printf("Captured %u frames\n", frames);
190 }
191
192 /* write header now all information is known */
193 if (!no_header) {
194 header.data_sz = frames * header.block_align;
195 header.riff_sz = header.data_sz + sizeof(header) - 8;
196 fseek(file, 0, SEEK_SET);
197 fwrite(&header, sizeof(struct wav_header), 1, file);
198 }
199
200 fclose(file);
201
202 return 0;
203 }
204
capture_sample(FILE * file,unsigned int card,unsigned int device,unsigned int channels,unsigned int rate,enum pcm_format format,unsigned int period_size,unsigned int period_count,unsigned int capture_time)205 unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
206 unsigned int channels, unsigned int rate,
207 enum pcm_format format, unsigned int period_size,
208 unsigned int period_count, unsigned int capture_time)
209 {
210 struct pcm_config config;
211 struct pcm *pcm;
212 char *buffer;
213 unsigned int size;
214 unsigned int frames_read;
215 unsigned int total_frames_read;
216 unsigned int bytes_per_frame;
217
218 memset(&config, 0, sizeof(config));
219 config.channels = channels;
220 config.rate = rate;
221 config.period_size = period_size;
222 config.period_count = period_count;
223 config.format = format;
224 config.start_threshold = 0;
225 config.stop_threshold = 0;
226 config.silence_threshold = 0;
227
228 pcm = pcm_open(card, device, PCM_IN, &config);
229 if (!pcm || !pcm_is_ready(pcm)) {
230 fprintf(stderr, "Unable to open PCM device (%s)\n",
231 pcm_get_error(pcm));
232 return 0;
233 }
234
235 size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
236 buffer = malloc(size);
237 if (!buffer) {
238 fprintf(stderr, "Unable to allocate %u bytes\n", size);
239 pcm_close(pcm);
240 return 0;
241 }
242
243 if (prinfo) {
244 printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
245 pcm_format_to_bits(format));
246 }
247
248 bytes_per_frame = pcm_frames_to_bytes(pcm, 1);
249 total_frames_read = 0;
250 frames_read = 0;
251 while (capturing) {
252 frames_read = pcm_readi(pcm, buffer, pcm_get_buffer_size(pcm));
253 total_frames_read += frames_read;
254 if ((total_frames_read / rate) >= capture_time) {
255 capturing = 0;
256 }
257 if (fwrite(buffer, bytes_per_frame, frames_read, file) != frames_read) {
258 fprintf(stderr,"Error capturing sample\n");
259 break;
260 }
261 }
262
263 free(buffer);
264 pcm_close(pcm);
265 return total_frames_read;
266 }
267
268