• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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