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 42struct 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 58int capturing = 1; 59 60unsigned int capture_sample(FILE *file, unsigned int device, 61 unsigned int channels, unsigned int rate, 62 unsigned int bits); 63 64void sigint_handler(int sig) 65{ 66 capturing = 0; 67} 68 69int 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 142unsigned 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