/* * Copyright (C) 2012 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. */ // TODO remove all the headers upto asound.h after removing pcm_drain hack #include <errno.h> #include <unistd.h> #include <poll.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/time.h> #include <limits.h> #include <linux/ioctl.h> #define __force #define __bitwise #define __user #include <sound/asound.h> #include <string.h> #include <tinyalsa/asoundlib.h> #include "audio/AudioHardware.h" #include "audio/Buffer.h" #include "Log.h" #include "audio/AudioPlaybackLocal.h" AudioPlaybackLocal::AudioPlaybackLocal(int hwId) : mHwId(hwId), mPcmHandle(NULL) { LOGV("AudioPlaybackLocal %x", (unsigned long)this); } AudioPlaybackLocal::~AudioPlaybackLocal() { LOGV("~AudioPlaybackLocal %x", (unsigned long)this); releaseHw(); } bool AudioPlaybackLocal::doPrepare(AudioHardware::SamplingRate samplingRate, int samplesInOneGo) { releaseHw(); struct pcm_config config; memset(&config, 0, sizeof(config)); config.channels = 2; config.rate = samplingRate; config.period_size = 1024; config.period_count = 64; config.format = PCM_FORMAT_S16_LE; config.start_threshold = 0; config.stop_threshold = 0; config.silence_threshold = 0; mPcmHandle = pcm_open(mHwId, 0, PCM_OUT, &config); if (!mPcmHandle || !pcm_is_ready(mPcmHandle)) { LOGE("Unable to open PCM device(%d) (%s)\n", mHwId, pcm_get_error(mPcmHandle)); return false; } mSamples = samplesInOneGo; mSizes = samplesInOneGo * 4; // stereo, 16bit return true; } bool AudioPlaybackLocal::doPlaybackOrRecord(android::sp<Buffer>& buffer) { if (buffer->amountToHandle() < (size_t)mSizes) { mSizes = buffer->amountToHandle(); } if (pcm_write(mPcmHandle, buffer->getUnhanledData(), mSizes)) { LOGE("AudioPlaybackLocal error %s", pcm_get_error(mPcmHandle)); return false; } buffer->increaseHandled(mSizes); LOGV("AudioPlaybackLocal::doPlaybackOrRecord %d", buffer->amountHandled()); return true; } void AudioPlaybackLocal::doStop() { // TODO: remove when pcm_stop does pcm_drain // hack to have snd_pcm_drain equivalent struct pcm_ { int fd; }; pcm_* pcm = (pcm_*)mPcmHandle; ioctl(pcm->fd, SNDRV_PCM_IOCTL_DRAIN); pcm_stop(mPcmHandle); } void AudioPlaybackLocal::releaseHw() { if (mPcmHandle != NULL) { LOGV("releaseHw %x", (unsigned long)this); doStop(); pcm_close(mPcmHandle); mPcmHandle = NULL; } }