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