summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xBoardConfig.mk4
-rw-r--r--libaudio2/Android.mk15
-rw-r--r--libaudio2/AudioHardware.cpp1398
-rw-r--r--libaudio2/AudioHardware.h214
-rw-r--r--libaudio2/alsa_audio.h7
-rw-r--r--libaudio2/alsa_pcm.c38
6 files changed, 1583 insertions, 93 deletions
diff --git a/BoardConfig.mk b/BoardConfig.mk
index 881cd52..c5253df 100755
--- a/BoardConfig.mk
+++ b/BoardConfig.mk
@@ -49,8 +49,8 @@ TARGET_ARCH_VARIANT := armv7-a-neon
ARCH_ARM_HAVE_TLS_REGISTER := true
ifeq ($(BOARD_USES_GENERIC_AUDIO),false)
-BOARD_USES_ALSA_AUDIO := true
-BUILD_WITH_ALSA_UTILS := true
+BOARD_USES_ALSA_AUDIO := false
+BUILD_WITH_ALSA_UTILS := false
endif
USE_CAMERA_STUB := false
diff --git a/libaudio2/Android.mk b/libaudio2/Android.mk
index 53979f3..cdabff3 100644
--- a/libaudio2/Android.mk
+++ b/libaudio2/Android.mk
@@ -5,21 +5,21 @@ ifeq ($(TARGET_DEVICE),crespo)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= aplay.c alsa_pcm.c alsa_mixer.c
LOCAL_MODULE:= aplay
-LOCAL_SHARED_LIBRARIES:= libc
+LOCAL_SHARED_LIBRARIES:= libc libcutils
LOCAL_MODULE_TAGS:= debug
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= arec.c alsa_pcm.c
LOCAL_MODULE:= arec
-LOCAL_SHARED_LIBRARIES:= libc
+LOCAL_SHARED_LIBRARIES:= libc libcutils
LOCAL_MODULE_TAGS:= debug
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= amix.c alsa_mixer.c
LOCAL_MODULE:= amix
-LOCAL_SHARED_LIBRARIES := libc
+LOCAL_SHARED_LIBRARIES := libc libcutils
LOCAL_MODULE_TAGS:= debug
include $(BUILD_EXECUTABLE)
@@ -29,10 +29,17 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= AudioHardware.cpp alsa_mixer.c alsa_pcm.c
LOCAL_MODULE:= libaudio
LOCAL_STATIC_LIBRARIES:= libaudiointerface
-LOCAL_SHARED_LIBRARIES:= libc libcutils libutils libmedia
+LOCAL_SHARED_LIBRARIES:= libc libcutils libutils libmedia libhardware_legacy
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_SHARED_LIBRARIES += liba2dp
endif
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
diff --git a/libaudio2/AudioHardware.cpp b/libaudio2/AudioHardware.cpp
index c0d20da..1a8975f 100644
--- a/libaudio2/AudioHardware.cpp
+++ b/libaudio2/AudioHardware.cpp
@@ -16,6 +16,8 @@
#include <math.h>
+//#define LOG_NDEBUG 0
+
#define LOG_TAG "AudioHardware"
#include <utils/Log.h>
@@ -31,6 +33,7 @@
#include "AudioHardware.h"
#include <media/AudioRecord.h>
+#include <hardware_legacy/power.h>
extern "C" {
#include "alsa_audio.h"
@@ -38,17 +41,59 @@ extern "C" {
namespace android {
+
+const uint32_t AudioHardware::inputSamplingRates[] = {
+ 8000, 11025, 16000, 22050, 44100
+};
+
// ----------------------------------------------------------------------------
AudioHardware::AudioHardware() :
- mInit(false), mMicMute(true), mOutput(0)
+ mInit(false),
+ mMicMute(false),
+ mOutput(NULL),
+ mPcm(NULL),
+ mMixer(NULL),
+ mPcmOpenCnt(0),
+ mMixerOpenCnt(0),
+ mVrModeEnabled(false),
+ mBluetoothNrec(true),
+ mSecRilLibHandle(NULL),
+ mRilClient(0),
+ mActivatedCP(false)
{
+ loadRILD();
mInit = true;
}
AudioHardware::~AudioHardware()
{
+ for (size_t index = 0; index < mInputs.size(); index++) {
+ closeInputStream((AudioStreamIn*)mInputs[index]);
+ }
+ mInputs.clear();
closeOutputStream((AudioStreamOut*)mOutput);
+
+ if (mMixer) {
+ mixer_close(mMixer);
+ }
+ if (mPcm) {
+ pcm_close(mPcm);
+ }
+
+ 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;
+ }
+
mInit = false;
}
@@ -57,10 +102,77 @@ status_t AudioHardware::initCheck()
return mInit ? NO_ERROR : NO_INIT;
}
+void AudioHardware::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 AudioHardware::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;
+}
+
AudioStreamOut* AudioHardware::openOutputStream(
uint32_t devices, int *format, uint32_t *channels,
uint32_t *sampleRate, status_t *status)
{
+ AudioStreamOutALSA* out = NULL;
+ status_t rc;
+
{ // scope for the lock
Mutex::Autolock lock(mLock);
@@ -69,32 +181,40 @@ AudioStreamOut* AudioHardware::openOutputStream(
if (status) {
*status = INVALID_OPERATION;
}
- return 0;
+ return NULL;
}
- AudioStreamOutALSA* out = new AudioStreamOutALSA();
+ out = new AudioStreamOutALSA();
- status_t rc = out->set(this, devices, format, channels, sampleRate);
- if (rc) {
- *status = rc;
- }
+ rc = out->set(this, devices, format, channels, sampleRate);
if (rc == NO_ERROR) {
mOutput = out;
- } else {
+ }
+ }
+
+ if (rc != NO_ERROR) {
+ if (out) {
delete out;
}
+ out = NULL;
+ }
+ if (status) {
+ *status = rc;
}
- return mOutput;
+
+ return out;
}
void AudioHardware::closeOutputStream(AudioStreamOut* out) {
- Mutex::Autolock lock(mLock);
- if (mOutput == 0 || mOutput != out) {
- LOGW("Attempt to close invalid output stream");
- } else {
- delete mOutput;
+ {
+ Mutex::Autolock lock(mLock);
+ if (mOutput == 0 || mOutput != out) {
+ LOGW("Attempt to close invalid output stream");
+ return;
+ }
mOutput = 0;
}
+ delete out;
}
AudioStreamIn* AudioHardware::openInputStream(
@@ -102,19 +222,118 @@ AudioStreamIn* AudioHardware::openInputStream(
uint32_t *sampleRate, status_t *status,
AudioSystem::audio_in_acoustics acoustic_flags)
{
- return 0;
+ // check for valid input source
+ if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
+ if (status) {
+ *status = BAD_VALUE;
+ }
+ return NULL;
+ }
+
+ status_t rc = NO_ERROR;
+ AudioStreamInALSA* in = NULL;;
+
+ { // scope for the lock
+ Mutex::Autolock lock(mLock);
+
+ in = new AudioStreamInALSA();
+ rc = in->set(this, devices, format, channels, sampleRate, acoustic_flags);
+ if (rc == NO_ERROR) {
+ mInputs.add(in);
+ }
+ }
+
+ if (rc != NO_ERROR) {
+ if (in) {
+ delete in;
+ }
+ in = NULL;
+ }
+ if (status) {
+ *status = rc;
+ }
+
+ LOGV("AudioHardware::openInputStream()%p", in);
+ return in;
}
void AudioHardware::closeInputStream(AudioStreamIn* in) {
+ {
+ Mutex::Autolock lock(mLock);
+
+ ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in);
+ if (index < 0) {
+ LOGW("Attempt to close invalid input stream");
+ return;
+ }
+ mInputs.removeAt(index);
+ }
+ LOGV("AudioHardware::closeInputStream()%p", in);
+ delete in;
}
+
status_t AudioHardware::setMode(int mode)
{
- return NO_ERROR;
+ AutoMutex lock(mLock);
+ int prevMode = mMode;
+ status_t status = AudioHardwareBase::setMode(mode);
+ LOGV("setMode() : new %d, old %d", mMode, prevMode);
+ if (status == NO_ERROR) {
+ // 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 ((mMode == AudioSystem::MODE_RINGTONE) || (mMode == AudioSystem::MODE_IN_CALL))
+ {
+ if ((!mActivatedCP) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK)) {
+ setCallClockSync(mRilClient, SOUND_CLOCK_START);
+ mActivatedCP = true;
+ }
+ }
+
+ if (((prevMode != AudioSystem::MODE_IN_CALL) && (mMode == AudioSystem::MODE_IN_CALL)) ||
+ ((prevMode == AudioSystem::MODE_IN_CALL) && (mMode != AudioSystem::MODE_IN_CALL))) {
+ if (mMode == AudioSystem::MODE_IN_CALL) {
+ openPcmOut_l();
+ openMixer_l();
+ setVoiceRecognition_l(false);
+ }
+ setIncallPath_l(mOutput->device());
+ if (mMode != AudioSystem::MODE_IN_CALL) {
+ setVoiceRecognition_l(mVrModeEnabled);
+ closeMixer_l();
+ mOutput->setNextRoute(getOutputRouteFromDevice(mOutput->device()));
+ closePcmOut_l();
+ AudioStreamInALSA *input = getActiveInput_l();
+ if (input != NULL) {
+ input->setNextRoute(getInputRouteFromDevice(input->device()));
+ }
+ }
+ }
+
+ if (mMode == AudioSystem::MODE_NORMAL) {
+ if(mActivatedCP)
+ mActivatedCP = false;
+ }
+ }
+
+ return status;
}
status_t AudioHardware::setMicMute(bool state)
{
+ LOGV("setMicMute(%d) mMicMute %d", state, mMicMute);
+ AutoMutex lock(mLock);
+ if (mMicMute != state) {
+ mMicMute = state;
+ if (mMode != AudioSystem::MODE_IN_CALL) {
+ AudioStreamInALSA *input = getActiveInput_l();
+ if (input != NULL) {
+ input->setNextRoute(getInputRouteFromDevice(input->device()));
+ }
+ }
+ }
+
return NO_ERROR;
}
@@ -126,6 +345,23 @@ status_t AudioHardware::getMicMute(bool* state)
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_NREC_VALUE_ON[] = "on";
+
+ key = String8(BT_NREC_KEY);
+ if (param.get(key, value) == NO_ERROR) {
+ if (value == BT_NREC_VALUE_ON) {
+ mBluetoothNrec = true;
+ } else {
+ mBluetoothNrec = false;
+ LOGI("Turning noise reduction and echo cancellation off for BT "
+ "headset");
+ }
+ }
+
return NO_ERROR;
}
@@ -141,17 +377,78 @@ String8 AudioHardware::getParameters(const String8& keys)
size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
- return 4096;
+ 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;
+ }
+ if (sampleRate != 8000 && sampleRate != 11025 && sampleRate != 16000 &&
+ sampleRate != 22050 && sampleRate != 44100) {
+ LOGW("getInputBufferSize bad sample rate: %d", sampleRate);
+ return 0;
+ }
+
+ return AudioStreamInALSA::getBufferSize(sampleRate, channelCount);
}
-status_t AudioHardware::setVoiceVolume(float v)
+
+status_t AudioHardware::setVoiceVolume(float volume)
{
+ LOGI("### setVoiceVolume");
+
+ AutoMutex lock(mLock);
+ if ( (AudioSystem::MODE_IN_CALL == mMode) && (mSecRilLibHandle) &&
+ (connectRILDIfRequired() == OK) ) {
+
+ uint32_t device = AudioSystem::DEVICE_OUT_EARPIECE;
+ if (mOutput != NULL) {
+ device = mOutput->device();
+ }
+ int int_volume = (int)(volume * 5);
+ SoundType type;
+
+ LOGI("### route(%d) call volume(%f)", device, volume);
+ switch (device) {
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ LOGI("### earpiece call volume");
+ type = SOUND_TYPE_VOICE;
+ break;
+
+ case AudioSystem::DEVICE_OUT_SPEAKER:
+ LOGI("### speaker call volume");
+ type = SOUND_TYPE_SPEAKER;
+ break;
+
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ LOGI("### bluetooth call volume");
+ type = SOUND_TYPE_BTVOICE;
+ break;
+
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE: // Use receive path with 3 pole headset.
+ LOGI("### headset call volume");
+ type = SOUND_TYPE_HEADSET;
+ break;
+
+ default:
+ LOGW("### Call volume setting error!!!0x%08x \n", device);
+ type = SOUND_TYPE_VOICE;
+ break;
+ }
+ setCallVolume(mRilClient, type, int_volume);
+ }
+
return NO_ERROR;
}
-status_t AudioHardware::setMasterVolume(float v)
+status_t AudioHardware::setMasterVolume(float volume)
{
- LOGI("Set master volume to %f.\n", v);
+ LOGV("Set master volume to %f.\n", volume);
// We return an error code here to let the audioflinger do in-software
// volume on top of the maximum volume that we set through the SND API.
// return error - software mixer will handle it
@@ -163,10 +460,296 @@ status_t AudioHardware::dump(int fd, const Vector<String16>& args)
return NO_ERROR;
}
+status_t AudioHardware::setIncallPath(uint32_t device) {
+ Mutex::Autolock lock(mLock);
+ return AudioHardware::setIncallPath_l(device);
+}
+
+status_t AudioHardware::setIncallPath_l(uint32_t device)
+{
+ LOGV("setIncallPath: device %x", device);
+
+ // Setup sound path for CP clocking
+ if ((mSecRilLibHandle) &&
+ (connectRILDIfRequired() == OK)) {
+
+ if (mMode == AudioSystem::MODE_IN_CALL) {
+ LOGI("### incall mode route (%d)", device);
+ AudioPath path;
+ switch(device){
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ LOGI("### incall mode earpiece route");
+ path = SOUND_AUDIO_PATH_HANDSET;
+ break;
+
+ case AudioSystem::DEVICE_OUT_SPEAKER:
+ LOGI("### incall mode speaker route");
+ path = SOUND_AUDIO_PATH_SPEAKER;
+ break;
+
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ LOGI("### incall mode bluetooth route %s NR", mBluetoothNrec ? "" : "NO");
+ if (mBluetoothNrec) {
+ path = SOUND_AUDIO_PATH_BLUETOOTH;
+ } else {
+ path = SOUND_AUDIO_PATH_BLUETOOTH_NO_NR;
+ }
+ break;
+
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET :
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE :
+ LOGI("### incall mode headset route");
+ path = SOUND_AUDIO_PATH_HEADSET;
+ break;
+
+ default:
+ LOGW("### incall mode Error!! route = [%d]", device);
+ path = SOUND_AUDIO_PATH_HANDSET;
+ break;
+ }
+
+ setCallAudioPath(mRilClient, path);
+
+ if (mMixer != NULL) {
+ struct mixer_ctl *ctl= mixer_get_control(mMixer, "Voice Call Path", 0);
+ LOGE_IF(ctl == NULL, "setIncallPath_l() could not get mixer ctl");
+ if (ctl != NULL) {
+ LOGV("setIncallPath_l() Voice Call Path, (%x)", device);
+ mixer_ctl_select(ctl, getVoiceRouteFromDevice(device));
+ }
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+struct pcm *AudioHardware::openPcmOut()
+{
+ AutoMutex lock(mLock);
+ return openPcmOut_l();
+}
+
+struct pcm *AudioHardware::openPcmOut_l()
+{
+ LOGI("openPcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt);
+ if (mPcmOpenCnt++ == 0) {
+ if (mPcm != NULL) {
+ LOGE("openPcmOut_l() mPcmOpenCnt == 0 and mPcm == %p\n", mPcm);
+ mPcmOpenCnt--;
+ return NULL;
+ }
+ unsigned flags = PCM_OUT;
+
+ flags |= (AUDIO_HW_OUT_PERIOD_MULT - 1) << PCM_PERIOD_SZ_SHIFT;
+ flags |= (AUDIO_HW_OUT_PERIOD_CNT - PCM_PERIOD_CNT_MIN) << PCM_PERIOD_CNT_SHIFT;
+
+ mPcm = pcm_open(flags);
+ if (!pcm_ready(mPcm)) {
+ LOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_error(mPcm));
+ pcm_close(mPcm);
+ mPcm = 0;
+ }
+ }
+ return mPcm;
+}
+
+void AudioHardware::closePcmOut()
+{
+ AutoMutex lock(mLock);
+ closePcmOut_l();
+}
+
+void AudioHardware::closePcmOut_l()
+{
+ LOGI("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt);
+ if (mPcmOpenCnt == 0) {
+ LOGE("closePcmOut_l() mPcmOpenCnt == 0");
+ return;
+ }
+
+ if (--mPcmOpenCnt == 0) {
+ pcm_close(mPcm);
+ mPcm = NULL;
+ }
+}
+
+struct mixer *AudioHardware::openMixer()
+{
+ AutoMutex lock(mLock);
+ return openMixer_l();
+}
+
+struct mixer *AudioHardware::openMixer_l()
+{
+ LOGV("openMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt);
+ if (mMixerOpenCnt++ == 0) {
+ if (mMixer != NULL) {
+ LOGE("openMixer_l() mMixerOpenCnt == 0 and mMixer == %p\n", mPcm);
+ mMixerOpenCnt--;
+ return NULL;
+ }
+ mMixer = mixer_open();
+ LOGE_IF(mMixer == NULL, "openMixer_l() cannot open mixer");
+ }
+ return mMixer;
+}
+
+void AudioHardware::closeMixer()
+{
+ AutoMutex lock(mLock);
+ closeMixer_l();
+}
+
+void AudioHardware::closeMixer_l()
+{
+ LOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt);
+ if (mMixerOpenCnt == 0) {
+ LOGE("closeMixer_l() mMixerOpenCnt == 0");
+ return;
+ }
+
+ if (--mMixerOpenCnt == 0) {
+ mixer_close(mMixer);
+ mMixer = NULL;
+ }
+}
+
+const char *AudioHardware::getOutputRouteFromDevice(uint32_t device)
+{
+ switch (device) {
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ return "RCV";
+ case AudioSystem::DEVICE_OUT_SPEAKER:
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK";
+ else return "SPK";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+//uncomment when kernel change is submitted
+// if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP_NO_MIC";
+// else return "HP_NO_MIC";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_HP";
+ else return "HP";
+ case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADPHONE):
+ case (AudioSystem::DEVICE_OUT_SPEAKER|AudioSystem::DEVICE_OUT_WIRED_HEADSET):
+ if (mMode == AudioSystem::MODE_RINGTONE) return "RING_SPK_HP";
+ else return "SPK_HP";
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ return "BT";
+ default:
+ return "OFF";
+ }
+}
+
+const char *AudioHardware::getVoiceRouteFromDevice(uint32_t device)
+{
+ switch (device) {
+ case AudioSystem::DEVICE_OUT_EARPIECE:
+ return "RCV";
+ case AudioSystem::DEVICE_OUT_SPEAKER:
+ return "SPK";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADPHONE:
+// return "HP_NO_MIC";
+ case AudioSystem::DEVICE_OUT_WIRED_HEADSET:
+ return "HP";
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ case AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ return "BT";
+ default:
+ return "OFF";
+ }
+}
+
+const char *AudioHardware::getInputRouteFromDevice(uint32_t device)
+{
+ if (mMicMute) {
+ return "MIC OFF";
+ }
+
+ switch (device) {
+ case AudioSystem::DEVICE_IN_BUILTIN_MIC:
+ return "Main Mic";
+ case AudioSystem::DEVICE_IN_WIRED_HEADSET:
+ return "Hands Free Mic";
+ case AudioSystem::DEVICE_IN_BLUETOOTH_SCO_HEADSET:
+ return "BT Sco Mic";
+ default:
+ return "MIC OFF";
+ }
+}
+
+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::AudioStreamInALSA *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]->checkStandby()) {
+ return mInputs[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+status_t AudioHardware::setVoiceRecognition(bool enable)
+{
+ AutoMutex lock(mLock);
+ return setVoiceRecognition_l(enable);
+}
+
+status_t AudioHardware::setVoiceRecognition_l(bool enable)
+{
+ LOGV("setVoiceRecognition_l(%d)", enable);
+ if (enable != mVrModeEnabled) {
+ if (!(enable && (mMode == AudioSystem::MODE_IN_CALL))) {
+ openMixer_l();
+ if (mMixer) {
+ struct mixer_ctl *ctl= mixer_get_control(mMixer, "Recognition Control", 0);
+ if (ctl == NULL) {
+ closeMixer_l();
+ return NO_INIT;
+ }
+ const char *mode = enable ? "RECOGNITION_ON" : "RECOGNITION_OFF";
+ LOGV("mixer_ctl_select, Recognition Control, (%s)", mode);
+ mixer_ctl_select(ctl, mode);
+ }
+ closeMixer_l();
+ }
+ mVrModeEnabled = enable;
+ }
+
+ return NO_ERROR;
+}
+
+
+//------------------------------------------------------------------------------
+// AudioStreamOutALSA
+//------------------------------------------------------------------------------
+
AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() :
- mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStartCount(0), mRetryCount(0),
+ mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStartCount(0),
mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS),
- mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_BUFSZ)
+ mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES)
{
}
@@ -202,7 +785,7 @@ status_t AudioHardware::AudioStreamOutALSA::set(
mChannels = lChannels;
mSampleRate = lRate;
- mBufferSize = 4096;
+ mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES;
return NO_ERROR;
}
@@ -214,51 +797,41 @@ AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA()
ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes)
{
- // LOGD("AudioStreamOutALSA::write(%p, %u)", buffer, bytes);
+ // LOGV("AudioStreamOutALSA::write(%p, %u)", buffer, bytes);
status_t status = NO_INIT;
const uint8_t* p = static_cast<const uint8_t*>(buffer);
int ret;
+ AutoMutex lock(mLock);
if (mStandby) {
LOGV("open pcm_out driver");
- next_route = "SPK";
-
- mPcm = pcm_open(PCM_OUT);
+ mPcm = mHardware->openPcmOut();
if (!pcm_ready(mPcm)) {
LOGE("cannot open pcm_out driver: %s\n", pcm_error(mPcm));
- pcm_close(mPcm);
+ mHardware->closePcmOut();
mPcm = 0;
goto Error;
}
mMixer = mixer_open();
- if (mMixer)
+ if (mMixer) {
+ LOGV("open playback normal");
mRouteCtl = mixer_get_control(mMixer, "Playback Path", 0);
-
-#if 0
- LOGV("set pcm_out config");
- config.channel_count = AudioSystem::popCount(channels());
- config.sample_rate = mSampleRate;
- config.buffer_size = mBufferSize;
- config.buffer_count = AUDIO_HW_NUM_OUT_BUF;
-// config.codec_type = CODEC_TYPE_PCM;
- status = ioctl(mFd, AUDIO_SET_CONFIG, &config);
- if (status < 0) {
- LOGE("Cannot set config");
- goto Error;
}
-
- LOGV("buffer_size: %u", config.buffer_size);
- LOGV("buffer_count: %u", config.buffer_count);
- LOGV("channel_count: %u", config.channel_count);
- LOGV("sample_rate: %u", config.sample_rate);
-#endif
-
+ if (mHardware->mode() == AudioSystem::MODE_IN_CALL) {
+ next_route = 0;
+ } else {
+ next_route = mHardware->getOutputRouteFromDevice(mDevices);
+ }
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
mStandby = false;
}
ret = pcm_write(mPcm,(void*) p, bytes);
+
+ // FIXME:: this is done here as setting the route doesn't seem to work before reading
+ // first buffer
if (ret == 0) {
if (next_route && mRouteCtl) {
mixer_ctl_select(mRouteCtl, next_route);
@@ -280,10 +853,17 @@ Error:
status_t AudioHardware::AudioStreamOutALSA::standby()
{
- if (mPcm) {
- pcm_close(mPcm);
- mPcm = 0;
+ AutoMutex lock(mLock);
+ if (!mStandby) {
+ if (next_route && mRouteCtl) {
+ mixer_ctl_select(mRouteCtl, next_route);
+ next_route = 0;
+ }
+ release_wake_lock("AudioOutLock");
+ mStandby = true;
+ LOGV("AudioHardware pcm playback is going to standby.");
}
+
if (mMixer) {
mixer_close(mMixer);
mMixer = 0;
@@ -291,8 +871,10 @@ status_t AudioHardware::AudioStreamOutALSA::standby()
mRouteCtl = 0;
}
}
- mStandby = true;
- LOGI("AudioHardware pcm playback is going to standby.");
+ if (mPcm) {
+ mHardware->closePcmOut();
+ mPcm = 0;
+ }
return NO_ERROR;
}
@@ -308,21 +890,729 @@ bool AudioHardware::AudioStreamOutALSA::checkStandby()
status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs)
{
- return NO_ERROR;
+ AudioParameter param = AudioParameter(keyValuePairs);
+ status_t status = NO_ERROR;
+ int device;
+ LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string());
+
+ if (mHardware == NULL) return NO_INIT;
+
+ if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR)
+ {
+ if (mDevices != (uint32_t)device) {
+ mDevices = (uint32_t)device;
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ next_route = mHardware->getOutputRouteFromDevice(device);
+ }
+ }
+ if (mHardware->mode() == AudioSystem::MODE_IN_CALL) {
+ mHardware->setIncallPath(device);
+ }
+
+ param.remove(String8(AudioParameter::keyRouting));
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+
+ return status;
+
}
String8 AudioHardware::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)mDevices);
+ }
+
LOGV("AudioStreamOutALSA::getParameters() %s", param.toString().string());
return param.toString();
}
status_t AudioHardware::AudioStreamOutALSA::getRenderPosition(uint32_t *dspFrames)
{
+ //TODO
return INVALID_OPERATION;
}
+//------------------------------------------------------------------------------
+// AudioStreamOutALSA
+//------------------------------------------------------------------------------
+
+AudioHardware::AudioStreamInALSA::AudioStreamInALSA() :
+ mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), mStartCount(0),
+ mStandby(true), mDevices(0), mChannels(AUDIO_HW_IN_CHANNELS), mChannelCount(1),
+ mSampleRate(AUDIO_HW_IN_SAMPLERATE), mBufferSize(AUDIO_HW_IN_PERIOD_BYTES),
+ mDownSampler(NULL), mReadStatus(NO_ERROR)
+{
+}
+
+status_t AudioHardware::AudioStreamInALSA::set(
+ AudioHardware* hw, uint32_t devices, int *pFormat,
+ uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics)
+{
+ if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) {
+ *pFormat = AUDIO_HW_IN_FORMAT;
+ return BAD_VALUE;
+ }
+ if (pRate == 0) {
+ return BAD_VALUE;
+ }
+ uint32_t rate = AudioHardware::getInputSampleRate(*pRate);
+ if (rate != *pRate) {
+ *pRate = rate;
+ return BAD_VALUE;
+ }
+
+ if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
+ *pChannels != AudioSystem::CHANNEL_IN_STEREO)) {
+ *pChannels = AUDIO_HW_IN_CHANNELS;
+ return BAD_VALUE;
+ }
+
+ mHardware = hw;
+
+ LOGV("AudioStreamInALSA::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);
+
+ mBufferSize = getBufferSize(*pRate, AudioSystem::popCount(*pChannels));
+ mDevices = devices;
+ mChannels = *pChannels;
+ mChannelCount = AudioSystem::popCount(mChannels);
+ mSampleRate = rate;
+ if (mSampleRate != AUDIO_HW_OUT_SAMPLERATE) {
+ mDownSampler = new AudioHardware::DownSampler(mSampleRate,
+ mChannelCount,
+ AUDIO_HW_IN_PERIOD_SZ,
+ this);
+ status_t status = mDownSampler->initCheck();
+ if (status != NO_ERROR) {
+ delete mDownSampler;
+ LOGW("AudioStreamInALSA::set() downsampler init failed: %d", status);
+ return status;
+ }
+
+ mPcmIn = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount];
+ }
+ return NO_ERROR;
+}
+
+AudioHardware::AudioStreamInALSA::~AudioStreamInALSA()
+{
+ standby();
+ if (mDownSampler != NULL) {
+ delete mDownSampler;
+ if (mPcmIn != NULL) {
+ delete[] mPcmIn;
+ }
+ }
+}
+
+ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes)
+{
+ // LOGV("AudioStreamInALSA::read(%p, %u)", buffer, bytes);
+ status_t status = NO_INIT;
+ int ret;
+
+ AutoMutex lock(mLock);
+
+ if (mStandby) {
+ LOGV("open pcm_in driver");
+
+ unsigned flags = PCM_IN;
+ if (mChannels == AudioSystem::CHANNEL_IN_MONO) {
+ flags |= PCM_MONO;
+ }
+ flags |= (AUDIO_HW_IN_PERIOD_MULT - 1) << PCM_PERIOD_SZ_SHIFT;
+ flags |= (AUDIO_HW_IN_PERIOD_CNT - PCM_PERIOD_CNT_MIN) << PCM_PERIOD_CNT_SHIFT;
+
+ mPcm = pcm_open(flags);
+ if (!pcm_ready(mPcm)) {
+ LOGE("cannot open pcm_out driver: %s\n", pcm_error(mPcm));
+ pcm_close(mPcm);
+ mPcm = 0;
+ goto Error;
+ }
+
+ mMixer = mixer_open();
+ if (mMixer) {
+ mRouteCtl = mixer_get_control(mMixer, "Capture MIC Path", 0);
+ mMicCtl = mixer_get_control(mMixer, "Mic Status", 0);
+ if (mMicCtl) {
+ LOGV("turn mic ON");
+ mixer_ctl_select(mRouteCtl, "MIC_USE");
+ }
+ }
+
+ if (mDownSampler != NULL) {
+ mInPcmInBuf = 0;
+ mDownSampler->reset();
+ }
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ next_route = mHardware->getInputRouteFromDevice(mDevices);
+ }
+ acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock");
+ mStandby = false;
+ }
+
+ if (mDownSampler != NULL) {
+ size_t frames = bytes / frameSize();
+ size_t framesIn = 0;
+ mReadStatus = 0;
+ do {
+ size_t outframes = frames - framesIn;
+ mDownSampler->resample((int16_t *)buffer + (framesIn * mChannelCount), &outframes);
+ framesIn += outframes;
+ } while ((framesIn < frames) && mReadStatus == 0);
+ ret = mReadStatus;
+ bytes = framesIn * frameSize();
+ } else {
+ ret = pcm_read(mPcm, buffer, bytes);
+ }
+
+ // FIXME:: this is done here as setting the route doesn't seem to work before reading
+ // first buffer
+ if (ret == 0) {
+ if (next_route && mRouteCtl) {
+ LOGV("set route : %s ", next_route);
+ mixer_ctl_select(mRouteCtl, next_route);
+ next_route = 0;
+ }
+ return bytes;
+ }
+
+ LOGW("read error: %d", ret);
+ status = ret;
+
+Error:
+ standby();
+
+ // Simulate audio output timing in case of error
+ usleep(bytes * 1000000 / frameSize() / sampleRate());
+
+ return status;
+}
+
+status_t AudioHardware::AudioStreamInALSA::standby()
+{
+ AutoMutex lock(mLock);
+
+ if (!mStandby) {
+ if (next_route && mRouteCtl) {
+ mixer_ctl_select(mRouteCtl, next_route);
+ next_route = 0;
+ }
+ release_wake_lock("AudioInLock");
+ mStandby = true;
+ LOGI("AudioHardware pcm playback is going to standby.");
+ }
+
+ if (mPcm) {
+ pcm_close(mPcm);
+ mPcm = 0;
+ }
+ if (mMixer) {
+ if (mMicCtl) {
+ LOGV("turn mic OFF");
+ mixer_ctl_select(mMicCtl, "MIC_NO_USE");
+ mMicCtl = 0;
+ }
+ mixer_close(mMixer);
+ mMixer = 0;
+ if (mRouteCtl) {
+ mRouteCtl = 0;
+ }
+
+ }
+ return NO_ERROR;
+}
+
+status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+bool AudioHardware::AudioStreamInALSA::checkStandby()
+{
+ return mStandby;
+}
+
+status_t AudioHardware::AudioStreamInALSA::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ status_t status = NO_ERROR;
+ int value;
+ LOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string());
+
+ if (mHardware == NULL) return NO_INIT;
+
+ if (param.getInt(String8(VOICE_REC_MODE_KEY), value) == NO_ERROR) {
+ mHardware->setVoiceRecognition((value != 0));
+ param.remove(String8(VOICE_REC_MODE_KEY));
+ }
+
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR)
+ {
+ if (value != 0) {
+ if (mDevices != (uint32_t)value) {
+ if (mHardware->mode() != AudioSystem::MODE_IN_CALL) {
+ next_route = mHardware->getInputRouteFromDevice((uint32_t)value);
+ }
+ mDevices = (uint32_t)value;
+ }
+ }
+ param.remove(String8(AudioParameter::keyRouting));
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+
+ return status;
+
+}
+
+String8 AudioHardware::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)mDevices);
+ }
+
+ LOGV("AudioStreamInALSA::getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
+status_t AudioHardware::AudioStreamInALSA::getNextBuffer(AudioHardware::BufferProvider::Buffer* buffer)
+{
+ if (mPcm == NULL) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return NO_INIT;
+ }
+
+ if (mInPcmInBuf == 0) {
+ mReadStatus = pcm_read(mPcm,(void*) mPcmIn, AUDIO_HW_IN_PERIOD_SZ * frameSize());
+ if (mReadStatus != 0) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return mReadStatus;
+ }
+ mInPcmInBuf = AUDIO_HW_IN_PERIOD_SZ;
+ }
+
+ buffer->frameCount = (buffer->frameCount > mInPcmInBuf) ? mInPcmInBuf : buffer->frameCount;
+ buffer->i16 = mPcmIn + (AUDIO_HW_IN_PERIOD_SZ - mInPcmInBuf) * mChannelCount;
+
+ return mReadStatus;
+}
+
+void AudioHardware::AudioStreamInALSA::releaseBuffer(Buffer* buffer)
+{
+ mInPcmInBuf -= buffer->frameCount;
+}
+
+size_t AudioHardware::AudioStreamInALSA::getBufferSize(uint32_t sampleRate, int channelCount)
+{
+ size_t ratio;
+
+ switch (sampleRate) {
+ case 8000:
+ case 11025:
+ ratio = 4;
+ break;
+ case 16000:
+ case 22050:
+ ratio = 2;
+ break;
+ case 44100:
+ default:
+ ratio = 1;
+ break;
+ }
+
+ return (AUDIO_HW_IN_PERIOD_SZ*channelCount*sizeof(int16_t)) / ratio ;
+}
+
+//------------------------------------------------------------------------------
+// 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;
+}
+
+
+AudioHardware::DownSampler::DownSampler(uint32_t outSampleRate,
+ uint32_t channelCount,
+ uint32_t frameCount,
+ AudioHardware::BufferProvider* 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("AudioHardware::DownSampler() cstor %p SR %d channels %d frames %d",
+ this, mSampleRate, mChannelCount, mFrameCount);
+
+ if (mSampleRate != 8000 && mSampleRate != 11025 && mSampleRate != 16000 &&
+ mSampleRate != 22050) {
+ LOGW("AudioHardware::DownSampler 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;
+}
+
+AudioHardware::DownSampler::~DownSampler()
+{
+ 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 AudioHardware::DownSampler::reset()
+{
+ mInInBuf = 0;
+ mInTmpBuf = 0;
+ mInTmp2Buf = 0;
+ mOutBufPos = 0;
+ mInOutBuf = 0;
+}
+
+
+int AudioHardware::DownSampler::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");
+
+ AudioHardware::BufferProvider::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;
+}
+
+
+
+
+
+
+
+//------------------------------------------------------------------------------
+// Factory
+//------------------------------------------------------------------------------
+
extern "C" AudioHardwareInterface* createAudioHardware(void) {
return new AudioHardware();
}
diff --git a/libaudio2/AudioHardware.h b/libaudio2/AudioHardware.h
index 9bdfe2f..ab81d9c 100644
--- a/libaudio2/AudioHardware.h
+++ b/libaudio2/AudioHardware.h
@@ -25,6 +25,8 @@
#include <hardware_legacy/AudioHardwareBase.h>
+#include "secril-client.h"
+
extern "C" {
struct pcm;
struct mixer;
@@ -33,11 +35,6 @@ extern "C" {
namespace android {
-#define CODEC_TYPE_PCM 0
-#define PCM_FILL_BUFFER_COUNT 1
-// Number of buffers in audio driver for output
-#define AUDIO_HW_NUM_OUT_BUF 2
-
// TODO: determine actual audio DSP and hardware latency
// Additionnal latency introduced by audio DSP and hardware in ms
#define AUDIO_HW_OUT_LATENCY_MS 0
@@ -47,27 +44,36 @@ namespace android {
#define AUDIO_HW_OUT_CHANNELS (AudioSystem::CHANNEL_OUT_STEREO)
// Default audio output sample format
#define AUDIO_HW_OUT_FORMAT (AudioSystem::PCM_16_BIT)
-// Default audio output buffer size
-#define AUDIO_HW_OUT_BUFSZ 4096
+// Kernel pcm out buffer size in frames at 44.1kHz
+#define AUDIO_HW_OUT_PERIOD_MULT 8 // (8 * 128 = 1024 frames)
+#define AUDIO_HW_OUT_PERIOD_SZ (PCM_PERIOD_SZ_MIN * AUDIO_HW_OUT_PERIOD_MULT)
+#define AUDIO_HW_OUT_PERIOD_CNT 4
+// Default audio output buffer size in bytes
+#define AUDIO_HW_OUT_PERIOD_BYTES (AUDIO_HW_OUT_PERIOD_SZ * 2 * sizeof(int16_t))
-#if 0
// Default audio input sample rate
#define AUDIO_HW_IN_SAMPLERATE 8000
// Default audio input channel mask
#define AUDIO_HW_IN_CHANNELS (AudioSystem::CHANNEL_IN_MONO)
// Default audio input sample format
#define AUDIO_HW_IN_FORMAT (AudioSystem::PCM_16_BIT)
-// Default audio input buffer size
-#define AUDIO_HW_IN_BUFSZ 256
+// Number of buffers in audio driver for input
+#define AUDIO_HW_NUM_IN_BUF 2
+// Kernel pcm in buffer size in frames at 44.1kHz (before resampling)
+#define AUDIO_HW_IN_PERIOD_MULT 16 // (16 * 128 = 2048 frames)
+#define AUDIO_HW_IN_PERIOD_SZ (PCM_PERIOD_SZ_MIN * AUDIO_HW_IN_PERIOD_MULT)
+#define AUDIO_HW_IN_PERIOD_CNT 2
+// Default audio input buffer size in bytes (8kHz mono)
+#define AUDIO_HW_IN_PERIOD_BYTES ((AUDIO_HW_IN_PERIOD_SZ*sizeof(int16_t))/8)
-// Maximum voice volume
-#define VOICE_VOLUME_MAX 5
-#endif
+#define VOICE_REC_MODE_KEY "vr_mode"
class AudioHardware : public AudioHardwareBase
{
class AudioStreamOutALSA;
+ class AudioStreamInALSA;
public:
+
AudioHardware();
virtual ~AudioHardware();
virtual status_t initCheck();
@@ -98,17 +104,66 @@ public:
virtual size_t getInputBufferSize(
uint32_t sampleRate, int format, int channelCount);
- void clearCurDevice() { }
+ int mode() { return mMode; }
+ const char *getOutputRouteFromDevice(uint32_t device);
+ const char *getInputRouteFromDevice(uint32_t device);
+ const char *getVoiceRouteFromDevice(uint32_t device);
+
+ status_t setIncallPath(uint32_t device);
+ status_t setIncallPath_l(uint32_t device);
+
+ status_t setVoiceRecognition(bool enable);
+ status_t setVoiceRecognition_l(bool enable);
+
+ static uint32_t getInputSampleRate(uint32_t sampleRate);
+ AudioStreamInALSA* getActiveInput_l();
+
+ void lock() { mLock.lock(); }
+ void unlock() { mLock.unlock(); }
+
+ struct pcm *openPcmOut();
+ struct pcm *openPcmOut_l();
+ void closePcmOut();
+ void closePcmOut_l();
+
+ struct mixer *openMixer();
+ struct mixer *openMixer_l();
+ void closeMixer();
+ void closeMixer_l();
protected:
virtual status_t dump(int fd, const Vector<String16>& args);
private:
- bool mInit;
- bool mMicMute;
- AudioStreamOutALSA *mOutput;
- Mutex mLock;
- struct mixer *mMixer;
+
+ bool mInit;
+ bool mMicMute;
+ AudioStreamOutALSA* mOutput;
+ SortedVector <AudioStreamInALSA*> mInputs;
+ Mutex mLock;
+ struct pcm* mPcm;
+ struct mixer* mMixer;
+ uint32_t mPcmOpenCnt;
+ uint32_t mMixerOpenCnt;
+
+ bool mVrModeEnabled;
+ bool mBluetoothNrec;
+ void* mSecRilLibHandle;
+ HRilClient mRilClient;
+ bool mActivatedCP;
+ HRilClient (*openClientRILD) (void);
+ int (*disconnectRILD) (HRilClient);
+ int (*closeClientRILD) (HRilClient);
+ int (*isConnectedRILD) (HRilClient);
+ int (*connectRILD) (HRilClient);
+ int (*setCallVolume) (HRilClient, SoundType, int);
+ int (*setCallAudioPath)(HRilClient, AudioPath);
+ int (*setCallClockSync)(HRilClient, SoundClockCondition);
+ void loadRILD(void);
+ status_t connectRILDIfRequired(void);
+
+ static uint32_t checkInputSampleRate(uint32_t sampleRate);
+ static const uint32_t inputSamplingRates[];
class AudioStreamOutALSA : public AudioStreamOut
{
@@ -129,7 +184,7 @@ private:
virtual int format()
const { return AUDIO_HW_OUT_FORMAT; }
virtual uint32_t latency()
- const { return (1000 * AUDIO_HW_NUM_OUT_BUF *
+ const { return (1000 * AUDIO_HW_OUT_PERIOD_CNT *
(bufferSize()/frameSize()))/sampleRate() +
AUDIO_HW_OUT_LATENCY_MS; }
virtual status_t setVolume(float left, float right)
@@ -140,11 +195,14 @@ private:
bool checkStandby();
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys);
- uint32_t devices()
- { return mDevices; }
+ uint32_t device() { return mDevices; }
virtual status_t getRenderPosition(uint32_t *dspFrames);
+ void setNextRoute(const char *route) { next_route = route; }
+
private:
+
+ Mutex mLock;
AudioHardware* mHardware;
struct pcm *mPcm;
struct mixer *mMixer;
@@ -158,6 +216,118 @@ private:
uint32_t mSampleRate;
size_t mBufferSize;
};
+
+ class DownSampler;
+
+ class BufferProvider
+ {
+ public:
+
+ struct Buffer {
+ union {
+ void* raw;
+ short* i16;
+ int8_t* i8;
+ };
+ size_t frameCount;
+ };
+
+ virtual ~BufferProvider() {}
+
+ virtual status_t getNextBuffer(Buffer* buffer) = 0;
+ virtual void releaseBuffer(Buffer* buffer) = 0;
+ };
+
+ class DownSampler {
+ public:
+ DownSampler(uint32_t outSampleRate,
+ uint32_t channelCount,
+ uint32_t frameCount,
+ BufferProvider* provider);
+
+ virtual ~DownSampler();
+
+ void reset();
+ status_t initCheck() { return mStatus; }
+ int resample(int16_t* out, size_t *outFrameCount);
+
+ private:
+ status_t mStatus;
+ BufferProvider* mProvider;
+ uint32_t mSampleRate;
+ uint32_t mChannelCount;
+ uint32_t mFrameCount;
+ int16_t *mInLeft;
+ int16_t *mInRight;
+ int16_t *mTmpLeft;
+ int16_t *mTmpRight;
+ int16_t *mTmp2Left;
+ int16_t *mTmp2Right;
+ int16_t *mOutLeft;
+ int16_t *mOutRight;
+ int mInInBuf;
+ int mInTmpBuf;
+ int mInTmp2Buf;
+ int mOutBufPos;
+ int mInOutBuf;
+ };
+
+
+ class AudioStreamInALSA : public AudioStreamIn, public BufferProvider
+ {
+
+ public:
+ AudioStreamInALSA();
+ virtual ~AudioStreamInALSA();
+ status_t set(AudioHardware* hw,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate,
+ AudioSystem::audio_in_acoustics acoustics);
+ virtual size_t bufferSize() const { return mBufferSize; }
+ virtual uint32_t channels() const { return mChannels; }
+ virtual int format() const { return AUDIO_HW_IN_FORMAT; }
+ virtual uint32_t sampleRate() const { return mSampleRate; }
+ virtual status_t setGain(float gain) { return INVALID_OPERATION; }
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t standby();
+ bool checkStandby();
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual unsigned int getInputFramesLost() const { return 0; }
+ uint32_t device() { return mDevices; }
+ void setNextRoute(const char *route) { next_route = route; }
+
+ static size_t getBufferSize(uint32_t sampleRate, int channelCount);
+
+ // BufferProvider
+ virtual status_t getNextBuffer(BufferProvider::Buffer* buffer);
+ virtual void releaseBuffer(BufferProvider::Buffer* buffer);
+
+ private:
+ Mutex mLock;
+ AudioHardware* mHardware;
+ struct pcm *mPcm;
+ struct mixer *mMixer;
+ struct mixer_ctl *mRouteCtl;
+ struct mixer_ctl *mMicCtl;
+ const char *next_route;
+ int mStartCount;
+ int mRetryCount;
+ bool mStandby;
+ uint32_t mDevices;
+ uint32_t mChannels;
+ uint32_t mChannelCount;
+ uint32_t mSampleRate;
+ size_t mBufferSize;
+ DownSampler *mDownSampler;
+ status_t mReadStatus;
+ size_t mInPcmInBuf;
+ int16_t *mPcmIn;
+ };
+
};
}; // namespace android
diff --git a/libaudio2/alsa_audio.h b/libaudio2/alsa_audio.h
index ecc78a9..3cb86d9 100644
--- a/libaudio2/alsa_audio.h
+++ b/libaudio2/alsa_audio.h
@@ -30,6 +30,13 @@ struct pcm;
#define PCM_8000HZ 0x00200000
#define PCM_RATE_MASK 0x00F00000
+#define PCM_PERIOD_CNT_MIN 2
+#define PCM_PERIOD_CNT_SHIFT 16
+#define PCM_PERIOD_CNT_MASK (0xF << PCM_PERIOD_CNT_SHIFT)
+#define PCM_PERIOD_SZ_MIN 128
+#define PCM_PERIOD_SZ_SHIFT 12
+#define PCM_PERIOD_SZ_MASK (0xF << PCM_PERIOD_SZ_SHIFT)
+
/* Acquire/release a pcm channel.
* Returns non-zero on error
*/
diff --git a/libaudio2/alsa_pcm.c b/libaudio2/alsa_pcm.c
index ad47b1a..c16a17e 100644
--- a/libaudio2/alsa_pcm.c
+++ b/libaudio2/alsa_pcm.c
@@ -14,6 +14,11 @@
** limitations under the License.
*/
+#define LOG_TAG "alsa_pcm"
+//#define LOG_NDEBUG 0
+#include <cutils/log.h>
+#include <cutils/config_utils.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
@@ -236,7 +241,6 @@ int pcm_write(struct pcm *pcm, void *data, unsigned count)
return oops(pcm, errno, "cannot prepare channel");
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x))
return oops(pcm, errno, "cannot write initial data");
- fprintf(stderr,"RUNNING\n");
pcm->running = 1;
return 0;
}
@@ -249,7 +253,6 @@ int pcm_write(struct pcm *pcm, void *data, unsigned count)
}
return oops(pcm, errno, "cannot write stream data");
}
- fprintf(stderr,".");
return 0;
}
}
@@ -264,13 +267,13 @@ int pcm_read(struct pcm *pcm, void *data, unsigned count)
x.buf = data;
x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4);
+// LOGV("read() %d frames", x.frames);
for (;;) {
if (!pcm->running) {
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE))
return oops(pcm, errno, "cannot prepare channel");
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_START))
return oops(pcm, errno, "cannot start channel");
- fprintf(stderr,"RUNNING\n");
pcm->running = 1;
return 0;
}
@@ -283,7 +286,7 @@ int pcm_read(struct pcm *pcm, void *data, unsigned count)
}
return oops(pcm, errno, "cannot read stream data");
}
- fprintf(stderr,".");
+// LOGV("read() got %d frames", x.frames);
return 0;
}
}
@@ -312,7 +315,10 @@ struct pcm *pcm_open(unsigned flags)
struct snd_pcm_info info;
struct snd_pcm_hw_params params;
struct snd_pcm_sw_params sparams;
- unsigned bufsz = 8192;
+ unsigned period_sz;
+ unsigned period_cnt;
+
+ LOGV("pcm_open(0x%08x)",flags);
pcm = calloc(1, sizeof(struct pcm));
if (!pcm)
@@ -324,6 +330,13 @@ struct pcm *pcm_open(unsigned flags)
dname = "/dev/snd/pcmC0D0p";
}
+ LOGV("pcm_open() period sz multiplier %d",
+ ((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1);
+ period_sz = 128 * (((flags & PCM_PERIOD_SZ_MASK) >> PCM_PERIOD_SZ_SHIFT) + 1);
+ LOGV("pcm_open() period cnt %d",
+ ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN);
+ period_cnt = ((flags & PCM_PERIOD_CNT_MASK) >> PCM_PERIOD_CNT_SHIFT) + PCM_PERIOD_CNT_MIN;
+
pcm->flags = flags;
pcm->fd = open(dname, O_RDWR);
if (pcm->fd < 0) {
@@ -337,6 +350,9 @@ struct pcm *pcm_open(unsigned flags)
}
info_dump(&info);
+ LOGV("pcm_open() period_cnt %d period_sz %d channels %d",
+ period_cnt, period_sz, (flags & PCM_MONO) ? 1 : 2);
+
param_init(&params);
param_set_mask(&params, SNDRV_PCM_HW_PARAM_ACCESS,
SNDRV_PCM_ACCESS_RW_INTERLEAVED);
@@ -344,13 +360,13 @@ struct pcm *pcm_open(unsigned flags)
SNDRV_PCM_FORMAT_S16_LE);
param_set_mask(&params, SNDRV_PCM_HW_PARAM_SUBFORMAT,
SNDRV_PCM_SUBFORMAT_STD);
- param_set_min(&params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, bufsz);
+ param_set_min(&params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, period_sz);
param_set_int(&params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16);
param_set_int(&params, SNDRV_PCM_HW_PARAM_FRAME_BITS,
(flags & PCM_MONO) ? 16 : 32);
param_set_int(&params, SNDRV_PCM_HW_PARAM_CHANNELS,
(flags & PCM_MONO) ? 1 : 2);
- param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, 2);
+ param_set_int(&params, SNDRV_PCM_HW_PARAM_PERIODS, period_cnt);
param_set_int(&params, SNDRV_PCM_HW_PARAM_RATE, 44100);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, &params)) {
@@ -363,9 +379,9 @@ struct pcm *pcm_open(unsigned flags)
sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
sparams.period_step = 1;
sparams.avail_min = 1;
- sparams.start_threshold = bufsz / 4;
- sparams.stop_threshold = bufsz / 4;
- sparams.xfer_align = bufsz / 8; /* needed for old kernels */
+ sparams.start_threshold = period_cnt * period_sz;
+ sparams.stop_threshold = period_cnt * period_sz;
+ sparams.xfer_align = period_sz / 2; /* needed for old kernels */
sparams.silence_size = 0;
sparams.silence_threshold = 0;
@@ -374,7 +390,7 @@ struct pcm *pcm_open(unsigned flags)
goto fail;
}
- pcm->buffer_size = bufsz / 2;
+ pcm->buffer_size = period_cnt * period_sz;
pcm->underruns = 0;
return pcm;