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
35 #define ID_RIFF 0x46464952
36 #define ID_WAVE 0x45564157
37 #define ID_FMT 0x20746d66
38 #define ID_DATA 0x61746164
39
40 #define FORMAT_PCM 1
41
42 struct wav_header {
43 uint32_t riff_id;
44 uint32_t riff_sz;
45 uint32_t riff_fmt;
46 uint32_t fmt_id;
47 uint32_t fmt_sz;
48 uint16_t audio_format;
49 uint16_t num_channels;
50 uint32_t sample_rate;
51 uint32_t byte_rate;
52 uint16_t block_align;
53 uint16_t bits_per_sample;
54 uint32_t data_id;
55 uint32_t data_sz;
56 };
57
58 int capturing = 1;
59
60 unsigned int capture_sample(FILE *file, unsigned int device,
61 unsigned int channels, unsigned int rate,
62 unsigned int bits);
63
sigint_handler(int sig)64 void sigint_handler(int sig)
65 {
66 capturing = 0;
67 }
68
main(int argc,char ** argv)69 int main(int argc, char **argv)
70 {
71 FILE *file;
72 struct wav_header header;
73 unsigned int device = 0;
74 unsigned int channels = 2;
75 unsigned int rate = 44100;
76 unsigned int bits = 16;
77 unsigned int frames;
78
79 if (argc < 2) {
80 fprintf(stderr, "Usage: %s file.wav [-d device] [-c channels] "
81 "[-r rate] [-b bits]\n", argv[0]);
82 return 1;
83 }
84
85 file = fopen(argv[1], "wb");
86 if (!file) {
87 fprintf(stderr, "Unable to create file '%s'\n", argv[1]);
88 return 1;
89 }
90
91 /* parse command line arguments */
92 argv += 2;
93 while (*argv) {
94 if (strcmp(*argv, "-d") == 0) {
95 argv++;
96 device = atoi(*argv);
97 } else if (strcmp(*argv, "-c") == 0) {
98 argv++;
99 channels = atoi(*argv);
100 } else if (strcmp(*argv, "-r") == 0) {
101 argv++;
102 rate = atoi(*argv);
103 } else if (strcmp(*argv, "-b") == 0) {
104 argv++;
105 bits = atoi(*argv);
106 }
107 argv++;
108 }
109
110 header.riff_id = ID_RIFF;
111 header.riff_sz = 0;
112 header.riff_fmt = ID_WAVE;
113 header.fmt_id = ID_FMT;
114 header.fmt_sz = 16;
115 header.audio_format = FORMAT_PCM;
116 header.num_channels = channels;
117 header.sample_rate = rate;
118 header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
119 header.block_align = channels * (header.bits_per_sample / 8);
120 header.bits_per_sample = bits;
121 header.data_id = ID_DATA;
122
123 /* leave enough room for header */
124 fseek(file, sizeof(struct wav_header), SEEK_SET);
125
126 /* install signal handler and begin capturing */
127 signal(SIGINT, sigint_handler);
128 frames = capture_sample(file, device, header.num_channels,
129 header.sample_rate, header.bits_per_sample);
130 printf("Captured %d frames\n", frames);
131
132 /* write header now all information is known */
133 header.data_sz = frames * header.block_align;
134 fseek(file, 0, SEEK_SET);
135 fwrite(&header, sizeof(struct wav_header), 1, file);
136
137 fclose(file);
138
139 return 0;
140 }
141
capture_sample(FILE * file,unsigned int device,unsigned int channels,unsigned int rate,unsigned int bits)142 unsigned int capture_sample(FILE *file, unsigned int device,
143 unsigned int channels, unsigned int rate,
144 unsigned int bits)
145 {
146 struct pcm_config config;
147 struct pcm *pcm;
148 char *buffer;
149 unsigned int size;
150 unsigned int bytes_read = 0;
151
152 config.channels = channels;
153 config.rate = rate;
154 config.period_size = 1024;
155 config.period_count = 4;
156 if (bits == 32)
157 config.format = PCM_FORMAT_S32_LE;
158 else if (bits == 16)
159 config.format = PCM_FORMAT_S16_LE;
160 config.start_threshold = 0;
161 config.stop_threshold = 0;
162 config.silence_threshold = 0;
163
164 pcm = pcm_open(0, device, PCM_IN, &config);
165 if (!pcm || !pcm_is_ready(pcm)) {
166 fprintf(stderr, "Unable to open PCM device (%s)\n",
167 pcm_get_error(pcm));
168 return 0;
169 }
170
171 size = pcm_get_buffer_size(pcm);
172 buffer = malloc(size);
173 if (!buffer) {
174 fprintf(stderr, "Unable to allocate %d bytes\n", size);
175 free(buffer);
176 pcm_close(pcm);
177 return 0;
178 }
179
180 printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate, bits);
181
182 while (capturing && !pcm_read(pcm, buffer, size)) {
183 if (fwrite(buffer, 1, size, file) != size) {
184 fprintf(stderr,"Error capturing sample\n");
185 break;
186 }
187 bytes_read += size;
188 }
189
190 free(buffer);
191 pcm_close(pcm);
192 return bytes_read / ((bits / 8) * channels);
193 }
194
195