From a35c8f521c9cbb64f3d32df5ded7eab2db2727ee Mon Sep 17 00:00:00 2001 From: Mingming Yin Date: Thu, 27 Dec 2012 17:43:05 -0800 Subject: qcom-fm: audio: add support for FM feature Change-Id: Idd5c7a0364710d54809ef5d4c7b2404b22dc4cf6 Conflicts: include/media/IAudioFlinger.h media/libmediaplayerservice/StagefrightRecorder.cpp media/libstagefright/Android.mk --- include/media/AudioSystem.h | 9 +- include/media/IAudioFlinger.h | 7 +- include/media/stagefright/FMA2DPWriter.h | 84 ++++++ media/libmedia/AudioSystem.cpp | 13 + media/libmedia/IAudioFlinger.cpp | 25 +- .../libmediaplayerservice/StagefrightRecorder.cpp | 29 +- media/libmediaplayerservice/StagefrightRecorder.h | 8 +- media/libstagefright/Android.mk | 5 + media/libstagefright/FMA2DPWriter.cpp | 323 +++++++++++++++++++++ services/audioflinger/AudioFlinger.cpp | 26 +- services/audioflinger/AudioFlinger.h | 10 +- services/audioflinger/AudioPolicyService.cpp | 69 +++++ services/audioflinger/AudioPolicyService.h | 22 +- 13 files changed, 618 insertions(+), 12 deletions(-) create mode 100644 include/media/stagefright/FMA2DPWriter.h create mode 100644 media/libstagefright/FMA2DPWriter.cpp diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index d8c57d3..31b1b08 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -1,10 +1,10 @@ /* - * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * * Not a Contribution, Apache license notifications and license are retained * for attribution purposes only. * - * 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 @@ -120,6 +120,9 @@ public: audio_channel_mask_t channelMask, size_t* buffSize); static status_t setVoiceVolume(float volume); +#ifdef QCOM_FM_ENABLED + static status_t setFmVolume(float volume); +#endif // return the number of audio frames written by AudioFlinger to audio HAL and // audio dsp to DAC since the output on which the specified stream is playing diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 0700a68..396f87b 100755 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -1,10 +1,10 @@ /* + * Copyright (C) 2007 The Android Open Source Project * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * * Not a Contribution, Apache license notifications and license are retained * for attribution purposes only. * - * Copyright (C) 2007 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 @@ -219,6 +219,9 @@ public: // that looks on primary device for a stream with fast flag, primary flag, or first one. virtual int32_t getPrimaryOutputSamplingRate() = 0; virtual int32_t getPrimaryOutputFrameCount() = 0; +#ifdef QCOM_FM_ENABLED + virtual status_t setFmVolume(float volume) = 0; +#endif }; diff --git a/include/media/stagefright/FMA2DPWriter.h b/include/media/stagefright/FMA2DPWriter.h new file mode 100644 index 0000000..f1c1694 --- /dev/null +++ b/include/media/stagefright/FMA2DPWriter.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only + * + * 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 FM_A2DP_WRITER_H_ + +#define FM_A2DP_WRITER_H_ + +#include + +#include +#include +#include +#include +#include +#include + +namespace android { + +struct MediaSource; +struct MetaData; + +struct audioBufferstruct { + public: + audioBufferstruct (void *buff, size_t bufflen) + :audioBuffer(buff), bufferlen(bufflen){} + + void *audioBuffer; + size_t bufferlen; + }; + +struct FMA2DPWriter : public MediaWriter { + FMA2DPWriter(); + + status_t initCheck() const; + virtual status_t addSource(const sp &source); + virtual bool reachedEOS(); + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual status_t pause(); + virtual status_t allocateBufferPool(); + +protected: + virtual ~FMA2DPWriter(); + +private: + List mFreeQ,mDataQ; + Mutex mFreeQLock,mDataQLock; + sem_t mReaderThreadWakeupsem,mWriterThreadWakeupsem; + pthread_t mReaderThread,mWriterThread; + bool mStarted; + volatile bool mDone; + int32_t mAudioChannels; + int32_t mSampleRate; + audio_format_t mAudioFormat; + audio_source_t mAudioSource; + size_t mBufferSize; + static void *ReaderThreadWrapper(void *); + static void *WriterThreadWrapper(void *); + status_t readerthread(); + status_t writerthread(); + FMA2DPWriter(const FMA2DPWriter &); + FMA2DPWriter &operator=(const FMA2DPWriter &); +}; + +} // namespace android + +#endif // FM_A2DP_WRITER_H_ diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index fd8c320..aaf2ddd 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -1,5 +1,9 @@ /* * Copyright (C) 2006-2007 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -411,6 +415,15 @@ void AudioSystem::releaseAudioSessionId(int audioSession) { } } +#ifdef QCOM_FM_ENABLED +status_t AudioSystem::setFmVolume(float value) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setFmVolume(value); +} +#endif + // --------------------------------------------------------------------------- void AudioSystem::AudioFlingerClient::binderDied(const wp& who) { diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index cc6a75c..9d5691f 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -1,7 +1,8 @@ /* ** ** Copyright 2007, The Android Open Source Project -** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +** ** Not a Contribution, Apache license notifications and license are retained ** for attribution purposes only. ** @@ -73,6 +74,9 @@ enum { GET_EFFECT_DESCRIPTOR, CREATE_EFFECT, MOVE_EFFECTS, +#ifdef QCOM_FM_ENABLED + SET_FM_VOLUME, +#endif LOAD_HW_MODULE, GET_PRIMARY_OUTPUT_SAMPLING_RATE, GET_PRIMARY_OUTPUT_FRAME_COUNT, @@ -730,6 +734,17 @@ public: return reply.readInt32(); } +#ifdef QCOM_FM_ENABLED + virtual status_t setFmVolume(float volume) + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeFloat(volume); + remote()->transact(SET_FM_VOLUME, data, &reply); + return reply.readInt32(); + } +#endif + virtual audio_module_handle_t loadHwModule(const char *name) { Parcel data, reply; @@ -1128,6 +1143,14 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32(moveEffects(session, srcOutput, dstOutput)); return NO_ERROR; } break; +#ifdef QCOM_FM_ENABLED + case SET_FM_VOLUME: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + float volume = data.readFloat(); + reply->writeInt32( setFmVolume(volume) ); + return NO_ERROR; + } break; +#endif case LOAD_HW_MODULE: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32(loadHwModule(data.readCString())); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 335dd43..94886e8 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1,6 +1,9 @@ /* * Copyright (C) 2009 The Android Open Source Project - * Copyright (c) 2010 - 2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +37,9 @@ #include #include #endif +#ifdef QCOM_FM_ENABLED +#include +#endif #include #include #include @@ -771,6 +777,10 @@ status_t StagefrightRecorder::start() { } status_t status = OK; +#ifdef QCOM_FM_ENABLED + if(AUDIO_SOURCE_FM_RX_A2DP == mAudioSource) + return startFMA2DPWriter(); +#endif switch (mOutputFormat) { case OUTPUT_FORMAT_DEFAULT: @@ -1036,6 +1046,23 @@ status_t StagefrightRecorder::startRawAudioRecording() { return OK; } +#ifdef QCOM_FM_ENABLED +status_t StagefrightRecorder::startFMA2DPWriter() { + /* FM soc outputs at 48k */ + mSampleRate = 48000; + mAudioChannels = 2; + + sp meta = new MetaData; + meta->setInt32(kKeyChannelCount, mAudioChannels); + meta->setInt32(kKeySampleRate, mSampleRate); + + mWriter = new FMA2DPWriter(); + mWriter->setListener(mListener); + mWriter->start(meta.get()); + return OK; +} +#endif + status_t StagefrightRecorder::startRTPRecording() { CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP); diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 3f0b821..fd58115 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -1,6 +1,9 @@ /* * Copyright (C) 2009 The Android Open Source Project - * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,6 +135,9 @@ private: sp *meta); status_t startMPEG4Recording(); status_t startAMRRecording(); +#ifdef QCOM_FM_ENABLED + status_t startFMA2DPWriter(); +#endif status_t startAACRecording(); #ifdef QCOM_HARDWARE status_t startWAVERecording(); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index e2209ff4..b394d90 100755 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -57,6 +57,11 @@ LOCAL_SRC_FILES:= \ mp4/FragmentedMP4Parser.cpp \ mp4/TrackFragment.cpp \ +ifeq ($(BOARD_HAVE_QCOM_FM),true) +LOCAL_SRC_FILES+= \ + FMA2DPWriter.cpp +endif + LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/include/media/stagefright/timedtext \ $(TOP)/frameworks/native/include/media/hardware \ diff --git a/media/libstagefright/FMA2DPWriter.cpp b/media/libstagefright/FMA2DPWriter.cpp new file mode 100644 index 0000000..1bcec17 --- /dev/null +++ b/media/libstagefright/FMA2DPWriter.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only + * + * 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_NDEBUG 0 +#define LOG_TAG "FMA2DPWriter" +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +namespace android { + +#define BUFFER_POOL_SIZE 5 +static int kMaxBufferSize = 2048; + +FMA2DPWriter::FMA2DPWriter() + :mStarted(false), + mAudioChannels(0), + mSampleRate(0), + mAudioFormat(AUDIO_FORMAT_PCM_16_BIT), + mAudioSource(AUDIO_SOURCE_FM_RX_A2DP), + mBufferSize(0){ + sem_init(&mReaderThreadWakeupsem,0,0); + sem_init(&mWriterThreadWakeupsem,0,0); +} + + + +FMA2DPWriter::~FMA2DPWriter() { + if (mStarted) { + stop(); + } + sem_destroy(&mReaderThreadWakeupsem); + sem_destroy(&mWriterThreadWakeupsem); +} + +status_t FMA2DPWriter::initCheck() const { +// API not need for FMA2DPWriter + return OK; +} + + +status_t FMA2DPWriter::addSource(const sp &source) { +// API not need for FMA2DPWriter + return OK; +} + +status_t FMA2DPWriter::allocateBufferPool() +{ + Mutex::Autolock lock(mFreeQLock); + + for (int i = 0; i < BUFFER_POOL_SIZE; ++i) { + int *buffer = (int*)malloc(mBufferSize); + if(buffer){ + audioBufferstruct audioBuffer(buffer,mBufferSize); + mFreeQ.push_back(audioBuffer); + } + else{ + ALOGE("fatal:failed to alloate buffer pool"); + return NO_INIT; + } + } + return OK; +} + +status_t FMA2DPWriter::start(MetaData *params) { + + if (mStarted) { + // Already started, does nothing + return OK; + } + + if(!mStarted){ + if(!params){ + ALOGE("fatal:params cannot be null"); + return NO_INIT; + } + CHECK( params->findInt32( kKeyChannelCount, &mAudioChannels ) ); + CHECK(mAudioChannels == 1 || mAudioChannels == 2); + CHECK( params->findInt32( kKeySampleRate, &mSampleRate ) ); + + if ( NO_ERROR != AudioSystem::getInputBufferSize( + mSampleRate, mAudioFormat, mAudioChannels, &mBufferSize) ){ + mBufferSize = kMaxBufferSize ; + } + ALOGV("mBufferSize = %d", mBufferSize); + } + + status_t err = allocateBufferPool(); + + if(err != OK) + return err; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + mDone = false; + + pthread_create(&mReaderThread, &attr, ReaderThreadWrapper, this); + pthread_create(&mWriterThread, &attr, WriterThreadWrapper, this); + + pthread_attr_destroy(&attr); + + + mStarted = true; + + return OK; +} + +status_t FMA2DPWriter::pause() { +// API not need for FMA2DPWriter + return OK; +} + +status_t FMA2DPWriter::stop() { + if (!mStarted) { + return OK; + } + + mDone = true; + + void *dummy; + pthread_join(mReaderThread, &dummy); + pthread_join(mWriterThread, &dummy); + + for ( List::iterator it = mDataQ.begin(); + it != mDataQ.end(); ++it){ + free(it->audioBuffer); + } + for ( List::iterator it = mFreeQ.begin(); + it != mFreeQ.end(); ++it){ + free(it->audioBuffer); + } + mStarted = false; + + return OK; +} + +void *FMA2DPWriter::ReaderThreadWrapper(void *me) { + return (void *) static_cast(me)->readerthread(); +} + +void *FMA2DPWriter::WriterThreadWrapper(void *me) { + return (void *) static_cast(me)->writerthread(); +} + +status_t FMA2DPWriter::readerthread() { + status_t err = OK; + int framecount =((4*mBufferSize)/mAudioChannels)/sizeof(int16_t); + //sizeof(int16_t) is frame size for PCM stream + int inChannel = + (mAudioChannels == 2) ? AUDIO_CHANNEL_IN_STEREO : + AUDIO_CHANNEL_IN_MONO; + + prctl(PR_SET_NAME, (unsigned long)"FMA2DPReaderThread", 0, 0, 0); + + AudioRecord* record = new AudioRecord( + mAudioSource, + mSampleRate, + mAudioFormat, + inChannel, + framecount); + if(!record){ + ALOGE("fatal:Not able to open audiorecord"); + return UNKNOWN_ERROR; + } + + status_t res = record->initCheck(); + if (res == NO_ERROR) + res = record->start(); + else{ + ALOGE("fatal:record init check failure"); + return UNKNOWN_ERROR; + } + + + while (!mDone) { + + mFreeQLock.lock(); + if(mFreeQ.empty()){ + mFreeQLock.unlock(); + ALOGV("FreeQ empty"); + sem_wait(&mReaderThreadWakeupsem); + ALOGV("FreeQ filled up"); + continue; + } + List::iterator it = mFreeQ.begin(); + audioBufferstruct buff ( it->audioBuffer,it->bufferlen); + mFreeQ.erase(it); + mFreeQLock.unlock(); + + buff.bufferlen = record->read(buff.audioBuffer, mBufferSize); + ALOGV("read %d bytes", buff.bufferlen); + if (buff.bufferlen <= 0){ + ALOGE("error in reading from audiorecord..bailing out."); + this ->notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, + ERROR_MALFORMED); + err = INVALID_OPERATION ; + break; + } + + mDataQLock.lock(); + if(mDataQ.empty()){ + ALOGV("waking up reader"); + sem_post(&mWriterThreadWakeupsem); + } + mDataQ.push_back(buff); + mDataQLock.unlock(); + } + record->stop(); + delete record; + + return err; +} + + +status_t FMA2DPWriter::writerthread(){ + status_t err = OK; + int framecount =(16*mBufferSize)/sizeof(int16_t); + //sizeof(int16_t) is frame size for PCM stream + int outChannel = (mAudioChannels== 2) ? AUDIO_CHANNEL_OUT_STEREO : + AUDIO_CHANNEL_OUT_MONO; + + prctl(PR_SET_NAME, (unsigned long)"FMA2DPWriterThread", 0, 0, 0); + + AudioTrack *audioTrack= new AudioTrack( + AUDIO_STREAM_FM, + mSampleRate, + mAudioFormat, + outChannel, + framecount, 0); + + if(!audioTrack){ + ALOGE("fatal:Not able to open audiotrack"); + return UNKNOWN_ERROR; + } + status_t res = audioTrack->initCheck(); + if (res == NO_ERROR) { + audioTrack->setVolume(1, 1); + audioTrack->start(); + } + else{ + ALOGE("fatal:audiotrack init check failure"); + return UNKNOWN_ERROR; + } + + + while (!mDone) { + + mDataQLock.lock(); + if(mDataQ.empty()){ + mDataQLock.unlock(); + ALOGV("dataQ empty"); + sem_wait(&mWriterThreadWakeupsem); + ALOGV("dataQ filled up"); + continue; + } + List::iterator it = mDataQ.begin(); + audioBufferstruct buff ( it->audioBuffer,it->bufferlen); + mDataQ.erase(it); + mDataQLock.unlock(); + + size_t retval = audioTrack->write(buff.audioBuffer, buff.bufferlen); + if(!retval){ + ALOGE("audio track write failure..bailing out"); + this ->notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, + ERROR_MALFORMED); + err = INVALID_OPERATION ; + break; + } + ALOGV("wrote %d bytes", buff.bufferlen); + + mFreeQLock.lock(); + if(mFreeQ.empty()){ + ALOGV("WAKING UP READER"); + sem_post(&mReaderThreadWakeupsem); + } + mFreeQ.push_back(buff); + mFreeQLock.unlock(); + } + audioTrack->stop(); + delete audioTrack; + + return err; +} + +bool FMA2DPWriter::reachedEOS() { +// API not need for FMA2DPWriter + return OK; +} + + +} // namespace android diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 5975b62..88da88c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1,9 +1,8 @@ /* ** ** Copyright 2007, The Android Open Source Project -** Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +** Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. ** -** Copyright (c) 2012, The Linux Foundation. All rights reserved. ** Not a Contribution, Apache license notifications and license are retained ** for attribution purposes only. ** @@ -1377,6 +1376,29 @@ status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrame return BAD_VALUE; } +#ifdef QCOM_FM_ENABLED +status_t AudioFlinger::setFmVolume(float value) +{ + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mHardwareLock); + audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); + mHardwareStatus = AUDIO_SET_FM_VOLUME; + ret = dev->set_fm_volume(dev, value); + mHardwareStatus = AUDIO_HW_IDLE; + + return ret; +} +#endif + void AudioFlinger::registerClient(const sp& client) { diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index cbda3fe..d6a3815 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -1,7 +1,8 @@ /* ** ** Copyright 2007, The Android Open Source Project -** Copyright (c) 2012, The Linux Foundation. All rights reserved. +** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. +** ** Not a Contribution, Apache license notifications and license are retained ** for attribution purposes only. ** @@ -228,6 +229,10 @@ public: virtual status_t moveEffects(int sessionId, audio_io_handle_t srcOutput, audio_io_handle_t dstOutput); +#ifdef QCOM_FM_ENABLED + virtual status_t setFmVolume(float volume); +#endif + virtual audio_module_handle_t loadHwModule(const char *name); virtual int32_t getPrimaryOutputSamplingRate(); @@ -2209,6 +2214,9 @@ mutable Mutex mLock; // mutex for process, commands and handl AUDIO_HW_SET_MIC_MUTE, // set_mic_mute AUDIO_HW_SET_VOICE_VOLUME, // set_voice_volume AUDIO_HW_SET_PARAMETER, // set_parameters +#ifdef QCOM_FM_ENABLED + AUDIO_SET_FM_VOLUME, +#endif AUDIO_HW_GET_INPUT_BUFFER_SIZE, // get_input_buffer_size AUDIO_HW_GET_MASTER_VOLUME, // get_master_volume AUDIO_HW_GET_PARAMETER, // get_parameters diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index 7dd46f2..388ad6c 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -1,5 +1,9 @@ /* * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -730,6 +734,18 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() } delete data; }break; +#ifdef QCOM_FM_ENABLED + case SET_FM_VOLUME: { + FmVolumeData *data = (FmVolumeData *)command->mParam; + ALOGV("AudioCommandThread() processing set fm volume volume %f", data->mVolume); + command->mStatus = AudioSystem::setFmVolume(data->mVolume); + if (command->mWaitStatus) { + command->mCond.signal(); + mWaitWorkCV.wait(mLock); + } + delete data; + }break; +#endif default: ALOGW("AudioCommandThread() unknown command %d", command->mCommand); } @@ -885,6 +901,34 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume return status; } +#ifdef QCOM_FM_ENABLED +status_t AudioPolicyService::AudioCommandThread::fmVolumeCommand(float volume, int delayMs) +{ + status_t status = NO_ERROR; + + AudioCommand *command = new AudioCommand(); + command->mCommand = SET_FM_VOLUME; + FmVolumeData *data = new FmVolumeData(); + data->mVolume = volume; + command->mParam = data; + if (delayMs == 0) { + command->mWaitStatus = true; + } else { + command->mWaitStatus = false; + } + Mutex::Autolock _l(mLock); + insertCommand_l(command, delayMs); + ALOGV("AudioCommandThread() adding set fm volume volume %f", volume); + mWaitWorkCV.signal(); + if (command->mWaitStatus) { + command->mCond.wait(mLock); + status = command->mStatus; + mWaitWorkCV.signal(); + } + return status; +} +#endif + // insertCommand_l() must be called with mLock held void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs) { @@ -950,6 +994,12 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma removedCommands.add(command2); time = command2->mTime; } break; +#ifdef QCOM_FM_ENABLED + case SET_FM_VOLUME: { + removedCommands.add(command2); + time = command2->mTime; + } break; +#endif case START_TONE: case STOP_TONE: default: @@ -1026,6 +1076,13 @@ int AudioPolicyService::setStreamVolume(audio_stream_type_t stream, output, delayMs); } +#ifdef QCOM_FM_ENABLED +status_t AudioPolicyService::setFmVolume(float volume, int delayMs) +{ + return mAudioCommandThread->fmVolumeCommand(volume, delayMs); +} +#endif + int AudioPolicyService::startTone(audio_policy_tone_t tone, audio_stream_type_t stream) { @@ -1546,6 +1603,15 @@ static int aps_set_voice_volume(void *service, float volume, int delay_ms) return audioPolicyService->setVoiceVolume(volume, delay_ms); } +#ifdef QCOM_FM_ENABLED +static int aps_set_fm_volume(void *service, float volume, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setFmVolume(volume, delay_ms); +} +#endif + }; // extern "C" namespace { @@ -1565,6 +1631,9 @@ namespace { stop_tone : aps_stop_tone, set_voice_volume : aps_set_voice_volume, move_effects : aps_move_effects, +#ifdef QCOM_FM_ENABLED + set_fm_volume : aps_set_fm_volume, +#endif load_hw_module : aps_load_hw_module, open_output_on_module : aps_open_output_on_module, open_input_on_module : aps_open_input_on_module, diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h index 63f9549..1176411 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audioflinger/AudioPolicyService.h @@ -1,5 +1,9 @@ /* * Copyright (C) 2009 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution, Apache license notifications and license are retained + * for attribution purposes only * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,6 +138,9 @@ public: virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); virtual status_t stopTone(); virtual status_t setVoiceVolume(float volume, int delayMs = 0); +#ifdef QCOM_FM_ENABLED + virtual status_t setFmVolume(float volume, int delayMs = 0); +#endif private: AudioPolicyService(); @@ -157,7 +164,10 @@ private: STOP_TONE, SET_VOLUME, SET_PARAMETERS, - SET_VOICE_VOLUME + SET_VOICE_VOLUME, +#ifdef QCOM_FM_ENABLED + SET_FM_VOLUME +#endif }; AudioCommandThread (String8 name); @@ -178,6 +188,9 @@ private: status_t parametersCommand(audio_io_handle_t ioHandle, const char *keyValuePairs, int delayMs = 0); status_t voiceVolumeCommand(float volume, int delayMs = 0); +#ifdef QCOM_FM_ENABLED + status_t fmVolumeCommand(float volume, int delayMs = 0); +#endif void insertCommand_l(AudioCommand *command, int delayMs = 0); private: @@ -222,6 +235,13 @@ private: float mVolume; }; +#ifdef QCOM_FM_ENABLED + class FmVolumeData { + public: + float mVolume; + }; +#endif + Mutex mLock; Condition mWaitWorkCV; Vector mAudioCommands; // list of pending commands -- cgit v1.1