/* * Copyright (C) 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. */ #include #define LOG_NDEBUG 0 #define LOG_TAG "A2dpAudioInterface" #include #include #include "A2dpAudioInterface.h" #include "audio/liba2dp.h" namespace android { // ---------------------------------------------------------------------------- //AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface() //{ // AudioHardwareInterface* hw = 0; // // hw = AudioHardwareInterface::create(); // LOGD("new A2dpAudioInterface(hw: %p)", hw); // hw = new A2dpAudioInterface(hw); // return hw; //} A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) : mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true) { } A2dpAudioInterface::~A2dpAudioInterface() { closeOutputStream((AudioStreamOut *)mOutput); delete mHardwareInterface; } status_t A2dpAudioInterface::initCheck() { if (mHardwareInterface == 0) return NO_INIT; return mHardwareInterface->initCheck(); } AudioStreamOut* A2dpAudioInterface::openOutputStream( uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices); return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status); } status_t err = 0; // only one output stream allowed if (mOutput) { if (status) *status = -1; return NULL; } // create new output stream A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) { mOutput = out; mOutput->setBluetoothEnabled(mBluetoothEnabled); } else { delete out; } if (status) *status = err; return mOutput; } void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) { if (mOutput == 0 || mOutput != out) { mHardwareInterface->closeOutputStream(out); } else { delete mOutput; mOutput = 0; } } AudioStreamIn* A2dpAudioInterface::openInputStream( uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) { return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); } void A2dpAudioInterface::closeInputStream(AudioStreamIn* in) { return mHardwareInterface->closeInputStream(in); } status_t A2dpAudioInterface::setMode(int mode) { return mHardwareInterface->setMode(mode); } status_t A2dpAudioInterface::setMicMute(bool state) { return mHardwareInterface->setMicMute(state); } status_t A2dpAudioInterface::getMicMute(bool* state) { return mHardwareInterface->getMicMute(state); } status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs) { AudioParameter param = AudioParameter(keyValuePairs); String8 value; String8 key; status_t status = NO_ERROR; LOGV("setParameters() %s", keyValuePairs.string()); key = "bluetooth_enabled"; if (param.get(key, value) == NO_ERROR) { mBluetoothEnabled = (value == "true"); if (mOutput) { mOutput->setBluetoothEnabled(mBluetoothEnabled); } param.remove(key); } if (param.size()) { status_t hwStatus = mHardwareInterface->setParameters(param.toString()); if (status == NO_ERROR) { status = hwStatus; } } return status; } String8 A2dpAudioInterface::getParameters(const String8& keys) { AudioParameter param = AudioParameter(keys); AudioParameter a2dpParam = AudioParameter(); String8 value; String8 key; key = "bluetooth_enabled"; if (param.get(key, value) == NO_ERROR) { value = mBluetoothEnabled ? "true" : "false"; a2dpParam.add(key, value); param.remove(key); } String8 keyValuePairs = a2dpParam.toString(); if (param.size()) { keyValuePairs += ";"; keyValuePairs += mHardwareInterface->getParameters(param.toString()); } LOGV("getParameters() %s", keyValuePairs.string()); return keyValuePairs; } size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount); } status_t A2dpAudioInterface::setVoiceVolume(float v) { return mHardwareInterface->setVoiceVolume(v); } status_t A2dpAudioInterface::setMasterVolume(float v) { return mHardwareInterface->setMasterVolume(v); } status_t A2dpAudioInterface::dump(int fd, const Vector& args) { return mHardwareInterface->dumpState(fd, args); } // ---------------------------------------------------------------------------- A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL), // assume BT enabled to start, this is safe because its only the // enabled->disabled transition we are worried about mBluetoothEnabled(true), mDevice(0), mClosing(false) { // use any address by default strcpy(mA2dpAddress, "00:00:00:00:00:00"); init(); } status_t A2dpAudioInterface::A2dpAudioStreamOut::set( uint32_t device, 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; LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate); // 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; mDevice = device; return NO_ERROR; } A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() { LOGV("A2dpAudioStreamOut destructor"); standby(); close(); LOGV("A2dpAudioStreamOut destructor returning from close()"); } ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) { Mutex::Autolock lock(mLock); size_t remaining = bytes; status_t status = -1; if (!mBluetoothEnabled || mClosing) { LOGW("A2dpAudioStreamOut::write(), but bluetooth disabled"); goto Error; } status = init(); if (status < 0) goto Error; while (remaining > 0) { status = a2dp_write(mData, buffer, remaining); if (status <= 0) { LOGE("a2dp_write failed err: %d\n", status); goto Error; } remaining -= status; buffer = ((char *)buffer) + status; } mStandby = false; return bytes; Error: // Simulate audio output timing in case of error usleep(bytes * 1000000 / frameSize() / sampleRate()); return status; } status_t A2dpAudioInterface::A2dpAudioStreamOut::init() { if (!mData) { status_t status = a2dp_init(44100, 2, &mData); if (status < 0) { LOGE("a2dp_init failed err: %d\n", status); mData = NULL; return status; } a2dp_set_sink(mData, mA2dpAddress); } return 0; } status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() { int result = 0; if (mClosing) { LOGV("Ignore standby, closing"); return result; } Mutex::Autolock lock(mLock); if (!mStandby) { result = a2dp_stop(mData); if (result == 0) mStandby = true; } return result; } status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs) { AudioParameter param = AudioParameter(keyValuePairs); String8 value; String8 key = String8("a2dp_sink_address"); status_t status = NO_ERROR; int device; LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string()); if (param.get(key, value) == NO_ERROR) { if (value.length() != strlen("00:00:00:00:00:00")) { status = BAD_VALUE; } else { setAddress(value.string()); } param.remove(key); } key = String8("closing"); if (param.get(key, value) == NO_ERROR) { mClosing = (value == "true"); param.remove(key); } key = AudioParameter::keyRouting; if (param.getInt(key, device) == NO_ERROR) { if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) { mDevice = device; status = NO_ERROR; } else { status = BAD_VALUE; } param.remove(key); } if (param.size()) { status = BAD_VALUE; } return status; } String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys) { AudioParameter param = AudioParameter(keys); String8 value; String8 key = String8("a2dp_sink_address"); if (param.get(key, value) == NO_ERROR) { value = mA2dpAddress; param.add(key, value); } key = AudioParameter::keyRouting; if (param.get(key, value) == NO_ERROR) { param.addInt(key, (int)mDevice); } LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string()); return param.toString(); } status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) { Mutex::Autolock lock(mLock); if (strlen(address) != strlen("00:00:00:00:00:00")) return -EINVAL; strcpy(mA2dpAddress, address); if (mData) a2dp_set_sink(mData, mA2dpAddress); return NO_ERROR; } status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enabled) { LOGD("setBluetoothEnabled %d", enabled); Mutex::Autolock lock(mLock); mBluetoothEnabled = enabled; if (!enabled) { return close_l(); } return NO_ERROR; } status_t A2dpAudioInterface::A2dpAudioStreamOut::close() { Mutex::Autolock lock(mLock); LOGV("A2dpAudioStreamOut::close() calling close_l()"); return close_l(); } status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l() { if (mData) { LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)"); a2dp_cleanup(mData); mData = NULL; } return NO_ERROR; } status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector& args) { return NO_ERROR; } }; // namespace android