initial audio HAL implementation for mako
alsa_sound is imported from codeaurora at:
c1217338f349fe746e0933fcf9b1b288b532808d
[remote "quic"]
url = git://git-android.quicinc.com/platform/hardware/alsa_sound.git
review = review-android.quicinc.com
projectname = platform/hardware/alsa_sound
fetch = +refs/heads/*:refs/remotes/quic/*
Change-Id: Ic985cc3a1088c3957b6e2ac5537e2c36caaf7212
Signed-off-by: Iliyan Malchev <malchev@google.com>
diff --git a/libalsa-intf/arec.c b/libalsa-intf/arec.c
new file mode 100644
index 0000000..08fe758
--- /dev/null
+++ b/libalsa-intf/arec.c
@@ -0,0 +1,620 @@
+/*
+** Copyright 2010, The Android Open-Source Project
+** Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <getopt.h>
+#include <limits.h>
+
+#include "alsa_audio.h"
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+
+#define FORMAT_PCM 1
+
+#ifndef ANDROID
+#define strlcat g_strlcat
+#define strlcpy g_strlcpy
+#endif
+
+static struct wav_header hdr;
+static int fd;
+static struct pcm *pcm;
+static debug = 0;
+static pcm_flag = 1;
+static duration = 0;
+static char *filename;
+static char *data;
+static int format = SNDRV_PCM_FORMAT_S16_LE;
+static int period = 0;
+static int piped = 0;
+
+static struct option long_options[] =
+{
+ {"pcm", 0, 0, 'P'},
+ {"debug", 0, 0, 'V'},
+ {"Mmap", 0, 0, 'M'},
+ {"HW", 1, 0, 'D'},
+ {"Rate", 1, 0, 'R'},
+ {"channel", 1, 0, 'C'},
+ {"duration", 1, 0, 'T'},
+ {"format", 1, 0, 'F'},
+ {"period", 1, 0, 'B'},
+ {0, 0, 0, 0}
+};
+
+struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */
+ uint16_t block_align; /* num_channels * bps / 8 */
+ uint16_t bits_per_sample;
+ uint32_t data_id;
+ uint32_t data_sz;
+};
+
+static int set_params(struct pcm *pcm)
+{
+ struct snd_pcm_hw_params *params;
+ struct snd_pcm_sw_params *sparams;
+
+ unsigned long periodSize, bufferSize, reqBuffSize;
+ unsigned int periodTime, bufferTime;
+ unsigned int requestedRate = pcm->rate;
+
+ params = (struct snd_pcm_hw_params*) calloc(1, sizeof(struct snd_pcm_hw_params));
+ if (!params) {
+ fprintf(stderr, "Arec:Failed to allocate ALSA hardware parameters!");
+ return -ENOMEM;
+ }
+
+ param_init(params);
+
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_ACCESS,
+ (pcm->flags & PCM_MMAP)? SNDRV_PCM_ACCESS_MMAP_INTERLEAVED : SNDRV_PCM_ACCESS_RW_INTERLEAVED);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, pcm->format);
+ param_set_mask(params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
+ SNDRV_PCM_SUBFORMAT_STD);
+ if (period)
+ param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, period);
+ else
+ param_set_min(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 10);
+ param_set_int(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16);
+ param_set_int(params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ pcm->channels * 16);
+ param_set_int(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+ pcm->channels);
+ param_set_int(params, SNDRV_PCM_HW_PARAM_RATE, pcm->rate);
+
+ param_set_hw_refine(pcm, params);
+
+ if (param_set_hw_params(pcm, params)) {
+ fprintf(stderr, "Arec:cannot set hw params");
+ return -errno;
+ }
+ if (debug)
+ param_dump(params);
+
+ pcm->buffer_size = pcm_buffer_size(params);
+ pcm->period_size = pcm_period_size(params);
+ pcm->period_cnt = pcm->buffer_size/pcm->period_size;
+ if (debug) {
+ fprintf (stderr,"period_size (%d)", pcm->period_size);
+ fprintf (stderr," buffer_size (%d)", pcm->buffer_size);
+ fprintf (stderr," period_cnt (%d)\n", pcm->period_cnt);
+ }
+ sparams = (struct snd_pcm_sw_params*) calloc(1, sizeof(struct snd_pcm_sw_params));
+ if (!sparams) {
+ fprintf(stderr, "Arec:Failed to allocate ALSA software parameters!\n");
+ return -ENOMEM;
+ }
+ sparams->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
+ sparams->period_step = 1;
+
+ if (pcm->flags & PCM_MONO) {
+ sparams->avail_min = pcm->period_size/2;
+ sparams->xfer_align = pcm->period_size/2;
+ } else if (pcm->flags & PCM_QUAD) {
+ sparams->avail_min = pcm->period_size/8;
+ sparams->xfer_align = pcm->period_size/8;
+ } else if (pcm->flags & PCM_5POINT1) {
+ sparams->avail_min = pcm->period_size/12;
+ sparams->xfer_align = pcm->period_size/12;
+ } else {
+ sparams->avail_min = pcm->period_size/4;
+ sparams->xfer_align = pcm->period_size/4;
+ }
+
+ sparams->start_threshold = 1;
+ sparams->stop_threshold = INT_MAX;
+ sparams->silence_size = 0;
+ sparams->silence_threshold = 0;
+
+ if (param_set_sw_params(pcm, sparams)) {
+ fprintf(stderr, "Arec:cannot set sw params");
+ return -errno;
+ }
+ if (debug) {
+ fprintf (stderr,"avail_min (%lu)\n", sparams->avail_min);
+ fprintf (stderr,"start_threshold (%lu)\n", sparams->start_threshold);
+ fprintf (stderr,"stop_threshold (%lu)\n", sparams->stop_threshold);
+ fprintf (stderr,"xfer_align (%lu)\n", sparams->xfer_align);
+ }
+ return 0;
+
+}
+
+int record_file(unsigned rate, unsigned channels, int fd, unsigned count, unsigned flags, const char *device)
+{
+ unsigned xfer, bufsize;
+ int r, avail;
+ int nfds = 1;
+ static int start = 0;
+ struct snd_xferi x;
+ long frames;
+ unsigned offset = 0;
+ int err;
+ struct pollfd pfd[1];
+ int rec_size = 0;
+
+ flags |= PCM_IN;
+
+ if (channels == 1)
+ flags |= PCM_MONO;
+ else if (channels == 4)
+ flags |= PCM_QUAD;
+ else if (channels == 6)
+ flags |= PCM_5POINT1;
+ else
+ flags |= PCM_STEREO;
+
+ pcm = pcm_open(flags, device);
+ if (!pcm_ready(pcm)) {
+ pcm_close(pcm);
+ goto fail;
+ }
+ pcm->channels = channels;
+ pcm->rate = rate;
+ pcm->flags = flags;
+ pcm->format = format;
+ if (set_params(pcm)) {
+ fprintf(stderr, "Arec:params setting failed\n");
+ pcm_close(pcm);
+ return -EINVAL;
+ }
+
+ if (!pcm_flag) {
+ if (pcm_prepare(pcm)) {
+ fprintf(stderr, "Arec:Failed in pcm_prepare\n");
+ pcm_close(pcm);
+ return -errno;
+ }
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
+ fprintf(stderr, "Arec: Hostless IOCTL_START Error no %d \n", errno);
+ pcm_close(pcm);
+ return -errno;
+ }
+ while(1);
+ }
+
+ if (flags & PCM_MMAP) {
+ u_int8_t *dst_addr = NULL;
+ struct snd_pcm_sync_ptr *sync_ptr1 = pcm->sync_ptr;
+ unsigned int tmp;
+
+ if (mmap_buffer(pcm)) {
+ fprintf(stderr, "Arec:params setting failed\n");
+ pcm_close(pcm);
+ return -EINVAL;
+ }
+ if (debug)
+ fprintf(stderr, "Arec:mmap_buffer done\n");
+
+ if (pcm_prepare(pcm)) {
+ fprintf(stderr, "Arec:Failed in pcm_prepare\n");
+ pcm_close(pcm);
+ return -errno;
+ }
+
+ bufsize = pcm->period_size;
+ if (debug)
+ fprintf(stderr, "Arec:bufsize = %d\n", bufsize);
+ if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) {
+ if (errno == EPIPE) {
+ fprintf(stderr, "Arec:Failed in SNDRV_PCM_IOCTL_START\n");
+ /* we failed to make our window -- try to restart */
+ pcm->running = 0;
+ } else {
+ fprintf(stderr, "Arec:Error no %d \n", errno);
+ return -errno;
+ }
+ }
+
+ pfd[0].fd = pcm->fd;
+ pfd[0].events = POLLIN;
+
+ hdr.data_sz = 0;
+ if (pcm->flags & PCM_MONO) {
+ frames = bufsize / 2;
+ } else if (pcm->flags & PCM_QUAD) {
+ frames = bufsize / 8;
+ } else if (pcm->flags & PCM_5POINT1) {
+ frames = bufsize / 12;
+ } else{
+ frames = bufsize / 4;
+ }
+ x.frames = frames;
+ for(;;) {
+ if (!pcm->running) {
+ if (pcm_prepare(pcm))
+ return --errno;
+ start = 0;
+ }
+ /* Sync the current Application pointer from the kernel */
+ pcm->sync_ptr->flags = SNDRV_PCM_SYNC_PTR_APPL | SNDRV_PCM_SYNC_PTR_AVAIL_MIN;//SNDRV_PCM_SYNC_PTR_HWSYNC;
+ err = sync_ptr(pcm);
+ if (err == EPIPE) {
+ fprintf(stderr, "Arec:Failed in sync_ptr \n");
+ /* we failed to make our window -- try to restart */
+ //pcm->overruns++;
+ pcm->running = 0;
+ continue;
+ }
+ /*
+ * Check for the available data in driver. If available data is
+ * less than avail_min we need to wait
+ */
+ avail = pcm_avail(pcm);
+ if (debug)
+ fprintf(stderr, "Arec:avail 1 = %d frames = %ld\n",avail, frames);
+ if (avail < 0)
+ return avail;
+ if (avail < pcm->sw_p->avail_min) {
+ poll(pfd, nfds, TIMEOUT_INFINITE);
+ continue;
+ }
+ if (x.frames > avail)
+ frames = avail;
+ /*
+ * Now that we have data size greater than avail_min available to
+ * to be read we need to calcutate the buffer offset where we can
+ * start reading from.
+ */
+ dst_addr = dst_address(pcm);
+
+ /*
+ * Write to the file at the destination address from kernel mmaped buffer
+ * This reduces a extra copy of intermediate buffer.
+ */
+ if (write(fd, dst_addr, bufsize) != bufsize) {
+ fprintf(stderr, "Arec:could not write %d bytes\n", bufsize);
+ return -errno;
+ }
+ x.frames -= frames;
+ pcm->sync_ptr->c.control.appl_ptr += frames;
+ pcm->sync_ptr->flags = 0;
+ err = sync_ptr(pcm);
+ if (err == EPIPE) {
+ fprintf(stderr, "Arec:Failed in sync_ptr \n");
+ /* we failed to make our window -- try to restart */
+ pcm->running = 0;
+ continue;
+ }
+ rec_size += bufsize;
+ hdr.data_sz += bufsize;
+ hdr.riff_sz = hdr.data_sz + 44 - 8;
+ if (!piped) {
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &hdr, sizeof(hdr));
+ lseek(fd, 0, SEEK_END);
+ }
+ if (rec_size >= count)
+ break;
+ }
+ } else {
+ bufsize = pcm->period_size;
+ if (pcm_prepare(pcm)) {
+ fprintf(stderr, "Arec:Failed in pcm_prepare\n");
+ pcm_close(pcm);
+ return -errno;
+ }
+
+ data = calloc(1, bufsize);
+ if (!data) {
+ fprintf(stderr, "Arec:could not allocate %d bytes\n", bufsize);
+ return -ENOMEM;
+ }
+
+ while (!pcm_read(pcm, data, bufsize)) {
+ if (write(fd, data, bufsize) != bufsize) {
+ fprintf(stderr, "Arec:could not write %d bytes\n", bufsize);
+ break;
+ }
+ rec_size += bufsize;
+ hdr.data_sz += bufsize;
+ hdr.riff_sz = hdr.data_sz + 44 - 8;
+ if (!piped) {
+ lseek(fd, 0, SEEK_SET);
+ write(fd, &hdr, sizeof(hdr));
+ lseek(fd, 0, SEEK_END);
+ }
+ if (rec_size >= count)
+ break;
+ }
+ }
+ fprintf(stderr, " rec_size =%d count =%d\n", rec_size, count);
+ close(fd);
+ free(data);
+ pcm_close(pcm);
+ return hdr.data_sz;
+
+fail:
+ fprintf(stderr, "Arec:pcm error: %s\n", pcm_error(pcm));
+ return -errno;
+}
+
+int rec_raw(const char *fg, const char *device, int rate, int ch,
+ const char *fn)
+{
+ unsigned flag = 0;
+ uint32_t rec_max_sz = 2147483648LL;
+ uint32_t count;
+ int i = 0;
+
+ if (!fn) {
+ fd = fileno(stdout);
+ piped = 1;
+ } else {
+ fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
+ if (fd < 0) {
+ fprintf(stderr, "Arec:arec: cannot open '%s'\n", fn);
+ return -EBADFD;
+ }
+ }
+ if (duration == 0) {
+ count = rec_max_sz;
+ } else {
+ count = rate * ch * 2;
+ count *= (uint32_t)duration;
+ }
+ count = count < rec_max_sz ? count : rec_max_sz;
+ if (debug)
+ fprintf(stderr, "arec: %d ch, %d hz, %d bit, format %x\n",
+ ch, rate, 16, format);
+
+ if (!strncmp(fg, "M", sizeof("M"))) {
+ flag = PCM_MMAP;
+ } else if (!strncmp(fg, "N", sizeof("N"))) {
+ flag = PCM_NMMAP;
+ }
+ return record_file(rate, ch, fd, count, flag, device);
+}
+
+int rec_wav(const char *fg, const char *device, int rate, int ch, const char *fn)
+{
+ unsigned flag = 0;
+ uint32_t rec_max_sz = 2147483648LL;
+ uint32_t count = 0;
+ int i = 0;
+
+ if (pcm_flag) {
+ if (!fn) {
+ fd = fileno(stdout);
+ piped = 1;
+ } else {
+ fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
+ if (fd < 0) {
+ fprintf(stderr, "Arec:arec: cannot open '%s'\n", fn);
+ return -EBADFD;
+ }
+ }
+ memset(&hdr, 0, sizeof(struct wav_header));
+ hdr.riff_id = ID_RIFF;
+ hdr.riff_fmt = ID_WAVE;
+ hdr.fmt_id = ID_FMT;
+ hdr.fmt_sz = 16;
+ hdr.audio_format = FORMAT_PCM;
+ hdr.num_channels = ch;
+ hdr.sample_rate = rate;
+ hdr.bits_per_sample = 16;
+ hdr.byte_rate = (rate * ch * hdr.bits_per_sample) / 8;
+ hdr.block_align = ( hdr.bits_per_sample * ch ) / 8;
+ hdr.data_id = ID_DATA;
+ hdr.data_sz = 0;
+
+ if (duration == 0) {
+ count = rec_max_sz;
+ } else {
+ count = rate * ch * 2;
+ count *= (uint32_t)duration;
+ }
+ hdr.riff_sz = hdr.data_sz + 44 - 8;
+ if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ if (debug)
+ fprintf(stderr, "arec: cannot write header\n");
+ return -errno;
+ }
+ if (debug)
+ fprintf(stderr, "arec: %d ch, %d hz, %d bit, %s\n",
+ hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample,
+ hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown");
+ } else {
+ hdr.sample_rate = rate;
+ hdr.num_channels = ch;
+ }
+
+ if (!strncmp(fg, "M", sizeof("M"))) {
+ flag = PCM_MMAP;
+ } else if (!strncmp(fg, "N", sizeof("N"))) {
+ flag = PCM_NMMAP;
+ }
+ return record_file(hdr.sample_rate, hdr.num_channels, fd, count, flag, device);
+}
+
+static void signal_handler(int sig)
+{
+ long file_size;
+ FILE *fp;
+
+ fprintf(stderr, "Arec:Aborted by signal %s...\n", strsignal(sig));
+ fprintf(stderr, "Arec:lseeked to %d", (int) lseek(fd, 0, SEEK_SET));
+ hdr.riff_sz = hdr.data_sz + 44 - 8;
+ fprintf(stderr, "Arec: hdr.data_sz =%d\n", hdr.data_sz);
+ fprintf(stderr, "Arec: hdr.riff_sz =%d\n", hdr.riff_sz);
+ if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ if (debug)
+ fprintf(stderr, "Arec:arec: cannot write header\n");
+ } else
+ fd = -1;
+
+ if (fd > 1) {
+ close(fd);
+ fd = -1;
+ }
+ free(filename);
+ free(data);
+ pcm = NULL;
+ raise(sig);
+}
+
+int main(int argc, char **argv)
+{
+ int rate = 48000;
+ int ch = 1;
+ int i = 0;
+ int option_index = 0;
+ int c;
+ char *mmap = "N";
+ char *device = "hw:0,0";
+ struct sigaction sa;
+ int rc = 0;
+
+ if (argc < 2) {
+ printf("\nUsage: arec [options] <file>\n"
+ "options:\n"
+ "-D <hw:C,D> -- Alsa PCM by name\n"
+ "-M -- Mmap stream\n"
+ "-P -- Hostless steam[No PCM]\n"
+ "-V -- verbose\n"
+ "-C -- Channels\n"
+ "-R -- Rate\n"
+ "-T -- Time in seconds for recording\n"
+ "-F -- Format\n"
+ "-B -- Period\n"
+ "<file> \n");
+ for (i = 0; i < SNDRV_PCM_FORMAT_LAST; ++i)
+ if (get_format_name(i))
+ fprintf(stderr, "%s ", get_format_name(i));
+ fprintf(stderr, "\nSome of these may not be available on selected hardware\n");
+ return 0;
+ }
+ while ((c = getopt_long(argc, argv, "PVMD:R:C:T:F:B:", long_options, &option_index)) != -1) {
+ switch (c) {
+ case 'P':
+ pcm_flag = 0;
+ break;
+ case 'V':
+ debug = 1;
+ break;
+ case 'M':
+ mmap = "M";
+ break;
+ case 'D':
+ device = optarg;
+ break;
+ case 'R':
+ rate = (int)strtol(optarg, NULL, 0);
+ break;
+ case 'C':
+ ch = (int)strtol(optarg, NULL, 0);
+ break;
+ case 'T':
+ duration = (int)strtol(optarg, NULL, 0);
+ break;
+ case 'F':
+ format = (int)get_format(optarg);
+ break;
+ case 'B':
+ period = (int)strtol(optarg, NULL, 0);
+ break;
+ default:
+ printf("\nUsage: arec [options] <file>\n"
+ "options:\n"
+ "-D <hw:C,D> -- Alsa PCM by name\n"
+ "-M -- Mmap stream\n"
+ "-P -- Hostless steam[No PCM]\n"
+ "-V -- verbose\n"
+ "-C -- Channels\n"
+ "-R -- Rate\n"
+ "-T -- Time in seconds for recording\n"
+ "-F -- Format\n"
+ "-B -- Period\n"
+ "<file> \n");
+ for (i = 0; i < SNDRV_PCM_FORMAT_LAST; ++i)
+ if (get_format_name(i))
+ fprintf(stderr, "%s ", get_format_name(i));
+ fprintf(stderr, "\nSome of these may not be available on selected hardware\n");
+ return -EINVAL;
+ }
+ }
+ filename = (char*) calloc(1, 30);
+ if (!filename) {
+ fprintf(stderr, "Arec:Failed to allocate filename!");
+ return -ENOMEM;
+ }
+ if (optind > argc - 1) {
+ free(filename);
+ filename = NULL;
+ } else {
+ strlcpy(filename, argv[optind++], 30);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signal_handler;
+ sigaction(SIGABRT, &sa, NULL);
+
+ if (pcm_flag) {
+ if (format == SNDRV_PCM_FORMAT_S16_LE)
+ rc = rec_wav(mmap, device, rate, ch, filename);
+ else
+ rc = rec_raw(mmap, device, rate, ch, filename);
+ } else {
+ rc = rec_wav(mmap, device, rate, ch, "dummy");
+ }
+ if (filename)
+ free(filename);
+
+ return rc;
+}
+