• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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