1/* 2** Copyright 2010, The Android Open-Source Project 3** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. 4** 5** Licensed under the Apache License, Version 2.0 (the "License"); 6** you may not use this file except in compliance with the License. 7** You may obtain a copy of the License at 8** 9** http://www.apache.org/licenses/LICENSE-2.0 10** 11** Unless required by applicable law or agreed to in writing, software 12** distributed under the License is distributed on an "AS IS" BASIS, 13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14** See the License for the specific language governing permissions and 15** limitations under the License. 16*/ 17 18#include <stdio.h> 19#include <stdlib.h> 20#include <fcntl.h> 21#include <unistd.h> 22#include <stdint.h> 23#include <string.h> 24#include <signal.h> 25#include <errno.h> 26#include <sys/poll.h> 27#include <sys/ioctl.h> 28#include <getopt.h> 29#include <limits.h> 30 31#include "alsa_audio.h" 32 33#define ID_RIFF 0x46464952 34#define ID_WAVE 0x45564157 35#define ID_FMT 0x20746d66 36#define ID_DATA 0x61746164 37 38#define FORMAT_PCM 1 39 40#ifndef ANDROID 41#define strlcat g_strlcat 42#define strlcpy g_strlcpy 43#endif 44 45static struct wav_header hdr; 46static int fd; 47static struct pcm *pcm; 48static int debug = 0; 49static int pcm_flag = 1; 50static int duration = 0; 51static char *filename; 52static char *data; 53static int format = SNDRV_PCM_FORMAT_S16_LE; 54static int period = 0; 55static int piped = 0; 56 57static struct option long_options[] = 58{ 59 {"pcm", 0, 0, 'P'}, 60 {"debug", 0, 0, 'V'}, 61 {"Mmap", 0, 0, 'M'}, 62 {"HW", 1, 0, 'D'}, 63 {"Rate", 1, 0, 'R'}, 64 {"channel", 1, 0, 'C'}, 65 {"duration", 1, 0, 'T'}, 66 {"format", 1, 0, 'F'}, 67 {"period", 1, 0, 'B'}, 68 {0, 0, 0, 0} 69}; 70 71struct wav_header { 72 uint32_t riff_id; 73 uint32_t riff_sz; 74 uint32_t riff_fmt; 75 uint32_t fmt_id; 76 uint32_t fmt_sz; 77 uint16_t audio_format; 78 uint16_t num_channels; 79 uint32_t sample_rate; 80 uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ 81 uint16_t block_align; /* num_channels * bps / 8 */ 82 uint16_t bits_per_sample; 83 uint32_t data_id; 84 uint32_t data_sz; 85}; 86 87static int set_params(struct pcm *pcm) 88{ 89 struct snd_pcm_hw_params *params; 90 struct snd_pcm_sw_params *sparams; 91 92 unsigned long periodSize, bufferSize, reqBuffSize; 93 unsigned int periodTime, bufferTime; 94 unsigned int requestedRate = pcm->rate; 95 96 params = (struct snd_pcm_hw_params*) calloc(1, sizeof(struct snd_pcm_hw_params)); 97 if (!params) { 98 fprintf(stderr, "Arec:Failed to allocate ALSA hardware parameters!"); 99 return -ENOMEM; 100 } 101 102 param_init(params); 103 104 param_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS, 105 (pcm->flags & PCM_MMAP)? SNDRV_PCM_ACCESS_MMAP_INTERLEAVED : SNDRV_PCM_ACCESS_RW_INTERLEAVED); 106 param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, pcm->format); 107 param_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT, 108 SNDRV_PCM_SUBFORMAT_STD); 109 if (period) 110 param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, period); 111 else 112 param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 10); 113 param_set_int(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16); 114 param_set_int(params, SNDRV_PCM_HW_PARAM_FRAME_BITS, 115 pcm->channels * 16); 116 param_set_int(params, SNDRV_PCM_HW_PARAM_CHANNELS, 117 pcm->channels); 118 param_set_int(params, SNDRV_PCM_HW_PARAM_RATE, pcm->rate); 119 120 param_set_hw_refine(pcm, params); 121 122 if (param_set_hw_params(pcm, params)) { 123 fprintf(stderr, "Arec:cannot set hw params"); 124 return -errno; 125 } 126 if (debug) 127 param_dump(params); 128 129 pcm->buffer_size = pcm_buffer_size(params); 130 pcm->period_size = pcm_period_size(params); 131 pcm->period_cnt = pcm->buffer_size/pcm->period_size; 132 if (debug) { 133 fprintf (stderr,"period_size (%d)", pcm->period_size); 134 fprintf (stderr," buffer_size (%d)", pcm->buffer_size); 135 fprintf (stderr," period_cnt (%d)\n", pcm->period_cnt); 136 } 137 sparams = (struct snd_pcm_sw_params*) calloc(1, sizeof(struct snd_pcm_sw_params)); 138 if (!sparams) { 139 fprintf(stderr, "Arec:Failed to allocate ALSA software parameters!\n"); 140 return -ENOMEM; 141 } 142 sparams->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; 143 sparams->period_step = 1; 144 145 if (pcm->flags & PCM_MONO) { 146 sparams->avail_min = pcm->period_size/2; 147 sparams->xfer_align = pcm->period_size/2; 148 } else if (pcm->flags & PCM_QUAD) { 149 sparams->avail_min = pcm->period_size/8; 150 sparams->xfer_align = pcm->period_size/8; 151 } else if (pcm->flags & PCM_5POINT1) { 152 sparams->avail_min = pcm->period_size/12; 153 sparams->xfer_align = pcm->period_size/12; 154 } else { 155 sparams->avail_min = pcm->period_size/4; 156 sparams->xfer_align = pcm->period_size/4; 157 } 158 159 sparams->start_threshold = 1; 160 sparams->stop_threshold = INT_MAX; 161 sparams->silence_size = 0; 162 sparams->silence_threshold = 0; 163 164 if (param_set_sw_params(pcm, sparams)) { 165 fprintf(stderr, "Arec:cannot set sw params"); 166 return -errno; 167 } 168 if (debug) { 169 fprintf (stderr,"avail_min (%lu)\n", sparams->avail_min); 170 fprintf (stderr,"start_threshold (%lu)\n", sparams->start_threshold); 171 fprintf (stderr,"stop_threshold (%lu)\n", sparams->stop_threshold); 172 fprintf (stderr,"xfer_align (%lu)\n", sparams->xfer_align); 173 } 174 return 0; 175 176} 177 178int record_file(unsigned rate, unsigned channels, int fd, unsigned count, unsigned flags, const char *device) 179{ 180 unsigned xfer, bufsize; 181 int r, avail; 182 int nfds = 1; 183 static int start = 0; 184 struct snd_xferi x; 185 long frames; 186 unsigned offset = 0; 187 int err; 188 struct pollfd pfd[1]; 189 int rec_size = 0; 190 191 flags |= PCM_IN; 192 193 if (channels == 1) 194 flags |= PCM_MONO; 195 else if (channels == 4) 196 flags |= PCM_QUAD; 197 else if (channels == 6) 198 flags |= PCM_5POINT1; 199 else 200 flags |= PCM_STEREO; 201 202 pcm = pcm_open(flags, device); 203 if (!pcm_ready(pcm)) { 204 pcm_close(pcm); 205 goto fail; 206 } 207 pcm->channels = channels; 208 pcm->rate = rate; 209 pcm->flags = flags; 210 pcm->format = format; 211 if (set_params(pcm)) { 212 fprintf(stderr, "Arec:params setting failed\n"); 213 pcm_close(pcm); 214 return -EINVAL; 215 } 216 217 if (!pcm_flag) { 218 if (pcm_prepare(pcm)) { 219 fprintf(stderr, "Arec:Failed in pcm_prepare\n"); 220 pcm_close(pcm); 221 return -errno; 222 } 223 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) { 224 fprintf(stderr, "Arec: Hostless IOCTL_START Error no %d \n", errno); 225 pcm_close(pcm); 226 return -errno; 227 } 228 while(1); 229 } 230 231 if (flags & PCM_MMAP) { 232 u_int8_t *dst_addr = NULL; 233 struct snd_pcm_sync_ptr *sync_ptr1 = pcm->sync_ptr; 234 unsigned int tmp; 235 236 if (mmap_buffer(pcm)) { 237 fprintf(stderr, "Arec:params setting failed\n"); 238 pcm_close(pcm); 239 return -EINVAL; 240 } 241 if (debug) 242 fprintf(stderr, "Arec:mmap_buffer done\n"); 243 244 if (pcm_prepare(pcm)) { 245 fprintf(stderr, "Arec:Failed in pcm_prepare\n"); 246 pcm_close(pcm); 247 return -errno; 248 } 249 250 bufsize = pcm->period_size; 251 if (debug) 252 fprintf(stderr, "Arec:bufsize = %d\n", bufsize); 253 if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) { 254 if (errno == EPIPE) { 255 fprintf(stderr, "Arec:Failed in SNDRV_PCM_IOCTL_START\n"); 256 /* we failed to make our window -- try to restart */ 257 pcm->running = 0; 258 } else { 259 fprintf(stderr, "Arec:Error no %d \n", errno); 260 return -errno; 261 } 262 } 263 264 pfd[0].fd = pcm->fd; 265 pfd[0].events = POLLIN; 266 267 hdr.data_sz = 0; 268 if (pcm->flags & PCM_MONO) { 269 frames = bufsize / 2; 270 } else if (pcm->flags & PCM_QUAD) { 271 frames = bufsize / 8; 272 } else if (pcm->flags & PCM_5POINT1) { 273 frames = bufsize / 12; 274 } else{ 275 frames = bufsize / 4; 276 } 277 x.frames = frames; 278 for(;;) { 279 if (!pcm->running) { 280 if (pcm_prepare(pcm)) 281 return --errno; 282 start = 0; 283 } 284 /* Sync the current Application pointer from the kernel */ 285 pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;//SNDRV_PCM_SYNC_PTR_HWSYNC; 286 err = sync_ptr(pcm); 287 if (err == EPIPE) { 288 fprintf(stderr, "Arec:Failed in sync_ptr \n"); 289 /* we failed to make our window -- try to restart */ 290 //pcm->overruns++; 291 pcm->running = 0; 292 continue; 293 } 294 /* 295 * Check for the available data in driver. If available data is 296 * less than avail_min we need to wait 297 */ 298 avail = pcm_avail(pcm); 299 if (debug) 300 fprintf(stderr, "Arec:avail 1 = %d frames = %ld\n",avail, frames); 301 if (avail < 0) 302 return avail; 303 if (avail < pcm->sw_p->avail_min) { 304 poll(pfd, nfds, TIMEOUT_INFINITE); 305 continue; 306 } 307 if (x.frames > avail) 308 frames = avail; 309 /* 310 * Now that we have data size greater than avail_min available to 311 * to be read we need to calcutate the buffer offset where we can 312 * start reading from. 313 */ 314 dst_addr = dst_address(pcm); 315 316 /* 317 * Write to the file at the destination address from kernel mmaped buffer 318 * This reduces a extra copy of intermediate buffer. 319 */ 320 if (write(fd, dst_addr, bufsize) != bufsize) { 321 fprintf(stderr, "Arec:could not write %d bytes\n", bufsize); 322 return -errno; 323 } 324 x.frames -= frames; 325 pcm->sync_ptr->c.control.appl_ptr += frames; 326 pcm->sync_ptr->flags = 0; 327 err = sync_ptr(pcm); 328 if (err == EPIPE) { 329 fprintf(stderr, "Arec:Failed in sync_ptr \n"); 330 /* we failed to make our window -- try to restart */ 331 pcm->running = 0; 332 continue; 333 } 334 rec_size += bufsize; 335 hdr.data_sz += bufsize; 336 hdr.riff_sz = hdr.data_sz + 44 - 8; 337 if (!piped) { 338 lseek(fd, 0, SEEK_SET); 339 write(fd, &hdr, sizeof(hdr)); 340 lseek(fd, 0, SEEK_END); 341 } 342 if (rec_size >= count) 343 break; 344 } 345 } else { 346 bufsize = pcm->period_size; 347 if (pcm_prepare(pcm)) { 348 fprintf(stderr, "Arec:Failed in pcm_prepare\n"); 349 pcm_close(pcm); 350 return -errno; 351 } 352 353 data = calloc(1, bufsize); 354 if (!data) { 355 fprintf(stderr, "Arec:could not allocate %d bytes\n", bufsize); 356 return -ENOMEM; 357 } 358 359 while (!pcm_read(pcm, data, bufsize)) { 360 if (write(fd, data, bufsize) != bufsize) { 361 fprintf(stderr, "Arec:could not write %d bytes\n", bufsize); 362 break; 363 } 364 rec_size += bufsize; 365 hdr.data_sz += bufsize; 366 hdr.riff_sz = hdr.data_sz + 44 - 8; 367 if (!piped) { 368 lseek(fd, 0, SEEK_SET); 369 write(fd, &hdr, sizeof(hdr)); 370 lseek(fd, 0, SEEK_END); 371 } 372 if (rec_size >= count) 373 break; 374 } 375 } 376 fprintf(stderr, " rec_size =%d count =%d\n", rec_size, count); 377 close(fd); 378 free(data); 379 pcm_close(pcm); 380 return hdr.data_sz; 381 382fail: 383 fprintf(stderr, "Arec:pcm error: %s\n", pcm_error(pcm)); 384 return -errno; 385} 386 387int rec_raw(const char *fg, const char *device, int rate, int ch, 388 const char *fn) 389{ 390 unsigned flag = 0; 391 uint32_t rec_max_sz = 2147483648LL; 392 uint32_t count; 393 int i = 0; 394 395 if (!fn) { 396 fd = fileno(stdout); 397 piped = 1; 398 } else { 399 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); 400 if (fd < 0) { 401 fprintf(stderr, "Arec:arec: cannot open '%s'\n", fn); 402 return -EBADFD; 403 } 404 } 405 if (duration == 0) { 406 count = rec_max_sz; 407 } else { 408 count = rate * ch * 2; 409 count *= (uint32_t)duration; 410 } 411 count = count < rec_max_sz ? count : rec_max_sz; 412 if (debug) 413 fprintf(stderr, "arec: %d ch, %d hz, %d bit, format %x\n", 414 ch, rate, 16, format); 415 416 if (!strncmp(fg, "M", sizeof("M"))) { 417 flag = PCM_MMAP; 418 } else if (!strncmp(fg, "N", sizeof("N"))) { 419 flag = PCM_NMMAP; 420 } 421 return record_file(rate, ch, fd, count, flag, device); 422} 423 424int rec_wav(const char *fg, const char *device, int rate, int ch, const char *fn) 425{ 426 unsigned flag = 0; 427 uint32_t rec_max_sz = 2147483648LL; 428 uint32_t count = 0; 429 int i = 0; 430 431 if (pcm_flag) { 432 if (!fn) { 433 fd = fileno(stdout); 434 piped = 1; 435 } else { 436 fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); 437 if (fd < 0) { 438 fprintf(stderr, "Arec:arec: cannot open '%s'\n", fn); 439 return -EBADFD; 440 } 441 } 442 memset(&hdr, 0, sizeof(struct wav_header)); 443 hdr.riff_id = ID_RIFF; 444 hdr.riff_fmt = ID_WAVE; 445 hdr.fmt_id = ID_FMT; 446 hdr.fmt_sz = 16; 447 hdr.audio_format = FORMAT_PCM; 448 hdr.num_channels = ch; 449 hdr.sample_rate = rate; 450 hdr.bits_per_sample = 16; 451 hdr.byte_rate = (rate * ch * hdr.bits_per_sample) / 8; 452 hdr.block_align = ( hdr.bits_per_sample * ch ) / 8; 453 hdr.data_id = ID_DATA; 454 hdr.data_sz = 0; 455 456 if (duration == 0) { 457 count = rec_max_sz; 458 } else { 459 count = rate * ch * 2; 460 count *= (uint32_t)duration; 461 } 462 hdr.riff_sz = hdr.data_sz + 44 - 8; 463 if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { 464 if (debug) 465 fprintf(stderr, "arec: cannot write header\n"); 466 return -errno; 467 } 468 if (debug) 469 fprintf(stderr, "arec: %d ch, %d hz, %d bit, %s\n", 470 hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample, 471 hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown"); 472 } else { 473 hdr.sample_rate = rate; 474 hdr.num_channels = ch; 475 } 476 477 if (!strncmp(fg, "M", sizeof("M"))) { 478 flag = PCM_MMAP; 479 } else if (!strncmp(fg, "N", sizeof("N"))) { 480 flag = PCM_NMMAP; 481 } 482 return record_file(hdr.sample_rate, hdr.num_channels, fd, count, flag, device); 483} 484 485static void signal_handler(int sig) 486{ 487 long file_size; 488 FILE *fp; 489 490 fprintf(stderr, "Arec:Aborted by signal %s...\n", strsignal(sig)); 491 fprintf(stderr, "Arec:lseeked to %d", (int) lseek(fd, 0, SEEK_SET)); 492 hdr.riff_sz = hdr.data_sz + 44 - 8; 493 fprintf(stderr, "Arec: hdr.data_sz =%d\n", hdr.data_sz); 494 fprintf(stderr, "Arec: hdr.riff_sz =%d\n", hdr.riff_sz); 495 if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { 496 if (debug) 497 fprintf(stderr, "Arec:arec: cannot write header\n"); 498 } else 499 fd = -1; 500 501 if (fd > 1) { 502 close(fd); 503 fd = -1; 504 } 505 free(filename); 506 free(data); 507 pcm = NULL; 508 raise(sig); 509} 510 511int main(int argc, char **argv) 512{ 513 int rate = 48000; 514 int ch = 1; 515 int i = 0; 516 int option_index = 0; 517 int c; 518 char *mmap = "N"; 519 char *device = "hw:0,0"; 520 struct sigaction sa; 521 int rc = 0; 522 523 if (argc < 2) { 524 printf("\nUsage: arec [options] <file>\n" 525 "options:\n" 526 "-D <hw:C,D> -- Alsa PCM by name\n" 527 "-M -- Mmap stream\n" 528 "-P -- Hostless steam[No PCM]\n" 529 "-V -- verbose\n" 530 "-C -- Channels\n" 531 "-R -- Rate\n" 532 "-T -- Time in seconds for recording\n" 533 "-F -- Format\n" 534 "-B -- Period\n" 535 "<file> \n"); 536 for (i = 0; i < SNDRV_PCM_FORMAT_LAST; ++i) 537 if (get_format_name(i)) 538 fprintf(stderr, "%s ", get_format_name(i)); 539 fprintf(stderr, "\nSome of these may not be available on selected hardware\n"); 540 return 0; 541 } 542 while ((c = getopt_long(argc, argv, "PVMD:R:C:T:F:B:", long_options, &option_index)) != -1) { 543 switch (c) { 544 case 'P': 545 pcm_flag = 0; 546 break; 547 case 'V': 548 debug = 1; 549 break; 550 case 'M': 551 mmap = "M"; 552 break; 553 case 'D': 554 device = optarg; 555 break; 556 case 'R': 557 rate = (int)strtol(optarg, NULL, 0); 558 break; 559 case 'C': 560 ch = (int)strtol(optarg, NULL, 0); 561 break; 562 case 'T': 563 duration = (int)strtol(optarg, NULL, 0); 564 break; 565 case 'F': 566 format = (int)get_format(optarg); 567 break; 568 case 'B': 569 period = (int)strtol(optarg, NULL, 0); 570 break; 571 default: 572 printf("\nUsage: arec [options] <file>\n" 573 "options:\n" 574 "-D <hw:C,D> -- Alsa PCM by name\n" 575 "-M -- Mmap stream\n" 576 "-P -- Hostless steam[No PCM]\n" 577 "-V -- verbose\n" 578 "-C -- Channels\n" 579 "-R -- Rate\n" 580 "-T -- Time in seconds for recording\n" 581 "-F -- Format\n" 582 "-B -- Period\n" 583 "<file> \n"); 584 for (i = 0; i < SNDRV_PCM_FORMAT_LAST; ++i) 585 if (get_format_name(i)) 586 fprintf(stderr, "%s ", get_format_name(i)); 587 fprintf(stderr, "\nSome of these may not be available on selected hardware\n"); 588 return -EINVAL; 589 } 590 } 591 filename = (char*) calloc(1, 30); 592 if (!filename) { 593 fprintf(stderr, "Arec:Failed to allocate filename!"); 594 return -ENOMEM; 595 } 596 if (optind > argc - 1) { 597 free(filename); 598 filename = NULL; 599 } else { 600 strlcpy(filename, argv[optind++], 30); 601 } 602 603 memset(&sa, 0, sizeof(sa)); 604 sa.sa_handler = &signal_handler; 605 sigaction(SIGABRT, &sa, NULL); 606 607 if (pcm_flag) { 608 if (format == SNDRV_PCM_FORMAT_S16_LE) 609 rc = rec_wav(mmap, device, rate, ch, filename); 610 else 611 rc = rec_raw(mmap, device, rate, ch, filename); 612 } else { 613 rc = rec_wav(mmap, device, rate, ch, "dummy"); 614 } 615 if (filename) 616 free(filename); 617 618 return rc; 619} 620 621