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
45 static struct wav_header hdr;
46 static int fd;
47 static struct pcm *pcm;
48 static int debug = 0;
49 static int pcm_flag = 1;
50 static int duration = 0;
51 static char *filename;
52 static char *data;
53 static int format = SNDRV_PCM_FORMAT_S16_LE;
54 static int period = 0;
55 static int piped = 0;
56
57 static 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
71 struct 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
set_params(struct pcm * pcm)87 static 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
record_file(unsigned rate,unsigned channels,int fd,unsigned count,unsigned flags,const char * device)178 int 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
382 fail:
383 fprintf(stderr, "Arec:pcm error: %s\n", pcm_error(pcm));
384 return -errno;
385 }
386
rec_raw(const char * fg,const char * device,int rate,int ch,const char * fn)387 int 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
rec_wav(const char * fg,const char * device,int rate,int ch,const char * fn)424 int 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
signal_handler(int sig)485 static 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
main(int argc,char ** argv)511 int 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