blob: 45223f3a5ecae39f6479558fc1425e4992480773 [file] [log] [blame]
/* AudioHardwareALSA.cpp
**
** Copyright 2008-2009 Wind River Systems
**
** 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 <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioHardwareALSA"
#include <utils/Log.h>
#include <utils/String8.h>
#include <cutils/properties.h>
#include <media/AudioRecord.h>
#include <hardware_legacy/power.h>
#include <alsa/asoundlib.h>
#include "AudioHardwareALSA.h"
// #define READ_FRAME_SIZE 2080
// #define READ_FRAME_SIZE_STANDARD 4160
#include <dlfcn.h>
#define SND_MIXER_VOL_RANGE_MIN (0)
#define SND_MIXER_VOL_RANGE_MAX (100)
#define ALSA_NAME_MAX 128
#define ALSA_STRCAT(x,y) \
if (strlen(x) + strlen(y) < ALSA_NAME_MAX) \
strcat(x, y);
extern "C"
{
extern int ffs(int i);
//
// Make sure this prototype is consistent with what's in
// external/libasound/alsa-lib-1.0.16/src/pcm/pcm_null.c!
//
extern int snd_pcm_null_open(snd_pcm_t **pcmp,
const char *name,
snd_pcm_stream_t stream,
int mode);
//
// Function for dlsym() to look up for creating a new AudioHardwareInterface.
//
android::AudioHardwareInterface *createAudioHardware(void) {
return new android::AudioHardwareALSA();
}
} // extern "C"
namespace android
{
typedef AudioSystem::audio_devices audio_routes;
#define ROUTE_ALL AudioSystem::DEVICE_OUT_ALL
#define ROUTE_EARPIECE AudioSystem::DEVICE_OUT_EARPIECE
#define ROUTE_HEADSET AudioSystem::DEVICE_OUT_WIRED_HEADSET
#define ROUTE_HEADPHONE AudioSystem::DEVICE_OUT_WIRED_HEADPHONE
#define ROUTE_SPEAKER AudioSystem::DEVICE_OUT_SPEAKER
#define ROUTE_BLUETOOTH_SCO AudioSystem::DEVICE_OUT_BLUETOOTH_SCO
#define ROUTE_BLUETOOTH_SCO_HEADSET AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET
#define ROUTE_BLUETOOTH_SCO_CARKIT AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT
#define ROUTE_BLUETOOTH_A2DP AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP
#define ROUTE_BLUETOOTH_A2DP_HEADPHONES AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
#define ROUTE_BLUETOOTH_A2DP_SPEAKER AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
// ----------------------------------------------------------------------------
static const char _nullALSADeviceName[] = "NULL_Device";
static void ALSAErrorHandler(const char *file,
int line,
const char *function,
int err,
const char *fmt,
...)
{
char buf[BUFSIZ];
va_list arg;
int l;
va_start(arg, fmt);
l = snprintf(buf, BUFSIZ, "%s:%i:(%s) ", file, line, function);
vsnprintf(buf + l, BUFSIZ - l, fmt, arg);
buf[BUFSIZ-1] = '\0';
LOGE("ALSALib %s.", buf);
va_end(arg);
}
// ----------------------------------------------------------------------------
/* The following table(s) need to match in order of the route bits
*/
static const char *deviceSuffix[] = {
// output devices
/* ROUTE_EARPIECE */ "_Earpiece",
/* ROUTE_SPEAKER */ "_Speaker",
/* ROUTE_HEADSET */ "_Headset",
/* ROUTE_HEADPHONE */ "_Headphone",
/* ROUTE_BLUETOOTH_SCO */ "_Bluetooth",
/* ROUTE_BLUETOOTH_SCO_HEADSET */ "_Bluetooth",
/* ROUTE_BLUETOOTH_SCO_CARKIT */ "_Bluetooth", //"_Bluetooth_Carkit"
/* ROUTE_BLUETOOTH_A2DP */ "_Bluetooth", //"_Bluetooth-A2DP"
/* ROUTE_BLUETOOTH_A2DP_HEADPHONES */ "_Bluetooth", //"_Bluetooth-A2DP_HeadPhone"
/* ROUTE_BLUETOOTH_A2DP_SPEAKER */ "_Bluetooth", // "_Bluetooth-A2DP_Speaker"
/* ROUTE_AUX_DIGITAL */ "_AuxDigital",
/* ROUTE_TV_OUT */ "_TvOut",
/* ROUTE_AUX_DIGITAL */ "_ExtraDockSpeaker",
/* ROUTE_NULL */ "_Null",
/* ROUTE_NULL */ "_Null",
/* ROUTE_DEFAULT */ "_OutDefault",
// input devices
/* ROUTE_COMMUNICATION */ "_Communication",
/* ROUTE_AMBIENT */ "_Ambient",
/* ROUTE_BUILTIN_MIC */ "_Speaker",
/* ROUTE_BLUETOOTH_SCO_HEADSET */ "_Bluetooth",
/* ROUTE_WIRED_HEADSET */ "_Headset",
/* ROUTE_AUX_DIGITAL */ "_AuxDigital",
/* ROUTE_VOICE_CALL */ "_VoiceCall",
/* ROUTE_BACK_MIC */ "_BackMic",
/* ROUTE_IN_DEFAULT */ "_InDefault",
};
static const int deviceSuffixLen = (sizeof(deviceSuffix) / sizeof(char *));
struct mixer_info_t;
struct alsa_properties_t
{
const audio_routes routes;
const char *propName;
const char *propDefault;
mixer_info_t *mInfo;
};
static alsa_properties_t masterPlaybackProp = {
ROUTE_ALL, "alsa.mixer.playback.master", "PCM", NULL
};
static alsa_properties_t masterCaptureProp = {
ROUTE_ALL, "alsa.mixer.capture.master", "Capture", NULL
};
static alsa_properties_t
mixerMasterProp[SND_PCM_STREAM_LAST+1] = {
{ ROUTE_ALL, "alsa.mixer.playback.master", "PCM", NULL},
{ ROUTE_ALL, "alsa.mixer.capture.master", "Capture", NULL}
};
static alsa_properties_t
mixerProp[][SND_PCM_STREAM_LAST+1] = {
{
{ROUTE_EARPIECE, "alsa.mixer.playback.earpiece", "Earpiece", NULL},
{ROUTE_EARPIECE, "alsa.mixer.capture.earpiece", "Capture", NULL}
},
{
{ROUTE_SPEAKER, "alsa.mixer.playback.speaker", "Speaker", NULL},
{ROUTE_SPEAKER, "alsa.mixer.capture.speaker", "", NULL}
},
{
{ROUTE_BLUETOOTH_SCO, "alsa.mixer.playback.bluetooth.sco", "Bluetooth", NULL},
{ROUTE_BLUETOOTH_SCO, "alsa.mixer.capture.bluetooth.sco", "Bluetooth Capture", NULL}
},
{
{ROUTE_HEADSET, "alsa.mixer.playback.headset", "Headphone", NULL},
{ROUTE_HEADSET, "alsa.mixer.capture.headset", "Capture", NULL}
},
{
{ROUTE_BLUETOOTH_A2DP, "alsa.mixer.playback.bluetooth.a2dp", "Bluetooth A2DP", NULL},
{ROUTE_BLUETOOTH_A2DP, "alsa.mixer.capture.bluetooth.a2dp", "Bluetooth A2DP Capture", NULL}
},
{
{static_cast<audio_routes>(0), NULL, NULL, NULL},
{static_cast<audio_routes>(0), NULL, NULL, NULL}
}
};
const uint32_t AudioHardwareALSA::inputSamplingRates[] = {
8000, 11025, 16000, 22050, 44100
};
// ----------------------------------------------------------------------------
AudioHardwareALSA::AudioHardwareALSA() :
mOutput(0),
mInput(0),
mSecRilLibHandle(NULL),
mRilClient(0),
mVrModeEnabled(false),
mActivatedCP(false),
mBluetoothECOff(false)
{
snd_lib_error_set_handler(&ALSAErrorHandler);
mMixer = new ALSAMixer;
loadRILD();
}
AudioHardwareALSA::~AudioHardwareALSA()
{
if (mOutput) delete mOutput;
if (mInput) delete mInput;
if (mMixer) delete mMixer;
if (mSecRilLibHandle) {
if (disconnectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS)
LOGE("Disconnect_RILD() error");
if (closeClientRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS)
LOGE("CloseClient_RILD() error");
mRilClient = 0;
dlclose(mSecRilLibHandle);
mSecRilLibHandle = NULL;
}
}
void AudioHardwareALSA::loadRILD(void)
{
mSecRilLibHandle = dlopen("libsecril-client.so", RTLD_NOW);
if (mSecRilLibHandle) {
LOGV("libsecril-client.so is loaded");
openClientRILD = (HRilClient (*)(void))
dlsym(mSecRilLibHandle, "OpenClient_RILD");
disconnectRILD = (int (*)(HRilClient))
dlsym(mSecRilLibHandle, "Disconnect_RILD");
closeClientRILD = (int (*)(HRilClient))
dlsym(mSecRilLibHandle, "CloseClient_RILD");
isConnectedRILD = (int (*)(HRilClient))
dlsym(mSecRilLibHandle, "isConnected_RILD");
connectRILD = (int (*)(HRilClient))
dlsym(mSecRilLibHandle, "Connect_RILD");
setCallVolume = (int (*)(HRilClient, SoundType, int))
dlsym(mSecRilLibHandle, "SetCallVolume");
setCallAudioPath = (int (*)(HRilClient, AudioPath))
dlsym(mSecRilLibHandle, "SetCallAudioPath");
setCallClockSync = (int (*)(HRilClient, SoundClockCondition))
dlsym(mSecRilLibHandle, "SetCallClockSync");
if (!openClientRILD || !disconnectRILD || !closeClientRILD ||
!isConnectedRILD || !connectRILD ||
!setCallVolume || !setCallAudioPath || !setCallClockSync) {
LOGE("Can't load all functions from libsecril-client.so");
dlclose(mSecRilLibHandle);
mSecRilLibHandle = NULL;
} else {
mRilClient = openClientRILD();
if (!mRilClient) {
LOGE("OpenClient_RILD() error");
dlclose(mSecRilLibHandle);
mSecRilLibHandle = NULL;
}
}
} else {
LOGE("Can't load libsecril-client.so");
}
}
status_t AudioHardwareALSA::initCheck()
{
if (mMixer && mMixer->isValid())
return NO_ERROR;
else
return NO_INIT;
}
status_t AudioHardwareALSA::connectRILDIfRequired(void)
{
if (!mSecRilLibHandle) {
LOGE("connectIfRequired() lib is not loaded");
return INVALID_OPERATION;
}
if (isConnectedRILD(mRilClient)) {
return OK;
}
if (connectRILD(mRilClient) != RIL_CLIENT_ERR_SUCCESS) {
LOGE("Connect_RILD() error");
return INVALID_OPERATION;
}
return OK;
}
status_t AudioHardwareALSA::setVoiceVolume(float volume)
{
LOGI("### setVoiceVolume");
AutoMutex lock(mLock);
// sangsu fix : transmic volume level IPC to modem
if ( (AudioSystem::MODE_IN_CALL == mMode) && (mSecRilLibHandle) &&
(connectRILDIfRequired() == OK) ) {
uint32_t routes = AudioSystem::ROUTE_EARPIECE;
if (mOutput != NULL) {
routes = mOutput->device();
}
int int_volume = (int)(volume * 5);
LOGI("### route(%d) call volume(%f)", routes, volume);
switch (routes) {
case AudioSystem::ROUTE_EARPIECE:
LOGI("### earpiece call volume");
setCallVolume(mRilClient, SOUND_TYPE_VOICE, int_volume);
break;
case AudioSystem::ROUTE_SPEAKER:
LOGI("### speaker call volume");
setCallVolume(mRilClient, SOUND_TYPE_SPEAKER, int_volume);
break;
case AudioSystem::ROUTE_BLUETOOTH_SCO:
case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
case AudioSystem::ROUTE_BLUETOOTH_A2DP:
LOGI("### bluetooth call volume");
setCallVolume(mRilClient, SOUND_TYPE_BTVOICE, int_volume);
break;
case AudioSystem::ROUTE_HEADSET:
case AudioSystem::ROUTE_HEADPHONE: // Use receive path with 3 pole headset.
LOGI("### headset call volume");
setCallVolume(mRilClient, SOUND_TYPE_HEADSET, int_volume);
break;
default:
LOGE("### Call volume setting error!!!0x%08x \n", routes);
break;
}
}
// sangsu fix end
// The voice volume is used by the VOICE_CALL audio stream.
if (mMixer)
return mMixer->setVolume(ROUTE_EARPIECE, volume);
else
return INVALID_OPERATION;
}
status_t AudioHardwareALSA::setMasterVolume(float volume)
{
if (mMixer)
return mMixer->setMasterVolume(volume);
else
return INVALID_OPERATION;
}
AudioStreamOut *
AudioHardwareALSA::openOutputStream(
uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
{
AudioStreamOutALSA *out = NULL;
status_t ret = NO_ERROR;
{
AutoMutex lock(mLock);
// only one output stream allowed
if (mOutput) {
ret = ALREADY_EXISTS;
goto exit;
}
LOGV("[[[[[[[[\n%s - format = %d, channels = %d, sampleRate = %d, devices = %d]]]]]]]]\n", __func__, *format, *channels, *sampleRate,devices);
out = new AudioStreamOutALSA(this);
ret = out->set(format, channels, sampleRate);
if (ret == NO_ERROR) {
mOutput = out;
}
}
exit:
if (ret == NO_ERROR) {
// Some information is expected to be available immediately after
// the device is open.
/* Tushar - Sets the current device output here - we may set device here */
LOGI("%s] Setting ALSA device.", __func__);
mOutput->setDevice(mMode, devices, PLAYBACK); /* tushar - Enable all devices as of now */
} else if (out) {
delete out;
}
if (status) {
*status = ret;
}
return mOutput;
}
void
AudioHardwareALSA::closeOutputStream(AudioStreamOut* out)
{
/* TODO:Tushar: May lead to segmentation fault - check*/
//delete out;
{
AutoMutex lock(mLock);
if (mOutput == 0 || mOutput != out) {
LOGW("Attempt to close invalid output stream");
return;
}
mOutput = 0;
}
delete out;
}
AudioStreamIn*
AudioHardwareALSA::openInputStream(
uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status,
AudioSystem::audio_in_acoustics acoustics)
{
AudioStreamInALSA *in = NULL;
status_t ret = NO_ERROR;
{
AutoMutex lock(mLock);
// only one input stream allowed
if (mInput) {
ret = ALREADY_EXISTS;
goto exit;
}
in = new AudioStreamInALSA(this);
ret = in->set(format, channels, sampleRate);
if (ret == NO_ERROR) {
mInput = in;
}
}
exit:
if (ret == NO_ERROR) {
// Some information is expected to be available immediately after
// the device is open.
mInput->setDevice(mMode, devices, CAPTURE); /* Tushar - as per modified arch */
} else if (in != NULL) {
delete in;
}
if (status) {
*status = ret;
}
return mInput;
}
void
AudioHardwareALSA::closeInputStream(AudioStreamIn* in)
{
/* TODO:Tushar: May lead to segmentation fault - check*/
//delete in;
{
AutoMutex lock(mLock);
if (mInput == 0 || mInput != in) {
LOGW("Attempt to close invalid input stream");
return;
} else {
mInput = 0;
}
}
delete in;
}
status_t AudioHardwareALSA::doRouting(uint32_t device, bool force)
{
AutoMutex lock(mLock);
return doRouting_l(device, force);
}
status_t AudioHardwareALSA::doRouting_l(uint32_t device, bool force)
{
status_t ret;
int mode = mMode; // Prevent to changing mode on setup sequence.
LOGV("doRouting: device %x, force %d", device, force);
if (mOutput) {
//device = 0; /* Tushar - temp implementation */
if (device == AudioSystem::DEVICE_OUT_DEFAULT) {
device = mOutput->device();
}
// Setup sound path for CP clocking
if ( (AudioSystem::MODE_IN_CALL == mode) && (mSecRilLibHandle) &&
(connectRILDIfRequired() == OK) ) {
LOGI("### incall mode route (%d)", device);
switch(device){
case AudioSystem::ROUTE_EARPIECE:
LOGI("### incall mode earpiece route");
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_HANDSET);
break;
case AudioSystem::ROUTE_SPEAKER:
LOGI("### incall mode speaker route");
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_SPEAKER);
break;
case AudioSystem::ROUTE_BLUETOOTH_SCO:
case AudioSystem::ROUTE_BLUETOOTH_SCO_HEADSET:
case AudioSystem::ROUTE_BLUETOOTH_SCO_CARKIT:
LOGI("### incall mode bluetooth route %s NR", mBluetoothECOff ? "NO" : "");
if (mBluetoothECOff)
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_BLUETOOTH_NO_NR);
else
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_BLUETOOTH);
break;
case AudioSystem::ROUTE_HEADSET :
case AudioSystem::ROUTE_HEADPHONE :
LOGI("### incall mode headset route");
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_HEADSET);
break;
case AudioSystem::ROUTE_BLUETOOTH_A2DP:
LOGI("### incall mode bluetooth route");
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_BLUETOOTH);
break;
default:
LOGE("### incall mode Error!! route = [%d]", device);
break;
}
}
ret = mOutput->setDevice(mode, device, PLAYBACK, force);
return ret;
}
return NO_INIT;
}
status_t AudioHardwareALSA::setMicMute(bool state)
{
if (mMixer)
return mMixer->setCaptureMuteState(ROUTE_EARPIECE, state);
return NO_INIT;
}
status_t AudioHardwareALSA::getMicMute(bool *state)
{
if (mMixer)
return mMixer->getCaptureMuteState(ROUTE_EARPIECE, state);
return NO_ERROR;
}
status_t AudioHardwareALSA::dump(int fd, const Vector<String16>& args)
{
return NO_ERROR;
}
uint32_t AudioHardwareALSA::bufferRatio(uint32_t samplingRate) {
switch (samplingRate) {
case 8000:
case 11025:
return 4;
case 16000:
case 22050:
return 2;
case 44100:
default:
break;
}
return 1;
}
size_t AudioHardwareALSA::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
if (sampleRate != 8000 && sampleRate != 11025 && sampleRate != 16000 &&
sampleRate != 22050 && sampleRate != 44100) {
LOGW("getInputBufferSize bad sampling rate: %d", sampleRate);
return 0;
}
if (format != AudioSystem::PCM_16_BIT) {
LOGW("getInputBufferSize bad format: %d", format);
return 0;
}
if (channelCount != 1) {
LOGW("getInputBufferSize bad channel count: %d", channelCount);
return 0;
}
size_t size = (PERIOD_SZ_CAPTURE / bufferRatio(sampleRate)) * sizeof(int16_t);
LOGV("getInputBufferSize() rate %d, ratio %d", sampleRate, size);
return size;
}
uint32_t AudioHardwareALSA::checkInputSampleRate(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];
}
status_t AudioHardwareALSA::setMode(int mode)
{
AutoMutex lock(mLock);
int prevMode = mMode;
status_t status = AudioHardwareBase::setMode(mode);
LOGV("setMode() : new %d, old %d", mMode, prevMode);
if (status == NO_ERROR) {
if ( (mMode == AudioSystem::MODE_RINGTONE) || (mMode == AudioSystem::MODE_IN_CALL) )
{
if ( (!mActivatedCP) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK) ) {
setCallClockSync(mRilClient, SOUND_CLOCK_START);
mActivatedCP = true;
}
}
// make sure that doAudioRouteOrMute() is called by doRouting()
// when entering or exiting in call mode even if the new device
// selected is the same as current one.
if ((prevMode != AudioSystem::MODE_IN_CALL) && (mMode == AudioSystem::MODE_IN_CALL)) {
LOGV("setMode() entering call");
doRouting_l(AudioSystem::DEVICE_OUT_DEFAULT, true);
setVoiceRecordGain_l(false);
}
if ((prevMode == AudioSystem::MODE_IN_CALL) && (mMode != AudioSystem::MODE_IN_CALL)) {
LOGV("setMode() exiting call");
doRouting_l(AudioSystem::DEVICE_OUT_DEFAULT, true);
if (mOutput != NULL && !mOutput->isActive()) {
mOutput->close();
}
}
if (mMode == AudioSystem::MODE_NORMAL) {
if(mActivatedCP)
mActivatedCP = false;
}
}
return status;
}
int AudioHardwareALSA::setVoiceRecordGain(bool enable)
{
AutoMutex lock(mLock);
return setVoiceRecordGain_l(enable);
}
int AudioHardwareALSA::setVoiceRecordGain_l(bool enable)
{
LOGI("[%s], enable=%d", __func__, enable);
if (enable != mVrModeEnabled &&
!(enable && (mMode == AudioSystem::MODE_IN_CALL))) {
ALSAControl *alsaControl = new ALSAControl();
status_t ret = alsaControl->set("Recognition Control", enable ? RECOGNITION_ON : RECOGNITION_OFF);
delete alsaControl;
mVrModeEnabled = enable;
}
return NO_ERROR;
}
status_t AudioHardwareALSA::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 bt_nrec_key = String8("bt_headset_nrec");
String8 value;
LOGV("setParameters(%s)", keyValuePairs.string());
if (param.get(bt_nrec_key, value) == NO_ERROR) {
setBluetoothNrEcOnOff((value == "on") ? false : true);
}
return NO_ERROR;
}
void AudioHardwareALSA::setBluetoothNrEcOnOff(bool disable)
{
LOGV("setBluetoothNrEcOnOff(%s)", disable ? "true" : "false");
if (disable != mBluetoothECOff)
{
mBluetoothECOff = disable;
if ( (mOutput) && (AudioSystem::MODE_IN_CALL == mMode) &&
(mSecRilLibHandle) && (connectRILDIfRequired() == OK)) {
uint32_t device = mOutput->device();
switch (device) {
case AudioSystem::ROUTE_BLUETOOTH_SCO:
case AudioSystem::ROUTE_BLUETOOTH_SCO_HEADSET:
case AudioSystem::ROUTE_BLUETOOTH_SCO_CARKIT:
LOGV("### incall mode bluetooth EC %s route", mBluetoothECOff ? "OFF" : "ON");
if (mBluetoothECOff)
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_BLUETOOTH_NO_NR);
else
setCallAudioPath(mRilClient, SOUND_AUDIO_PATH_BLUETOOTH);
break;
default :
LOGE("Bluetooth path is not activated!!");
break;
}
}
}
}
// ----------------------------------------------------------------------------
ALSAStreamOps::ALSAStreamOps() :
mHandle(0),
mHardwareParams(0),
mSoftwareParams(0),
mDevice(0)
{
if (snd_pcm_hw_params_malloc(&mHardwareParams) < 0) {
LOG_ALWAYS_FATAL("Failed to allocate ALSA hardware parameters!");
}
if (snd_pcm_sw_params_malloc(&mSoftwareParams) < 0) {
LOG_ALWAYS_FATAL("Failed to allocate ALSA software parameters!");
}
}
ALSAStreamOps::~ALSAStreamOps()
{
AutoMutex lock(mLock);
close();
if (mHardwareParams)
snd_pcm_hw_params_free(mHardwareParams);
if (mSoftwareParams)
snd_pcm_sw_params_free(mSoftwareParams);
}
status_t ALSAStreamOps::set(int *pformat,
uint32_t *pchannels,
uint32_t *prate)
{
int lformat = pformat ? *pformat : 0;
unsigned int lchannels = pchannels ? *pchannels : 0;
unsigned int lrate = prate ? *prate : 0;
LOGD("ALSAStreamOps - input - format = %d, channels = %d, rate = %d\n", lformat, lchannels, lrate);
LOGD("ALSAStreamOps - default - format = %d, channelCount = %d, rate = %d\n", mDefaults->format, mDefaults->channelCount, mDefaults->sampleRate);
if (lformat == 0) lformat = getAndroidFormat(mDefaults->format);//format();
if (lchannels == 0) lchannels = getAndroidChannels(mDefaults->channelCount);// channelCount();
if (lrate == 0) lrate = mDefaults->sampleRate;
if ( (lformat != getAndroidFormat(mDefaults->format)) ||
(lchannels != getAndroidChannels(mDefaults->channelCount)) ) {
if (pformat) *pformat = getAndroidFormat(mDefaults->format);
if (pchannels) *pchannels = getAndroidChannels(mDefaults->channelCount);
return BAD_VALUE;
}
if (mDefaults->direction == SND_PCM_STREAM_PLAYBACK) {
if (lrate != mDefaults->sampleRate) {
if (prate) *prate = mDefaults->sampleRate;
return BAD_VALUE;
}
} else {
uint32_t rate = AudioHardwareALSA::checkInputSampleRate(lrate);
if (rate != lrate) {
if (prate) *prate = rate;
return BAD_VALUE;
}
lrate = rate;
}
mDefaults->bufferRatio = AudioHardwareALSA::bufferRatio(lrate);
mDefaults->sampleRate = lrate;
if(pformat) *pformat = getAndroidFormat(mDefaults->format);
if(pchannels) *pchannels = getAndroidChannels(mDefaults->channelCount);
if(prate) *prate = mDefaults->sampleRate;
return NO_ERROR;
}
uint32_t ALSAStreamOps::sampleRate() const
{
return mDefaults->sampleRate;
}
status_t ALSAStreamOps::sampleRate(uint32_t rate)
{
const char *stream;
unsigned int requestedRate;
int err;
if (!mHandle)
return NO_INIT;
stream = streamName();
requestedRate = rate;
err = snd_pcm_hw_params_set_rate_near(mHandle,
mHardwareParams,
&requestedRate,
0);
if (err < 0) {
LOGE("Unable to set %s sample rate to %u: %s",
stream, rate, snd_strerror(err));
return BAD_VALUE;
}
if (requestedRate != rate) {
// Some devices have a fixed sample rate, and can not be changed.
// This may cause resampling problems; i.e. PCM playback will be too
// slow or fast.
LOGW("Requested rate (%u HZ) does not match actual rate (%u HZ)",
rate, requestedRate);
}
else {
LOGD("Set %s sample rate to %u HZ", stream, requestedRate);
}
return NO_ERROR;
}
//
// Return the number of bytes (not frames)
//
size_t ALSAStreamOps::bufferSize() const
{
int err;
size_t size = ((mDefaults->periodSize / mDefaults->bufferRatio) * mDefaults->channelCount *
snd_pcm_format_physical_width(mDefaults->format)) / 8;
LOGV("bufferSize() channelCount %d, bufferRatio %d, size %d",
mDefaults->channelCount, mDefaults->bufferRatio, size);
return size;
}
int ALSAStreamOps::getAndroidFormat(snd_pcm_format_t format)
{
int pcmFormatBitWidth;
int audioSystemFormat;
pcmFormatBitWidth = snd_pcm_format_physical_width(format);
audioSystemFormat = AudioSystem::DEFAULT;
switch(pcmFormatBitWidth) {
case 8:
audioSystemFormat = AudioSystem::PCM_8_BIT;
break;
case 16:
audioSystemFormat = AudioSystem::PCM_16_BIT;
break;
default:
LOG_FATAL("Unknown AudioSystem bit width %i!", pcmFormatBitWidth);
}
return audioSystemFormat;
}
int ALSAStreamOps::format() const
{
snd_pcm_format_t ALSAFormat;
int pcmFormatBitWidth;
int audioSystemFormat;
if (snd_pcm_hw_params_get_format(mHardwareParams, &ALSAFormat) < 0) {
return -1;
}
pcmFormatBitWidth = snd_pcm_format_physical_width(ALSAFormat);
audioSystemFormat = AudioSystem::DEFAULT;
switch(pcmFormatBitWidth) {
case 8:
audioSystemFormat = AudioSystem::PCM_8_BIT;
break;
case 16:
audioSystemFormat = AudioSystem::PCM_16_BIT;
break;
default:
LOG_FATAL("Unknown AudioSystem bit width %i!", pcmFormatBitWidth);
}
return audioSystemFormat;
}
uint32_t ALSAStreamOps::getAndroidChannels(int channelCount) const
{
int AudioSystemChannels = AudioSystem::DEFAULT;
if (mDefaults->direction == SND_PCM_STREAM_PLAYBACK) {
switch(channelCount){
case 1:
AudioSystemChannels = AudioSystem::CHANNEL_OUT_MONO;
break;
case 2:
AudioSystemChannels = AudioSystem::CHANNEL_OUT_STEREO;
break;
case 4:
AudioSystemChannels = AudioSystem::CHANNEL_OUT_QUAD;
break;
case 6:
AudioSystemChannels = AudioSystem::CHANNEL_OUT_5POINT1;
break;
default:
LOGE("FATAL: AudioSystem does not support %d output channels.", channelCount);
}
} else {
switch(channelCount){
case 1:
AudioSystemChannels = AudioSystem::CHANNEL_IN_MONO;
break;
case 2:
AudioSystemChannels = AudioSystem::CHANNEL_IN_STEREO;
break;
default:
LOGE("FATAL: AudioSystem does not support %d input channels.", channelCount);
}
}
return AudioSystemChannels;
}
uint32_t ALSAStreamOps::channels() const
{
return getAndroidChannels(mDefaults->channelCount);
}
int ALSAStreamOps::channelCount() const
{
return mDefaults->channelCount;
}
status_t ALSAStreamOps::channelCount(int channelCount) {
int err;
if (!mHandle)
return NO_INIT;
err = snd_pcm_hw_params_set_channels(mHandle, mHardwareParams, channelCount);
if (err < 0) {
LOGE("Unable to set channel count to %i: %s",
channelCount, snd_strerror(err));
return BAD_VALUE;
}
LOGD("Using %i %s for %s.",
channelCount, channelCount == 1 ? "channel" : "channels", streamName());
return NO_ERROR;
}
status_t ALSAStreamOps::open(int mode, uint32_t device)
{
const char *stream = streamName();
const char *devName = deviceName(mode, device);
int err;
LOGI("Try to open ALSA %s device %s", stream, devName);
for(;;) {
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
if (err == 0) break;
// See if there is a less specific name we can try.
// Note: We are changing the contents of a const char * here.
char *tail = strrchr(devName, '_');
if (! tail) break;
*tail = 0;
}
if (err < 0) {
// None of the Android defined audio devices exist. Open a generic one.
devName = "hw:00,1"; // 090507 SMDKC110 Froyo
err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
if (err < 0) {
// Last resort is the NULL device (i.e. the bit bucket).
devName = _nullALSADeviceName;
err = snd_pcm_open(&mHandle, devName, mDefaults->direction, 0);
}
}
mDevice = device;
LOGI("Initialized ALSA %s device %s", stream, devName);
return err;
}
void ALSAStreamOps::close()
{
snd_pcm_t *handle = mHandle;
mHandle = NULL;
if (handle) {
LOGV("ALSAStreamOps::close()");
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
}
status_t ALSAStreamOps::setSoftwareParams()
{
if (!mHandle)
return NO_INIT;
int err;
// Get the current software parameters
err = snd_pcm_sw_params_current(mHandle, mSoftwareParams);
if (err < 0) {
LOGE("Unable to get software parameters: %s", snd_strerror(err));
return NO_INIT;
}
snd_pcm_uframes_t bufferSize = 0;
snd_pcm_uframes_t periodSize = 0;
snd_pcm_uframes_t startThreshold;
// Configure ALSA to start the transfer when the buffer is almost full.
snd_pcm_get_params(mHandle, &bufferSize, &periodSize);
LOGE("bufferSize %d, periodSize %d\n", (int)bufferSize, (int)periodSize);
if (mDefaults->direction == SND_PCM_STREAM_PLAYBACK) {
// For playback, configure ALSA to start the transfer when the
// buffer is almost full.
startThreshold = (bufferSize / periodSize) * periodSize;
//startThreshold = 1;
}
else {
// For recording, configure ALSA to start the transfer on the
// first frame.
startThreshold = 1;
}
err = snd_pcm_sw_params_set_start_threshold(mHandle,
mSoftwareParams,
startThreshold);
if (err < 0) {
LOGE("Unable to set start threshold to %lu frames: %s",
startThreshold, snd_strerror(err));
return NO_INIT;
}
// Stop the transfer when the buffer is full.
err = snd_pcm_sw_params_set_stop_threshold(mHandle,
mSoftwareParams,
bufferSize);
if (err < 0) {
LOGE("Unable to set stop threshold to %lu frames: %s",
bufferSize, snd_strerror(err));
return NO_INIT;
}
// Allow the transfer to start when at least periodSize samples can be
// processed.
err = snd_pcm_sw_params_set_avail_min(mHandle,
mSoftwareParams,
periodSize);
if (err < 0) {
LOGE("Unable to configure available minimum to %lu: %s",
periodSize, snd_strerror(err));
return NO_INIT;
}
// Commit the software parameters back to the device.
err = snd_pcm_sw_params(mHandle, mSoftwareParams);
if (err < 0) {
LOGE("Unable to configure software parameters: %s",
snd_strerror(err));
return NO_INIT;
}
return NO_ERROR;
}
status_t ALSAStreamOps::setPCMFormat(snd_pcm_format_t format)
{
const char *formatDesc;
const char *formatName;
bool validFormat;
int err;
// snd_pcm_format_description() and snd_pcm_format_name() do not perform
// proper bounds checking.
validFormat = (static_cast<int>(format) > SND_PCM_FORMAT_UNKNOWN) &&
(static_cast<int>(format) <= SND_PCM_FORMAT_LAST);
formatDesc = validFormat ?
snd_pcm_format_description(format) : "Invalid Format";
formatName = validFormat ?
snd_pcm_format_name(format) : "UNKNOWN";
err = snd_pcm_hw_params_set_format(mHandle, mHardwareParams, format);
if (err < 0) {
LOGE("Unable to configure PCM format %s (%s): %s",
formatName, formatDesc, snd_strerror(err));
return NO_INIT;
}
LOGD("Set %s PCM format to %s (%s)", streamName(), formatName, formatDesc);
return NO_ERROR;
}
status_t ALSAStreamOps::setHardwareResample(bool resample)
{
int err;
err = snd_pcm_hw_params_set_rate_resample(mHandle,
mHardwareParams,
static_cast<int>(resample));
if (err < 0) {
LOGE("Unable to %s hardware resampling: %s",
resample ? "enable" : "disable",
snd_strerror(err));
return NO_INIT;
}
return NO_ERROR;
}
const char *ALSAStreamOps::streamName()
{
// Don't use snd_pcm_stream(mHandle), as the PCM stream may not be
// opened yet. In such case, snd_pcm_stream() will abort().
return snd_pcm_stream_name(mDefaults->direction);
}
//
// Set playback or capture PCM device. It's possible to support audio output
// or input from multiple devices by using the ALSA plugins, but this is
// not supported for simplicity.
//
// The AudioHardwareALSA API does not allow one to set the input routing.
//
// If the "routes" value does not map to a valid device, the default playback
// device is used.
//
status_t ALSAStreamOps::setDevice(int mode, uint32_t device, uint audio_mode)
{
// Close off previously opened device.
// It would be nice to determine if the underlying device actually
// changes, but we might be manipulating mixer settings (see asound.conf).
//
close();
const char *stream = streamName();
LOGD("\n------------------------>>>>>> ALSA OPEN mode %d,device %d \n",mode,device);
status_t status = open (mode, device);
int err;
unsigned int period_val;
if (status != NO_ERROR)
return status;
err = snd_pcm_hw_params_any(mHandle, mHardwareParams);
if (err < 0) {
LOGE("Unable to configure hardware: %s", snd_strerror(err));
return NO_INIT;
}
status = setPCMFormat(mDefaults->format);
// Set the interleaved read and write format.
err = snd_pcm_hw_params_set_access(mHandle, mHardwareParams,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
LOGE("Unable to configure PCM read/write format: %s",
snd_strerror(err));
return NO_INIT;
}
//
// Some devices do not have the default two channels. Force an error to
// prevent AudioMixer from crashing and taking the whole system down.
//
// Note that some devices will return an -EINVAL if the channel count
// is queried before it has been set. i.e. calling channelCount()
// before channelCount(channels) may return -EINVAL.
//
status = channelCount(mDefaults->channelCount);
if (status != NO_ERROR)
return status;
// Don't check for failure; some devices do not support the default
// sample rate.
// FIXME:: always use default sampling rate
sampleRate(DEFAULT_SAMPLE_RATE);
snd_pcm_uframes_t bufferSize = mDefaults->bufferSize;
snd_pcm_uframes_t periodSize = mDefaults->periodSize;
period_val = bufferSize/periodSize;
unsigned int latency = mDefaults->latency;
// Make sure we have at least the size we originally wanted
err = snd_pcm_hw_params_set_buffer_size(mHandle, mHardwareParams, bufferSize);
if (err < 0) {
LOGE("Unable to set buffer size to %d: %s",
(int)bufferSize, snd_strerror(err));
return NO_INIT;
}
err = snd_pcm_hw_params_set_period_size (mHandle, mHardwareParams, periodSize, NULL);
if (err < 0) {
LOGE("Unable to set the period size for latency: %s", snd_strerror(err));
return NO_INIT;
}
err = snd_pcm_hw_params_get_period_size (mHardwareParams, &periodSize, NULL);
if (err < 0) {
LOGE("Unable to get the period size for latency: %s", snd_strerror(err));
return NO_INIT;
}
// err = snd_pcm_hw_params_get_period_size (mHardwareParams, &periodSize, NULL);
// if (err < 0) {
// LOGE("Unable to get the period size for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// // Setup buffers for latency
// err = snd_pcm_hw_params_set_buffer_time_near (mHandle, mHardwareParams,
// &latency, NULL);
// if(audio_mode == PLAYBACK) {
// period_val = PERIODS_PLAYBACK;
// if(snd_pcm_hw_params_set_periods(mHandle, mHardwareParams, period_val, 0) < 0)
// LOGE("Fail to set period size %d for playback", period_val);
// }
// else
// period_val = PERIODS_CAPTURE;
//
// if (err < 0) {
// LOGD("snd_pcm_hw_params_set_buffer_time_near() failed: %s", snd_strerror(err));
// /* That didn't work, set the period instead */
// unsigned int periodTime = latency / period_val;
// err = snd_pcm_hw_params_set_period_time_near (mHandle, mHardwareParams,
// &periodTime, NULL);
// if (err < 0) {
// LOGE("Unable to set the period time for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// err = snd_pcm_hw_params_get_period_size (mHardwareParams, &periodSize, NULL);
// if (err < 0) {
// LOGE("Unable to get the period size for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// bufferSize = periodSize * period_val;
// if (bufferSize < mDefaults->bufferSize)
// bufferSize = mDefaults->bufferSize;
// err = snd_pcm_hw_params_set_buffer_size_near (mHandle, mHardwareParams, &bufferSize);
// if (err < 0) {
// LOGE("Unable to set the buffer size for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// } else {
// LOGD("snd_pcm_hw_params_set_buffer_time_near() OK");
// // OK, we got buffer time near what we expect. See what that did for bufferSize.
// err = snd_pcm_hw_params_get_buffer_size (mHardwareParams, &bufferSize);
// if (err < 0) {
// LOGE("Unable to get the buffer size for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// // Does set_buffer_time_near change the passed value? It should.
// err = snd_pcm_hw_params_get_buffer_time (mHardwareParams, &latency, NULL);
// if (err < 0) {
// LOGE("Unable to get the buffer time for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// LOGD("got latency %d for bufferSize %d", latency, bufferSize);
// unsigned int periodTime = latency / period_val;
// LOGD("got latency %d for bufferSize %d => periodTime %d", latency, bufferSize, periodTime);
// err = snd_pcm_hw_params_set_period_time_near (mHandle, mHardwareParams,
// &periodTime, NULL);
// if (err < 0) {
// LOGE("Unable to set the period time for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// err = snd_pcm_hw_params_get_period_size (mHardwareParams, &periodSize, NULL);
// if (err < 0) {
// LOGE("Unable to get the period size for latency: %s", snd_strerror(err));
// return NO_INIT;
// }
// }
LOGD("Buffer size: %d", (int)bufferSize);
LOGD("Period size: %d", (int)periodSize);
LOGD("Latency: %d", (int)latency);
mDefaults->bufferSize = bufferSize;
mDefaults->latency = latency;
mDefaults->periodSize = periodSize;
// Commit the hardware parameters back to the device.
err = snd_pcm_hw_params(mHandle, mHardwareParams);
if (err < 0) {
LOGE("Unable to set hardware parameters: %s", snd_strerror(err));
return NO_INIT;
}
status = setSoftwareParams();
return status;
}
const char *ALSAStreamOps::deviceName(int mode, uint32_t device)
{
static char devString[ALSA_NAME_MAX];
int dev;
int hasDevExt = 0;
strcpy (devString, mDefaults->devicePrefix);
for (dev=0; device; dev++)
if (device & (1 << dev)) {
/* Don't go past the end of our list */
if (dev >= deviceSuffixLen)
break;
ALSA_STRCAT (devString, deviceSuffix[dev]);
device &= ~(1 << dev);
hasDevExt = 1;
}
if (hasDevExt)
switch (mode) {
case AudioSystem::MODE_NORMAL:
ALSA_STRCAT (devString, "_normal");
break;
case AudioSystem::MODE_RINGTONE:
ALSA_STRCAT (devString, "_ringtone");
break;
case AudioSystem::MODE_IN_CALL:
ALSA_STRCAT (devString, "_incall");
break;
};
return devString;
}
// ----------------------------------------------------------------------------
AudioStreamOutALSA::AudioStreamOutALSA(AudioHardwareALSA *parent) :
mParent(parent),
mPowerLock(false)
{
static StreamDefaults _defaults = {
devicePrefix : "AndroidPlayback",
direction : SND_PCM_STREAM_PLAYBACK,
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channelCount : 2,
sampleRate : DEFAULT_SAMPLE_RATE,
bufferRatio : 1,
latency : LATENCY_PLAYBACK_MS, // Desired Delay in usec
bufferSize : BUFFER_SZ_PLAYBACK, // Desired Number of samples
periodSize : PERIOD_SZ_PLAYBACK
};
setStreamDefaults(&_defaults);
}
AudioStreamOutALSA::~AudioStreamOutALSA()
{
standby();
}
/* New arch */
status_t AudioStreamOutALSA::setVolume(float left, float right)
{
if (! mParent->mMixer || ! mDevice)
return NO_INIT;
/** Tushar - Need to decide on the volume value
* that we pass onto the mixer. */
return mParent->mMixer->setVolume (mDevice, (left + right)/2);
}
status_t AudioStreamOutALSA::setVolume(float volume)
{
if (! mParent->mMixer || ! mDevice)
return NO_INIT;
return mParent->mMixer->setVolume (mDevice, volume);
}
/* New Arch */
status_t AudioStreamOutALSA::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
status_t status = NO_ERROR;
int device;
int value;
LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string());
if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR)
{
mParent->doRouting(device);
param.remove(String8(AudioParameter::keyRouting));
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
String8 AudioStreamOutALSA::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
String8 value;
String8 key = String8(AudioParameter::keyRouting);
if (param.get(key, value) == NO_ERROR) {
param.addInt(key, (int)mDevice);
}
LOGD("AudioStreamOutALSA::getParameters() %s", param.toString().string());
return param.toString();
}
status_t AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames)
{
//TODO: enable when supported by driver
return INVALID_OPERATION;
}
ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{
snd_pcm_sframes_t n;
size_t sent = 0;
status_t err;
mParent->lock().lock();
AutoMutex lock(mLock);
if (!mPowerLock) {
LOGD("Calling setDevice from write @..%d.\n",__LINE__);
ALSAStreamOps::setDevice(mParent->mode(), mDevice, PLAYBACK);
acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
mPowerLock = true;
}
mParent->lock().unlock();
do {
// write correct number of bytes per attempt
n = snd_pcm_writei(mHandle, (char *) buffer + sent, snd_pcm_bytes_to_frames(mHandle, bytes
- sent));
if (n == -EBADFD) {
LOGD("Calling setDevice.. pcm_write returned error @..%d.\n",__LINE__);
// Somehow the stream is in a bad state. The driver probably
// has a bug and snd_pcm_recover() doesn't seem to handle this.
ALSAStreamOps::setDevice(mParent->mode(), mDevice, PLAYBACK);
} else if (n < 0) {
if (mHandle) {
// snd_pcm_recover() will return 0 if successful in recovering from
// // an error, or -errno if the error was unrecoverable.
// We can make silent bit on as we are now handling the under-run and there will not be any data loss due to under-run
n = snd_pcm_recover(mHandle, n, 1);
if (n)
return static_cast<ssize_t> (n);
}
} else
sent += static_cast<ssize_t> (snd_pcm_frames_to_bytes(mHandle, n));
} while (mHandle && sent < bytes);
//LOGI("Request Bytes=%d, Actual Written=%d",bytes,sent);
return snd_pcm_frames_to_bytes(mHandle, sent);
}
status_t AudioStreamOutALSA::dump(int fd, const Vector<String16>& args)
{
return NO_ERROR;
}
status_t AudioStreamOutALSA::setDevice(int mode,
uint32_t newDevice,
uint32_t audio_mode,
bool force)
{
AutoMutex lock(mLock);
LOGV("AudioStreamOutALSA::setDevice(mode %d, newDevice %x, audio_mode %d), mDevice %x",
mode, newDevice, audio_mode, mDevice);
if (newDevice != mDevice || force) {
return ALSAStreamOps::setDevice(mode, newDevice, audio_mode);
}
return NO_ERROR;
}
status_t AudioStreamOutALSA::standby() {
AutoMutex _l(mParent->lock());
AutoMutex lock(mLock);
LOGD("Inside AudioStreamOutALSA::standby\n");
if (mParent->mode() != AudioSystem::MODE_IN_CALL) {
ALSAStreamOps::close();
}
if (mPowerLock) {
release_wake_lock("AudioOutLock");
mPowerLock = false;
}
return NO_ERROR;
}
#define USEC_TO_MSEC(x) ((x + 999) / 1000)
uint32_t AudioStreamOutALSA::latency() const
{
// Android wants latency in milliseconds.
return USEC_TO_MSEC (mDefaults->latency);
}
// ----------------------------------------------------------------------------
AudioStreamInALSA::AudioStreamInALSA(AudioHardwareALSA *parent) :
mParent(parent), mPowerLock(false),
mDownSampler(NULL), mPcmIn(NULL)
{
static StreamDefaults _defaults = {
devicePrefix : "AndroidRecord",
direction : SND_PCM_STREAM_CAPTURE,
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channelCount : 1,
sampleRate : DEFAULT_SAMPLE_RATE,
bufferRatio : 1,
latency : LATENCY_CAPTURE_MS,// Desired Delay in usec
bufferSize : BUFFER_SZ_CAPTURE, // Desired Number of samples
periodSize : PERIOD_SZ_CAPTURE
};
setStreamDefaults(&_defaults);
}
status_t AudioStreamInALSA::set(int *pformat,
uint32_t *pchannels,
uint32_t *prate)
{
status_t status = ALSAStreamOps::set(pformat, pchannels, prate);
if (status == NO_ERROR && prate && *prate != DEFAULT_SAMPLE_RATE) {
mDownSampler = new ALSADownsampler(*prate,
mDefaults->channelCount,
PERIOD_SZ_CAPTURE,
this);
status = mDownSampler->initCheck();
if (status != NO_ERROR) {
return status;
}
mPcmIn = new int16_t[PERIOD_SZ_CAPTURE * mDefaults->channelCount];
}
return status;
}
AudioStreamInALSA::~AudioStreamInALSA()
{
standby();
if (mDownSampler != NULL) {
delete mDownSampler;
}
if (mPcmIn != NULL) {
delete[] mPcmIn;
}
}
status_t AudioStreamInALSA::setGain(float gain)
{
if (mParent->mMixer)
return mParent->mMixer->setMasterGain (gain);
else
return NO_INIT;
}
ssize_t AudioStreamInALSA::read(void *buffer, ssize_t bytes)
{
snd_pcm_sframes_t n;
mParent->lock().lock();
AutoMutex lock(mLock);
if (!mPowerLock) {
acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock");
LOGD("Calling setDevice from read@..%d.\n",__LINE__);
ALSAStreamOps::setDevice(mParent->mode(), mDevice, CAPTURE);
if (mDownSampler != NULL) {
mDownSampler->reset();
mReadStatus = 0;
mInPcmInBuf = 0;
}
mPowerLock = true;
}
mParent->lock().unlock();
if (!mHandle) {
return -1;
}
// FIXME: only support reads of exactly bufferSize() for now
if (bytes != (ssize_t)bufferSize()) {
LOGW("AudioStreamInALSA::read bad read size %d expected %d", (int)bytes, bufferSize());
return -1;
}
size_t frames = snd_pcm_bytes_to_frames(mHandle, bytes);
do {
if (mDownSampler) {
status_t status = mDownSampler->resample((int16_t *)buffer, &frames);
if (status != NO_ERROR) {
if (mReadStatus != 0) {
n = mReadStatus;
} else {
n = status;
}
frames = snd_pcm_bytes_to_frames(mHandle, bytes);
} else {
n = frames;
}
} else {
n = snd_pcm_readi(mHandle,
(uint8_t *)buffer,
frames);
}
if (n < 0) {
LOGD("AudioStreamInALSA::read error %d", (int)n);
n = snd_pcm_recover(mHandle, n, 0);
LOGD("AudioStreamInALSA::snd_pcm_recover error %d", (int)n);
if (n)
return static_cast<ssize_t> (n);
}
} while (n == 0);
return snd_pcm_frames_to_bytes(mHandle, n);
}
status_t AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
{
return NO_ERROR;
}
status_t AudioStreamInALSA::setDevice(int mode,
uint32_t newDevice,
uint32_t audio_mode,
bool force)
{
AutoMutex lock(mLock);
return ALSAStreamOps::setDevice(mode, newDevice, audio_mode);
}
status_t AudioStreamInALSA::standby()
{
AutoMutex _l(mParent->lock());
AutoMutex lock(mLock);
LOGD("Entering AudioStreamInALSA::standby\n");
ALSAStreamOps::close();
if (mPowerLock) {
release_wake_lock ("AudioInLock");
mPowerLock = false;
}
return NO_ERROR;
}
/* New Arch */
status_t AudioStreamInALSA::setParameters(const String8& keyValuePairs)
{
AudioParameter param = AudioParameter(keyValuePairs);
String8 key = String8("vr_mode");
status_t status = NO_ERROR;
int value;
LOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string());
if (param.getInt(key, value) == NO_ERROR) {
mParent->setVoiceRecordGain((value != 0));
param.remove(key);
}
key = String8(AudioParameter::keyRouting);
if (param.getInt(key, value) == NO_ERROR) {
if(mHandle != NULL && value != 0)
setDevice(mParent->mode(), value, CAPTURE);
param.remove(key);
}
if (param.size()) {
status = BAD_VALUE;
}
return status;
}
String8 AudioStreamInALSA::getParameters(const String8& keys)
{
AudioParameter param = AudioParameter(keys);
String8 value;
String8 key = String8(AudioParameter::keyRouting);
if (param.get(key, value) == NO_ERROR) {
param.addInt(key, (int)mDevice);
}
LOGD("AudioStreamInALSA::getParameters() %s", param.toString().string());
return param.toString();
}
status_t AudioStreamInALSA::getNextBuffer(ALSABufferProvider::Buffer* buffer)
{
if (mHandle == NULL) {
buffer->raw = NULL;
buffer->frameCount = 0;
return NO_INIT;
}
if (mInPcmInBuf == 0) {
while (mInPcmInBuf < PERIOD_SZ_CAPTURE) {
mReadStatus = snd_pcm_readi(mHandle,
(uint8_t *)mPcmIn +
(mInPcmInBuf * mDefaults->channelCount * sizeof(int16_t)),
PERIOD_SZ_CAPTURE - mInPcmInBuf);
if (mReadStatus < 0) {
buffer->raw = NULL;
buffer->frameCount = 0;
LOGV("resampler read error %d", mReadStatus);
return mReadStatus;
}
mInPcmInBuf += mReadStatus;
}
}
buffer->frameCount = (buffer->frameCount > mInPcmInBuf) ? mInPcmInBuf : buffer->frameCount;
buffer->i16 = mPcmIn + (PERIOD_SZ_CAPTURE - mInPcmInBuf) * mDefaults->channelCount;
return NO_ERROR;
}
void AudioStreamInALSA::releaseBuffer(ALSABufferProvider::Buffer* buffer)
{
mInPcmInBuf -= buffer->frameCount;
}
// ----------------------------------------------------------------------------
struct mixer_info_t
{
mixer_info_t() :
elem(0),
min(SND_MIXER_VOL_RANGE_MIN),
max(SND_MIXER_VOL_RANGE_MAX),
mute(false)
{
}
snd_mixer_elem_t *elem;
long min;
long max;
long volume;
bool mute;
char name[ALSA_NAME_MAX];
};
static int initMixer (snd_mixer_t **mixer, const char *name)
{
int err;
if ((err = snd_mixer_open(mixer, 0)) < 0) {
LOGE("Unable to open mixer: %s", snd_strerror(err));
return err;
}
if ((err = snd_mixer_attach(*mixer, name)) < 0) {
LOGE("Unable to attach mixer to device %s: %s",
name, snd_strerror(err));
if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
LOGE("Unable to attach mixer to device default: %s",
snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
}
if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
LOGE("Unable to register mixer elements: %s", snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
// Get the mixer controls from the kernel
if ((err = snd_mixer_load(*mixer)) < 0) {
LOGE("Unable to load mixer elements: %s", snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
return 0;
}
typedef int (*hasVolume_t)(snd_mixer_elem_t*);
static const hasVolume_t hasVolume[] = {
snd_mixer_selem_has_playback_volume,
snd_mixer_selem_has_capture_volume
};
typedef int (*getVolumeRange_t)(snd_mixer_elem_t*, long int*, long int*);
static const getVolumeRange_t getVolumeRange[] = {
snd_mixer_selem_get_playback_volume_range,
snd_mixer_selem_get_capture_volume_range
};
typedef int (*setVolume_t)(snd_mixer_elem_t*, long int);
static const setVolume_t setVol[] = {
snd_mixer_selem_set_playback_volume_all,
snd_mixer_selem_set_capture_volume_all
};
ALSAMixer::ALSAMixer()
{
int err;
initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], "AndroidPlayback");
initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], "AndroidRecord");
snd_mixer_selem_id_t *sid;
snd_mixer_selem_id_alloca(&sid);
for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
property_get (mixerMasterProp[i].propName,
info->name,
mixerMasterProp[i].propDefault);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
elem;
elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
// Find PCM playback volume control element.
const char *elementName = snd_mixer_selem_id_get_name(sid);
if (hasVolume[i] (elem))
LOGD ("Mixer: element name: '%s'", elementName);
if (info->elem == NULL &&
strcmp(elementName, info->name) == 0 &&
hasVolume[i] (elem)) {
info->elem = elem;
getVolumeRange[i] (elem, &info->min, &info->max);
info->volume = info->max;
setVol[i] (elem, info->volume);
if (i == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_switch (elem))
snd_mixer_selem_set_playback_switch_all (elem, 1);
break;
}
}
LOGD ("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
for (int j = 0; mixerProp[j][i].routes; j++) {
mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
property_get (mixerProp[j][i].propName,
info->name,
mixerProp[j][i].propDefault);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
elem;
elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
// Find PCM playback volume control element.
const char *elementName = snd_mixer_selem_id_get_name(sid);
if (info->elem == NULL &&
strcmp(elementName, info->name) == 0 &&
hasVolume[i] (elem)) {
info->elem = elem;
getVolumeRange[i] (elem, &info->min, &info->max);
info->volume = info->max;
setVol[i] (elem, info->volume);
if (i == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_switch (elem))
snd_mixer_selem_set_playback_switch_all (elem, 1);
break;
}
}
LOGD ("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
}
}
LOGD("mixer initialized.");
}
ALSAMixer::~ALSAMixer()
{
for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
if (mMixer[i]) snd_mixer_close (mMixer[i]);
if (mixerMasterProp[i].mInfo) {
delete mixerMasterProp[i].mInfo;
mixerMasterProp[i].mInfo = NULL;
}
for (int j = 0; mixerProp[j][i].routes; j++) {
if (mixerProp[j][i].mInfo) {
delete mixerProp[j][i].mInfo;
mixerProp[j][i].mInfo = NULL;
}
}
}
LOGD("mixer destroyed.");
}
status_t ALSAMixer::setMasterVolume(float volume)
{
mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_PLAYBACK].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
long minVol = info->min;
long maxVol = info->max;
// Make sure volume is between bounds.
long vol = minVol + volume * (maxVol - minVol);
if (vol > maxVol) vol = maxVol;
if (vol < minVol) vol = minVol;
info->volume = vol;
snd_mixer_selem_set_playback_volume_all (info->elem, vol);
return NO_ERROR;
}
status_t ALSAMixer::setMasterGain(float gain)
{
mixer_info_t *info = mixerMasterProp[SND_PCM_STREAM_CAPTURE].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
long minVol = info->min;
long maxVol = info->max;
// Make sure volume is between bounds.
long vol = minVol + gain * (maxVol - minVol);
if (vol > maxVol) vol = maxVol;
if (vol < minVol) vol = minVol;
info->volume = vol;
snd_mixer_selem_set_capture_volume_all (info->elem, vol);
return NO_ERROR;
}
status_t ALSAMixer::setVolume(uint32_t device, float volume)
{
for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes; j++)
if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
long minVol = info->min;
long maxVol = info->max;
// Make sure volume is between bounds.
long vol = minVol + volume * (maxVol - minVol);
if (vol > maxVol) vol = maxVol;
if (vol < minVol) vol = minVol;
info->volume = vol;
snd_mixer_selem_set_playback_volume_all (info->elem, vol);
}
return NO_ERROR;
}
status_t ALSAMixer::setGain(uint32_t device, float gain)
{
for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].routes; j++)
if (mixerProp[j][SND_PCM_STREAM_CAPTURE].routes & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
long minVol = info->min;
long maxVol = info->max;
// Make sure volume is between bounds.
long vol = minVol + gain * (maxVol - minVol);
if (vol > maxVol) vol = maxVol;
if (vol < minVol) vol = minVol;
info->volume = vol;
snd_mixer_selem_set_capture_volume_all (info->elem, vol);
}
return NO_ERROR;
}
status_t ALSAMixer::setCaptureMuteState(uint32_t device, bool state)
{
for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].routes; j++)
if (mixerProp[j][SND_PCM_STREAM_CAPTURE].routes & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
if (snd_mixer_selem_has_capture_switch (info->elem)) {
int err = snd_mixer_selem_set_capture_switch_all (info->elem, static_cast<int>(!state));
if (err < 0) {
LOGE("Unable to %s capture mixer switch %s",
state ? "enable" : "disable", info->name);
return INVALID_OPERATION;
}
}
info->mute = state;
}
return NO_ERROR;
}
status_t ALSAMixer::getCaptureMuteState(uint32_t device, bool *state)
{
if (! state) return BAD_VALUE;
for (int j = 0; mixerProp[j][SND_PCM_STREAM_CAPTURE].routes; j++)
if (mixerProp[j][SND_PCM_STREAM_CAPTURE].routes & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_CAPTURE].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
*state = info->mute;
return NO_ERROR;
}
return BAD_VALUE;
}
status_t ALSAMixer::setPlaybackMuteState(uint32_t device, bool state)
{
LOGE("\n set playback mute device %d, state %d \n", device,state);
for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes; j++)
if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
if (snd_mixer_selem_has_playback_switch (info->elem)) {
int err = snd_mixer_selem_set_playback_switch_all (info->elem, static_cast<int>(!state));
if (err < 0) {
LOGE("Unable to %s playback mixer switch %s",
state ? "enable" : "disable", info->name);
return INVALID_OPERATION;
}
}
info->mute = state;
}
return NO_ERROR;
}
status_t ALSAMixer::getPlaybackMuteState(uint32_t device, bool *state)
{
if (! state) return BAD_VALUE;
for (int j = 0; mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes; j++)
if (mixerProp[j][SND_PCM_STREAM_PLAYBACK].routes & device) {
mixer_info_t *info = mixerProp[j][SND_PCM_STREAM_PLAYBACK].mInfo;
if (!info || !info->elem) return INVALID_OPERATION;
*state = info->mute;
return NO_ERROR;
}
return BAD_VALUE;
}
// ----------------------------------------------------------------------------
ALSAControl::ALSAControl(const char *device)
{
snd_ctl_open(&mHandle, device, 0);
}
ALSAControl::~ALSAControl()
{
if (mHandle) snd_ctl_close(mHandle);
}
status_t ALSAControl::get(const char *name, unsigned int &value, int index)
{
if (!mHandle) return NO_INIT;
snd_ctl_elem_id_t *id;
snd_ctl_elem_info_t *info;
snd_ctl_elem_value_t *control;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_value_alloca(&control);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_id_set_name(id, name);
snd_ctl_elem_info_set_id(info, id);
int ret = snd_ctl_elem_info(mHandle, info);
if (ret < 0) return BAD_VALUE;
snd_ctl_elem_info_get_id(info, id);
snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
unsigned int count = snd_ctl_elem_info_get_count(info);
if ((unsigned int)index >= count) return BAD_VALUE;
snd_ctl_elem_value_set_id(control, id);
ret = snd_ctl_elem_read(mHandle, control);
if (ret < 0) return BAD_VALUE;
switch (type) {
case SND_CTL_ELEM_TYPE_BOOLEAN:
value = snd_ctl_elem_value_get_boolean(control, index);
break;
case SND_CTL_ELEM_TYPE_INTEGER:
value = snd_ctl_elem_value_get_integer(control, index);
break;
case SND_CTL_ELEM_TYPE_INTEGER64:
value = snd_ctl_elem_value_get_integer64(control, index);
break;
case SND_CTL_ELEM_TYPE_ENUMERATED:
value = snd_ctl_elem_value_get_enumerated(control, index);
break;
case SND_CTL_ELEM_TYPE_BYTES:
value = snd_ctl_elem_value_get_byte(control, index);
break;
default:
return BAD_VALUE;
}
return NO_ERROR;
}
status_t ALSAControl::set(const char *name, unsigned int value, int index)
{
if (!mHandle) return NO_INIT;
snd_ctl_elem_id_t *id;
snd_ctl_elem_info_t *info;
snd_ctl_elem_value_t *control;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_value_alloca(&control);
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
snd_ctl_elem_id_set_name(id, name);
snd_ctl_elem_info_set_id(info, id);
int ret = snd_ctl_elem_info(mHandle, info);
if (ret < 0) return BAD_VALUE;
snd_ctl_elem_info_get_id(info, id);
snd_ctl_elem_type_t type = snd_ctl_elem_info_get_type(info);
unsigned int count = snd_ctl_elem_info_get_count(info);
if (index >= (int)count) return BAD_VALUE;
if (index == -1)
index = 0; // Range over all of them
else
count = index + 1; // Just do the one specified
snd_ctl_elem_value_set_id(control, id);
for (unsigned int i = index; i < count; i++)
switch (type) {
case SND_CTL_ELEM_TYPE_BOOLEAN:
snd_ctl_elem_value_set_boolean(control, i, value);
break;
case SND_CTL_ELEM_TYPE_INTEGER:
snd_ctl_elem_value_set_integer(control, i, value);
break;
case SND_CTL_ELEM_TYPE_INTEGER64:
snd_ctl_elem_value_set_integer64(control, i, value);
break;
case SND_CTL_ELEM_TYPE_ENUMERATED:
snd_ctl_elem_value_set_enumerated(control, i, value);
break;
case SND_CTL_ELEM_TYPE_BYTES:
snd_ctl_elem_value_set_byte(control, i, value);
break;
default:
break;
}
ret = snd_ctl_elem_write(mHandle, control);
return (ret < 0) ? BAD_VALUE : NO_ERROR;
}
//------------------------------------------------------------------------------
// Downsampler
//------------------------------------------------------------------------------
/*
* 2.30 fixed point FIR filter coefficients for conversion 44100 -> 22050.
* (Works equivalently for 22010 -> 11025 or any other halving, of course.)
*
* Transition band from about 18 kHz, passband ripple < 0.1 dB,
* stopband ripple at about -55 dB, linear phase.
*
* Design and display in MATLAB or Octave using:
*
* filter = fir1(19, 0.5); filter = round(filter * 2**30); freqz(filter * 2**-30);
*/
static const int32_t filter_22khz_coeff[] = {
2089257, 2898328, -5820678, -10484531,
19038724, 30542725, -50469415, -81505260,
152544464, 478517512, 478517512, 152544464,
-81505260, -50469415, 30542725, 19038724,
-10484531, -5820678, 2898328, 2089257,
};
#define NUM_COEFF_22KHZ (sizeof(filter_22khz_coeff) / sizeof(filter_22khz_coeff[0]))
#define OVERLAP_22KHZ (NUM_COEFF_22KHZ - 2)
/*
* Convolution of signals A and reverse(B). (In our case, the filter response
* is symmetric, so the reversing doesn't matter.)
* A is taken to be in 0.16 fixed-point, and B is taken to be in 2.30 fixed-point.
* The answer will be in 16.16 fixed-point, unclipped.
*
* This function would probably be the prime candidate for SIMD conversion if
* you want more speed.
*/
int32_t fir_convolve(const int16_t* a, const int32_t* b, int num_samples)
{
int32_t sum = 1 << 13;
for (int i = 0; i < num_samples; ++i) {
sum += a[i] * (b[i] >> 16);
}
return sum >> 14;
}
/* Clip from 16.16 fixed-point to 0.16 fixed-point. */
int16_t clip(int32_t x)
{
if (x < -32768) {
return -32768;
} else if (x > 32767) {
return 32767;
} else {
return x;
}
}
/*
* Convert a chunk from 44 kHz to 22 kHz. Will update num_samples_in and num_samples_out
* accordingly, since it may leave input samples in the buffer due to overlap.
*
* Input and output are taken to be in 0.16 fixed-point.
*/
void resample_2_1(int16_t* input, int16_t* output, int* num_samples_in, int* num_samples_out)
{
if (*num_samples_in < (int)NUM_COEFF_22KHZ) {
*num_samples_out = 0;
return;
}
int odd_smp = *num_samples_in & 0x1;
int num_samples = *num_samples_in - odd_smp - OVERLAP_22KHZ;
for (int i = 0; i < num_samples; i += 2) {
output[i / 2] = clip(fir_convolve(input + i, filter_22khz_coeff, NUM_COEFF_22KHZ));
}
memmove(input, input + num_samples, (OVERLAP_22KHZ + odd_smp) * sizeof(*input));
*num_samples_out = num_samples / 2;
*num_samples_in = OVERLAP_22KHZ + odd_smp;
}
/*
* 2.30 fixed point FIR filter coefficients for conversion 22050 -> 16000,
* or 11025 -> 8000.
*
* Transition band from about 14 kHz, passband ripple < 0.1 dB,
* stopband ripple at about -50 dB, linear phase.
*
* Design and display in MATLAB or Octave using:
*
* filter = fir1(23, 16000 / 22050); filter = round(filter * 2**30); freqz(filter * 2**-30);
*/
static const int32_t filter_16khz_coeff[] = {
2057290, -2973608, 1880478, 4362037,
-14639744, 18523609, -1609189, -38502470,
78073125, -68353935, -59103896, 617555440,
617555440, -59103896, -68353935, 78073125,
-38502470, -1609189, 18523609, -14639744,
4362037, 1880478, -2973608, 2057290,
};
#define NUM_COEFF_16KHZ (sizeof(filter_16khz_coeff) / sizeof(filter_16khz_coeff[0]))
#define OVERLAP_16KHZ (NUM_COEFF_16KHZ - 1)
/*
* Convert a chunk from 22 kHz to 16 kHz. Will update num_samples_in and
* num_samples_out accordingly, since it may leave input samples in the buffer
* due to overlap.
*
* This implementation is rather ad-hoc; it first low-pass filters the data
* into a temporary buffer, and then converts chunks of 441 input samples at a
* time into 320 output samples by simple linear interpolation. A better
* implementation would use a polyphase filter bank to do these two operations
* in one step.
*
* Input and output are taken to be in 0.16 fixed-point.
*/
#define RESAMPLE_16KHZ_SAMPLES_IN 441
#define RESAMPLE_16KHZ_SAMPLES_OUT 320
void resample_441_320(int16_t* input, int16_t* output, int* num_samples_in, int* num_samples_out)
{
const int num_blocks = (*num_samples_in - OVERLAP_16KHZ) / RESAMPLE_16KHZ_SAMPLES_IN;
if (num_blocks < 1) {
*num_samples_out = 0;
return;
}
for (int i = 0; i < num_blocks; ++i) {
uint32_t tmp[RESAMPLE_16KHZ_SAMPLES_IN];
for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_IN; ++j) {
tmp[j] = fir_convolve(input + i * RESAMPLE_16KHZ_SAMPLES_IN + j,
filter_16khz_coeff,
NUM_COEFF_16KHZ);
}
const float step_float = (float)RESAMPLE_16KHZ_SAMPLES_IN / (float)RESAMPLE_16KHZ_SAMPLES_OUT;
uint32_t in_sample_num = 0; // 16.16 fixed point
const uint32_t step = (uint32_t)(step_float * 65536.0f + 0.5f); // 16.16 fixed point
for (int j = 0; j < RESAMPLE_16KHZ_SAMPLES_OUT; ++j, in_sample_num += step) {
const uint32_t whole = in_sample_num >> 16;
const uint32_t frac = (in_sample_num & 0xffff); // 0.16 fixed point
const int32_t s1 = tmp[whole];
const int32_t s2 = tmp[whole + 1];
*output++ = clip(s1 + (((s2 - s1) * (int32_t)frac) >> 16));
}
}
const int samples_consumed = num_blocks * RESAMPLE_16KHZ_SAMPLES_IN;
memmove(input, input + samples_consumed, (*num_samples_in - samples_consumed) * sizeof(*input));
*num_samples_in -= samples_consumed;
*num_samples_out = RESAMPLE_16KHZ_SAMPLES_OUT * num_blocks;
}
ALSADownsampler::ALSADownsampler(uint32_t outSampleRate,
uint32_t channelCount,
uint32_t frameCount,
ALSABufferProvider* provider)
: mStatus(NO_INIT), mProvider(provider), mSampleRate(outSampleRate),
mChannelCount(channelCount), mFrameCount(frameCount),
mInLeft(NULL), mInRight(NULL), mTmpLeft(NULL), mTmpRight(NULL),
mTmp2Left(NULL), mTmp2Right(NULL), mOutLeft(NULL), mOutRight(NULL)
{
LOGV("ALSADownsampler() cstor SR %d channels %d frames %d",
mSampleRate, mChannelCount, mFrameCount);
if (mSampleRate != 8000 && mSampleRate != 11025 && mSampleRate != 16000 &&
mSampleRate != 22050) {
LOGW("ALSADownsampler cstor: bad sampling rate: %d", mSampleRate);
return;
}
mInLeft = new int16_t[mFrameCount];
mInRight = new int16_t[mFrameCount];
mTmpLeft = new int16_t[mFrameCount];
mTmpRight = new int16_t[mFrameCount];
mTmp2Left = new int16_t[mFrameCount];
mTmp2Right = new int16_t[mFrameCount];
mOutLeft = new int16_t[mFrameCount];
mOutRight = new int16_t[mFrameCount];
mStatus = NO_ERROR;
}
ALSADownsampler::~ALSADownsampler()
{
if (mInLeft) delete[] mInLeft;
if (mInRight) delete[] mInRight;
if (mTmpLeft) delete[] mTmpLeft;
if (mTmpRight) delete[] mTmpRight;
if (mTmp2Left) delete[] mTmp2Left;
if (mTmp2Right) delete[] mTmp2Right;
if (mOutLeft) delete[] mOutLeft;
if (mOutRight) delete[] mOutRight;
}
void ALSADownsampler::reset()
{
mInInBuf = 0;
mInTmpBuf = 0;
mInTmp2Buf = 0;
mOutBufPos = 0;
mInOutBuf = 0;
}
int ALSADownsampler::resample(int16_t* out, size_t *outFrameCount)
{
if (mStatus != NO_ERROR) {
return mStatus;
}
if (out == NULL || outFrameCount == NULL) {
return BAD_VALUE;
}
int16_t *outLeft = mTmp2Left;
int16_t *outRight = mTmp2Left;
if (mSampleRate == 22050) {
outLeft = mTmpLeft;
outRight = mTmpRight;
} else if (mSampleRate == 8000){
outLeft = mOutLeft;
outRight = mOutRight;
}
int outFrames = 0;
int remaingFrames = *outFrameCount;
if (mInOutBuf) {
int frames = (remaingFrames > mInOutBuf) ? mInOutBuf : remaingFrames;
for (int i = 0; i < frames; ++i) {
out[i] = outLeft[mOutBufPos + i];
}
if (mChannelCount == 2) {
for (int i = 0; i < frames; ++i) {
out[i * 2] = outLeft[mOutBufPos + i];
out[i * 2 + 1] = outRight[mOutBufPos + i];
}
}
remaingFrames -= frames;
mInOutBuf -= frames;
mOutBufPos += frames;
outFrames += frames;
}
while (remaingFrames) {
LOGW_IF((mInOutBuf != 0), "mInOutBuf should be 0 here");
ALSABufferProvider::Buffer buf;
buf.frameCount = mFrameCount - mInInBuf;
int ret = mProvider->getNextBuffer(&buf);
if (buf.raw == NULL) {
*outFrameCount = outFrames;
return ret;
}
for (size_t i = 0; i < buf.frameCount; ++i) {
mInLeft[i + mInInBuf] = buf.i16[i];
}
if (mChannelCount == 2) {
for (size_t i = 0; i < buf.frameCount; ++i) {
mInLeft[i + mInInBuf] = buf.i16[i * 2];
mInRight[i + mInInBuf] = buf.i16[i * 2 + 1];
}
}
mInInBuf += buf.frameCount;
mProvider->releaseBuffer(&buf);
/* 44010 -> 22050 */
{
int samples_in_left = mInInBuf;
int samples_out_left;
resample_2_1(mInLeft, mTmpLeft + mInTmpBuf, &samples_in_left, &samples_out_left);
if (mChannelCount == 2) {
int samples_in_right = mInInBuf;
int samples_out_right;
resample_2_1(mInRight, mTmpRight + mInTmpBuf, &samples_in_right, &samples_out_right);
}
mInInBuf = samples_in_left;
mInTmpBuf += samples_out_left;
mInOutBuf = samples_out_left;
}
if (mSampleRate == 11025 || mSampleRate == 8000) {
/* 22050 - > 11025 */
int samples_in_left = mInTmpBuf;
int samples_out_left;
resample_2_1(mTmpLeft, mTmp2Left + mInTmp2Buf, &samples_in_left, &samples_out_left);
if (mChannelCount == 2) {
int samples_in_right = mInTmpBuf;
int samples_out_right;
resample_2_1(mTmpRight, mTmp2Right + mInTmp2Buf, &samples_in_right, &samples_out_right);
}
mInTmpBuf = samples_in_left;
mInTmp2Buf += samples_out_left;
mInOutBuf = samples_out_left;
if (mSampleRate == 8000) {
/* 11025 -> 8000*/
int samples_in_left = mInTmp2Buf;
int samples_out_left;
resample_441_320(mTmp2Left, mOutLeft, &samples_in_left, &samples_out_left);
if (mChannelCount == 2) {
int samples_in_right = mInTmp2Buf;
int samples_out_right;
resample_441_320(mTmp2Right, mOutRight, &samples_in_right, &samples_out_right);
}
mInTmp2Buf = samples_in_left;
mInOutBuf = samples_out_left;
} else {
mInTmp2Buf = 0;
}
} else if (mSampleRate == 16000) {
/* 22050 -> 16000*/
int samples_in_left = mInTmpBuf;
int samples_out_left;
resample_441_320(mTmpLeft, mTmp2Left, &samples_in_left, &samples_out_left);
if (mChannelCount == 2) {
int samples_in_right = mInTmpBuf;
int samples_out_right;
resample_441_320(mTmpRight, mTmp2Right, &samples_in_right, &samples_out_right);
}
mInTmpBuf = samples_in_left;
mInOutBuf = samples_out_left;
} else {
mInTmpBuf = 0;
}
int frames = (remaingFrames > mInOutBuf) ? mInOutBuf : remaingFrames;
for (int i = 0; i < frames; ++i) {
out[outFrames + i] = outLeft[i];
}
if (mChannelCount == 2) {
for (int i = 0; i < frames; ++i) {
out[(outFrames + i) * 2] = outLeft[i];
out[(outFrames + i) * 2 + 1] = outRight[i];
}
}
remaingFrames -= frames;
outFrames += frames;
mOutBufPos = frames;
mInOutBuf -= frames;
}
return 0;
}
}; // namespace android