/* ** Copyright 2010, The Android Open-Source Project ** ** 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. */ #define LOG_TAG "alsa_pcm" //#define LOG_NDEBUG 0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "alsa_audio.h" #define __force #define __bitwise #define __user #include "asound.h" #define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL int64_t last_read_time = 0; static int64_t systemTime() { struct timespec t; t.tv_sec = t.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC, &t); return t.tv_sec*1000000000LL + t.tv_nsec; } static inline int param_is_mask(int p) { return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); } static inline int param_is_interval(int p) { return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); } static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) { return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); } static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) { return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); } static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) { if (bit >= SNDRV_MASK_MAX) return; if (param_is_mask(n)) { struct snd_mask *m = param_to_mask(p, n); m->bits[0] = 0; m->bits[1] = 0; m->bits[bit >> 5] |= (1 << (bit & 31)); } } static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned val) { if (param_is_interval(n)) { struct snd_interval *i = param_to_interval(p, n); i->min = val; } } static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned val) { if (param_is_interval(n)) { struct snd_interval *i = param_to_interval(p, n); i->max = val; } } static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned val) { if (param_is_interval(n)) { struct snd_interval *i = param_to_interval(p, n); i->min = val; i->max = val; i->integer = 1; } } static void param_init(struct snd_pcm_hw_params *p) { int n; memset(p, 0, sizeof(*p)); for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { struct snd_mask *m = param_to_mask(p, n); m->bits[0] = ~0; m->bits[1] = ~0; } for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { struct snd_interval *i = param_to_interval(p, n); i->min = 0; i->max = ~0; } } /* debugging gunk */ #if DEBUG static const char *param_name[PARAM_MAX+1] = { [SNDRV_PCM_HW_PARAM_ACCESS] = "access", [SNDRV_PCM_HW_PARAM_FORMAT] = "format", [SNDRV_PCM_HW_PARAM_SUBFORMAT] = "subformat", [SNDRV_PCM_HW_PARAM_SAMPLE_BITS] = "sample_bits", [SNDRV_PCM_HW_PARAM_FRAME_BITS] = "frame_bits", [SNDRV_PCM_HW_PARAM_CHANNELS] = "channels", [SNDRV_PCM_HW_PARAM_RATE] = "rate", [SNDRV_PCM_HW_PARAM_PERIOD_TIME] = "period_time", [SNDRV_PCM_HW_PARAM_PERIOD_SIZE] = "period_size", [SNDRV_PCM_HW_PARAM_PERIOD_BYTES] = "period_bytes", [SNDRV_PCM_HW_PARAM_PERIODS] = "periods", [SNDRV_PCM_HW_PARAM_BUFFER_TIME] = "buffer_time", [SNDRV_PCM_HW_PARAM_BUFFER_SIZE] = "buffer_size", [SNDRV_PCM_HW_PARAM_BUFFER_BYTES] = "buffer_bytes", [SNDRV_PCM_HW_PARAM_TICK_TIME] = "tick_time", }; static void param_dump(struct snd_pcm_hw_params *p) { int n; for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { struct snd_mask *m = param_to_mask(p, n); ALOGV("%s = %08x%08x\n", param_name[n], m->bits[1], m->bits[0]); } for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { struct snd_interval *i = param_to_interval(p, n); ALOGV("%s = (%d,%d) omin=%d omax=%d int=%d empty=%d\n", param_name[n], i->min, i->max, i->openmin, i->openmax, i->integer, i->empty); } ALOGV("info = %08x\n", p->info); ALOGV("msbits = %d\n", p->msbits); ALOGV("rate = %d/%d\n", p->rate_num, p->rate_den); ALOGV("fifo = %d\n", (int) p->fifo_size); } static void info_dump(struct snd_pcm_info *info) { ALOGV("device = %d\n", info->device); ALOGV("subdevice = %d\n", info->subdevice); ALOGV("stream = %d\n", info->stream); ALOGV("card = %d\n", info->card); ALOGV("id = '%s'\n", info->id); ALOGV("name = '%s'\n", info->name); ALOGV("subname = '%s'\n", info->subname); ALOGV("dev_class = %d\n", info->dev_class); ALOGV("dev_subclass = %d\n", info->dev_subclass); ALOGV("subdevices_count = %d\n", info->subdevices_count); ALOGV("subdevices_avail = %d\n", info->subdevices_avail); } #else static void param_dump(struct snd_pcm_hw_params *p) {} static void info_dump(struct snd_pcm_info *info) {} #endif unsigned pcm_buffer_size(struct pcm *pcm) { return pcm->buffer_size; } const char* pcm_error(struct pcm *pcm) { return pcm->error; } static int oops(struct pcm *pcm, int e, const char *fmt, ...) __attribute__((format(printf, 3, 4))); static int oops(struct pcm *pcm, int e, const char *fmt, ...) { va_list ap; int sz; va_start(ap, fmt); vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); va_end(ap); sz = strlen(pcm->error); if (errno) snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, ": %s", strerror(e)); return -1; } int pcm_write(struct pcm *pcm, void *data, unsigned count) { struct snd_xferi x; if (pcm->flags & PCM_IN) return -EINVAL; x.buf = data; x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); for (;;) { if (!pcm->running) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) return oops(pcm, errno, "cannot prepare channel"); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) return oops(pcm, errno, "cannot write initial data"); pcm->running = 1; return 0; } if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { #ifdef SUPPORT_USB //usb sound card out, so sleep for data and return no error. unsigned int usleep_time = 0; unsigned int frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); if ((pcm->flags & PCM_RATE_MASK) == PCM_8000HZ) usleep_time = frames * 1000 * 1000 / 8000; else if ((pcm->flags & PCM_RATE_MASK) == PCM_48000HZ) usleep_time = frames * 1000 * 1000 / 48000; else usleep_time = frames * 1000 * 1000 / 44100; usleep(usleep_time); return 0; #endif pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart */ pcm->underruns++; continue; } return oops(pcm, errno, "cannot write stream data"); } return 0; } } /******************************** author:charles chen data:2012.09.27 parameter data: the input data buf point len: the input data len need consider the pcm_format ret: 0:Left and right channel is valid 1:Left channel is valid 2:Right channel is valid defalt the input signal is like LRLRLR,default pcm_format is 16bit *********************************/ #define SAMPLECOUNT 441*5*2*2 int channalFlags = -1;//mean the channel is not checked now int startCheckCount = 0; int channel_check(void * data, unsigned len) { short * pcmLeftChannel = (short *)data; short * pcmRightChannel = pcmLeftChannel+1; unsigned index = 0; int leftValid = 0x0; int rightValid = 0x0; short checkValue = 0; checkValue = *pcmLeftChannel; //checkleft first for(index = 0; index < len; index += 2) { if((pcmLeftChannel[index] >= checkValue+50)||(pcmLeftChannel[index] <= checkValue-50)) { leftValid++;// = 0x01; //ALOGI("-->pcmLeftChannel[%d] = %d checkValue %d leftValid %d",index,pcmLeftChannel[index],checkValue,leftValid); //break; } } if(leftValid >20) leftValid = 0x01; else leftValid = 0; checkValue = *pcmRightChannel; //then check right for(index = 0; index < len; index += 2) { if((pcmRightChannel[index] >= checkValue+50)||(pcmRightChannel[index] <= checkValue-50)) { rightValid++;//= 0x02; //ALOGI("-->pcmRightChannel[%d] = %d checkValue %d rightValid %d",index,pcmRightChannel[index],checkValue,rightValid); //break; } } if(rightValid >20) rightValid = 0x02; else rightValid = 0; ALOGI("leftValid %d rightValid %d",leftValid,rightValid); return leftValid|rightValid; } void channel_fixed(void * data, unsigned len, int chFlag) { //we just fixed when chFlag is 1 or 2. if(chFlag <= 0 || chFlag > 2 ) return; short * pcmValid = (short *)data; short * pcmInvalid = pcmValid; if(chFlag == 1) pcmInvalid += 1; else if (chFlag == 2) pcmValid += 1; unsigned index ; for(index = 0; index < len; index += 2) { pcmInvalid[index] = pcmValid[index]; } return; } int pcm_read(struct pcm *pcm, void *data, unsigned count) { struct snd_xferi x; int ret = 0; if (!(pcm->flags & PCM_IN)) return -EINVAL; x.buf = data; x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); // ALOGV("read() %d frames", x.frames); for (;;) { if (!pcm->running) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) return oops(pcm, errno, "cannot prepare channel"); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START)) return oops(pcm, errno, "cannot start channel"); pcm->running = 1; } if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { //#ifdef SUPPORT_USB if (((pcm->flags & PCM_CARD_MASK) >> PCM_CARD_SHIFT) == PCM_CARD2) { //usb sound card out, so set data to 0, and sleep for data int need_usleep_time; unsigned int usleep_time = 0; unsigned int frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); if ((pcm->flags & PCM_RATE_MASK) == PCM_8000HZ) usleep_time = frames * 1000 * 1000 / 8000; else if ((pcm->flags & PCM_RATE_MASK) == PCM_48000HZ) usleep_time = frames * 1000 * 1000 / 48000; else usleep_time = frames * 1000 * 1000 / 44100; memset(data, 0, count); if (last_read_time == 0) last_read_time = systemTime(); need_usleep_time = usleep_time - (systemTime() - last_read_time) / 1000; if (need_usleep_time > 0) usleep(need_usleep_time); last_read_time = systemTime(); return 0; } //#endif pcm->running = 0; if (errno == EPIPE) { /* we failed to make our window -- try to restart */ pcm->underruns++; continue; } return oops(pcm, errno, "cannot read stream data"); } last_read_time = systemTime(); // ALOGV("read() got %d frames", x.frames); if(!(pcm->flags & PCM_MONO)) { //LOGI("read() get %d", x.frames); if(channalFlags == -1 ) { if(startCheckCount < SAMPLECOUNT) { startCheckCount += count; } else { channalFlags = channel_check(data,count/2); } }//if(channalFlags == -1) channel_fixed(data,count/2, channalFlags); } return 0; } } static struct pcm bad_pcm = { .fd = -1, }; int pcm_close(struct pcm *pcm) { ALOGD("pcm_close() card %u, device %u, %s", (pcm->flags & PCM_CARD_MASK) >> PCM_CARD_SHIFT, (pcm->flags & PCM_DEVICE_MASK) >> PCM_DEVICE_SHIFT, (pcm->flags & PCM_IN) ? "Capture" : "Playback"); if (pcm == &bad_pcm) return 0; if (pcm->fd >= 0) close(pcm->fd); pcm->running = 0; pcm->buffer_size = 0; pcm->fd = -1; free(pcm); return 0; } struct pcm *pcm_open(unsigned flags) { const char *dfmt = "/dev/snd/pcmC%uD%u%c"; char dname[sizeof(dfmt) + 20]; struct pcm *pcm; struct snd_pcm_info info; struct snd_pcm_hw_params params; struct snd_pcm_sw_params sparams; unsigned card; unsigned device; unsigned period_sz; unsigned period_cnt; ALOGD("pcm_open(0x%08x)", flags); pcm = calloc(1, sizeof(struct pcm)); if (!pcm) return &bad_pcm; __open_again: card = (flags & PCM_CARD_MASK) >> PCM_CARD_SHIFT; device = (flags & PCM_DEVICE_MASK) >> PCM_DEVICE_SHIFT; sprintf(dname, dfmt, card, device, flags & PCM_IN ? 'c' : 'p'); ALOGD("pcm_open() card %u, device %u, %s", card, device, (flags & PCM_IN) ? "Capture" : "Playback"); pcm->flags = flags; pcm->fd = open(dname, O_RDWR|O_CLOEXEC); if (pcm->fd < 0) { oops(pcm, errno, "cannot open device '%s'", dname); if ((flags & PCM_CARD_MASK) == PCM_CARD1) { ALOGD("Open sound card1 for HDMI error, open sound card0"); flags &= ~PCM_CARD_MASK; goto __open_again; } return pcm; } while(pcm->fd == 0 || pcm->fd == 1 || pcm->fd == 2) { ALOGD("pcm_open old_fd=%d",pcm->fd); int tmp_fd = pcm->fd; pcm->fd = dup(tmp_fd); close(tmp_fd); ALOGD("pcm_open new_fd=%d",pcm->fd); } if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { oops(pcm, errno, "cannot get info - %s", dname); goto fail; } info_dump(&info); ALOGV("pcm_open() period sz multiplier %d", ((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1); period_sz = PCM_PERIOD_SZ_MIN * (((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1); ALOGV("pcm_open() period cnt %d", ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN); period_cnt = ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN; ALOGV("pcm_open() period_cnt %d period_sz %d channels %d", period_cnt, period_sz, (flags & PCM_MONO) ? 1 : 2); param_init(¶ms); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_FORMAT_S16_LE); param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, SNDRV_PCM_SUBFORMAT_STD); param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, period_sz); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, (flags & PCM_MONO) ? 16 : 32); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, (flags & PCM_MONO) ? 1 : 2); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, period_cnt); if ((flags & PCM_RATE_MASK) == PCM_8000HZ) { ALOGD("set audio rate 8KHz"); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 8000); } else if ((flags & PCM_RATE_MASK) == PCM_48000HZ) { ALOGD("set audio rate 48KHz"); param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 48000); } else param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 44100); if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { oops(pcm, errno, "cannot set hw params"); goto fail; } param_dump(¶ms); memset(&sparams, 0, sizeof(sparams)); sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; sparams.period_step = 1; sparams.avail_min = 1; sparams.start_threshold = period_cnt * period_sz; sparams.stop_threshold = period_cnt * period_sz; sparams.xfer_align = period_sz / 2; /* needed for old kernels */ sparams.silence_size = 0; sparams.silence_threshold = 0; if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { oops(pcm, errno, "cannot set sw params"); goto fail; } //Set prepare for device 1/2 of codec if (device != 0 && card == 0) { if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) ALOGE("pcm_open() cannot set prepare for card %d, device %d", card, device); } pcm->buffer_size = period_cnt * period_sz; pcm->underruns = 0; return pcm; fail: close(pcm->fd); pcm->fd = -1; return pcm; } int pcm_ready(struct pcm *pcm) { return pcm->fd >= 0; }