blob: 942090f4fc31d7192cd66d74ec1bf9f7ed6d83ea [file] [log] [blame]
/*
** Copyright 2008-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.
*/
#include <math.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioHardwareTegra"
#include <utils/Log.h>
#include <utils/String8.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <fcntl.h>
#include "AudioHardware.h"
#include <media/AudioRecord.h>
namespace android {
const uint32_t AudioHardware::inputSamplingRates[] = {
8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000
};
// ----------------------------------------------------------------------------
AudioHardware::AudioHardware() :
mInit(false), mMicMute(true), mBluetoothNrec(true), mBluetoothId(0),
mOutput(0), mCpcapCtlFd(-1), mMasterVol(1.0), mVoiceVol(1.0)
{
LOGV("AudioHardware constructor");
mCpcapCtlFd = ::open("/dev/audio_ctl", O_RDWR);
if (mCpcapCtlFd < 0) {
LOGE("Failed to initialized: %s\n", strerror(errno));
return;
}
::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_OUTPUT, &mCurOutDevice);
::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_INPUT, &mCurInDevice);
// For bookkeeping only
::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_RATE, &mHwOutRate);
::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_RATE, &mHwInRate);
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
// Init the MM Audio Post Processing
mAudioPP.setAudioDev(&mCurOutDevice, &mCurInDevice, false, false, false);
#endif
readHwGainFile();
mInit = true;
}
AudioHardware::~AudioHardware()
{
LOGV("AudioHardware destructor");
for (size_t index = 0; index < mInputs.size(); index++) {
closeInputStream((AudioStreamIn*)mInputs[index]);
}
mInputs.clear();
closeOutputStream((AudioStreamOut*)mOutput);
::close(mCpcapCtlFd);
}
void AudioHardware::readHwGainFile()
{
int fd;
int rc=0;
int i;
uint32_t format, version, barker;
fd = open("/system/etc/cpcap_gain.bin", O_RDONLY);
if (fd>=0) {
::read(fd, &format, sizeof(uint32_t));
::read(fd, &version, sizeof(uint32_t));
::read(fd, &barker, sizeof(uint32_t));
rc = ::read(fd, mCpcapGain, sizeof(mCpcapGain));
LOGD("Read gain file, format %X version %X", format, version);
::close(fd);
}
if (rc != sizeof(mCpcapGain) || format != 0x30303031) {
int gain;
LOGE("CPCAP gain file not valid. Using defaults.");
for (int i=0; i<AUDIO_HW_GAIN_NUM_DIRECTIONS; i++) {
if (i==AUDIO_HW_GAIN_SPKR_GAIN)
gain = 11;
else
gain = 31;
for (int j=0; j<AUDIO_HW_GAIN_NUM_USECASES; j++)
for (int k=0; k<AUDIO_HW_GAIN_NUM_PATHS; k++)
mCpcapGain[i][j][k]=gain;
}
}
return;
}
status_t AudioHardware::initCheck()
{
return mInit ? NO_ERROR : NO_INIT;
}
AudioStreamOut* AudioHardware::openOutputStream(
uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
{
{ // scope for the lock
Mutex::Autolock lock(mLock);
// only one output stream allowed
if (mOutput) {
if (status) {
*status = INVALID_OPERATION;
}
return 0;
}
// create new output stream
AudioStreamOutTegra* out = new AudioStreamOutTegra();
status_t lStatus = out->set(this, devices, format, channels, sampleRate);
if (status) {
*status = lStatus;
}
if (lStatus == NO_ERROR) {
mOutput = out;
} else {
mLock.unlock();
delete out;
mLock.lock();
}
}
return mOutput;
}
void AudioHardware::closeOutputStream(AudioStreamOut* out) {
Mutex::Autolock lock(mLock);
if (mOutput == 0 || mOutput != out) {
LOGW("Attempt to close invalid output stream");
}
else {
mLock.unlock();
delete mOutput;
mLock.lock();
mOutput = 0;
}
}
AudioStreamIn* AudioHardware::openInputStream(
uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
AudioSystem::audio_in_acoustics acoustic_flags)
{
// check for valid input source
if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
return 0;
}
Mutex::Autolock lock(mLock);
AudioStreamInTegra* in = new AudioStreamInTegra();
status_t lStatus = in->set(this, devices, format, channels, sampleRate, acoustic_flags);
if (status) {
*status = lStatus;
}
if (lStatus != NO_ERROR) {
mLock.unlock();
delete in;
mLock.lock();
return 0;
}
mInputs.add(in);
return in;
}
void AudioHardware::closeInputStream(AudioStreamIn* in)
{
Mutex::Autolock lock(mLock);
ssize_t index = mInputs.indexOf((AudioStreamInTegra *)in);
if (index < 0) {
LOGW("Attempt to close invalid input stream");
} else {
mLock.unlock();
delete mInputs[index];
mLock.lock();
mInputs.removeAt(index);
}
}
status_t AudioHardware::setMode(int mode)
{
return AudioHardwareBase::setMode(mode);
}
// Must be called with mLock held
status_t AudioHardware::doStandby(int stop_fd, bool output, bool enable)
{
status_t status = NO_ERROR;
struct cpcap_audio_stream standby;
LOGV("AudioHardware::doStandby() putting %s in %s mode",
output ? "output" : "input",
enable ? "standby" : "online" );
// Debug code
if (!mLock.tryLock()) {
LOGE("doStandby called without mLock held.");
mLock.unlock();
}
// end Debug code
if (output) {
standby.id = CPCAP_AUDIO_OUT_STANDBY;
standby.on = enable;
if (enable) {
/* Flush the queued playback data. Putting the output in standby
* will cause CPCAP to not drive the i2s interface, and write()
* will block until playback is resumed.
*/
if (mOutput)
mOutput->flush();
}
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_OUTPUT, &standby) < 0) {
LOGE("could not turn off current output device: %s\n",
strerror(errno));
status = errno;
}
::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_OUTPUT, &mCurOutDevice);
LOGV("%s: after standby %s, output is %s", __FUNCTION__,
enable ? "enable" : "disable",
mCurOutDevice.on ? "on" : "off");
} else {
standby.id = CPCAP_AUDIO_IN_STANDBY;
standby.on = enable;
if (enable && stop_fd >= 0) {
/* Stop recording, if ongoing. Muting the microphone will cause
* CPCAP to not send data through the i2s interface, and read()
* will block until recording is resumed.
*/
LOGV("%s: stop recording", __FUNCTION__);
if (::ioctl(stop_fd, TEGRA_AUDIO_IN_STOP) < 0) {
LOGE("could not stop recording: %s\n",
strerror(errno));
}
}
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_INPUT, &standby) < 0) {
LOGE("could not turn off current input device: %s\n",
strerror(errno));
status = errno;
}
::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_INPUT, &mCurInDevice);
LOGV("%s: after standby %s, input is %s", __FUNCTION__,
enable ? "enable" : "disable",
mCurInDevice.on ? "on" : "off");
}
return status;
}
status_t AudioHardware::setMicMute(bool state)
{
Mutex::Autolock lock(mLock);
return setMicMute_nosync(state);
}
// always call with mutex held
status_t AudioHardware::setMicMute_nosync(bool state)
{
if (mMicMute != state) {
mMicMute = state;
return NO_ERROR; //doAudioRouteOrMute(SND_DEVICE_CURRENT);
}
return NO_ERROR;
}
status_t AudioHardware::getMicMute(bool* state)
{
*state = mMicMute;
return NO_ERROR;
}
status_t AudioHardware::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 value;
String8 key;
const char BT_NREC_KEY[] = "bt_headset_nrec";
const char BT_NAME_KEY[] = "bt_headset_name";
const char BT_NREC_VALUE_ON[] = "on";
LOGV("setParameters() %s", keyValuePairs.string());
if (keyValuePairs.length() == 0) return BAD_VALUE;
key = String8(BT_NREC_KEY);
if (param.get(key, value) == NO_ERROR) {
if (value == BT_NREC_VALUE_ON) {
mBluetoothNrec = true;
LOGI("Turn on bluetooth NREC");
} else {
mBluetoothNrec = false;
LOGI("Turning noise reduction and echo cancellation off for BT "
"headset");
}
doRouting();
}
key = String8(BT_NAME_KEY);
if (param.get(key, value) == NO_ERROR) {
mBluetoothId = 0;
#if 0
for (int i = 0; i < mNumSndEndpoints; i++) {
if (!strcasecmp(value.string(), mSndEndpoints[i].name)) {
mBluetoothId = mSndEndpoints[i].id;
LOGI("Using custom acoustic parameters for %s", value.string());
break;
}
}
#endif
if (mBluetoothId == 0) {
LOGI("Using default acoustic parameters "
"(%s not in acoustic database)", value.string());
doRouting();
}
}
return NO_ERROR;
}
String8 AudioHardware::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
return param.toString();
}
size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
size_t bufsize;
if (format != AudioSystem::PCM_16_BIT) {
LOGW("getInputBufferSize bad format: %d", format);
return 0;
}
if (channelCount < 1 || channelCount > 2) {
LOGW("getInputBufferSize bad channel count: %d", channelCount);
return 0;
}
// Return 20 msec input buffer size.
bufsize = sampleRate * sizeof(int16_t) * channelCount / 50;
if (bufsize & 0x7) {
// Not divisible by 8.
bufsize +=8;
bufsize &= ~0x7;
}
LOGD("%s: returns %d for rate %d", __FUNCTION__, bufsize, sampleRate);
return bufsize;
}
//setVoiceVolume is only useful for setting sidetone gains with a baseband
//controlling volume. Don't adjust hardware volume with this API.
//
//(On Stingray, don't use mVoiceVol for anything.)
status_t AudioHardware::setVoiceVolume(float v)
{
if (v < 0.0)
v = 0.0;
else if (v > 1.0)
v = 1.0;
LOGI("Setting unused in-call vol to %f",v);
mVoiceVol = v;
return NO_ERROR;
}
status_t AudioHardware::setMasterVolume(float v)
{
if (v < 0.0)
v = 0.0;
else if (v > 1.0)
v = 1.0;
LOGV("Set master vol to %f.\n", v);
mMasterVol = v;
Mutex::Autolock lock(mLock);
if (isInCall())
setVolume_l(v, AUDIO_HW_GAIN_USECASE_VOICE);
else
setVolume_l(v, AUDIO_HW_GAIN_USECASE_MM);
return NO_ERROR;
}
// Call with mLock held.
status_t AudioHardware::setVolume_l(float v, int usecase)
{
status_t ret;
int spkr = getGain(AUDIO_HW_GAIN_SPKR_GAIN, usecase);
int mic = getGain(AUDIO_HW_GAIN_MIC_GAIN, usecase);
if (spkr==0) {
// no device to set volume on. Ignore request.
return -1;
}
spkr = ceil(v * spkr);
LOGD("Set tx volume to %d, rx to %d.\n", spkr, mic);
ret = ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_VOLUME, spkr);
if (ret < 0)
LOGE("could not set spkr volume: %s\n", strerror(errno));
else {
ret = ::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_VOLUME, mic);
if (ret < 0)
LOGE("could not set mic volume: %s\n", strerror(errno));
}
return ret;
}
uint8_t AudioHardware::getGain(int direction, int usecase)
{
int path;
AudioStreamInTegra *input = getActiveInput_l();
uint32_t inDev = (input == NULL) ? 0 : input->devices();
if (!mOutput) {
LOGE("No output device.");
return 0;
}
uint32_t outDev = mOutput->devices();
// In case of an actual phone, with an actual earpiece, uncomment.
// if (outDev & AudioSystem::DEVICE_OUT_EARPIECE)
// path = AUDIO_HW_GAIN_EARPIECE;
// else
if (outDev & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE)
path = AUDIO_HW_GAIN_HEADSET_NO_MIC;
else if (outDev & AudioSystem::DEVICE_OUT_WIRED_HEADSET)
path = AUDIO_HW_GAIN_HEADSET_W_MIC;
// TODO: support dock.
// else if (outDev & AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET)
// path = AUDIO_HW_GAIN_EMU_DEVICE;
else
path = AUDIO_HW_GAIN_SPEAKERPHONE;
LOGV("Picked gain[%d][%d][%d] which is %d.",direction, usecase, path,
mCpcapGain[direction][usecase][path]);
return mCpcapGain[direction][usecase][path];
}
int AudioHardware::getActiveInputRate()
{
AudioStreamInTegra *input = getActiveInput_l();
return (input != NULL) ? input->sampleRate() : 0;
}
status_t AudioHardware::doRouting()
{
Mutex::Autolock lock(mLock);
return doRouting_l();
}
// Call this with mLock held.
status_t AudioHardware::doRouting_l()
{
uint32_t outputDevices = mOutput->devices();
AudioStreamInTegra *input = getActiveInput_l();
uint32_t inputDevice = (input == NULL) ? 0 : input->devices();
uint32_t btScoOutDevices = outputDevices & (
AudioSystem::DEVICE_OUT_BLUETOOTH_SCO |
AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT );
uint32_t spdifOutDevices = outputDevices & (
AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET |
AudioSystem::DEVICE_OUT_AUX_DIGITAL );
uint32_t speakerOutDevices = outputDevices ^ btScoOutDevices ^ spdifOutDevices;
uint32_t btScoInDevice = inputDevice & AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET;
uint32_t micInDevice = inputDevice ^ btScoInDevice;
int sndOutDevice = -1;
int sndInDevice = -1;
LOGV("%s: inputDevice %x, outputDevices %x", __FUNCTION__,
inputDevice, outputDevices);
switch (inputDevice) {
case AudioSystem::DEVICE_IN_DEFAULT:
case AudioSystem::DEVICE_IN_BUILTIN_MIC:
sndInDevice = CPCAP_AUDIO_IN_MIC1;
break;
case AudioSystem::DEVICE_IN_WIRED_HEADSET:
sndInDevice = CPCAP_AUDIO_IN_MIC2;
break;
default:
break;
}
switch (speakerOutDevices) {
case AudioSystem::DEVICE_OUT_EARPIECE:
case AudioSystem::DEVICE_OUT_DEFAULT:
case AudioSystem::DEVICE_OUT_SPEAKER:
sndOutDevice = CPCAP_AUDIO_OUT_SPEAKER;
break;
case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
sndOutDevice = CPCAP_AUDIO_OUT_HEADSET;
break;
case AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADSET:
case AudioSystem::DEVICE_OUT_SPEAKER | AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
sndOutDevice = CPCAP_AUDIO_OUT_HEADSET_AND_SPEAKER;
break;
case AudioSystem::DEVICE_OUT_ANLG_DOCK_HEADSET:
sndOutDevice = CPCAP_AUDIO_OUT_ANLG_DOCK_HEADSET;
break;
case AudioSystem::DEVICE_OUT_DGTL_DOCK_HEADSET:
// To be implemented
break;
default:
break;
}
if (sndInDevice == -1) {
LOGV("input device set %x not supported, defaulting to on-board mic",
inputDevice);
mCurInDevice.id = CPCAP_AUDIO_IN_MIC1;
}
else
mCurInDevice.id = sndInDevice;
if (sndOutDevice == -1) {
LOGW("output device set %x not supported, defaulting to speaker",
outputDevices);
mCurOutDevice.id = CPCAP_AUDIO_OUT_SPEAKER;
}
else
mCurOutDevice.id = sndOutDevice;
LOGV("current input %d, %s",
mCurInDevice.id,
mCurInDevice.on ? "on" : "off");
LOGV("current output %d, %s",
mCurOutDevice.id,
mCurOutDevice.on ? "on" : "off");
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_INPUT,
&mCurInDevice) < 0)
LOGE("could not set input (%d, on %d): %s\n",
mCurInDevice.id, mCurInDevice.on, strerror(errno));
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_OUTPUT,
&mCurOutDevice) < 0)
LOGE("could not set output (%d, on %d): %s\n",
mCurOutDevice.id, mCurOutDevice.on,
strerror(errno));
mOutput->setDriver(speakerOutDevices?true:false,
btScoOutDevices||btScoInDevice,
spdifOutDevices?true:false);
if (input)
input->setDriver(micInDevice?true:false,
btScoInDevice?true:false);
//TODO: EC/NS decision that doesn't isn't so presumptuous.
bool ecnsEnabled = mCurOutDevice.on && mCurInDevice.on && getActiveInputRate();
int oldInRate=mHwInRate, oldOutRate=mHwOutRate;
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
int ecnsRate = getActiveInputRate() < 16000? 8000 : 16000;
mAudioPP.setAudioDev(&mCurOutDevice, &mCurInDevice,
btScoOutDevices||btScoInDevice, mBluetoothNrec,
spdifOutDevices?true:false);
mAudioPP.enableEcns(ecnsEnabled);
// Check input/output rates for HW.
if (ecnsEnabled) {
mHwInRate = ecnsRate;
mHwOutRate = mHwInRate;
LOGD("EC/NS active, requests rate as %d for in/out", mHwInRate);
}
else
#endif
{
mHwInRate = getActiveInputRate();
if (mHwInRate == 0)
mHwInRate = 44100;
mHwOutRate = 44100;
LOGV("No EC/NS, set input rate %d, output %d.", mHwInRate, mHwOutRate);
}
if (btScoOutDevices||btScoInDevice) {
mHwOutRate = 8000;
mHwInRate = 8000;
LOGD("Bluetooth SCO active, rate forced to 8K");
}
int speakerOutRate = 0;
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_GET_RATE, &speakerOutRate))
LOGE("could not read output rate: %s\n",
strerror(errno));
if (mHwOutRate != oldOutRate ||
(speakerOutRate!=44100 && (btScoOutDevices||btScoInDevice))) {
int speaker_rate = mHwOutRate;
if (btScoOutDevices||btScoInDevice) {
speaker_rate = 44100;
}
// Flush old data (wrong rate) from I2S driver before changing rate.
if (mOutput)
mOutput->flush();
// Now the DMA is empty, change the rate.
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_OUT_SET_RATE,
speaker_rate) < 0)
LOGE("could not set output rate(%d): %s\n",
speaker_rate, strerror(errno));
}
if (mHwInRate != oldInRate) {
LOGV("Minor TODO: Flush input if active.");
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_SET_RATE,
mHwInRate) < 0)
LOGE("could not set input rate(%d): %s\n",
mHwInRate, strerror(errno));
if (::ioctl(mCpcapCtlFd, CPCAP_AUDIO_IN_GET_RATE, &mHwInRate))
LOGE("CPCAP driver error reading rates.");
}
// Since HW path may have changed, set the hardware gains.
if (ecnsEnabled)
setVolume_l(mMasterVol, AUDIO_HW_GAIN_USECASE_VOICE);
else
setVolume_l(mMasterVol, AUDIO_HW_GAIN_USECASE_MM);
return NO_ERROR;
}
status_t AudioHardware::checkMicMute()
{
Mutex::Autolock lock(mLock);
if (!isInCall()) {
setMicMute_nosync(true);
}
return NO_ERROR;
}
status_t AudioHardware::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
result.append("AudioHardware::dumpInternals\n");
snprintf(buffer, SIZE, "\tmInit: %s\n", mInit? "true": "false");
result.append(buffer);
snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false");
result.append(buffer);
snprintf(buffer, SIZE, "\tmBluetoothNrec: %s\n", mBluetoothNrec? "true": "false");
result.append(buffer);
snprintf(buffer, SIZE, "\tmBluetoothId: %d\n", mBluetoothId);
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
status_t AudioHardware::dump(int fd, const Vector<String16>& args)
{
dumpInternals(fd, args);
for (size_t index = 0; index < mInputs.size(); index++) {
mInputs[index]->dump(fd, args);
}
if (mOutput) {
mOutput->dump(fd, args);
}
return NO_ERROR;
}
uint32_t AudioHardware::getInputSampleRate(uint32_t sampleRate)
{
uint32_t i;
uint32_t prevDelta;
uint32_t delta;
for (i = 0, prevDelta = 0xFFFFFFFF; i < sizeof(inputSamplingRates)/sizeof(uint32_t); i++, prevDelta = delta) {
delta = abs(sampleRate - inputSamplingRates[i]);
if (delta > prevDelta) break;
}
// i is always > 0 here
return inputSamplingRates[i-1];
}
// getActiveInput_l() must be called with mLock held
AudioHardware::AudioStreamInTegra *AudioHardware::getActiveInput_l()
{
for (size_t i = 0; i < mInputs.size(); i++) {
// return first input found not being in standby mode
// as only one input can be in this state
if (mInputs[i]->state() > AudioStreamInTegra::AUDIO_INPUT_CLOSED) {
return mInputs[i];
}
}
return NULL;
}
// ----------------------------------------------------------------------------
// Sample Rate Converter wrapper
//
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
AudioHardware::AudioStreamSrc::AudioStreamSrc()
{
}
AudioHardware::AudioStreamSrc::~AudioStreamSrc()
{
}
void AudioHardware::AudioStreamSrc::init(int inRate, int outRate)
{
SRC_MODE_T srcMode = MODE_END;
SRC_STATUS_T initResult;
mSrcInitted = false;
mSrcStaticData.scratch_buffer = mSrcScratchMem;
// Lots of modes supported, but let's implement only what is required.
if (inRate == 44100) {
srcMode = (
outRate == 8000 ? SRC_44_08 :
outRate == 11025 ? SRC_44_11 :
outRate == 12000 ? SRC_44_12 :
outRate == 16000 ? SRC_44_16 :
outRate == 22050 ? SRC_44_22 :
outRate == 24000 ? SRC_44_24 :
outRate == 32000 ? SRC_44_32 :
/* Invalid */ MODE_END
);
} else if (inRate == 8000) {
srcMode = (
outRate == 11025 ? SRC_08_11 :
outRate == 12000 ? SRC_08_12 :
outRate == 16000 ? SRC_08_16 :
outRate == 22050 ? SRC_08_22 :
outRate == 24000 ? SRC_08_24 :
outRate == 32000 ? SRC_08_32 :
outRate == 44100 ? SRC_08_44 :
outRate == 48000 ? SRC_08_48 :
/* Invalid */ MODE_END
);
} else if (inRate == 16000) {
srcMode = (
outRate == 8000 ? SRC_16_08 :
outRate == 11025 ? SRC_16_11 :
outRate == 12000 ? SRC_16_12 :
outRate == 22050 ? SRC_16_22 :
outRate == 24000 ? SRC_16_24 :
outRate == 32000 ? SRC_16_32 :
outRate == 44100 ? SRC_16_44 :
outRate == 48000 ? SRC_16_48 :
/* Invalid */ MODE_END
);
}
if (srcMode == MODE_END) {
LOGE("Failed to initialize sample rate converter - bad rate");
return;
}
// Do MONO sample rate conversion. Should be half the MCPS of stereo.
// Use SRC_STEREO_INTERLEAVED for stereo data.
initResult = src_init(&mSrcStaticData, srcMode, SRC_MONO);
if (initResult != SRC_SUCCESS) {
LOGE("Failed to initialize sample rate converter - %d",initResult);
return;
}
mSrcInitted = true;
mSrcInRate = inRate;
mSrcOutRate = outRate;
}
#endif
// ----------------------------------------------------------------------------
AudioHardware::AudioStreamOutTegra::AudioStreamOutTegra() :
mHardware(0), mFd(-1), mFdCtl(-1), mStartCount(0), mRetryCount(0), mDevices(0),
mIsSpkrEnabled(0), mIsBtEnabled(0), mIsSpdifEnabled(0)
{
mFd = ::open("/dev/audio0_out", O_RDWR);
mFdCtl = ::open("/dev/audio0_out_ctl", O_RDWR);
mBtFd = ::open("/dev/audio1_out", O_RDWR);
mBtFdCtl = ::open("/dev/audio1_out_ctl", O_RDWR);
mBtFdIoCtl = ::open("/dev/audio1_ctl", O_RDWR);
mSpdifFd = ::open("/dev/spdif_out", O_RDWR);
mSpdifFdCtl = ::open("/dev/spdif_out_ctl", O_RDWR);
}
// Called with mHardware->mLock held.
void AudioHardware::AudioStreamOutTegra::setDriver(bool speaker, bool bluetooth, bool spdif) {
int bit_format = TEGRA_AUDIO_BIT_FORMAT_DEFAULT;
bool is_bt_bypass = false;
Mutex::Autolock lock(mLock);
LOGV("%s: Analog speaker? %s. Bluetooth? %s. S/PDIF? %s.", __FUNCTION__,
speaker?"yes":"no", bluetooth?"yes":"no", spdif?"yes":"no");
if ((mIsBtEnabled && !bluetooth) ||
(mIsSpdifEnabled && !spdif))
flush();
if (mIsSpkrEnabled && !speaker)
mHardware->doStandby(mFdCtl, true, true);
mIsSpkrEnabled = speaker;
mIsBtEnabled = bluetooth;
mIsSpdifEnabled = spdif;
if (mIsBtEnabled) {
bit_format = TEGRA_AUDIO_BIT_FORMAT_DSP;
is_bt_bypass = true;
}
// Setup the I2S2-> DAP2/4 capture/playback path.
::ioctl(mBtFdIoCtl, TEGRA_AUDIO_SET_BIT_FORMAT, &bit_format);
::ioctl(mHardware->mCpcapCtlFd, CPCAP_AUDIO_SET_BLUETOOTH_BYPASS, is_bt_bypass);
}
status_t AudioHardware::AudioStreamOutTegra::set(
AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
{
int lFormat = pFormat ? *pFormat : 0;
uint32_t lChannels = pChannels ? *pChannels : 0;
uint32_t lRate = pRate ? *pRate : 0;
mHardware = hw;
// fix up defaults
if (lFormat == 0) lFormat = format();
if (lChannels == 0) lChannels = channels();
if (lRate == 0) lRate = sampleRate();
// check values
if ((lFormat != format()) ||
(lChannels != channels()) ||
(lRate != sampleRate())) {
if (pFormat) *pFormat = format();
if (pChannels) *pChannels = channels();
if (pRate) *pRate = sampleRate();
return BAD_VALUE;
}
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
mHardware->mAudioPP.setPlayAudioRate(lRate);
#endif
if (pFormat) *pFormat = lFormat;
if (pChannels) *pChannels = lChannels;
if (pRate) *pRate = lRate;
mDevices = devices;
if (mFd >= 0 && mFdCtl >= 0 &&
mBtFd >= 0 &&
mBtFdCtl >= 0 &&
mBtFdIoCtl >= 0) {
if (mSpdifFd < 0 || mSpdifFdCtl < 0)
LOGW("s/pdif driver not present");
return NO_ERROR;
} else {
LOGE("Problem opening device files - Is your kernel compatible?");
return NO_INIT;
}
}
AudioHardware::AudioStreamOutTegra::~AudioStreamOutTegra()
{
standby();
// Prevent someone from flushing the fd during a close.
Mutex::Autolock lock(mFdLock);
if (mFd >= 0) ::close(mFd);
if (mFdCtl >= 0) ::close(mFdCtl);
if (mBtFd >= 0) ::close(mBtFd);
if (mBtFdCtl >= 0) ::close(mBtFdCtl);
if (mBtFdIoCtl >= 0) ::close(mBtFdIoCtl);
if (mSpdifFd >= 0) ::close(mSpdifFd);
if (mSpdifFdCtl >= 0) ::close(mSpdifFdCtl);
}
ssize_t AudioHardware::AudioStreamOutTegra::write(const void* buffer, size_t bytes)
{
// LOGD("AudioStreamOutTegra::write(%p, %u)", buffer, bytes);
int status = NO_INIT;
ssize_t written = 0;
const uint8_t* p = static_cast<const uint8_t*>(buffer);
size_t outsize = bytes;
// Protect output state during the write process.
mHardware->mLock.lock();
Mutex::Autolock lock(mLock);
int outFd = mFd;
int outFdCtl = mFdCtl;
bool stereo = mIsBtEnabled?false:
(channels() == AudioSystem::CHANNEL_OUT_STEREO);
int driverRate;
status = online(); // if already online, a no-op
if (status < 0) {
goto error;
}
driverRate = mHardware->mHwOutRate;
mHardware->mLock.unlock();
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
// Do Multimedia processing if appropriate for device and usecase.
mHardware->mAudioPP.doMmProcessing((void *)buffer, bytes / frameSize());
#endif
if (mIsSpkrEnabled && mIsBtEnabled) {
// When dual routing to CPCAP and Bluetooth, piggyback CPCAP audio now,
// and then down convert for the BT.
// CPCAP is always 44.1 in this case.
// This also works in the three-way routing case.
Mutex::Autolock lock2(mFdLock);
::write(outFd, buffer, outsize);
}
if (mIsSpkrEnabled && mIsSpdifEnabled) {
// When dual routing to Speaker and HDMI, piggyback HDMI now, since it
// has no mic we'll leave the rest of the acoustic processing for the
// CPCAP hardware path.
// This also works in the three-way routing case, except the acoustic
// tuning will be done on Bluetooth, since it has the exclusive mic amd
// it also needs the sample rate conversion
Mutex::Autolock lock2(mFdLock);
::write(mSpdifFd, buffer, outsize);
}
if (mIsBtEnabled) {
outFd = mBtFd;
outFdCtl = mBtFdCtl;
} else if (mIsSpdifEnabled && !mIsSpkrEnabled) {
outFd = mSpdifFd;
outFdCtl = mSpdifFdCtl;
}
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
// Check if sample rate conversion or ECNS are required.
// Caution: Upconversion (from 44.1 to 48) would require a new output buffer larger than the
// original one.
if (driverRate != (int)sampleRate()) {
if (!mSrc.initted() ||
mSrc.inRate() != (int)sampleRate() ||
mSrc.outRate() != driverRate) {
LOGI("%s: downconvert started from %d to %d",__FUNCTION__,
sampleRate(), driverRate);
mSrc.init(sampleRate(), driverRate);
if (!mSrc.initted()) {
status = -1;
goto error;
}
// Workaround to give multiple of 4 bytes to driver: Keep one sample
// buffered in case SRC returns an odd number of samples.
mHaveSpareSample = false;
}
} else {
mSrc.deinit();
}
if (mHardware->mAudioPP.isEcnsEnabled() || mSrc.initted())
{
// cut audio down to Mono for SRC or ECNS
if (channels() == AudioSystem::CHANNEL_OUT_STEREO)
{
// Do stereo-to-mono downmix before SRC, in-place
int16_t *destBuf = (int16_t *) buffer;
for (int i = 0; i < (int)bytes/2; i++) {
destBuf[i] = (destBuf[i*2]>>1) + (destBuf[i*2+1]>>1);
}
outsize >>= 1;
}
}
if (mSrc.initted()) {
// Apply the sample rate conversion.
mSrc.mIoData.input_ptrL = (SRC_INT16_T *) (buffer);
mSrc.mIoData.input_count = outsize / sizeof(SRC_INT16_T);
mSrc.mIoData.input_ptrR = 0;
mSrc.mIoData.output_ptr = (SRC_INT16_T *) (buffer);
mSrc.mIoData.output_count = outsize / sizeof(SRC_INT16_T);
if (mHaveSpareSample) {
// Leave room for placing the spare.
mSrc.mIoData.output_ptr++;
}
mSrc.srcConvert();
LOGV("Converted %d bytes at %d to %d bytes at %d",
outsize, sampleRate(), mSrc.mIoData.output_count*2, driverRate);
if (mHaveSpareSample) {
int16_t *bufp = (int16_t*)buffer;
bufp[0]=mSpareSample;
mSrc.mIoData.output_count++;
mHaveSpareSample = false;
}
outsize = mSrc.mIoData.output_count*2;
LOGV("Outsize is now %d", outsize);
}
if (mHardware->mAudioPP.isEcnsEnabled()) {
// EC/NS is a blocking interface, to synchronise with read.
// It also consumes data when EC/NS is running.
// It expects MONO data.
// If EC/NS is not running, it will return 0, and we need to write this data to the
// driver ourselves.
mLock.unlock();
// Don't call writeDownlinkEcns() with the lock held, or else the
// read thread will block on dorouting / setdevice, and we'll block on
// the read thread (and timeout after 1 sec).
written = mHardware->mAudioPP.writeDownlinkEcns(outFd,(void *)buffer,
stereo, outsize, &mFdLock);
mLock.lock();
}
if (mHardware->mAudioPP.isEcnsEnabled() || mSrc.initted()) {
// Move audio back up to Stereo, if the EC/NS wasn't in fact running and we're
// writing to a stereo device.
if (stereo &&
written != (ssize_t)outsize) {
// Back up to stereo, in place.
int16_t *destBuf = (int16_t *) buffer;
for (int i = outsize/2-1; i >= 0; i--) {
destBuf[i*2] = destBuf[i];
destBuf[i*2+1] = destBuf[i];
}
outsize <<= 1;
}
}
#endif
if (written != (ssize_t)outsize) {
// The sample rate conversion modifies the output size.
if (outsize&0x3) {
int16_t* bufp = (int16_t *)buffer;
LOGV("Keep the spare sample away from the driver.");
mHaveSpareSample = true;
mSpareSample = bufp[outsize/2 - 1];
}
Mutex::Autolock lock2(mFdLock);
written = ::write(outFd, buffer, outsize&(~0x3));
if (written != ((ssize_t)outsize&(~0x3))) {
status = written;
goto error;
}
}
if (written < 0)
LOGE("Error writing %d bytes to output: %s", outsize, strerror(errno));
// Sample rate converter may be stashing a couple of bytes here or there,
// so just report that all bytes were consumed. (it would be a bug not to.)
return (written>0)?bytes:0;
error:
LOGE("write(): error, return %d", status);
usleep(bytes * 1000 / frameSize() / sampleRate() * 1000);
return status;
}
void AudioHardware::AudioStreamOutTegra::flush()
{
// Prevent someone from writing the fd while we flush
Mutex::Autolock lock(mFdLock);
LOGD("AudioStreamOutTegra::flush()");
if (::ioctl(mFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0)
LOGE("could not flush playback: %s\n", strerror(errno));
if (::ioctl(mBtFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0)
LOGE("could not flush bluetooth: %s\n", strerror(errno));
if (::ioctl(mSpdifFdCtl, TEGRA_AUDIO_OUT_FLUSH) < 0)
LOGE("could not flush spdif: %s\n", strerror(errno));
LOGD("AudioStreamOutTegra::flush() returns");
}
// Called with mLock and mHardware->mLock held
status_t AudioHardware::AudioStreamOutTegra::online()
{
// Return if the speaker is already on and the output path.
if (mIsSpkrEnabled && mHardware->mCurOutDevice.on)
return NO_ERROR;
// If there's no hardware speaker, leave the HW alone. (i.e. SCO/SPDIF is on)
if (!mIsSpkrEnabled)
return NO_ERROR;
return mHardware->doStandby(mFdCtl, true, false); // output, online
}
status_t AudioHardware::AudioStreamOutTegra::standby()
{
status_t status = NO_ERROR;
Mutex::Autolock lock(mHardware->mLock);
if (!mHardware->mCurOutDevice.on && !mIsBtEnabled) {
LOGV("%s: output already in standby", __FUNCTION__);
return NO_ERROR;
}
// Prevent EC/NS from writing to the file anymore.
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
mHardware->mAudioPP.writeDownlinkEcns(-1,0,false,0,&mFdLock);
#endif
status = mHardware->doStandby(mFdCtl, true, true); // output, standby
mIsBtEnabled = false;
return status;
}
status_t AudioHardware::AudioStreamOutTegra::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
result.append("AudioStreamOutTegra::dump\n");
snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware);
result.append(buffer);
snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
result.append(buffer);
snprintf(buffer, SIZE, "\tmStartCount: %d\n", mStartCount);
result.append(buffer);
snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount);
result.append(buffer);
if (mHardware)
snprintf(buffer, SIZE, "\tmStandby: %s\n",
mHardware->mCurOutDevice.on ? "false": "true");
else
snprintf(buffer, SIZE, "\tmStandby: unknown\n");
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
bool AudioHardware::AudioStreamOutTegra::getStandby()
{
return mHardware ? !mHardware->mCurOutDevice.on : true;
}
status_t AudioHardware::AudioStreamOutTegra::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int device;
LOGV("AudioStreamOutTegra::setParameters() %s", keyValuePairs.string());
if (param.getInt(key, device) == NO_ERROR) {
mDevices = device;
LOGV("set output routing %x", mDevices);
status = mHardware->doRouting();
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
String8 AudioHardware::AudioStreamOutTegra::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
String8 value;
String8 key = String8(AudioParameter::keyRouting);
if (param.get(key, value) == NO_ERROR) {
LOGV("get routing %x", mDevices);
param.addInt(key, (int)mDevices);
}
LOGV("AudioStreamOutTegra::getParameters() %s", param.toString().string());
return param.toString();
}
status_t AudioHardware::AudioStreamOutTegra::getRenderPosition(uint32_t *dspFrames)
{
//TODO: enable when supported by driver
return INVALID_OPERATION;
}
// ----------------------------------------------------------------------------
AudioHardware::AudioStreamInTegra::AudioStreamInTegra() :
mHardware(0), mFd(-1), mFdCtl(-1), mState(AUDIO_INPUT_CLOSED), mRetryCount(0),
mFormat(AUDIO_HW_IN_FORMAT), mChannels(AUDIO_HW_IN_CHANNELS),
mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_BUFFERSIZE),
mAcoustics((AudioSystem::audio_in_acoustics)0), mDevices(0),
mIsMicEnabled(0), mIsBtEnabled(0)
{
}
status_t AudioHardware::AudioStreamInTegra::set(
AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels, uint32_t *pRate,
AudioSystem::audio_in_acoustics acoustic_flags)
{
Mutex::Autolock lock(mLock);
status_t status = BAD_VALUE;
mHardware = hw;
if (pFormat == 0)
return status;
if (*pFormat != AUDIO_HW_IN_FORMAT) {
LOGE("wrong in format %d, expecting %lld", *pFormat, AUDIO_HW_IN_FORMAT);
*pFormat = AUDIO_HW_IN_FORMAT;
return status;
}
if (pRate == 0)
return status;
uint32_t rate = hw->getInputSampleRate(*pRate);
if (rate != *pRate) {
LOGE("wrong sample rate %d, expecting %d", *pRate, rate);
*pRate = rate;
return status;
}
if (pChannels == 0)
return status;
if (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
*pChannels != AudioSystem::CHANNEL_IN_STEREO) {
LOGE("wrong number of channels %d", *pChannels);
*pChannels = AUDIO_HW_IN_CHANNELS;
return status;
}
LOGV("AudioStreamInTegra::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);
mDevices = devices;
mFormat = AUDIO_HW_IN_FORMAT;
mChannels = *pChannels;
mSampleRate = *pRate;
mBufferSize = mHardware->getInputBufferSize(mSampleRate, AudioSystem::PCM_16_BIT,
AudioSystem::popCount(mChannels));
mNeedsOnline = true;
return NO_ERROR;
}
AudioHardware::AudioStreamInTegra::~AudioStreamInTegra()
{
LOGV("AudioStreamInTegra destructor");
standby();
if (mFd >= 0)
::close(mFd);
if (mFdCtl >= 0)
::close(mFdCtl);
}
// Called with mHardware->mLock held.
void AudioHardware::AudioStreamInTegra::setDriver(bool mic, bool bluetooth)
{
Mutex::Autolock lock(mLock);
LOGD("%s: Analog mic? %s. Bluetooth? %s.", __FUNCTION__,
mic?"yes":"no", bluetooth?"yes":"no");
if (mic != mIsMicEnabled || bluetooth != mIsBtEnabled)
mNeedsOnline = true;
mIsMicEnabled = mic;
mIsBtEnabled = bluetooth;
}
ssize_t AudioHardware::AudioStreamInTegra::read(void* buffer, ssize_t bytes)
{
ssize_t ret;
ssize_t ret2 = 0;
int driverRate;
LOGV("AudioStreamInTegra::read(%p, %ld)", buffer, bytes);
if (!mHardware) {
LOGE("%s: mHardware is null", __FUNCTION__);
return -1;
}
bool srcReqd;
int hwReadBytes;
int16_t * inbuf;
mHardware->mLock.lock();
Mutex::Autolock lock(mLock);
if (mState < AUDIO_INPUT_STARTED) {
mState = AUDIO_INPUT_STARTED;
// Unlock since doRouting_l will call setDriver
mLock.unlock();
mHardware->doRouting_l();
mLock.lock();
}
ret = online();
if (ret != NO_ERROR) {
LOGE("%s: Problem switching to online.",__FUNCTION__);
mHardware->mLock.unlock();
return -1;
}
// Snapshot of the driver rate to stay coherent in this function
driverRate = mHardware->mHwInRate;
mHardware->mLock.unlock();
srcReqd = (driverRate != (int)mSampleRate);
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
if (srcReqd) {
hwReadBytes = ( bytes*driverRate/mSampleRate ) & (~0x7);
LOGV("Running capture SRC. HW=%d bytes at %d, Flinger=%d bytes at %d",
hwReadBytes, driverRate, (int)bytes, mSampleRate);
inbuf = mInScratch;
if ((size_t)bytes > sizeof(mInScratch)) {
LOGE("read: buf size problem. %d>%d",(int)bytes,sizeof(mInScratch));
return -1;
}
// Check if we need to init the rate converter
if (!mSrc.initted() ||
mSrc.inRate() != driverRate ||
mSrc.outRate() != (int)mSampleRate) {
LOGI ("%s: Upconvert started from %d to %d", __FUNCTION__,
driverRate, mSampleRate);
mSrc.init(driverRate, mSampleRate);
if (!mSrc.initted())
return -1;
reopenReconfigDriver();
}
} else {
hwReadBytes = bytes;
inbuf = (int16_t *)buffer;
mSrc.deinit();
}
#else
if (srcReqd) {
LOGE("%s: sample rate mismatch HAL %d, driver %d",
__FUNCTION__, mSampleRate, driverRate);
return -1;
}
#endif
ret = ::read(mFd, inbuf, hwReadBytes/2);
if (ret >= 0)
ret2 = ::read(mFd, (char *)inbuf+hwReadBytes/2, hwReadBytes/2);
if (ret2 < 0)
ret = ret2;
if (ret < 0)
LOGE("Error reading from audio in: %s", strerror(errno));
else
ret += ret2;
#ifdef USE_PROPRIETARY_AUDIO_EXTENSIONS
if (ret>0)
mHardware->mAudioPP.applyUplinkEcns(inbuf, hwReadBytes, driverRate);
else if (mHardware->mAudioPP.isEcnsEnabled()) {
LOGE("Read is failing, disable EC/NS until something changes");
mHardware->mAudioPP.enableEcns(false);
}
if (ret>0 && srcReqd) {
mSrc.mIoData.input_ptrL = (SRC_INT16_T *) (inbuf);
mSrc.mIoData.input_count = hwReadBytes / sizeof(SRC_INT16_T);
mSrc.mIoData.input_ptrR = 0;
mSrc.mIoData.output_ptr = (SRC_INT16_T *) (buffer);
mSrc.mIoData.output_count = bytes/sizeof(SRC_INT16_T);
mSrc.srcConvert();
ret = mSrc.mIoData.output_count*sizeof(SRC_INT16_T);
if (ret > bytes) {
LOGE("read: buffer overrun");
}
}
#endif
LOGV("%s returns %d.",__FUNCTION__, (int)ret);
return ret;
}
bool AudioHardware::AudioStreamInTegra::getStandby()
{
return mState == AUDIO_INPUT_CLOSED;
}
status_t AudioHardware::AudioStreamInTegra::standby()
{
Mutex::Autolock lock(mHardware->mLock);
Mutex::Autolock lock2(mLock);
mState = AUDIO_INPUT_CLOSED;
if (!mHardware)
return -1;
mLock.unlock();
mHardware->doRouting_l();
mLock.lock();
return mHardware->doStandby(mFdCtl, false, true); // input, standby
}
// Called with mLock and mHardware->mLock held
status_t AudioHardware::AudioStreamInTegra::online()
{
status_t status;
if (mNeedsOnline) {
// Don't no-op this function.
mNeedsOnline = false;
} else {
if (mIsMicEnabled && mHardware->mCurInDevice.on) {
return NO_ERROR;
}
if (!mIsMicEnabled && !mHardware->mCurInDevice.on)
return NO_ERROR;
}
LOGV("%s", __FUNCTION__);
reopenReconfigDriver();
// configuration
struct tegra_audio_in_config config;
status = ::ioctl(mFdCtl, TEGRA_AUDIO_IN_GET_CONFIG, &config);
if (status < 0) {
LOGE("cannot read input config: %s", strerror(errno));
return status;
}
config.stereo = AudioSystem::popCount(mChannels) == 2;
config.rate = mSampleRate;
status = ::ioctl(mFdCtl, TEGRA_AUDIO_IN_SET_CONFIG, &config);
if (status < 0) {
LOGE("cannot set input config: %s", strerror(errno));
if (::ioctl(mFdCtl, TEGRA_AUDIO_IN_GET_CONFIG, &config) == 0) {
if (config.stereo) {
mChannels = AudioSystem::CHANNEL_IN_STEREO;
} else {
mChannels = AudioSystem::CHANNEL_IN_MONO;
}
}
}
mState = AUDIO_INPUT_OPENED;
// Use standby to flush the driver. mHardware->mLock should already be held
mHardware->doStandby(mFdCtl, false, true);
if (mIsMicEnabled)
return mHardware->doStandby(mFdCtl, false, false);
else
return NO_ERROR;
}
void AudioHardware::AudioStreamInTegra::reopenReconfigDriver()
{
// Need to "restart" the driver when changing the buffer configuration.
if (mFdCtl != -1 && ::ioctl(mFdCtl, TEGRA_AUDIO_IN_STOP) < 0)
LOGE("%s: could not stop recording: %s\n", __FUNCTION__, strerror(errno));
if (mFd != -1)
::close(mFd);
if (mFdCtl != -1)
::close(mFdCtl);
mFd = ::open("/dev/audio1_in", O_RDWR);
mFdCtl = ::open("/dev/audio1_in_ctl", O_RDWR);
}
status_t AudioHardware::AudioStreamInTegra::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
result.append("AudioStreamInTegra::dump\n");
snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
snprintf(buffer, SIZE, "\tmHardware: %p\n", mHardware);
result.append(buffer);
snprintf(buffer, SIZE, "\tmFd count: %d\n", mFd);
result.append(buffer);
snprintf(buffer, SIZE, "\tmState: %d\n", mState);
result.append(buffer);
snprintf(buffer, SIZE, "\tmRetryCount: %d\n", mRetryCount);
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
status_t AudioHardware::AudioStreamInTegra::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int device;
LOGV("AudioStreamInTegra::setParameters() %s", keyValuePairs.string());
if (param.getInt(key, device) == NO_ERROR) {
LOGV("set input routing %x", device);
if (device & (device - 1)) {
status = BAD_VALUE;
} else {
mDevices = device;
status = mHardware->doRouting();
}
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
String8 AudioHardware::AudioStreamInTegra::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
String8 value;
String8 key = String8(AudioParameter::keyRouting);
if (param.get(key, value) == NO_ERROR) {
LOGV("get routing %x", mDevices);
param.addInt(key, (int)mDevices);
}
LOGV("AudioStreamInTegra::getParameters() %s", param.toString().string());
return param.toString();
}
// ----------------------------------------------------------------------------
extern "C" AudioHardwareInterface* createAudioHardware(void) {
return new AudioHardware();
}
}; // namespace android