From 2739a5099838025963269b6ff0e448c9c8a3ba52 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 9 Nov 2010 17:27:36 -0800 Subject: Removed ALSA user space library and utilities Change-Id: I0a6492f7c834ea572531e77f75486bcc385e345b --- libaudio/Android.mk | 83 +- libaudio/AudioHardware.cpp | 2014 +++++++++++++++++++++++++++++ libaudio/AudioHardware.h | 348 +++++ libaudio/AudioHardwareALSA.cpp | 2654 --------------------------------------- libaudio/AudioHardwareALSA.h | 432 ------- libaudio/AudioPolicyManager.cpp | 29 + libaudio/AudioPolicyManager.h | 1 + libaudio/NOTICE | 191 --- libaudio/alsa_audio.h | 77 ++ libaudio/alsa_mixer.c | 371 ++++++ libaudio/alsa_pcm.c | 405 ++++++ libaudio/amix.c | 78 ++ libaudio/aplay.c | 140 +++ libaudio/arec.c | 128 ++ libaudio/asound.h | 814 ++++++++++++ libaudio/secril-client.h | 3 +- 16 files changed, 4441 insertions(+), 3327 deletions(-) mode change 100755 => 100644 libaudio/Android.mk create mode 100644 libaudio/AudioHardware.cpp create mode 100644 libaudio/AudioHardware.h delete mode 100755 libaudio/AudioHardwareALSA.cpp delete mode 100755 libaudio/AudioHardwareALSA.h delete mode 100644 libaudio/NOTICE create mode 100644 libaudio/alsa_audio.h create mode 100644 libaudio/alsa_mixer.c create mode 100644 libaudio/alsa_pcm.c create mode 100644 libaudio/amix.c create mode 100644 libaudio/aplay.c create mode 100644 libaudio/arec.c create mode 100644 libaudio/asound.h mode change 100755 => 100644 libaudio/secril-client.h (limited to 'libaudio') diff --git a/libaudio/Android.mk b/libaudio/Android.mk old mode 100755 new mode 100644 index 04f24e0..5ff0a21 --- a/libaudio/Android.mk +++ b/libaudio/Android.mk @@ -1,68 +1,53 @@ -# hardware/libaudio-alsa/Android.mk -# -# Copyright 2008 Wind River Systems -# +LOCAL_PATH:= $(call my-dir) ifeq ($(TARGET_DEVICE),crespo) -ifeq ($(BOARD_USES_ALSA_AUDIO),true) -ifeq ($(filter-out s5pc110 s5pc100 s5p6440,$(TARGET_BOARD_PLATFORM)),) -ifeq ($(BOARD_USES_GENERIC_AUDIO),false) - LOCAL_PATH := $(call my-dir) - - include $(CLEAR_VARS) - - LOCAL_ARM_MODE := arm - LOCAL_CFLAGS := -D_POSIX_SOURCE - LOCAL_WHOLE_STATIC_LIBRARIES := libasound - - ifneq ($(ALSA_DEFAULT_SAMPLE_RATE),) - LOCAL_CFLAGS += -DALSA_DEFAULT_SAMPLE_RATE=$(ALSA_DEFAULT_SAMPLE_RATE) - endif - - LOCAL_C_INCLUDES += device/samsung/crespo/alsa-lib/include - LOCAL_SRC_FILES := AudioHardwareALSA.cpp - - LOCAL_MODULE := libaudio +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= aplay.c alsa_pcm.c alsa_mixer.c +LOCAL_MODULE:= aplay +LOCAL_SHARED_LIBRARIES:= libc libcutils +LOCAL_MODULE_TAGS:= debug +include $(BUILD_EXECUTABLE) - LOCAL_STATIC_LIBRARIES += libaudiointerface +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= arec.c alsa_pcm.c +LOCAL_MODULE:= arec +LOCAL_SHARED_LIBRARIES:= libc libcutils +LOCAL_MODULE_TAGS:= debug +include $(BUILD_EXECUTABLE) - LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - libmedia \ - libhardware_legacy \ - libdl \ - libc +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= amix.c alsa_mixer.c +LOCAL_MODULE:= amix +LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_MODULE_TAGS:= debug +include $(BUILD_EXECUTABLE) +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 libhardware_legacy ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_SHARED_LIBRARIES += liba2dp endif - include $(BUILD_SHARED_LIBRARY) - -# To build audiopolicy library -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - AudioPolicyManager.cpp - -LOCAL_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - libmedia +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif -LOCAL_STATIC_LIBRARIES := libaudiopolicybase +include $(BUILD_SHARED_LIBRARY) +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= AudioPolicyManager.cpp LOCAL_MODULE:= libaudiopolicy - +LOCAL_STATIC_LIBRARIES:= libaudiopolicybase +LOCAL_SHARED_LIBRARIES:= libc libcutils libutils libmedia ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_CFLAGS += -DWITH_A2DP endif - include $(BUILD_SHARED_LIBRARY) endif -endif -endif -endif diff --git a/libaudio/AudioHardware.cpp b/libaudio/AudioHardware.cpp new file mode 100644 index 0000000..10409a1 --- /dev/null +++ b/libaudio/AudioHardware.cpp @@ -0,0 +1,2014 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include + +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioHardware" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AudioHardware.h" +#include +#include + +extern "C" { +#include "alsa_audio.h" +} + + +namespace android { + +const uint32_t AudioHardware::inputSamplingRates[] = { + 8000, 11025, 16000, 22050, 44100 +}; + +// trace driver operations for dump +// +#define DRIVER_TRACE + +enum { + DRV_NONE, + DRV_PCM_OPEN, + DRV_PCM_CLOSE, + DRV_PCM_WRITE, + DRV_PCM_READ, + DRV_MIXER_OPEN, + DRV_MIXER_CLOSE, + DRV_MIXER_GET, + DRV_MIXER_SEL +}; + +#ifdef DRIVER_TRACE +#define TRACE_DRIVER_IN(op) mDriverOp = op; +#define TRACE_DRIVER_OUT mDriverOp = DRV_NONE; +#else +#define TRACE_DRIVER_IN(op) +#define TRACE_DRIVER_OUT +#endif + +// ---------------------------------------------------------------------------- + +AudioHardware::AudioHardware() : + mInit(false), + mMicMute(false), + mPcm(NULL), + mMixer(NULL), + mPcmOpenCnt(0), + mMixerOpenCnt(0), + mInCallAudioMode(false), + mInputSource("Default"), + mBluetoothNrec(true), + mSecRilLibHandle(NULL), + mRilClient(0), + mActivatedCP(false), + mDriverOp(DRV_NONE) +{ + loadRILD(); + mInit = true; +} + +AudioHardware::~AudioHardware() +{ + for (size_t index = 0; index < mInputs.size(); index++) { + closeInputStream(mInputs[index].get()); + } + mInputs.clear(); + closeOutputStream((AudioStreamOut*)mOutput.get()); + + if (mMixer) { + TRACE_DRIVER_IN(DRV_MIXER_CLOSE) + mixer_close(mMixer); + TRACE_DRIVER_OUT + } + if (mPcm) { + TRACE_DRIVER_IN(DRV_PCM_CLOSE) + pcm_close(mPcm); + TRACE_DRIVER_OUT + } + + 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; +} + +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) +{ + sp out; + status_t rc; + + { // scope for the lock + Mutex::Autolock lock(mLock); + + // only one output stream allowed + if (mOutput != 0) { + if (status) { + *status = INVALID_OPERATION; + } + return NULL; + } + + out = new AudioStreamOutALSA(); + + rc = out->set(this, devices, format, channels, sampleRate); + if (rc == NO_ERROR) { + mOutput = out; + } + } + + if (rc != NO_ERROR) { + if (out != 0) { + out.clear(); + } + } + if (status) { + *status = rc; + } + + return out.get(); +} + +void AudioHardware::closeOutputStream(AudioStreamOut* out) { + sp spOut; + { + Mutex::Autolock lock(mLock); + if (mOutput == 0 || mOutput.get() != out) { + LOGW("Attempt to close invalid output stream"); + return; + } + spOut = mOutput; + mOutput.clear(); + } + spOut.clear(); +} + +AudioStreamIn* AudioHardware::openInputStream( + uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustic_flags) +{ + // check for valid input source + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { + if (status) { + *status = BAD_VALUE; + } + return NULL; + } + + status_t rc = NO_ERROR; + sp in; + + { // 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 != 0) { + in.clear(); + } + } + if (status) { + *status = rc; + } + + LOGV("AudioHardware::openInputStream()%p", in.get()); + return in.get(); +} + +void AudioHardware::closeInputStream(AudioStreamIn* in) { + + sp spIn; + { + Mutex::Autolock lock(mLock); + + ssize_t index = mInputs.indexOf((AudioStreamInALSA *)in); + if (index < 0) { + LOGW("Attempt to close invalid input stream"); + return; + } + spIn = mInputs[index]; + mInputs.removeAt(index); + } + LOGV("AudioHardware::closeInputStream()%p", in); + spIn.clear(); +} + + +status_t AudioHardware::setMode(int mode) +{ + sp spOut; + sp spIn; + status_t status; + + // bump thread priority to speed up mutex acquisition + int priority = getpriority(PRIO_PROCESS, 0); + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_URGENT_AUDIO); + + // Mutex acquisition order is always out -> in -> hw + AutoMutex lock(mLock); + + spOut = mOutput; + while (spOut != 0) { + if (!spOut->checkStandby()) { + int cnt = spOut->standbyCnt(); + mLock.unlock(); + spOut->lock(); + mLock.lock(); + // make sure that another thread did not change output state while the + // mutex is released + if ((spOut == mOutput) && (cnt == spOut->standbyCnt())) { + break; + } + spOut->unlock(); + spOut = mOutput; + } else { + spOut.clear(); + } + } + // spOut is not 0 here only if the output is active + + spIn = getActiveInput_l(); + while (spIn != 0) { + int cnt = spIn->standbyCnt(); + mLock.unlock(); + spIn->lock(); + mLock.lock(); + // make sure that another thread did not change input state while the + // mutex is released + if ((spIn == getActiveInput_l()) && (cnt == spIn->standbyCnt())) { + break; + } + spIn->unlock(); + spIn = getActiveInput_l(); + } + // spIn is not 0 here only if the input is active + + setpriority(PRIO_PROCESS, 0, priority); + + int prevMode = mMode; + status = AudioHardwareBase::setMode(mode); + LOGV("setMode() : new %d, old %d", mMode, prevMode); + if (status == NO_ERROR) { + // activate call clock in radio when entering in call or ringtone mode + if (prevMode == AudioSystem::MODE_NORMAL) + { + if ((!mActivatedCP) && (mSecRilLibHandle) && (connectRILDIfRequired() == OK)) { + setCallClockSync(mRilClient, SOUND_CLOCK_START); + mActivatedCP = true; + } + } + + if (mMode == AudioSystem::MODE_IN_CALL && !mInCallAudioMode) { + if (spOut != 0) { + LOGV("setMode() in call force output standby"); + spOut->doStandby_l(); + } + if (spIn != 0) { + LOGV("setMode() in call force input standby"); + spIn->doStandby_l(); + } + + LOGV("setMode() openPcmOut_l()"); + openPcmOut_l(); + openMixer_l(); + setInputSource_l(String8("Default")); + mInCallAudioMode = true; + } + if (mMode == AudioSystem::MODE_NORMAL && mInCallAudioMode) { + setInputSource_l(mInputSource); + if (mMixer != NULL) { + TRACE_DRIVER_IN(DRV_MIXER_GET) + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Playback Path", 0); + TRACE_DRIVER_OUT + if (ctl != NULL) { + LOGV("setMode() reset Playback Path to RCV"); + TRACE_DRIVER_IN(DRV_MIXER_SEL) + mixer_ctl_select(ctl, "RCV"); + TRACE_DRIVER_OUT + } + } + LOGV("setMode() closePcmOut_l()"); + closeMixer_l(); + closePcmOut_l(); + + if (spOut != 0) { + LOGV("setMode() off call force output standby"); + spOut->doStandby_l(); + } + if (spIn != 0) { + LOGV("setMode() off call force input standby"); + spIn->doStandby_l(); + } + + mInCallAudioMode = false; + } + + if (mMode == AudioSystem::MODE_NORMAL) { + if(mActivatedCP) + mActivatedCP = false; + } + } + + if (spIn != 0) { + spIn->unlock(); + } + if (spOut != 0) { + spOut->unlock(); + } + + return status; +} + +status_t AudioHardware::setMicMute(bool state) +{ + LOGV("setMicMute(%d) mMicMute %d", state, mMicMute); + sp spIn; + { + AutoMutex lock(mLock); + if (mMicMute != state) { + mMicMute = state; + // in call mute is handled by RIL + if (mMode != AudioSystem::MODE_IN_CALL) { + spIn = getActiveInput_l(); + } + } + } + + if (spIn != 0) { + spIn->standby(); + } + + return NO_ERROR; +} + +status_t AudioHardware::getMicMute(bool* state) +{ + *state = mMicMute; + return NO_ERROR; +} + +status_t AudioHardware::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key; + const char BT_NREC_KEY[] = "bt_headset_nrec"; + const char BT_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; + LOGD("Turning noise reduction and echo cancellation off for BT " + "headset"); + } + } + + return NO_ERROR; +} + +String8 AudioHardware::getParameters(const String8& keys) +{ + AudioParameter request = AudioParameter(keys); + AudioParameter reply = AudioParameter(); + + LOGV("getParameters() %s", keys.string()); + + return reply.toString(); +} + +size_t AudioHardware::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + 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 volume) +{ + LOGD("### setVoiceVolume"); + + AutoMutex lock(mLock); + if ( (AudioSystem::MODE_IN_CALL == mMode) && (mSecRilLibHandle) && + (connectRILDIfRequired() == OK) ) { + + uint32_t device = AudioSystem::DEVICE_OUT_EARPIECE; + if (mOutput != 0) { + device = mOutput->device(); + } + int int_volume = (int)(volume * 5); + SoundType type; + + LOGD("### route(%d) call volume(%f)", device, volume); + switch (device) { + case AudioSystem::DEVICE_OUT_EARPIECE: + LOGD("### earpiece call volume"); + type = SOUND_TYPE_VOICE; + break; + + case AudioSystem::DEVICE_OUT_SPEAKER: + LOGD("### 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: + LOGD("### 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. + LOGD("### 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 volume) +{ + 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 + return -1; +} + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 20000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t AudioHardware::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + bool locked = tryLock(mLock); + if (!locked) { + snprintf(buffer, SIZE, "\n\tAudioHardware maybe deadlocked\n"); + } else { + mLock.unlock(); + } + + snprintf(buffer, SIZE, "\tInit %s\n", (mInit) ? "OK" : "Failed"); + result.append(buffer); + snprintf(buffer, SIZE, "\tMic Mute %s\n", (mMicMute) ? "ON" : "OFF"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmPcm: %p\n", mPcm); + result.append(buffer); + snprintf(buffer, SIZE, "\tmPcmOpenCnt: %d\n", mPcmOpenCnt); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMixer: %p\n", mMixer); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMixerOpenCnt: %d\n", mMixerOpenCnt); + result.append(buffer); + snprintf(buffer, SIZE, "\tIn Call Audio Mode %s\n", + (mInCallAudioMode) ? "ON" : "OFF"); + result.append(buffer); + snprintf(buffer, SIZE, "\tInput source %s\n", mInputSource.string()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmSecRilLibHandle: %p\n", mSecRilLibHandle); + result.append(buffer); + snprintf(buffer, SIZE, "\tmRilClient: %p\n", mRilClient); + result.append(buffer); + snprintf(buffer, SIZE, "\tCP %s\n", + (mActivatedCP) ? "Activated" : "Deactivated"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmDriverOp: %d\n", mDriverOp); + result.append(buffer); + + snprintf(buffer, SIZE, "\n\tmOutput %p dump:\n", mOutput.get()); + result.append(buffer); + write(fd, result.string(), result.size()); + if (mOutput != 0) { + mOutput->dump(fd, args); + } + + snprintf(buffer, SIZE, "\n\t%d inputs opened:\n", mInputs.size()); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mInputs.size(); i++) { + snprintf(buffer, SIZE, "\t- input %d dump:\n", i); + write(fd, buffer, strlen(buffer)); + mInputs[i]->dump(fd, args); + } + + return NO_ERROR; +} + +status_t AudioHardware::setIncallPath_l(uint32_t device) +{ + LOGV("setIncallPath_l: device %x", device); + + // Setup sound path for CP clocking + if ((mSecRilLibHandle) && + (connectRILDIfRequired() == OK)) { + + if (mMode == AudioSystem::MODE_IN_CALL) { + LOGD("### incall mode route (%d)", device); + AudioPath path; + switch(device){ + case AudioSystem::DEVICE_OUT_EARPIECE: + LOGD("### incall mode earpiece route"); + path = SOUND_AUDIO_PATH_HANDSET; + break; + + case AudioSystem::DEVICE_OUT_SPEAKER: + LOGD("### 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: + LOGD("### 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_HEADPHONE : + LOGD("### incall mode headphone route"); + path = SOUND_AUDIO_PATH_HEADPHONE; + break; + + case AudioSystem::DEVICE_OUT_WIRED_HEADSET : + LOGD("### 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) { + TRACE_DRIVER_IN(DRV_MIXER_GET) + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Voice Call Path", 0); + TRACE_DRIVER_OUT + LOGE_IF(ctl == NULL, "setIncallPath_l() could not get mixer ctl"); + if (ctl != NULL) { + LOGV("setIncallPath_l() Voice Call Path, (%x)", device); + TRACE_DRIVER_IN(DRV_MIXER_SEL) + mixer_ctl_select(ctl, getVoiceRouteFromDevice(device)); + TRACE_DRIVER_OUT + } + } + } + } + return NO_ERROR; +} + +struct pcm *AudioHardware::openPcmOut_l() +{ + LOGD("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; + + TRACE_DRIVER_IN(DRV_PCM_OPEN) + mPcm = pcm_open(flags); + TRACE_DRIVER_OUT + if (!pcm_ready(mPcm)) { + LOGE("openPcmOut_l() cannot open pcm_out driver: %s\n", pcm_error(mPcm)); + TRACE_DRIVER_IN(DRV_PCM_CLOSE) + pcm_close(mPcm); + TRACE_DRIVER_OUT + mPcmOpenCnt--; + mPcm = NULL; + } + } + return mPcm; +} + +void AudioHardware::closePcmOut_l() +{ + LOGD("closePcmOut_l() mPcmOpenCnt: %d", mPcmOpenCnt); + if (mPcmOpenCnt == 0) { + LOGE("closePcmOut_l() mPcmOpenCnt == 0"); + return; + } + + if (--mPcmOpenCnt == 0) { + TRACE_DRIVER_IN(DRV_PCM_CLOSE) + pcm_close(mPcm); + TRACE_DRIVER_OUT + mPcm = NULL; + } +} + +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", mMixer); + mMixerOpenCnt--; + return NULL; + } + TRACE_DRIVER_IN(DRV_MIXER_OPEN) + mMixer = mixer_open(); + TRACE_DRIVER_OUT + if (mMixer == NULL) { + LOGE("openMixer_l() cannot open mixer"); + mMixerOpenCnt--; + return NULL; + } + } + return mMixer; +} + +void AudioHardware::closeMixer_l() +{ + LOGV("closeMixer_l() mMixerOpenCnt: %d", mMixerOpenCnt); + if (mMixerOpenCnt == 0) { + LOGE("closeMixer_l() mMixerOpenCnt == 0"); + return; + } + + if (--mMixerOpenCnt == 0) { + TRACE_DRIVER_IN(DRV_MIXER_CLOSE) + mixer_close(mMixer); + TRACE_DRIVER_OUT + 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: + if (mMode == AudioSystem::MODE_RINGTONE) return "RING_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 +sp AudioHardware::getActiveInput_l() +{ + sp< AudioHardware::AudioStreamInALSA> spIn; + + 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()) { + spIn = mInputs[i]; + break; + } + } + + return spIn; +} + +status_t AudioHardware::setInputSource_l(String8 source) +{ + LOGV("setInputSource_l(%s)", source.string()); + if (source != mInputSource) { + if ((source == "Default") || (mMode != AudioSystem::MODE_IN_CALL)) { + if (mMixer) { + TRACE_DRIVER_IN(DRV_MIXER_GET) + struct mixer_ctl *ctl= mixer_get_control(mMixer, "Input Source", 0); + TRACE_DRIVER_OUT + if (ctl == NULL) { + return NO_INIT; + } + LOGV("mixer_ctl_select, Input Source, (%s)", source.string()); + TRACE_DRIVER_IN(DRV_MIXER_SEL) + mixer_ctl_select(ctl, source.string()); + TRACE_DRIVER_OUT + } + } + mInputSource = source; + } + + return NO_ERROR; +} + + +//------------------------------------------------------------------------------ +// AudioStreamOutALSA +//------------------------------------------------------------------------------ + +AudioHardware::AudioStreamOutALSA::AudioStreamOutALSA() : + mHardware(0), mPcm(0), mMixer(0), mRouteCtl(0), + mStandby(true), mDevices(0), mChannels(AUDIO_HW_OUT_CHANNELS), + mSampleRate(AUDIO_HW_OUT_SAMPLERATE), mBufferSize(AUDIO_HW_OUT_PERIOD_BYTES), + mDriverOp(DRV_NONE), mStandbyCnt(0) +{ +} + +status_t AudioHardware::AudioStreamOutALSA::set( + AudioHardware* hw, uint32_t devices, int *pFormat, + uint32_t *pChannels, uint32_t *pRate) +{ + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + mHardware = hw; + mDevices = devices; + + // fix up defaults + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); + + // check values + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())) { + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); + return BAD_VALUE; + } + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mChannels = lChannels; + mSampleRate = lRate; + mBufferSize = AUDIO_HW_OUT_PERIOD_BYTES; + + return NO_ERROR; +} + +AudioHardware::AudioStreamOutALSA::~AudioStreamOutALSA() +{ + standby(); +} + +ssize_t AudioHardware::AudioStreamOutALSA::write(const void* buffer, size_t bytes) +{ + // LOGV("AudioStreamOutALSA::write(%p, %u)", buffer, bytes); + status_t status = NO_INIT; + const uint8_t* p = static_cast(buffer); + int ret; + + if (mHardware == NULL) return NO_INIT; + + { // scope for the lock + + AutoMutex lock(mLock); + + if (mStandby) { + AutoMutex hwLock(mHardware->lock()); + + LOGD("AudioHardware pcm playback is exiting standby."); + acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock"); + + sp spIn = mHardware->getActiveInput_l(); + while (spIn != 0) { + int cnt = spIn->standbyCnt(); + mHardware->lock().unlock(); + // Mutex acquisition order is always out -> in -> hw + spIn->lock(); + mHardware->lock().lock(); + // make sure that another thread did not change input state + // while the mutex is released + if ((spIn == mHardware->getActiveInput_l()) && + (cnt == spIn->standbyCnt())) { + LOGV("AudioStreamOutALSA::write() force input standby"); + spIn->close_l(); + break; + } + spIn->unlock(); + spIn = mHardware->getActiveInput_l(); + } + // spIn is not 0 here only if the input was active and has been + // closed above + + // open output before input + open_l(); + + if (spIn != 0) { + if (spIn->open_l() != NO_ERROR) { + spIn->doStandby_l(); + } + spIn->unlock(); + } + if (mPcm == NULL) { + release_wake_lock("AudioOutLock"); + goto Error; + } + mStandby = false; + } + + TRACE_DRIVER_IN(DRV_PCM_WRITE) + ret = pcm_write(mPcm,(void*) p, bytes); + TRACE_DRIVER_OUT + + if (ret == 0) { + return bytes; + } + LOGW("write error: %d", errno); + status = -errno; + } +Error: + + standby(); + + // Simulate audio output timing in case of error + usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate()); + + return status; +} + +status_t AudioHardware::AudioStreamOutALSA::standby() +{ + if (mHardware == NULL) return NO_INIT; + + AutoMutex lock(mLock); + + { // scope for the AudioHardware lock + AutoMutex hwLock(mHardware->lock()); + + doStandby_l(); + } + + return NO_ERROR; +} + +void AudioHardware::AudioStreamOutALSA::doStandby_l() +{ + mStandbyCnt++; + + if (!mStandby) { + LOGD("AudioHardware pcm playback is going to standby."); + release_wake_lock("AudioOutLock"); + mStandby = true; + } + + close_l(); +} + +void AudioHardware::AudioStreamOutALSA::close_l() +{ + if (mMixer) { + mHardware->closeMixer_l(); + mMixer = NULL; + mRouteCtl = NULL; + } + if (mPcm) { + mHardware->closePcmOut_l(); + mPcm = NULL; + } +} + +status_t AudioHardware::AudioStreamOutALSA::open_l() +{ + LOGV("open pcm_out driver"); + mPcm = mHardware->openPcmOut_l(); + if (mPcm == NULL) { + return NO_INIT; + } + + mMixer = mHardware->openMixer_l(); + if (mMixer) { + LOGV("open playback normal"); + TRACE_DRIVER_IN(DRV_MIXER_GET) + mRouteCtl = mixer_get_control(mMixer, "Playback Path", 0); + TRACE_DRIVER_OUT + } + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + const char *route = mHardware->getOutputRouteFromDevice(mDevices); + LOGV("write() wakeup setting route %s", route); + if (mRouteCtl) { + TRACE_DRIVER_IN(DRV_MIXER_SEL) + mixer_ctl_select(mRouteCtl, route); + TRACE_DRIVER_OUT + } + } + return NO_ERROR; +} + +status_t AudioHardware::AudioStreamOutALSA::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + bool locked = tryLock(mLock); + if (!locked) { + snprintf(buffer, SIZE, "\n\t\tAudioStreamOutALSA maybe deadlocked\n"); + } else { + mLock.unlock(); + } + + snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmRouteCtl: %p\n", mRouteCtl); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF"); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp); + result.append(buffer); + + ::write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +bool AudioHardware::AudioStreamOutALSA::checkStandby() +{ + return mStandby; +} + +status_t AudioHardware::AudioStreamOutALSA::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + status_t status = NO_ERROR; + int device; + LOGD("AudioStreamOutALSA::setParameters() %s", keyValuePairs.string()); + + if (mHardware == NULL) return NO_INIT; + + { + AutoMutex lock(mLock); + + if (param.getInt(String8(AudioParameter::keyRouting), device) == NO_ERROR) + { + AutoMutex hwLock(mHardware->lock()); + + if (mDevices != (uint32_t)device) { + mDevices = (uint32_t)device; + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + doStandby_l(); + } + } + if (mHardware->mode() == AudioSystem::MODE_IN_CALL) { + mHardware->setIncallPath_l(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; +} + +//------------------------------------------------------------------------------ +// AudioStreamInALSA +//------------------------------------------------------------------------------ + +AudioHardware::AudioStreamInALSA::AudioStreamInALSA() : + mHardware(0), mPcm(0), mMixer(0), mRouteCtl(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), mDriverOp(DRV_NONE), + mStandbyCnt(0) +{ +} + +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; + + if (mHardware == NULL) return NO_INIT; + + { // scope for the lock + AutoMutex lock(mLock); + + if (mStandby) { + AutoMutex hwLock(mHardware->lock()); + + LOGD("AudioHardware pcm capture is exiting standby."); + acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioInLock"); + + sp spOut = mHardware->output(); + while (spOut != 0) { + if (!spOut->checkStandby()) { + int cnt = spOut->standbyCnt(); + mHardware->lock().unlock(); + mLock.unlock(); + // Mutex acquisition order is always out -> in -> hw + spOut->lock(); + mLock.lock(); + mHardware->lock().lock(); + // make sure that another thread did not change output state + // while the mutex is released + if ((spOut == mHardware->output()) && (cnt == spOut->standbyCnt())) { + LOGV("AudioStreamInALSA::read() force output standby"); + spOut->close_l(); + break; + } + spOut->unlock(); + spOut = mHardware->output(); + } else { + spOut.clear(); + } + } + // spOut is not 0 here only if the output was active and has been + // closed above + + // open output before input + if (spOut != 0) { + if (spOut->open_l() != NO_ERROR) { + spOut->doStandby_l(); + } + spOut->unlock(); + } + + open_l(); + + if (mPcm == NULL) { + release_wake_lock("AudioInLock"); + goto Error; + } + 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 { + TRACE_DRIVER_IN(DRV_PCM_READ) + ret = pcm_read(mPcm, buffer, bytes); + TRACE_DRIVER_OUT + } + + if (ret == 0) { + return bytes; + } + + LOGW("read error: %d", ret); + status = ret; + } + +Error: + + standby(); + + // Simulate audio output timing in case of error + usleep((((bytes * 1000) / frameSize()) * 1000) / sampleRate()); + + return status; +} + +status_t AudioHardware::AudioStreamInALSA::standby() +{ + if (mHardware == NULL) return NO_INIT; + + AutoMutex lock(mLock); + + { // scope for AudioHardware lock + AutoMutex hwLock(mHardware->lock()); + + doStandby_l(); + } + return NO_ERROR; +} + +void AudioHardware::AudioStreamInALSA::doStandby_l() +{ + mStandbyCnt++; + + if (!mStandby) { + LOGD("AudioHardware pcm capture is going to standby."); + release_wake_lock("AudioInLock"); + mStandby = true; + } + close_l(); +} + +void AudioHardware::AudioStreamInALSA::close_l() +{ + if (mMixer) { + mHardware->closeMixer_l(); + mMixer = NULL; + mRouteCtl = NULL; + } + + if (mPcm) { + TRACE_DRIVER_IN(DRV_PCM_CLOSE) + pcm_close(mPcm); + TRACE_DRIVER_OUT + mPcm = NULL; + } +} + +status_t AudioHardware::AudioStreamInALSA::open_l() +{ + 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; + + LOGV("open pcm_in driver"); + TRACE_DRIVER_IN(DRV_PCM_OPEN) + mPcm = pcm_open(flags); + TRACE_DRIVER_OUT + if (!pcm_ready(mPcm)) { + LOGE("cannot open pcm_in driver: %s\n", pcm_error(mPcm)); + TRACE_DRIVER_IN(DRV_PCM_CLOSE) + pcm_close(mPcm); + TRACE_DRIVER_OUT + mPcm = NULL; + return NO_INIT; + } + + if (mDownSampler != NULL) { + mInPcmInBuf = 0; + mDownSampler->reset(); + } + + mMixer = mHardware->openMixer_l(); + if (mMixer) { + TRACE_DRIVER_IN(DRV_MIXER_GET) + mRouteCtl = mixer_get_control(mMixer, "Capture MIC Path", 0); + TRACE_DRIVER_OUT + } + + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + const char *route = mHardware->getInputRouteFromDevice(mDevices); + LOGV("read() wakeup setting route %s", route); + if (mRouteCtl) { + TRACE_DRIVER_IN(DRV_MIXER_SEL) + mixer_ctl_select(mRouteCtl, route); + TRACE_DRIVER_OUT + } + } + + return NO_ERROR; +} + +status_t AudioHardware::AudioStreamInALSA::dump(int fd, const Vector& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + bool locked = tryLock(mLock); + if (!locked) { + snprintf(buffer, SIZE, "\n\t\tAudioStreamInALSA maybe deadlocked\n"); + } else { + mLock.unlock(); + } + + snprintf(buffer, SIZE, "\t\tmHardware: %p\n", mHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmPcm: %p\n", mPcm); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmMixer: %p\n", mMixer); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tStandby %s\n", (mStandby) ? "ON" : "OFF"); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmDevices: 0x%08x\n", mDevices); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmChannels: 0x%08x\n", mChannels); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmSampleRate: %d\n", mSampleRate); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmBufferSize: %d\n", mBufferSize); + result.append(buffer); + snprintf(buffer, SIZE, "\t\tmDriverOp: %d\n", mDriverOp); + result.append(buffer); + write(fd, result.string(), result.size()); + + 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; + String8 source; + + LOGD("AudioStreamInALSA::setParameters() %s", keyValuePairs.string()); + + if (mHardware == NULL) return NO_INIT; + + { + AutoMutex lock(mLock); + + if (param.get(String8(INPUT_SOURCE_KEY), source) == NO_ERROR) { + AutoMutex hwLock(mHardware->lock()); + + mHardware->openMixer_l(); + mHardware->setInputSource_l(source); + mHardware->closeMixer_l(); + + param.remove(String8(INPUT_SOURCE_KEY)); + } + + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) + { + if (value != 0) { + AutoMutex hwLock(mHardware->lock()); + + if (mDevices != (uint32_t)value) { + mDevices = (uint32_t)value; + if (mHardware->mode() != AudioSystem::MODE_IN_CALL) { + doStandby_l(); + } + } + } + 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; + mReadStatus = NO_INIT; + return NO_INIT; + } + + if (mInPcmInBuf == 0) { + TRACE_DRIVER_IN(DRV_PCM_READ) + mReadStatus = pcm_read(mPcm,(void*) mPcmIn, AUDIO_HW_IN_PERIOD_SZ * frameSize()); + TRACE_DRIVER_OUT + 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(); +} + +}; // namespace android diff --git a/libaudio/AudioHardware.h b/libaudio/AudioHardware.h new file mode 100644 index 0000000..1379495 --- /dev/null +++ b/libaudio/AudioHardware.h @@ -0,0 +1,348 @@ +/* +** Copyright 2008, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_AUDIO_HARDWARE_H +#define ANDROID_AUDIO_HARDWARE_H + +#include +#include + +#include +#include + +#include + +#include "secril-client.h" + +extern "C" { + struct pcm; + struct mixer; + struct mixer_ctl; +}; + +namespace android { + +// 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 +// Default audio output sample rate +#define AUDIO_HW_OUT_SAMPLERATE 44100 +// Default audio output channel mask +#define AUDIO_HW_OUT_CHANNELS (AudioSystem::CHANNEL_OUT_STEREO) +// Default audio output sample format +#define AUDIO_HW_OUT_FORMAT (AudioSystem::PCM_16_BIT) +// 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)) + +// 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) +// 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) + +#define INPUT_SOURCE_KEY "Input Source" + +class AudioHardware : public AudioHardwareBase +{ + class AudioStreamOutALSA; + class AudioStreamInALSA; +public: + + AudioHardware(); + virtual ~AudioHardware(); + virtual status_t initCheck(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + virtual status_t setMode(int mode); + + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual AudioStreamOut* openOutputStream( + uint32_t devices, int *format=0, uint32_t *channels=0, + uint32_t *sampleRate=0, status_t *status=0); + + virtual AudioStreamIn* openInputStream( + uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics); + + virtual void closeOutputStream(AudioStreamOut* out); + virtual void closeInputStream(AudioStreamIn* in); + + virtual size_t getInputBufferSize( + uint32_t sampleRate, int format, int channelCount); + + 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_l(uint32_t device); + + status_t setInputSource_l(String8 source); + + static uint32_t getInputSampleRate(uint32_t sampleRate); + sp getActiveInput_l(); + + Mutex& lock() { return mLock; } + + struct pcm *openPcmOut_l(); + void closePcmOut_l(); + + struct mixer *openMixer_l(); + void closeMixer_l(); + + sp output() { return mOutput; } + +protected: + virtual status_t dump(int fd, const Vector& args); + +private: + + bool mInit; + bool mMicMute; + sp mOutput; + SortedVector < sp > mInputs; + Mutex mLock; + struct pcm* mPcm; + struct mixer* mMixer; + uint32_t mPcmOpenCnt; + uint32_t mMixerOpenCnt; + bool mInCallAudioMode; + + String8 mInputSource; + 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); + + // trace driver operations for dump + int mDriverOp; + + static uint32_t checkInputSampleRate(uint32_t sampleRate); + static const uint32_t inputSamplingRates[]; + + class AudioStreamOutALSA : public AudioStreamOut, public RefBase + { + public: + AudioStreamOutALSA(); + virtual ~AudioStreamOutALSA(); + status_t set(AudioHardware* mHardware, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); + virtual uint32_t sampleRate() + const { return mSampleRate; } + virtual size_t bufferSize() + const { return mBufferSize; } + virtual uint32_t channels() + const { return mChannels; } + virtual int format() + const { return AUDIO_HW_OUT_FORMAT; } + virtual uint32_t latency() + const { return (1000 * AUDIO_HW_OUT_PERIOD_CNT * + (bufferSize()/frameSize()))/sampleRate() + + AUDIO_HW_OUT_LATENCY_MS; } + virtual status_t setVolume(float left, float right) + { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + bool checkStandby(); + + virtual status_t dump(int fd, const Vector& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + uint32_t device() { return mDevices; } + virtual status_t getRenderPosition(uint32_t *dspFrames); + + void doStandby_l(); + void close_l(); + status_t open_l(); + int standbyCnt() { return mStandbyCnt; } + + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } + + private: + + Mutex mLock; + AudioHardware* mHardware; + struct pcm *mPcm; + struct mixer *mMixer; + struct mixer_ctl *mRouteCtl; + const char *next_route; + bool mStandby; + uint32_t mDevices; + uint32_t mChannels; + uint32_t mSampleRate; + size_t mBufferSize; + // trace driver operations for dump + int mDriverOp; + int mStandbyCnt; + }; + + 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 RefBase + { + + 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& 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 doStandby_l(); + void close_l(); + status_t open_l(); + int standbyCnt() { return mStandbyCnt; } + + static size_t getBufferSize(uint32_t sampleRate, int channelCount); + + // BufferProvider + virtual status_t getNextBuffer(BufferProvider::Buffer* buffer); + virtual void releaseBuffer(BufferProvider::Buffer* buffer); + + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } + + private: + Mutex mLock; + AudioHardware* mHardware; + struct pcm *mPcm; + struct mixer *mMixer; + struct mixer_ctl *mRouteCtl; + const char *next_route; + 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; + // trace driver operations for dump + int mDriverOp; + int mStandbyCnt; + }; + +}; + +}; // namespace android + +#endif diff --git a/libaudio/AudioHardwareALSA.cpp b/libaudio/AudioHardwareALSA.cpp deleted file mode 100755 index 45223f3..0000000 --- a/libaudio/AudioHardwareALSA.cpp +++ /dev/null @@ -1,2654 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include - -//#define LOG_NDEBUG 0 -#define LOG_TAG "AudioHardwareALSA" -#include -#include - -#include -#include -#include - -#include -#include "AudioHardwareALSA.h" -// #define READ_FRAME_SIZE 2080 -// #define READ_FRAME_SIZE_STANDARD 4160 - -#include - -#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(0), NULL, NULL, NULL}, - {static_cast(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& 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(format) > SND_PCM_FORMAT_UNKNOWN) && - (static_cast(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(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 (n); - } - } else - sent += static_cast (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& 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 (n); - } - } while (n == 0); - - return snd_pcm_frames_to_bytes(mHandle, n); -} - -status_t AudioStreamInALSA::dump(int fd, const Vector& 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(!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(!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 - diff --git a/libaudio/AudioHardwareALSA.h b/libaudio/AudioHardwareALSA.h deleted file mode 100755 index c3b6caa..0000000 --- a/libaudio/AudioHardwareALSA.h +++ /dev/null @@ -1,432 +0,0 @@ -/* AudioHardwareALSA.h - ** - ** Copyright 2008, 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. - */ - -#ifndef ANDROID_AUDIO_HARDWARE_ALSA_H -#define ANDROID_AUDIO_HARDWARE_ALSA_H - -#include -#include -#include - -#include - -// sangsu fix : headers for IPC -#include "secril-client.h" - -#ifndef ALSA_DEFAULT_SAMPLE_RATE -#define ALSA_DEFAULT_SAMPLE_RATE 44100 // in Hz -#endif - -#define DEFAULT_SAMPLE_RATE ALSA_DEFAULT_SAMPLE_RATE - -#define PLAYBACK 0 -#define PERIOD_SZ_PLAYBACK 1024 -#define PERIODS_PLAYBACK 4 -#define BUFFER_SZ_PLAYBACK (PERIODS_PLAYBACK * PERIOD_SZ_PLAYBACK) -#define LATENCY_PLAYBACK_MS ((BUFFER_SZ_PLAYBACK * 1000 / DEFAULT_SAMPLE_RATE) * 1000) - -#define CAPTURE 1 -#define PERIOD_SZ_CAPTURE 1024 -#define PERIODS_CAPTURE 4 -#define BUFFER_SZ_CAPTURE (PERIODS_CAPTURE * PERIOD_SZ_CAPTURE) -#define LATENCY_CAPTURE_MS ((BUFFER_SZ_CAPTURE * 1000 / DEFAULT_SAMPLE_RATE) * 1000) - -//Recognition param -#define RECOGNITION_OFF 0 -#define RECOGNITION_ON 1 - -namespace android -{ - - class AudioHardwareALSA; - - // ---------------------------------------------------------------------------- - - class ALSAMixer - { - public: - ALSAMixer(); - virtual ~ALSAMixer(); - - bool isValid() { return !!mMixer[SND_PCM_STREAM_PLAYBACK]; } - status_t setMasterVolume(float volume); - status_t setMasterGain(float gain); - - status_t setVolume(uint32_t device, float volume); - status_t setGain(uint32_t device, float gain); - - status_t setCaptureMuteState(uint32_t device, bool state); - status_t getCaptureMuteState(uint32_t device, bool *state); - status_t setPlaybackMuteState(uint32_t device, bool state); - status_t getPlaybackMuteState(uint32_t device, bool *state); - - private: - snd_mixer_t *mMixer[SND_PCM_STREAM_LAST+1]; - }; - - class ALSAControl - { - public: - ALSAControl(const char *device = "default"); - virtual ~ALSAControl(); - - status_t get(const char *name, unsigned int &value, int index = 0); - status_t set(const char *name, unsigned int value, int index = -1); - - private: - snd_ctl_t *mHandle; - }; - - class ALSAStreamOps - { - public: - uint32_t device() { return mDevice; } - void close(); - - protected: - friend class AudioStreamOutALSA; - friend class AudioStreamInALSA; - - struct StreamDefaults - { - const char * devicePrefix; - snd_pcm_stream_t direction; // playback or capture - snd_pcm_format_t format; - int channelCount; - uint32_t sampleRate; - uint32_t bufferRatio; - unsigned int latency; // Delay in usec - unsigned int bufferSize; // Size of sample buffer - unsigned int periodSize; // Size of sample buffer - }; - - ALSAStreamOps(); - virtual ~ALSAStreamOps(); - - status_t set(int *format, - uint32_t *channels, - uint32_t *rate); - virtual uint32_t sampleRate() const; - status_t sampleRate(uint32_t rate); - virtual size_t bufferSize() const; - virtual int format() const; - int getAndroidFormat(snd_pcm_format_t format); - - virtual uint32_t channels() const; - int channelCount() const; - status_t channelCount(int channelCount); - uint32_t getAndroidChannels(int channelCount) const; - - status_t open(int mode, uint32_t device); - status_t setSoftwareParams(); - status_t setPCMFormat(snd_pcm_format_t format); - status_t setHardwareResample(bool resample); - - const char *streamName(); - status_t setDevice(int mode, uint32_t device, uint32_t audio_mode); - - const char *deviceName(int mode, uint32_t device); - - void setStreamDefaults(StreamDefaults *dev) { - mDefaults = dev; - } - - Mutex mLock; - - private: - snd_pcm_t *mHandle; - snd_pcm_hw_params_t *mHardwareParams; - snd_pcm_sw_params_t *mSoftwareParams; - uint32_t mDevice; - - StreamDefaults *mDefaults; - }; - - // ---------------------------------------------------------------------------- - - class AudioStreamOutALSA : public AudioStreamOut, public ALSAStreamOps - { - public: - AudioStreamOutALSA(AudioHardwareALSA *parent); - virtual ~AudioStreamOutALSA(); - - - status_t set(int *format, - uint32_t *channelCount, - uint32_t *sampleRate){ - return ALSAStreamOps::set(format, channelCount, sampleRate); - } - - virtual uint32_t sampleRate() const { - return ALSAStreamOps::sampleRate(); - } - - virtual size_t bufferSize() const - { - return ALSAStreamOps::bufferSize(); - } - - virtual uint32_t channels() const - { - return ALSAStreamOps::channels(); - } - - virtual int format() const - { - return ALSAStreamOps::format(); - } - - virtual uint32_t latency() const; - - virtual ssize_t write(const void *buffer, size_t bytes); - virtual status_t dump(int fd, const Vector& args); - status_t setDevice(int mode, uint32_t newDevice, uint32_t audio_mode, - bool force = false); - virtual status_t setVolume(float left, float right); //Tushar: New arch - - status_t setVolume(float volume); - - status_t standby(); - - virtual status_t setParameters(const String8& keyValuePairs); - virtual String8 getParameters(const String8& keys); - - virtual status_t getRenderPosition(uint32_t *dspFrames); - bool isActive() { return mPowerLock; } - - private: - AudioHardwareALSA *mParent; - bool mPowerLock; - }; - - class ALSADownsampler; - - class ALSABufferProvider - { - public: - - struct Buffer { - union { - void* raw; - short* i16; - int8_t* i8; - }; - size_t frameCount; - }; - - virtual ~ALSABufferProvider() {} - - virtual status_t getNextBuffer(Buffer* buffer) = 0; - virtual void releaseBuffer(Buffer* buffer) = 0; - }; - - class AudioStreamInALSA : public AudioStreamIn, public ALSAStreamOps, public ALSABufferProvider - { - public: - AudioStreamInALSA(AudioHardwareALSA *parent); - virtual ~AudioStreamInALSA(); - - status_t set(int *format, - uint32_t *channelCount, - uint32_t *sampleRate); - - virtual uint32_t sampleRate() const { - return ALSAStreamOps::sampleRate(); - } - - virtual size_t bufferSize() const - { - return ALSAStreamOps::bufferSize(); - } - - virtual uint32_t channels() const - { - return ALSAStreamOps::channels(); - } - - virtual int format() const - { - return ALSAStreamOps::format(); - } - - virtual ssize_t read(void* buffer, ssize_t bytes); - virtual status_t dump(int fd, const Vector& args); - status_t setDevice(int mode, uint32_t newDevice, uint32_t audio_mode, - bool force = false); - - virtual status_t setGain(float gain); - - virtual status_t standby(); - - virtual status_t setParameters(const String8& keyValuePairs); - virtual String8 getParameters(const String8& keys); - - virtual unsigned int getInputFramesLost() const { return 0; } - - bool isActive() { return mPowerLock; } - - // ALSABufferProvider - virtual status_t getNextBuffer(ALSABufferProvider::Buffer* buffer); - virtual void releaseBuffer(ALSABufferProvider::Buffer* buffer); - - private: - AudioHardwareALSA *mParent; - bool mPowerLock; - ALSADownsampler *mDownSampler; - status_t mReadStatus; - size_t mInPcmInBuf; - int16_t *mPcmIn; - }; - - class AudioHardwareALSA : public AudioHardwareBase - { - public: - AudioHardwareALSA(); - virtual ~AudioHardwareALSA(); - - /** - * check to see if the audio hardware interface has been initialized. - * return status based on values defined in include/utils/Errors.h - */ - virtual status_t initCheck(); - - /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */ - virtual status_t setVoiceVolume(float volume); - - virtual status_t setMode(int mode); - - /** - * set the audio volume for all audio activities other than voice call. - * Range between 0.0 and 1.0. If any value other than NO_ERROR is returned, - * the software mixer will emulate this capability. - */ - virtual status_t setMasterVolume(float volume); - - // mic mute - virtual status_t setMicMute(bool state); - virtual status_t getMicMute(bool* state); - virtual size_t getInputBufferSize(uint32_t sampleRate, - int format, - int channelCount); - - /** This method creates and opens the audio hardware output stream */ - virtual AudioStreamOut* openOutputStream(uint32_t devices, - int *format = 0, - uint32_t *channels = 0, - uint32_t *sampleRate = 0, - status_t *status = 0); - virtual void closeOutputStream(AudioStreamOut* out); - - /** This method creates and opens the audio hardware input stream */ - virtual AudioStreamIn* openInputStream(uint32_t devices, - int *format, - uint32_t *channels, - uint32_t *sampleRate, - status_t *status, - AudioSystem::audio_in_acoustics acoustics); - virtual void closeInputStream(AudioStreamIn* in); - - static uint32_t checkInputSampleRate(uint32_t sampleRate); - static const uint32_t inputSamplingRates[]; - static uint32_t bufferRatio(uint32_t samplingRate); - - int mode() { return mMode; } - Mutex& lock() { return mLock; } - - int setVoiceRecordGain(bool enable); - int setVoiceRecordGain_l(bool enable); - - virtual status_t setParameters(const String8& keyValuePairs); - - protected: - /** - * doRouting actually initiates the routing. A call to setRouting - * or setMode may result in a routing change. The generic logic calls - * doRouting when required. If the device has any special requirements these - * methods can be overriden. - */ - status_t doRouting(uint32_t device, bool force = false); - status_t doRouting_l(uint32_t device, bool force = false); - - virtual status_t dump(int fd, const Vector& args); - - friend class AudioStreamOutALSA; - friend class AudioStreamInALSA; - - ALSAMixer *mMixer; - AudioStreamOutALSA *mOutput; - AudioStreamInALSA *mInput; - - private: - Mutex mLock; - void *mSecRilLibHandle; - HRilClient mRilClient; - bool mVrModeEnabled; - bool mActivatedCP; - bool mBluetoothECOff; - - 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); - void setBluetoothNrEcOnOff(bool disable); - - }; - - class ALSADownsampler { - public: - ALSADownsampler(uint32_t outSampleRate, - uint32_t channelCount, - uint32_t frameCount, - ALSABufferProvider* provider); - - virtual ~ALSADownsampler(); - - void reset(); - status_t initCheck() { return mStatus; } - int resample(int16_t* out, size_t *outFrameCount); - - private: - status_t mStatus; - ALSABufferProvider* 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; - }; - -}; // namespace android -#endif // ANDROID_AUDIO_HARDWARE_ALSA_H diff --git a/libaudio/AudioPolicyManager.cpp b/libaudio/AudioPolicyManager.cpp index 41c0fd0..93d70d8 100644 --- a/libaudio/AudioPolicyManager.cpp +++ b/libaudio/AudioPolicyManager.cpp @@ -42,4 +42,33 @@ extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) delete interface; } + +status_t AudioPolicyManager::startInput(audio_io_handle_t input) +{ + status_t status = AudioPolicyManagerBase::startInput(input); + + if (status == NO_ERROR) { + AudioInputDescriptor *inputDesc = mInputs.valueFor(input); + String8 key = String8("Input Source"); + String8 value; + switch(inputDesc->mInputSource) { + case AUDIO_SOURCE_VOICE_RECOGNITION: + value = String8("Voice Recognition"); + break; + case AUDIO_SOURCE_CAMCORDER: + value = String8("Camcorder"); + break; + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + value = String8("Default"); + default: + break; + } + AudioParameter param = AudioParameter(); + param.add(key, value); + mpClientInterface->setParameters(input, param.toString()); + } + return status; +} + }; // namespace android diff --git a/libaudio/AudioPolicyManager.h b/libaudio/AudioPolicyManager.h index 03141e5..ae283db 100644 --- a/libaudio/AudioPolicyManager.h +++ b/libaudio/AudioPolicyManager.h @@ -34,6 +34,7 @@ public: virtual ~AudioPolicyManager() {} + virtual status_t startInput(audio_io_handle_t input); protected: // true is current platform implements a back microphone virtual bool hasBackMicrophone() const { return false; } diff --git a/libaudio/NOTICE b/libaudio/NOTICE deleted file mode 100644 index ada44e1..0000000 --- a/libaudio/NOTICE +++ /dev/null @@ -1,191 +0,0 @@ - - Copyright (c) 2005-2008, The Android Open Source Project - Copyright 2008 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libaudio/alsa_audio.h b/libaudio/alsa_audio.h new file mode 100644 index 0000000..3cb86d9 --- /dev/null +++ b/libaudio/alsa_audio.h @@ -0,0 +1,77 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef _AUDIO_H_ +#define _AUDIO_H_ + +struct pcm; + +#define PCM_OUT 0x00000000 +#define PCM_IN 0x10000000 + +#define PCM_STEREO 0x00000000 +#define PCM_MONO 0x01000000 + +#define PCM_44100HZ 0x00000000 +#define PCM_48000HZ 0x00100000 +#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 + */ +struct pcm *pcm_open(unsigned flags); +int pcm_close(struct pcm *pcm); +int pcm_ready(struct pcm *pcm); + +/* Returns a human readable reason for the last error. */ +const char *pcm_error(struct pcm *pcm); + +/* Returns the buffer size (int bytes) that should be used for pcm_write. + * This will be 1/2 of the actual fifo size. + */ +unsigned pcm_buffer_size(struct pcm *pcm); + +/* Write data to the fifo. + * Will start playback on the first write or on a write that + * occurs after a fifo underrun. + */ +int pcm_write(struct pcm *pcm, void *data, unsigned count); +int pcm_read(struct pcm *pcm, void *data, unsigned count); + +struct mixer; +struct mixer_ctl; + +struct mixer *mixer_open(void); +void mixer_close(struct mixer *mixer); +void mixer_dump(struct mixer *mixer); + +struct mixer_ctl *mixer_get_control(struct mixer *mixer, + const char *name, unsigned index); +struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n); + +int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent); +int mixer_ctl_select(struct mixer_ctl *ctl, const char *value); +void mixer_ctl_print(struct mixer_ctl *ctl); + +#endif diff --git a/libaudio/alsa_mixer.c b/libaudio/alsa_mixer.c new file mode 100644 index 0000000..3036ef8 --- /dev/null +++ b/libaudio/alsa_mixer.c @@ -0,0 +1,371 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#define __force +#define __bitwise +#define __user +#include "asound.h" + +#include "alsa_audio.h" + +static const char *elem_iface_name(snd_ctl_elem_iface_t n) +{ + switch (n) { + case SNDRV_CTL_ELEM_IFACE_CARD: return "CARD"; + case SNDRV_CTL_ELEM_IFACE_HWDEP: return "HWDEP"; + case SNDRV_CTL_ELEM_IFACE_MIXER: return "MIXER"; + case SNDRV_CTL_ELEM_IFACE_PCM: return "PCM"; + case SNDRV_CTL_ELEM_IFACE_RAWMIDI: return "MIDI"; + case SNDRV_CTL_ELEM_IFACE_TIMER: return "TIMER"; + case SNDRV_CTL_ELEM_IFACE_SEQUENCER: return "SEQ"; + default: return "???"; + } +} + +static const char *elem_type_name(snd_ctl_elem_type_t n) +{ + switch (n) { + case SNDRV_CTL_ELEM_TYPE_NONE: return "NONE"; + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: return "BOOL"; + case SNDRV_CTL_ELEM_TYPE_INTEGER: return "INT32"; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: return "ENUM"; + case SNDRV_CTL_ELEM_TYPE_BYTES: return "BYTES"; + case SNDRV_CTL_ELEM_TYPE_IEC958: return "IEC958"; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: return "INT64"; + default: return "???"; + } +} + + +struct mixer_ctl { + struct mixer *mixer; + struct snd_ctl_elem_info *info; + char **ename; +}; + +struct mixer { + int fd; + struct snd_ctl_elem_info *info; + struct mixer_ctl *ctl; + unsigned count; +}; + +void mixer_close(struct mixer *mixer) +{ + unsigned n,m; + + if (mixer->fd >= 0) + close(mixer->fd); + + if (mixer->ctl) { + for (n = 0; n < mixer->count; n++) { + if (mixer->ctl[n].ename) { + unsigned max = mixer->ctl[n].info->value.enumerated.items; + for (m = 0; m < max; m++) + free(mixer->ctl[n].ename[m]); + free(mixer->ctl[n].ename); + } + } + free(mixer->ctl); + } + + if (mixer->info) + free(mixer->info); + + free(mixer); +} + +struct mixer *mixer_open(void) +{ + struct snd_ctl_elem_list elist; + struct snd_ctl_elem_info tmp; + struct snd_ctl_elem_id *eid = NULL; + struct mixer *mixer = NULL; + unsigned n, m; + int fd; + + fd = open("/dev/snd/controlC0", O_RDWR); + if (fd < 0) + return 0; + + memset(&elist, 0, sizeof(elist)); + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) + goto fail; + + mixer = calloc(1, sizeof(*mixer)); + if (!mixer) + goto fail; + + mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl)); + mixer->info = calloc(elist.count, sizeof(struct snd_ctl_elem_info)); + if (!mixer->ctl || !mixer->info) + goto fail; + + eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id)); + if (!eid) + goto fail; + + mixer->count = elist.count; + mixer->fd = fd; + elist.space = mixer->count; + elist.pids = eid; + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) + goto fail; + + for (n = 0; n < mixer->count; n++) { + struct snd_ctl_elem_info *ei = mixer->info + n; + ei->id.numid = eid[n].numid; + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0) + goto fail; + mixer->ctl[n].info = ei; + mixer->ctl[n].mixer = mixer; + if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { + char **enames = calloc(ei->value.enumerated.items, sizeof(char*)); + if (!enames) + goto fail; + mixer->ctl[n].ename = enames; + for (m = 0; m < ei->value.enumerated.items; m++) { + memset(&tmp, 0, sizeof(tmp)); + tmp.id.numid = ei->id.numid; + tmp.value.enumerated.item = m; + if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0) + goto fail; + enames[m] = strdup(tmp.value.enumerated.name); + if (!enames[m]) + goto fail; + } + } + } + + free(eid); + return mixer; + +fail: + if (eid) + free(eid); + if (mixer) + mixer_close(mixer); + else if (fd >= 0) + close(fd); + return 0; +} + +void mixer_dump(struct mixer *mixer) +{ + unsigned n, m; + + printf(" id iface dev sub idx num perms type name\n"); + for (n = 0; n < mixer->count; n++) { + struct snd_ctl_elem_info *ei = mixer->info + n; + + printf("%4d %5s %3d %3d %3d %3d %c%c%c%c%c%c%c%c%c %-6s %s", + ei->id.numid, elem_iface_name(ei->id.iface), + ei->id.device, ei->id.subdevice, ei->id.index, + ei->count, + (ei->access & SNDRV_CTL_ELEM_ACCESS_READ) ? 'r' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ? 'w' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE) ? 'V' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_TIMESTAMP) ? 'T' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ? 'R' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) ? 'W' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) ? 'C' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) ? 'I' : ' ', + (ei->access & SNDRV_CTL_ELEM_ACCESS_LOCK) ? 'L' : ' ', + elem_type_name(ei->type), + ei->id.name); + switch (ei->type) { + case SNDRV_CTL_ELEM_TYPE_INTEGER: + printf(ei->value.integer.step ? + " { %ld-%ld, %ld }\n" : " { %ld-%ld }", + ei->value.integer.min, + ei->value.integer.max, + ei->value.integer.step); + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + printf(ei->value.integer64.step ? + " { %lld-%lld, %lld }\n" : " { %lld-%lld }", + ei->value.integer64.min, + ei->value.integer64.max, + ei->value.integer64.step); + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: { + unsigned m; + printf(" { %s=0", mixer->ctl[n].ename[0]); + for (m = 1; m < ei->value.enumerated.items; m++) + printf(", %s=%d", mixer->ctl[n].ename[m],m); + printf(" }"); + break; + } + } + printf("\n"); + } +} + +struct mixer_ctl *mixer_get_control(struct mixer *mixer, + const char *name, unsigned index) +{ + unsigned n; + for (n = 0; n < mixer->count; n++) { + if (mixer->info[n].id.index == index) { + if (!strcmp(name, (char*) mixer->info[n].id.name)) { + return mixer->ctl + n; + } + } + } + return 0; +} + +struct mixer_ctl *mixer_get_nth_control(struct mixer *mixer, unsigned n) +{ + if (n < mixer->count) + return mixer->ctl + n; + return 0; +} + +void mixer_ctl_print(struct mixer_ctl *ctl) +{ + struct snd_ctl_elem_value ev; + unsigned n; + + memset(&ev, 0, sizeof(ev)); + ev.id.numid = ctl->info->id.numid; + if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev)) + return; + printf("%s:", ctl->info->id.name); + + switch (ctl->info->type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + for (n = 0; n < ctl->info->count; n++) + printf(" %s", ev.value.integer.value[n] ? "ON" : "OFF"); + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER: { + for (n = 0; n < ctl->info->count; n++) + printf(" %ld", ev.value.integer.value[n]); + break; + } + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + for (n = 0; n < ctl->info->count; n++) + printf(" %lld", ev.value.integer64.value[n]); + break; + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: + for (n = 0; n < ctl->info->count; n++) { + unsigned v = ev.value.enumerated.item[n]; + printf(" %d (%s)", v, + (v < ctl->info->value.enumerated.items) ? ctl->ename[v] : "???"); + } + break; + default: + printf(" ???"); + } + printf("\n"); +} + +static long scale_int(struct snd_ctl_elem_info *ei, unsigned _percent) +{ + long percent; + long range; + + if (_percent > 100) + percent = 100; + else + percent = (long) _percent; + + range = (ei->value.integer.max - ei->value.integer.min); + + return ei->value.integer.min + (range * percent) / 100LL; +} + +static long long scale_int64(struct snd_ctl_elem_info *ei, unsigned _percent) +{ + long long percent; + long long range; + + if (_percent > 100) + percent = 100; + else + percent = (long) _percent; + + range = (ei->value.integer.max - ei->value.integer.min) * 100LL; + + return ei->value.integer.min + (range / percent); +} + +int mixer_ctl_set(struct mixer_ctl *ctl, unsigned percent) +{ + struct snd_ctl_elem_value ev; + unsigned n; + + memset(&ev, 0, sizeof(ev)); + ev.id.numid = ctl->info->id.numid; + switch (ctl->info->type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + for (n = 0; n < ctl->info->count; n++) + ev.value.integer.value[n] = !!percent; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER: { + long value = scale_int(ctl->info, percent); + for (n = 0; n < ctl->info->count; n++) + ev.value.integer.value[n] = value; + break; + } + case SNDRV_CTL_ELEM_TYPE_INTEGER64: { + long long value = scale_int64(ctl->info, percent); + for (n = 0; n < ctl->info->count; n++) + ev.value.integer64.value[n] = value; + break; + } + default: + errno = EINVAL; + return -1; + } + + return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev); +} + +int mixer_ctl_select(struct mixer_ctl *ctl, const char *value) +{ + unsigned n, max; + struct snd_ctl_elem_value ev; + + if (ctl->info->type != SNDRV_CTL_ELEM_TYPE_ENUMERATED) { + errno = EINVAL; + return -1; + } + + max = ctl->info->value.enumerated.items; + for (n = 0; n < max; n++) { + if (!strcmp(value, ctl->ename[n])) { + memset(&ev, 0, sizeof(ev)); + ev.value.enumerated.item[0] = n; + ev.id.numid = ctl->info->id.numid; + if (ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev) < 0) + return -1; + return 0; + } + } + + errno = EINVAL; + return -1; +} diff --git a/libaudio/alsa_pcm.c b/libaudio/alsa_pcm.c new file mode 100644 index 0000000..5673391 --- /dev/null +++ b/libaudio/alsa_pcm.c @@ -0,0 +1,405 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "alsa_pcm" +//#define LOG_NDEBUG 0 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "alsa_audio.h" + +#define __force +#define __bitwise +#define __user +#include "asound.h" + +#define DEBUG 0 + +/* alsa parameter manipulation cruft */ + +#define PARAM_MAX SNDRV_PCM_HW_PARAM_LAST_INTERVAL + +static inline int param_is_mask(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (p <= SNDRV_PCM_HW_PARAM_LAST_MASK); +} + +static inline int param_is_interval(int p) +{ + return (p >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) && + (p <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL); +} + +static inline struct snd_interval *param_to_interval(struct snd_pcm_hw_params *p, int n) +{ + return &(p->intervals[n - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]); +} + +static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n) +{ + return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit) +{ + if (bit >= SNDRV_MASK_MAX) + return; + if (param_is_mask(n)) { + struct snd_mask *m = param_to_mask(p, n); + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static void param_set_min(struct snd_pcm_hw_params *p, int n, unsigned val) +{ + if (param_is_interval(n)) { + struct snd_interval *i = param_to_interval(p, n); + i->min = val; + } +} + +static void param_set_max(struct snd_pcm_hw_params *p, int n, unsigned val) +{ + if (param_is_interval(n)) { + struct snd_interval *i = param_to_interval(p, n); + i->max = val; + } +} + +static void param_set_int(struct snd_pcm_hw_params *p, int n, unsigned val) +{ + if (param_is_interval(n)) { + struct snd_interval *i = param_to_interval(p, n); + i->min = val; + i->max = val; + i->integer = 1; + } +} + +static void param_init(struct snd_pcm_hw_params *p) +{ + int n; + memset(p, 0, sizeof(*p)); + for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; + n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { + struct snd_mask *m = param_to_mask(p, n); + m->bits[0] = ~0; + m->bits[1] = ~0; + } + for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; + n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { + struct snd_interval *i = param_to_interval(p, n); + i->min = 0; + i->max = ~0; + } +} + +/* debugging gunk */ + +#if DEBUG +static const char *param_name[PARAM_MAX+1] = { + [SNDRV_PCM_HW_PARAM_ACCESS] = "access", + [SNDRV_PCM_HW_PARAM_FORMAT] = "format", + [SNDRV_PCM_HW_PARAM_SUBFORMAT] = "subformat", + + [SNDRV_PCM_HW_PARAM_SAMPLE_BITS] = "sample_bits", + [SNDRV_PCM_HW_PARAM_FRAME_BITS] = "frame_bits", + [SNDRV_PCM_HW_PARAM_CHANNELS] = "channels", + [SNDRV_PCM_HW_PARAM_RATE] = "rate", + [SNDRV_PCM_HW_PARAM_PERIOD_TIME] = "period_time", + [SNDRV_PCM_HW_PARAM_PERIOD_SIZE] = "period_size", + [SNDRV_PCM_HW_PARAM_PERIOD_BYTES] = "period_bytes", + [SNDRV_PCM_HW_PARAM_PERIODS] = "periods", + [SNDRV_PCM_HW_PARAM_BUFFER_TIME] = "buffer_time", + [SNDRV_PCM_HW_PARAM_BUFFER_SIZE] = "buffer_size", + [SNDRV_PCM_HW_PARAM_BUFFER_BYTES] = "buffer_bytes", + [SNDRV_PCM_HW_PARAM_TICK_TIME] = "tick_time", +}; + +static void param_dump(struct snd_pcm_hw_params *p) +{ + int n; + + for (n = SNDRV_PCM_HW_PARAM_FIRST_MASK; + n <= SNDRV_PCM_HW_PARAM_LAST_MASK; n++) { + struct snd_mask *m = param_to_mask(p, n); + LOGV("%s = %08x%08x\n", param_name[n], + m->bits[1], m->bits[0]); + } + for (n = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; + n <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; n++) { + struct snd_interval *i = param_to_interval(p, n); + LOGV("%s = (%d,%d) omin=%d omax=%d int=%d empty=%d\n", + param_name[n], i->min, i->max, i->openmin, + i->openmax, i->integer, i->empty); + } + LOGV("info = %08x\n", p->info); + LOGV("msbits = %d\n", p->msbits); + LOGV("rate = %d/%d\n", p->rate_num, p->rate_den); + LOGV("fifo = %d\n", (int) p->fifo_size); +} + +static void info_dump(struct snd_pcm_info *info) +{ + LOGV("device = %d\n", info->device); + LOGV("subdevice = %d\n", info->subdevice); + LOGV("stream = %d\n", info->stream); + LOGV("card = %d\n", info->card); + LOGV("id = '%s'\n", info->id); + LOGV("name = '%s'\n", info->name); + LOGV("subname = '%s'\n", info->subname); + LOGV("dev_class = %d\n", info->dev_class); + LOGV("dev_subclass = %d\n", info->dev_subclass); + LOGV("subdevices_count = %d\n", info->subdevices_count); + LOGV("subdevices_avail = %d\n", info->subdevices_avail); +} +#else +static void param_dump(struct snd_pcm_hw_params *p) {} +static void info_dump(struct snd_pcm_info *info) {} +#endif + +#define PCM_ERROR_MAX 128 + +struct pcm { + int fd; + unsigned flags; + int running:1; + int underruns; + unsigned buffer_size; + char error[PCM_ERROR_MAX]; +}; + +unsigned pcm_buffer_size(struct pcm *pcm) +{ + return pcm->buffer_size; +} + +const char* pcm_error(struct pcm *pcm) +{ + return pcm->error; +} + +static int oops(struct pcm *pcm, int e, const char *fmt, ...) +{ + va_list ap; + int sz; + + va_start(ap, fmt); + vsnprintf(pcm->error, PCM_ERROR_MAX, fmt, ap); + va_end(ap); + sz = strlen(pcm->error); + + if (errno) + snprintf(pcm->error + sz, PCM_ERROR_MAX - sz, + ": %s", strerror(e)); + return -1; +} + +int pcm_write(struct pcm *pcm, void *data, unsigned count) +{ + struct snd_xferi x; + + if (pcm->flags & PCM_IN) + return -EINVAL; + + x.buf = data; + x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); + + for (;;) { + if (!pcm->running) { + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_PREPARE)) + return oops(pcm, errno, "cannot prepare channel"); + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) + return oops(pcm, errno, "cannot write initial data"); + pcm->running = 1; + return 0; + } + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &x)) { + pcm->running = 0; + if (errno == EPIPE) { + /* we failed to make our window -- try to restart */ + pcm->underruns++; + continue; + } + return oops(pcm, errno, "cannot write stream data"); + } + return 0; + } +} + +int pcm_read(struct pcm *pcm, void *data, unsigned count) +{ + struct snd_xferi x; + + if (!(pcm->flags & PCM_IN)) + return -EINVAL; + + x.buf = data; + x.frames = (pcm->flags & PCM_MONO) ? (count / 2) : (count / 4); + +// 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"); + pcm->running = 1; + } + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_READI_FRAMES, &x)) { + pcm->running = 0; + if (errno == EPIPE) { + /* we failed to make our window -- try to restart */ + pcm->underruns++; + continue; + } + return oops(pcm, errno, "cannot read stream data"); + } +// LOGV("read() got %d frames", x.frames); + return 0; + } +} + +static struct pcm bad_pcm = { + .fd = -1, +}; + +int pcm_close(struct pcm *pcm) +{ + if (pcm == &bad_pcm) + return 0; + + if (pcm->fd >= 0) + close(pcm->fd); + pcm->running = 0; + pcm->buffer_size = 0; + pcm->fd = -1; + return 0; +} + +struct pcm *pcm_open(unsigned flags) +{ + const char *dname; + struct pcm *pcm; + struct snd_pcm_info info; + struct snd_pcm_hw_params params; + struct snd_pcm_sw_params sparams; + unsigned period_sz; + unsigned period_cnt; + + LOGV("pcm_open(0x%08x)",flags); + + pcm = calloc(1, sizeof(struct pcm)); + if (!pcm) + return &bad_pcm; + + if (flags & PCM_IN) { + dname = "/dev/snd/pcmC0D0c"; + } else { + 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) { + oops(pcm, errno, "cannot open device '%s'"); + return pcm; + } + + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) { + oops(pcm, errno, "cannot get info - %s"); + goto fail; + } + 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(¶ms); + param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_ACCESS_RW_INTERLEAVED); + param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S16_LE); + param_set_mask(¶ms, SNDRV_PCM_HW_PARAM_SUBFORMAT, + SNDRV_PCM_SUBFORMAT_STD); + param_set_min(¶ms, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, period_sz); + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 16); + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_FRAME_BITS, + (flags & PCM_MONO) ? 16 : 32); + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, + (flags & PCM_MONO) ? 1 : 2); + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_PERIODS, period_cnt); + param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 44100); + + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) { + oops(pcm, errno, "cannot set hw params"); + goto fail; + } + param_dump(¶ms); + + memset(&sparams, 0, sizeof(sparams)); + sparams.tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + sparams.period_step = 1; + sparams.avail_min = 1; + sparams.start_threshold = period_cnt * period_sz; + sparams.stop_threshold = period_cnt * period_sz; + sparams.xfer_align = period_sz / 2; /* needed for old kernels */ + sparams.silence_size = 0; + sparams.silence_threshold = 0; + + if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sparams)) { + oops(pcm, errno, "cannot set sw params"); + goto fail; + } + + pcm->buffer_size = period_cnt * period_sz; + pcm->underruns = 0; + return pcm; + +fail: + close(pcm->fd); + pcm->fd = -1; + return pcm; +} + +int pcm_ready(struct pcm *pcm) +{ + return pcm->fd >= 0; +} diff --git a/libaudio/amix.c b/libaudio/amix.c new file mode 100644 index 0000000..d978caa --- /dev/null +++ b/libaudio/amix.c @@ -0,0 +1,78 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include + +#include "alsa_audio.h" + + +struct mixer_ctl *get_ctl(struct mixer *mixer, char *name) +{ + char *p; + unsigned idx = 0; + + if (isdigit(name[0])) + return mixer_get_nth_control(mixer, atoi(name) - 1); + + p = strrchr(name, '#'); + if (p) { + *p++ = 0; + idx = atoi(p); + } + + return mixer_get_control(mixer, name, idx); +} + +int main(int argc, char **argv) +{ + struct mixer *mixer; + struct mixer_ctl *ctl; + int r; + + mixer = mixer_open(); + if (!mixer) + return -1; + + if (argc == 1) { + mixer_dump(mixer); + return 0; + } + + ctl = get_ctl(mixer, argv[1]); + argc -= 2; + argv += 2; + + if (!ctl) { + fprintf(stderr,"can't find control\n"); + return -1; + } + + if (argc) { + if (isdigit(argv[0][0])) + r = mixer_ctl_set(ctl, atoi(argv[0])); + else + r = mixer_ctl_select(ctl, argv[0]); + if (r) + fprintf(stderr,"oops: %s\n", strerror(errno)); + } else { + mixer_ctl_print(ctl); + } + return 0; +} diff --git a/libaudio/aplay.c b/libaudio/aplay.c new file mode 100644 index 0000000..0ac0ac0 --- /dev/null +++ b/libaudio/aplay.c @@ -0,0 +1,140 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include + +#include "alsa_audio.h" + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +#define FORMAT_PCM 1 + +struct wav_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t riff_fmt; + uint32_t fmt_id; + uint32_t fmt_sz; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ + uint16_t block_align; /* num_channels * bps / 8 */ + uint16_t bits_per_sample; + uint32_t data_id; + uint32_t data_sz; +}; + +int play_file(unsigned rate, unsigned channels, int fd, unsigned count) +{ + struct pcm *pcm; + struct mixer *mixer; + struct pcm_ctl *ctl = NULL; + unsigned bufsize; + char *data; + unsigned flags = PCM_OUT; + + if (channels == 1) + flags |= PCM_MONO; + else + flags |= PCM_STEREO; + + pcm = pcm_open(flags); + if (!pcm_ready(pcm)) { + pcm_close(pcm); + return -1; + } + + mixer = mixer_open(); + if (mixer) + ctl = mixer_get_control(mixer,"Playback Path", 0); + + bufsize = pcm_buffer_size(pcm); + data = malloc(bufsize); + if (!data) { + fprintf(stderr,"could not allocate %d bytes\n", count); + return -1; + } + + while (read(fd, data, bufsize) == bufsize) { + if (pcm_write(pcm, data, bufsize)) + break; + + /* HACK: remove */ + if (ctl) { + //mixer_ctl_select(ctl, "SPK"); + ctl = 0; + } + } + pcm_close(pcm); + return 0; +} + +int play_wav(const char *fn) +{ + struct wav_header hdr; + unsigned rate, channels; + int fd; + fd = open(fn, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "aplay: cannot open '%s'\n", fn); + return -1; + } + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + fprintf(stderr, "aplay: cannot read header\n"); + return -1; + } + fprintf(stderr,"aplay: %d ch, %d hz, %d bit, %s\n", + hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample, + hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown"); + + if ((hdr.riff_id != ID_RIFF) || + (hdr.riff_fmt != ID_WAVE) || + (hdr.fmt_id != ID_FMT)) { + fprintf(stderr, "aplay: '%s' is not a riff/wave file\n", fn); + return -1; + } + if ((hdr.audio_format != FORMAT_PCM) || + (hdr.fmt_sz != 16)) { + fprintf(stderr, "aplay: '%s' is not pcm format\n", fn); + return -1; + } + if (hdr.bits_per_sample != 16) { + fprintf(stderr, "aplay: '%s' is not 16bit per sample\n", fn); + return -1; + } + + return play_file(hdr.sample_rate, hdr.num_channels, fd, hdr.data_sz); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr,"usage: aplay \n"); + return -1; + } + + return play_wav(argv[1]); +} + diff --git a/libaudio/arec.c b/libaudio/arec.c new file mode 100644 index 0000000..b1e9eda --- /dev/null +++ b/libaudio/arec.c @@ -0,0 +1,128 @@ +/* +** Copyright 2010, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include + +#include "alsa_audio.h" + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +#define FORMAT_PCM 1 + +struct wav_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t riff_fmt; + uint32_t fmt_id; + uint32_t fmt_sz; + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */ + uint16_t block_align; /* num_channels * bps / 8 */ + uint16_t bits_per_sample; + uint32_t data_id; + uint32_t data_sz; +}; + +int record_file(unsigned rate, unsigned channels, int fd, unsigned count) +{ + struct pcm *pcm; + unsigned avail, xfer, bufsize; + char *data, *next; + int r; + + pcm = pcm_open(PCM_IN|PCM_MONO); + if (!pcm_ready(pcm)) { + pcm_close(pcm); + goto fail; + } + + bufsize = pcm_buffer_size(pcm); + + data = malloc(bufsize); + if (!data) { + fprintf(stderr,"could not allocate %d bytes\n", count); + return -1; + } + + while (!pcm_read(pcm, data, bufsize)) { + if (write(fd, data, bufsize) != bufsize) { + fprintf(stderr,"could not write %d bytes\n", bufsize); + return -1; + } + } + + close(fd); + pcm_close(pcm); + return 0; + +fail: + fprintf(stderr,"pcm error: %s\n", pcm_error(pcm)); + return -1; +} + +int rec_wav(const char *fn) +{ + struct wav_header hdr; + unsigned rate, channels; + int fd; + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); + if (fd < 0) { + fprintf(stderr, "arec: cannot open '%s'\n", fn); + return -1; + } + + hdr.riff_id = ID_RIFF; + hdr.riff_fmt = ID_WAVE; + hdr.fmt_id = ID_FMT; + hdr.audio_format = FORMAT_PCM; + hdr.fmt_sz = 16; + hdr.bits_per_sample = 16; + hdr.num_channels = 1; + hdr.data_sz = 0; + hdr.sample_rate = 44100; + + if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + fprintf(stderr, "arec: cannot write header\n"); + return -1; + } + fprintf(stderr,"arec: %d ch, %d hz, %d bit, %s\n", + hdr.num_channels, hdr.sample_rate, hdr.bits_per_sample, + hdr.audio_format == FORMAT_PCM ? "PCM" : "unknown"); + + + return record_file(hdr.sample_rate, hdr.num_channels, fd, hdr.data_sz); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr,"usage: arec \n"); + return -1; + } + + return rec_wav(argv[1]); +} + diff --git a/libaudio/asound.h b/libaudio/asound.h new file mode 100644 index 0000000..6a17f29 --- /dev/null +++ b/libaudio/asound.h @@ -0,0 +1,814 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + **************************************************************************** + ****************************************************************************/ +#ifndef __SOUND_ASOUND_H +#define __SOUND_ASOUND_H + +#include + +#define SNDRV_PROTOCOL_VERSION(major, minor, subminor) (((major)<<16)|((minor)<<8)|(subminor)) +#define SNDRV_PROTOCOL_MAJOR(version) (((version)>>16)&0xffff) +#define SNDRV_PROTOCOL_MINOR(version) (((version)>>8)&0xff) +#define SNDRV_PROTOCOL_MICRO(version) ((version)&0xff) +#define SNDRV_PROTOCOL_INCOMPATIBLE(kversion, uversion) (SNDRV_PROTOCOL_MAJOR(kversion) != SNDRV_PROTOCOL_MAJOR(uversion) || (SNDRV_PROTOCOL_MAJOR(kversion) == SNDRV_PROTOCOL_MAJOR(uversion) && SNDRV_PROTOCOL_MINOR(kversion) != SNDRV_PROTOCOL_MINOR(uversion))) + +struct snd_aes_iec958 { + unsigned char status[24]; + unsigned char subcode[147]; + unsigned char pad; + unsigned char dig_subframe[4]; +}; + +#define SNDRV_HWDEP_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 1) + +enum { + SNDRV_HWDEP_IFACE_OPL2 = 0, + SNDRV_HWDEP_IFACE_OPL3, + SNDRV_HWDEP_IFACE_OPL4, + SNDRV_HWDEP_IFACE_SB16CSP, + SNDRV_HWDEP_IFACE_EMU10K1, + SNDRV_HWDEP_IFACE_YSS225, + SNDRV_HWDEP_IFACE_ICS2115, + SNDRV_HWDEP_IFACE_SSCAPE, + SNDRV_HWDEP_IFACE_VX, + SNDRV_HWDEP_IFACE_MIXART, + SNDRV_HWDEP_IFACE_USX2Y, + SNDRV_HWDEP_IFACE_EMUX_WAVETABLE, + SNDRV_HWDEP_IFACE_BLUETOOTH, + SNDRV_HWDEP_IFACE_USX2Y_PCM, + SNDRV_HWDEP_IFACE_PCXHR, + SNDRV_HWDEP_IFACE_SB_RC, + SNDRV_HWDEP_IFACE_HDA, + SNDRV_HWDEP_IFACE_USB_STREAM, + + SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM +}; + +struct snd_hwdep_info { + unsigned int device; + int card; + unsigned char id[64]; + unsigned char name[80]; + int iface; + unsigned char reserved[64]; +}; + +struct snd_hwdep_dsp_status { + unsigned int version; + unsigned char id[32]; + unsigned int num_dsps; + unsigned int dsp_loaded; + unsigned int chip_ready; + unsigned char reserved[16]; +}; + +struct snd_hwdep_dsp_image { + unsigned int index; + unsigned char name[64]; + unsigned char __user *image; + size_t length; + unsigned long driver_data; +}; + +#define SNDRV_HWDEP_IOCTL_PVERSION _IOR ('H', 0x00, int) +#define SNDRV_HWDEP_IOCTL_INFO _IOR ('H', 0x01, struct snd_hwdep_info) +#define SNDRV_HWDEP_IOCTL_DSP_STATUS _IOR('H', 0x02, struct snd_hwdep_dsp_status) +#define SNDRV_HWDEP_IOCTL_DSP_LOAD _IOW('H', 0x03, struct snd_hwdep_dsp_image) + +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 10) + +typedef unsigned long snd_pcm_uframes_t; +typedef signed long snd_pcm_sframes_t; + +enum { + SNDRV_PCM_CLASS_GENERIC = 0, + SNDRV_PCM_CLASS_MULTI, + SNDRV_PCM_CLASS_MODEM, + SNDRV_PCM_CLASS_DIGITIZER, + + SNDRV_PCM_CLASS_LAST = SNDRV_PCM_CLASS_DIGITIZER, +}; + +enum { + SNDRV_PCM_SUBCLASS_GENERIC_MIX = 0, + SNDRV_PCM_SUBCLASS_MULTI_MIX, + + SNDRV_PCM_SUBCLASS_LAST = SNDRV_PCM_SUBCLASS_MULTI_MIX, +}; + +enum { + SNDRV_PCM_STREAM_PLAYBACK = 0, + SNDRV_PCM_STREAM_CAPTURE, + SNDRV_PCM_STREAM_LAST = SNDRV_PCM_STREAM_CAPTURE, +}; + +typedef int __bitwise snd_pcm_access_t; +#define SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ((__force snd_pcm_access_t) 0) +#define SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED ((__force snd_pcm_access_t) 1) +#define SNDRV_PCM_ACCESS_MMAP_COMPLEX ((__force snd_pcm_access_t) 2) +#define SNDRV_PCM_ACCESS_RW_INTERLEAVED ((__force snd_pcm_access_t) 3) +#define SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ((__force snd_pcm_access_t) 4) +#define SNDRV_PCM_ACCESS_LAST SNDRV_PCM_ACCESS_RW_NONINTERLEAVED + +typedef int __bitwise snd_pcm_format_t; +#define SNDRV_PCM_FORMAT_S8 ((__force snd_pcm_format_t) 0) +#define SNDRV_PCM_FORMAT_U8 ((__force snd_pcm_format_t) 1) +#define SNDRV_PCM_FORMAT_S16_LE ((__force snd_pcm_format_t) 2) +#define SNDRV_PCM_FORMAT_S16_BE ((__force snd_pcm_format_t) 3) +#define SNDRV_PCM_FORMAT_U16_LE ((__force snd_pcm_format_t) 4) +#define SNDRV_PCM_FORMAT_U16_BE ((__force snd_pcm_format_t) 5) +#define SNDRV_PCM_FORMAT_S24_LE ((__force snd_pcm_format_t) 6) +#define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) +#define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) +#define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) +#define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10) +#define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11) +#define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12) +#define SNDRV_PCM_FORMAT_U32_BE ((__force snd_pcm_format_t) 13) +#define SNDRV_PCM_FORMAT_FLOAT_LE ((__force snd_pcm_format_t) 14) +#define SNDRV_PCM_FORMAT_FLOAT_BE ((__force snd_pcm_format_t) 15) +#define SNDRV_PCM_FORMAT_FLOAT64_LE ((__force snd_pcm_format_t) 16) +#define SNDRV_PCM_FORMAT_FLOAT64_BE ((__force snd_pcm_format_t) 17) +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE ((__force snd_pcm_format_t) 18) +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE ((__force snd_pcm_format_t) 19) +#define SNDRV_PCM_FORMAT_MU_LAW ((__force snd_pcm_format_t) 20) +#define SNDRV_PCM_FORMAT_A_LAW ((__force snd_pcm_format_t) 21) +#define SNDRV_PCM_FORMAT_IMA_ADPCM ((__force snd_pcm_format_t) 22) +#define SNDRV_PCM_FORMAT_MPEG ((__force snd_pcm_format_t) 23) +#define SNDRV_PCM_FORMAT_GSM ((__force snd_pcm_format_t) 24) +#define SNDRV_PCM_FORMAT_SPECIAL ((__force snd_pcm_format_t) 31) +#define SNDRV_PCM_FORMAT_S24_3LE ((__force snd_pcm_format_t) 32) +#define SNDRV_PCM_FORMAT_S24_3BE ((__force snd_pcm_format_t) 33) +#define SNDRV_PCM_FORMAT_U24_3LE ((__force snd_pcm_format_t) 34) +#define SNDRV_PCM_FORMAT_U24_3BE ((__force snd_pcm_format_t) 35) +#define SNDRV_PCM_FORMAT_S20_3LE ((__force snd_pcm_format_t) 36) +#define SNDRV_PCM_FORMAT_S20_3BE ((__force snd_pcm_format_t) 37) +#define SNDRV_PCM_FORMAT_U20_3LE ((__force snd_pcm_format_t) 38) +#define SNDRV_PCM_FORMAT_U20_3BE ((__force snd_pcm_format_t) 39) +#define SNDRV_PCM_FORMAT_S18_3LE ((__force snd_pcm_format_t) 40) +#define SNDRV_PCM_FORMAT_S18_3BE ((__force snd_pcm_format_t) 41) +#define SNDRV_PCM_FORMAT_U18_3LE ((__force snd_pcm_format_t) 42) +#define SNDRV_PCM_FORMAT_U18_3BE ((__force snd_pcm_format_t) 43) +#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_U18_3BE + +#ifdef SNDRV_LITTLE_ENDIAN +#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE +#define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_LE +#define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_LE +#define SNDRV_PCM_FORMAT_U24 SNDRV_PCM_FORMAT_U24_LE +#define SNDRV_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32_LE +#define SNDRV_PCM_FORMAT_U32 SNDRV_PCM_FORMAT_U32_LE +#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_LE +#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_LE +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE +#endif +#ifdef SNDRV_BIG_ENDIAN +#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE +#define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_BE +#define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_BE +#define SNDRV_PCM_FORMAT_U24 SNDRV_PCM_FORMAT_U24_BE +#define SNDRV_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32_BE +#define SNDRV_PCM_FORMAT_U32 SNDRV_PCM_FORMAT_U32_BE +#define SNDRV_PCM_FORMAT_FLOAT SNDRV_PCM_FORMAT_FLOAT_BE +#define SNDRV_PCM_FORMAT_FLOAT64 SNDRV_PCM_FORMAT_FLOAT64_BE +#define SNDRV_PCM_FORMAT_IEC958_SUBFRAME SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE +#endif + +typedef int __bitwise snd_pcm_subformat_t; +#define SNDRV_PCM_SUBFORMAT_STD ((__force snd_pcm_subformat_t) 0) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD + +#define SNDRV_PCM_INFO_MMAP 0x00000001 +#define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 +#define SNDRV_PCM_INFO_DOUBLE 0x00000004 +#define SNDRV_PCM_INFO_BATCH 0x00000010 +#define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 +#define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 +#define SNDRV_PCM_INFO_COMPLEX 0x00000400 +#define SNDRV_PCM_INFO_BLOCK_TRANSFER 0x00010000 +#define SNDRV_PCM_INFO_OVERRANGE 0x00020000 +#define SNDRV_PCM_INFO_RESUME 0x00040000 +#define SNDRV_PCM_INFO_PAUSE 0x00080000 +#define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 +#define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 +#define SNDRV_PCM_INFO_SYNC_START 0x00400000 +#define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 + +typedef int __bitwise snd_pcm_state_t; +#define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) +#define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) +#define SNDRV_PCM_STATE_PREPARED ((__force snd_pcm_state_t) 2) +#define SNDRV_PCM_STATE_RUNNING ((__force snd_pcm_state_t) 3) +#define SNDRV_PCM_STATE_XRUN ((__force snd_pcm_state_t) 4) +#define SNDRV_PCM_STATE_DRAINING ((__force snd_pcm_state_t) 5) +#define SNDRV_PCM_STATE_PAUSED ((__force snd_pcm_state_t) 6) +#define SNDRV_PCM_STATE_SUSPENDED ((__force snd_pcm_state_t) 7) +#define SNDRV_PCM_STATE_DISCONNECTED ((__force snd_pcm_state_t) 8) +#define SNDRV_PCM_STATE_LAST SNDRV_PCM_STATE_DISCONNECTED + +enum { + SNDRV_PCM_MMAP_OFFSET_DATA = 0x00000000, + SNDRV_PCM_MMAP_OFFSET_STATUS = 0x80000000, + SNDRV_PCM_MMAP_OFFSET_CONTROL = 0x81000000, +}; + +union snd_pcm_sync_id { + unsigned char id[16]; + unsigned short id16[8]; + unsigned int id32[4]; +}; + +struct snd_pcm_info { + unsigned int device; + unsigned int subdevice; + int stream; + int card; + unsigned char id[64]; + unsigned char name[80]; + unsigned char subname[32]; + int dev_class; + int dev_subclass; + unsigned int subdevices_count; + unsigned int subdevices_avail; + union snd_pcm_sync_id sync; + unsigned char reserved[64]; +}; + +typedef int snd_pcm_hw_param_t; +#define SNDRV_PCM_HW_PARAM_ACCESS 0 +#define SNDRV_PCM_HW_PARAM_FORMAT 1 +#define SNDRV_PCM_HW_PARAM_SUBFORMAT 2 +#define SNDRV_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_ACCESS +#define SNDRV_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_SUBFORMAT + +#define SNDRV_PCM_HW_PARAM_SAMPLE_BITS 8 +#define SNDRV_PCM_HW_PARAM_FRAME_BITS 9 +#define SNDRV_PCM_HW_PARAM_CHANNELS 10 +#define SNDRV_PCM_HW_PARAM_RATE 11 +#define SNDRV_PCM_HW_PARAM_PERIOD_TIME 12 +#define SNDRV_PCM_HW_PARAM_PERIOD_SIZE 13 +#define SNDRV_PCM_HW_PARAM_PERIOD_BYTES 14 +#define SNDRV_PCM_HW_PARAM_PERIODS 15 +#define SNDRV_PCM_HW_PARAM_BUFFER_TIME 16 +#define SNDRV_PCM_HW_PARAM_BUFFER_SIZE 17 +#define SNDRV_PCM_HW_PARAM_BUFFER_BYTES 18 +#define SNDRV_PCM_HW_PARAM_TICK_TIME 19 +#define SNDRV_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_SAMPLE_BITS +#define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME + +#define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) + +struct snd_interval { + unsigned int min, max; + unsigned int openmin:1, + openmax:1, + integer:1, + empty:1; +}; + +#define SNDRV_MASK_MAX 256 + +struct snd_mask { + __u32 bits[(SNDRV_MASK_MAX+31)/32]; +}; + +struct snd_pcm_hw_params { + unsigned int flags; + struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct snd_mask mres[5]; + struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct snd_interval ires[9]; + unsigned int rmask; + unsigned int cmask; + unsigned int info; + unsigned int msbits; + unsigned int rate_num; + unsigned int rate_den; + snd_pcm_uframes_t fifo_size; + unsigned char reserved[64]; +}; + +enum { + SNDRV_PCM_TSTAMP_NONE = 0, + SNDRV_PCM_TSTAMP_ENABLE, + SNDRV_PCM_TSTAMP_LAST = SNDRV_PCM_TSTAMP_ENABLE, +}; + +struct snd_pcm_sw_params { + int tstamp_mode; + unsigned int period_step; + unsigned int sleep_min; + snd_pcm_uframes_t avail_min; + snd_pcm_uframes_t xfer_align; + snd_pcm_uframes_t start_threshold; + snd_pcm_uframes_t stop_threshold; + snd_pcm_uframes_t silence_threshold; + snd_pcm_uframes_t silence_size; + snd_pcm_uframes_t boundary; + unsigned char reserved[64]; +}; + +struct snd_pcm_channel_info { + unsigned int channel; + __kernel_off_t offset; + unsigned int first; + unsigned int step; +}; + +struct snd_pcm_status { + snd_pcm_state_t state; + struct timespec trigger_tstamp; + struct timespec tstamp; + snd_pcm_uframes_t appl_ptr; + snd_pcm_uframes_t hw_ptr; + snd_pcm_sframes_t delay; + snd_pcm_uframes_t avail; + snd_pcm_uframes_t avail_max; + snd_pcm_uframes_t overrange; + snd_pcm_state_t suspended_state; + unsigned char reserved[60]; +}; + +struct snd_pcm_mmap_status { + snd_pcm_state_t state; + int pad1; + snd_pcm_uframes_t hw_ptr; + struct timespec tstamp; + snd_pcm_state_t suspended_state; +}; + +struct snd_pcm_mmap_control { + snd_pcm_uframes_t appl_ptr; + snd_pcm_uframes_t avail_min; +}; + +#define SNDRV_PCM_SYNC_PTR_HWSYNC (1<<0) +#define SNDRV_PCM_SYNC_PTR_APPL (1<<1) +#define SNDRV_PCM_SYNC_PTR_AVAIL_MIN (1<<2) + +struct snd_pcm_sync_ptr { + unsigned int flags; + union { + struct snd_pcm_mmap_status status; + unsigned char reserved[64]; + } s; + union { + struct snd_pcm_mmap_control control; + unsigned char reserved[64]; + } c; +}; + +struct snd_xferi { + snd_pcm_sframes_t result; + void __user *buf; + snd_pcm_uframes_t frames; +}; + +struct snd_xfern { + snd_pcm_sframes_t result; + void __user * __user *bufs; + snd_pcm_uframes_t frames; +}; + +enum { + SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, + SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, + SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC, +}; + +#define SNDRV_PCM_IOCTL_PVERSION _IOR('A', 0x00, int) +#define SNDRV_PCM_IOCTL_INFO _IOR('A', 0x01, struct snd_pcm_info) +#define SNDRV_PCM_IOCTL_TSTAMP _IOW('A', 0x02, int) +#define SNDRV_PCM_IOCTL_TTSTAMP _IOW('A', 0x03, int) +#define SNDRV_PCM_IOCTL_HW_REFINE _IOWR('A', 0x10, struct snd_pcm_hw_params) +#define SNDRV_PCM_IOCTL_HW_PARAMS _IOWR('A', 0x11, struct snd_pcm_hw_params) +#define SNDRV_PCM_IOCTL_HW_FREE _IO('A', 0x12) +#define SNDRV_PCM_IOCTL_SW_PARAMS _IOWR('A', 0x13, struct snd_pcm_sw_params) +#define SNDRV_PCM_IOCTL_STATUS _IOR('A', 0x20, struct snd_pcm_status) +#define SNDRV_PCM_IOCTL_DELAY _IOR('A', 0x21, snd_pcm_sframes_t) +#define SNDRV_PCM_IOCTL_HWSYNC _IO('A', 0x22) +#define SNDRV_PCM_IOCTL_SYNC_PTR _IOWR('A', 0x23, struct snd_pcm_sync_ptr) +#define SNDRV_PCM_IOCTL_CHANNEL_INFO _IOR('A', 0x32, struct snd_pcm_channel_info) +#define SNDRV_PCM_IOCTL_PREPARE _IO('A', 0x40) +#define SNDRV_PCM_IOCTL_RESET _IO('A', 0x41) +#define SNDRV_PCM_IOCTL_START _IO('A', 0x42) +#define SNDRV_PCM_IOCTL_DROP _IO('A', 0x43) +#define SNDRV_PCM_IOCTL_DRAIN _IO('A', 0x44) +#define SNDRV_PCM_IOCTL_PAUSE _IOW('A', 0x45, int) +#define SNDRV_PCM_IOCTL_REWIND _IOW('A', 0x46, snd_pcm_uframes_t) +#define SNDRV_PCM_IOCTL_RESUME _IO('A', 0x47) +#define SNDRV_PCM_IOCTL_XRUN _IO('A', 0x48) +#define SNDRV_PCM_IOCTL_FORWARD _IOW('A', 0x49, snd_pcm_uframes_t) +#define SNDRV_PCM_IOCTL_WRITEI_FRAMES _IOW('A', 0x50, struct snd_xferi) +#define SNDRV_PCM_IOCTL_READI_FRAMES _IOR('A', 0x51, struct snd_xferi) +#define SNDRV_PCM_IOCTL_WRITEN_FRAMES _IOW('A', 0x52, struct snd_xfern) +#define SNDRV_PCM_IOCTL_READN_FRAMES _IOR('A', 0x53, struct snd_xfern) +#define SNDRV_PCM_IOCTL_LINK _IOW('A', 0x60, int) +#define SNDRV_PCM_IOCTL_UNLINK _IO('A', 0x61) + +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 0) + +enum { + SNDRV_RAWMIDI_STREAM_OUTPUT = 0, + SNDRV_RAWMIDI_STREAM_INPUT, + SNDRV_RAWMIDI_STREAM_LAST = SNDRV_RAWMIDI_STREAM_INPUT, +}; + +#define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 +#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 +#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 + +struct snd_rawmidi_info { + unsigned int device; + unsigned int subdevice; + int stream; + int card; + unsigned int flags; + unsigned char id[64]; + unsigned char name[80]; + unsigned char subname[32]; + unsigned int subdevices_count; + unsigned int subdevices_avail; + unsigned char reserved[64]; +}; + +struct snd_rawmidi_params { + int stream; + size_t buffer_size; + size_t avail_min; + unsigned int no_active_sensing: 1; + unsigned char reserved[16]; +}; + +struct snd_rawmidi_status { + int stream; + struct timespec tstamp; + size_t avail; + size_t xruns; + unsigned char reserved[16]; +}; + +#define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) +#define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) +#define SNDRV_RAWMIDI_IOCTL_PARAMS _IOWR('W', 0x10, struct snd_rawmidi_params) +#define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status) +#define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int) +#define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int) + +#define SNDRV_TIMER_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) + +enum { + SNDRV_TIMER_CLASS_NONE = -1, + SNDRV_TIMER_CLASS_SLAVE = 0, + SNDRV_TIMER_CLASS_GLOBAL, + SNDRV_TIMER_CLASS_CARD, + SNDRV_TIMER_CLASS_PCM, + SNDRV_TIMER_CLASS_LAST = SNDRV_TIMER_CLASS_PCM, +}; + +enum { + SNDRV_TIMER_SCLASS_NONE = 0, + SNDRV_TIMER_SCLASS_APPLICATION, + SNDRV_TIMER_SCLASS_SEQUENCER, + SNDRV_TIMER_SCLASS_OSS_SEQUENCER, + SNDRV_TIMER_SCLASS_LAST = SNDRV_TIMER_SCLASS_OSS_SEQUENCER, +}; + +#define SNDRV_TIMER_GLOBAL_SYSTEM 0 +#define SNDRV_TIMER_GLOBAL_RTC 1 +#define SNDRV_TIMER_GLOBAL_HPET 2 +#define SNDRV_TIMER_GLOBAL_HRTIMER 3 + +#define SNDRV_TIMER_FLG_SLAVE (1<<0) + +struct snd_timer_id { + int dev_class; + int dev_sclass; + int card; + int device; + int subdevice; +}; + +struct snd_timer_ginfo { + struct snd_timer_id tid; + unsigned int flags; + int card; + unsigned char id[64]; + unsigned char name[80]; + unsigned long reserved0; + unsigned long resolution; + unsigned long resolution_min; + unsigned long resolution_max; + unsigned int clients; + unsigned char reserved[32]; +}; + +struct snd_timer_gparams { + struct snd_timer_id tid; + unsigned long period_num; + unsigned long period_den; + unsigned char reserved[32]; +}; + +struct snd_timer_gstatus { + struct snd_timer_id tid; + unsigned long resolution; + unsigned long resolution_num; + unsigned long resolution_den; + unsigned char reserved[32]; +}; + +struct snd_timer_select { + struct snd_timer_id id; + unsigned char reserved[32]; +}; + +struct snd_timer_info { + unsigned int flags; + int card; + unsigned char id[64]; + unsigned char name[80]; + unsigned long reserved0; + unsigned long resolution; + unsigned char reserved[64]; +}; + +#define SNDRV_TIMER_PSFLG_AUTO (1<<0) +#define SNDRV_TIMER_PSFLG_EXCLUSIVE (1<<1) +#define SNDRV_TIMER_PSFLG_EARLY_EVENT (1<<2) + +struct snd_timer_params { + unsigned int flags; + unsigned int ticks; + unsigned int queue_size; + unsigned int reserved0; + unsigned int filter; + unsigned char reserved[60]; +}; + +struct snd_timer_status { + struct timespec tstamp; + unsigned int resolution; + unsigned int lost; + unsigned int overrun; + unsigned int queue; + unsigned char reserved[64]; +}; + +#define SNDRV_TIMER_IOCTL_PVERSION _IOR('T', 0x00, int) +#define SNDRV_TIMER_IOCTL_NEXT_DEVICE _IOWR('T', 0x01, struct snd_timer_id) +#define SNDRV_TIMER_IOCTL_TREAD _IOW('T', 0x02, int) +#define SNDRV_TIMER_IOCTL_GINFO _IOWR('T', 0x03, struct snd_timer_ginfo) +#define SNDRV_TIMER_IOCTL_GPARAMS _IOW('T', 0x04, struct snd_timer_gparams) +#define SNDRV_TIMER_IOCTL_GSTATUS _IOWR('T', 0x05, struct snd_timer_gstatus) +#define SNDRV_TIMER_IOCTL_SELECT _IOW('T', 0x10, struct snd_timer_select) +#define SNDRV_TIMER_IOCTL_INFO _IOR('T', 0x11, struct snd_timer_info) +#define SNDRV_TIMER_IOCTL_PARAMS _IOW('T', 0x12, struct snd_timer_params) +#define SNDRV_TIMER_IOCTL_STATUS _IOR('T', 0x14, struct snd_timer_status) + +#define SNDRV_TIMER_IOCTL_START _IO('T', 0xa0) +#define SNDRV_TIMER_IOCTL_STOP _IO('T', 0xa1) +#define SNDRV_TIMER_IOCTL_CONTINUE _IO('T', 0xa2) +#define SNDRV_TIMER_IOCTL_PAUSE _IO('T', 0xa3) + +struct snd_timer_read { + unsigned int resolution; + unsigned int ticks; +}; + +enum { + SNDRV_TIMER_EVENT_RESOLUTION = 0, + SNDRV_TIMER_EVENT_TICK, + SNDRV_TIMER_EVENT_START, + SNDRV_TIMER_EVENT_STOP, + SNDRV_TIMER_EVENT_CONTINUE, + SNDRV_TIMER_EVENT_PAUSE, + SNDRV_TIMER_EVENT_EARLY, + SNDRV_TIMER_EVENT_SUSPEND, + SNDRV_TIMER_EVENT_RESUME, + + SNDRV_TIMER_EVENT_MSTART = SNDRV_TIMER_EVENT_START + 10, + SNDRV_TIMER_EVENT_MSTOP = SNDRV_TIMER_EVENT_STOP + 10, + SNDRV_TIMER_EVENT_MCONTINUE = SNDRV_TIMER_EVENT_CONTINUE + 10, + SNDRV_TIMER_EVENT_MPAUSE = SNDRV_TIMER_EVENT_PAUSE + 10, + SNDRV_TIMER_EVENT_MSUSPEND = SNDRV_TIMER_EVENT_SUSPEND + 10, + SNDRV_TIMER_EVENT_MRESUME = SNDRV_TIMER_EVENT_RESUME + 10, +}; + +struct snd_timer_tread { + int event; + struct timespec tstamp; + unsigned int val; +}; + +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) + +struct snd_ctl_card_info { + int card; + int pad; + unsigned char id[16]; + unsigned char driver[16]; + unsigned char name[32]; + unsigned char longname[80]; + unsigned char reserved_[16]; + unsigned char mixername[80]; + unsigned char components[128]; +}; + +typedef int __bitwise snd_ctl_elem_type_t; +#define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0) +#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1) +#define SNDRV_CTL_ELEM_TYPE_INTEGER ((__force snd_ctl_elem_type_t) 2) +#define SNDRV_CTL_ELEM_TYPE_ENUMERATED ((__force snd_ctl_elem_type_t) 3) +#define SNDRV_CTL_ELEM_TYPE_BYTES ((__force snd_ctl_elem_type_t) 4) +#define SNDRV_CTL_ELEM_TYPE_IEC958 ((__force snd_ctl_elem_type_t) 5) +#define SNDRV_CTL_ELEM_TYPE_INTEGER64 ((__force snd_ctl_elem_type_t) 6) +#define SNDRV_CTL_ELEM_TYPE_LAST SNDRV_CTL_ELEM_TYPE_INTEGER64 + +typedef int __bitwise snd_ctl_elem_iface_t; +#define SNDRV_CTL_ELEM_IFACE_CARD ((__force snd_ctl_elem_iface_t) 0) +#define SNDRV_CTL_ELEM_IFACE_HWDEP ((__force snd_ctl_elem_iface_t) 1) +#define SNDRV_CTL_ELEM_IFACE_MIXER ((__force snd_ctl_elem_iface_t) 2) +#define SNDRV_CTL_ELEM_IFACE_PCM ((__force snd_ctl_elem_iface_t) 3) +#define SNDRV_CTL_ELEM_IFACE_RAWMIDI ((__force snd_ctl_elem_iface_t) 4) +#define SNDRV_CTL_ELEM_IFACE_TIMER ((__force snd_ctl_elem_iface_t) 5) +#define SNDRV_CTL_ELEM_IFACE_SEQUENCER ((__force snd_ctl_elem_iface_t) 6) +#define SNDRV_CTL_ELEM_IFACE_LAST SNDRV_CTL_ELEM_IFACE_SEQUENCER + +#define SNDRV_CTL_ELEM_ACCESS_READ (1<<0) +#define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) +#define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) +#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) +#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) +#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) +#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) +#define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) +#define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) +#define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) +#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) +#define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) + +#define SNDRV_CTL_POWER_D0 0x0000 +#define SNDRV_CTL_POWER_D1 0x0100 +#define SNDRV_CTL_POWER_D2 0x0200 +#define SNDRV_CTL_POWER_D3 0x0300 +#define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) +#define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) + +struct snd_ctl_elem_id { + unsigned int numid; + snd_ctl_elem_iface_t iface; + unsigned int device; + unsigned int subdevice; + unsigned char name[44]; + unsigned int index; +}; + +struct snd_ctl_elem_list { + unsigned int offset; + unsigned int space; + unsigned int used; + unsigned int count; + struct snd_ctl_elem_id __user *pids; + unsigned char reserved[50]; +}; + +struct snd_ctl_elem_info { + struct snd_ctl_elem_id id; + snd_ctl_elem_type_t type; + unsigned int access; + unsigned int count; + __kernel_pid_t owner; + union { + struct { + long min; + long max; + long step; + } integer; + struct { + long long min; + long long max; + long long step; + } integer64; + struct { + unsigned int items; + unsigned int item; + char name[64]; + } enumerated; + unsigned char reserved[128]; + } value; + union { + unsigned short d[4]; + unsigned short *d_ptr; + } dimen; + unsigned char reserved[64-4*sizeof(unsigned short)]; +}; + +struct snd_ctl_elem_value { + struct snd_ctl_elem_id id; + unsigned int indirect: 1; + union { + union { + long value[128]; + long *value_ptr; + } integer; + union { + long long value[64]; + long long *value_ptr; + } integer64; + union { + unsigned int item[128]; + unsigned int *item_ptr; + } enumerated; + union { + unsigned char data[512]; + unsigned char *data_ptr; + } bytes; + struct snd_aes_iec958 iec958; + } value; + struct timespec tstamp; + unsigned char reserved[128-sizeof(struct timespec)]; +}; + +struct snd_ctl_tlv { + unsigned int numid; + unsigned int length; + unsigned int tlv[0]; +}; + +#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int) +#define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info) +#define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list) +#define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info) +#define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value) +#define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value) +#define SNDRV_CTL_IOCTL_ELEM_LOCK _IOW('U', 0x14, struct snd_ctl_elem_id) +#define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id) +#define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int) +#define SNDRV_CTL_IOCTL_ELEM_ADD _IOWR('U', 0x17, struct snd_ctl_elem_info) +#define SNDRV_CTL_IOCTL_ELEM_REPLACE _IOWR('U', 0x18, struct snd_ctl_elem_info) +#define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id) +#define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv) +#define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv) +#define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv) +#define SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE _IOWR('U', 0x20, int) +#define SNDRV_CTL_IOCTL_HWDEP_INFO _IOR('U', 0x21, struct snd_hwdep_info) +#define SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE _IOR('U', 0x30, int) +#define SNDRV_CTL_IOCTL_PCM_INFO _IOWR('U', 0x31, struct snd_pcm_info) +#define SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE _IOW('U', 0x32, int) +#define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int) +#define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info) +#define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int) +#define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int) +#define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int) + +enum sndrv_ctl_event_type { + SNDRV_CTL_EVENT_ELEM = 0, + SNDRV_CTL_EVENT_LAST = SNDRV_CTL_EVENT_ELEM, +}; + +#define SNDRV_CTL_EVENT_MASK_VALUE (1<<0) +#define SNDRV_CTL_EVENT_MASK_INFO (1<<1) +#define SNDRV_CTL_EVENT_MASK_ADD (1<<2) +#define SNDRV_CTL_EVENT_MASK_TLV (1<<3) +#define SNDRV_CTL_EVENT_MASK_REMOVE (~0U) + +struct snd_ctl_event { + int type; + union { + struct { + unsigned int mask; + struct snd_ctl_elem_id id; + } elem; + unsigned char data8[60]; + } data; +}; + +#define SNDRV_CTL_NAME_NONE "" +#define SNDRV_CTL_NAME_PLAYBACK "Playback " +#define SNDRV_CTL_NAME_CAPTURE "Capture " + +#define SNDRV_CTL_NAME_IEC958_NONE "" +#define SNDRV_CTL_NAME_IEC958_SWITCH "Switch" +#define SNDRV_CTL_NAME_IEC958_VOLUME "Volume" +#define SNDRV_CTL_NAME_IEC958_DEFAULT "Default" +#define SNDRV_CTL_NAME_IEC958_MASK "Mask" +#define SNDRV_CTL_NAME_IEC958_CON_MASK "Con Mask" +#define SNDRV_CTL_NAME_IEC958_PRO_MASK "Pro Mask" +#define SNDRV_CTL_NAME_IEC958_PCM_STREAM "PCM Stream" +#define SNDRV_CTL_NAME_IEC958(expl,direction,what) "IEC958 " expl SNDRV_CTL_NAME_##direction SNDRV_CTL_NAME_IEC958_##what + +#endif + diff --git a/libaudio/secril-client.h b/libaudio/secril-client.h old mode 100755 new mode 100644 index 2473ae7..7bbaa03 --- a/libaudio/secril-client.h +++ b/libaudio/secril-client.h @@ -138,7 +138,8 @@ typedef enum _AudioPath { SOUND_AUDIO_PATH_HEADSET, SOUND_AUDIO_PATH_SPEAKER, SOUND_AUDIO_PATH_BLUETOOTH, - SOUND_AUDIO_PATH_BLUETOOTH_NO_NR + SOUND_AUDIO_PATH_BLUETOOTH_NO_NR, + SOUND_AUDIO_PATH_HEADPHONE } AudioPath; /** -- cgit v1.1