xref: /aosp_15_r20/external/tinyalsa_new/utils/tinycap.c (revision 02e95f1a335b55495d41ca67eaf42361f13704fa)
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