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