diff options
Diffstat (limited to 'libs/audioflinger')
21 files changed, 7256 insertions, 0 deletions
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp new file mode 100644 index 0000000..eb00f8c --- /dev/null +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -0,0 +1,242 @@ +/* + * 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 <math.h> + +#define LOG_NDEBUG 0 +#define LOG_TAG "A2dpAudioInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "A2dpAudioInterface.h" +#include "audio/liba2dp.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioInterface() : + mOutput(0) +{ +} + +A2dpAudioInterface::~A2dpAudioInterface() +{ + delete mOutput; +} + +status_t A2dpAudioInterface::initCheck() +{ + return 0; +} + +AudioStreamOut* A2dpAudioInterface::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate); + Mutex::Autolock lock(mLock); + 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(format, channelCount, sampleRate)) == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + + if (status) + *status = err; + return mOutput; +} + +AudioStreamIn* A2dpAudioInterface::openInputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) +{ + if (status) + *status = -1; + return NULL; +} + +status_t A2dpAudioInterface::setMicMute(bool state) +{ + return 0; +} + +status_t A2dpAudioInterface::getMicMute(bool* state) +{ + return 0; +} + +status_t A2dpAudioInterface::setParameter(const char *key, const char *value) +{ + LOGD("setParameter %s,%s\n", key, value); + + if (!key || !value) + return -EINVAL; + + if (strcmp(key, "a2dp_sink_address") == 0) { + return mOutput->setAddress(value); + } + if (strcmp(key, "bluetooth_enabled") == 0 && + strcmp(value, "false") == 0) { + return mOutput->close(); + } + + return 0; +} + +status_t A2dpAudioInterface::setVoiceVolume(float v) +{ + return 0; +} + +status_t A2dpAudioInterface::setMasterVolume(float v) +{ + return 0; +} + +status_t A2dpAudioInterface::doRouting() +{ + return 0; +} + +status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) +{ + return 0; +} + +// ---------------------------------------------------------------------------- + +A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : + mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL) +{ + // use any address by default + strncpy(mA2dpAddress, "00:00:00:00:00:00", sizeof(mA2dpAddress)); +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::set( + int format, int channels, uint32_t rate) +{ + LOGD("A2dpAudioStreamOut::set %d, %d, %d\n", format, channels, rate); + + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) + return BAD_VALUE; + + return NO_ERROR; +} + +A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() +{ + close(); +} + +ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) +{ + status_t status = NO_INIT; + size_t remaining = bytes; + + if (!mData) { + status = a2dp_init(44100, 2, &mData); + if (status < 0) { + LOGE("a2dp_init failed err: %d\n", status); + mData = NULL; + goto Error; + } + a2dp_set_sink(mData, mA2dpAddress); + } + + 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::standby() +{ + int result = 0; + + if (!mStandby) { + result = a2dp_stop(mData); + if (result == 0) + mStandby = true; + } + + return result; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) +{ + if (strlen(address) < sizeof(mA2dpAddress)) + return -EINVAL; + + if (strcmp(address, mA2dpAddress)) { + strcpy(mA2dpAddress, address); + if (mData) + a2dp_set_sink(mData, mA2dpAddress); + } + + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::close() +{ + if (mData) { + a2dp_cleanup(mData); + mData = NULL; + } + return NO_ERROR; +} + +status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<String16>& args) +{ + return NO_ERROR; +} + + +}; // namespace android diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h new file mode 100644 index 0000000..a56e8a0 --- /dev/null +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#ifndef A2DP_AUDIO_HARDWARE_H +#define A2DP_AUDIO_HARDWARE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware_legacy/AudioHardwareBase.h> + + +namespace android { + +class A2dpAudioInterface : public AudioHardwareBase +{ + class A2dpAudioStreamOut; + +public: + A2dpAudioInterface(); + virtual ~A2dpAudioInterface(); + virtual status_t initCheck(); + + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + // Temporary interface, do not use + // TODO: Replace with a more generic key:value get/set mechanism + virtual status_t setParameter(const char *key, const char *value); + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + +protected: + virtual status_t doRouting(); + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + class A2dpAudioStreamOut : public AudioStreamOut { + public: + A2dpAudioStreamOut(); + virtual ~A2dpAudioStreamOut(); + status_t set(int format, + int channelCount, + uint32_t sampleRate); + virtual uint32_t sampleRate() const { return 44100; } + // SBC codec wants a multiple of 512 + virtual size_t bufferSize() const { return 512 * 20; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; } + virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + status_t standby(); + status_t close(); + virtual status_t dump(int fd, const Vector<String16>& args); + + private: + friend class A2dpAudioInterface; + status_t setAddress(const char* address); + + private: + int mFd; + bool mStandby; + int mStartCount; + int mRetryCount; + char mA2dpAddress[20]; + void* mData; + }; + + Mutex mLock; + A2dpAudioStreamOut* mOutput; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // A2DP_AUDIO_HARDWARE_H diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk new file mode 100644 index 0000000..50d516b --- /dev/null +++ b/libs/audioflinger/Android.mk @@ -0,0 +1,56 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioHardwareGeneric.cpp \ + AudioHardwareStub.cpp \ + AudioDumpInterface.cpp \ + AudioHardwareInterface.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia \ + libhardware_legacy + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_CFLAGS += -DGENERIC_AUDIO +endif + +LOCAL_MODULE:= libaudiointerface + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioFlinger.cpp \ + AudioMixer.cpp.arm \ + AudioResampler.cpp.arm \ + AudioResamplerSinc.cpp.arm \ + AudioResamplerCubic.cpp.arm + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia \ + libhardware_legacy + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_STATIC_LIBRARIES += libaudiointerface +else + LOCAL_SHARED_LIBRARIES += libaudio +endif + +LOCAL_MODULE:= libaudioflinger + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_SRC_FILES += A2dpAudioInterface.cpp + LOCAL_SHARED_LIBRARIES += liba2dp + LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP + LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs) + LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils) +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/audioflinger/AudioBufferProvider.h b/libs/audioflinger/AudioBufferProvider.h new file mode 100644 index 0000000..1a467c7 --- /dev/null +++ b/libs/audioflinger/AudioBufferProvider.h @@ -0,0 +1,47 @@ +/* + * 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 + * + * 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_BUFFER_PROVIDER_H +#define ANDROID_AUDIO_BUFFER_PROVIDER_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Errors.h> + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioBufferProvider +{ +public: + + struct Buffer { + union { + void* raw; + short* i16; + int8_t* i8; + }; + size_t frameCount; + }; + + virtual status_t getNextBuffer(Buffer* buffer) = 0; + virtual void releaseBuffer(Buffer* buffer) = 0; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp new file mode 100644 index 0000000..b4940cb --- /dev/null +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -0,0 +1,117 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.cpp +** +** 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. +*/ + +#define LOG_TAG "AudioFlingerDump" + +#include <stdint.h> +#include <sys/types.h> +#include <utils/Log.h> + +#include <stdlib.h> +#include <unistd.h> + +#include "AudioDumpInterface.h" + +namespace android { + +bool gFirst = true; // true if first write after a standby + +// ---------------------------------------------------------------------------- + +AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) +{ + if(hw == 0) { + LOGE("Dump construct hw = 0"); + } + mFinalInterface = hw; + mStreamOut = 0; +} + + +AudioDumpInterface::~AudioDumpInterface() +{ + if(mFinalInterface) delete mFinalInterface; + if(mStreamOut) delete mStreamOut; +} + + +AudioStreamOut* AudioDumpInterface::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status); + + if(outFinal) { + mStreamOut = new AudioStreamOutDump(outFinal); + return mStreamOut; + } else { + LOGE("Dump outFinal=0"); + return 0; + } +} + +// ---------------------------------------------------------------------------- + +AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* finalStream) +{ + mFinalStream = finalStream; + mOutFile = 0; +} + + +AudioStreamOutDump::~AudioStreamOutDump() +{ + Close(); + delete mFinalStream; +} + +ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) +{ + ssize_t ret; + + ret = mFinalStream->write(buffer, bytes); + if(!mOutFile && gFirst) { + gFirst = false; + // check if dump file exist + mOutFile = fopen(FLINGER_DUMP_NAME, "r"); + if(mOutFile) { + fclose(mOutFile); + mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + } + } + if (mOutFile) { + fwrite(buffer, bytes, 1, mOutFile); + } + return ret; +} + +status_t AudioStreamOutDump::standby() +{ + Close(); + gFirst = true; + return mFinalStream->standby(); +} + + +void AudioStreamOutDump::Close(void) +{ + if(mOutFile) { + fclose(mOutFile); + mOutFile = 0; + } +} + +}; // namespace android diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h new file mode 100644 index 0000000..9a94102 --- /dev/null +++ b/libs/audioflinger/AudioDumpInterface.h @@ -0,0 +1,97 @@ +/* //device/servers/AudioFlinger/AudioDumpInterface.h +** +** 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_DUMP_INTERFACE_H +#define ANDROID_AUDIO_DUMP_INTERFACE_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm" // name of file used for dump + +class AudioStreamOutDump : public AudioStreamOut { +public: + AudioStreamOutDump( AudioStreamOut* FinalStream); + ~AudioStreamOutDump(); + virtual ssize_t write(const void* buffer, size_t bytes); + + virtual uint32_t sampleRate() const { return mFinalStream->sampleRate(); } + virtual size_t bufferSize() const { return mFinalStream->bufferSize(); } + virtual int channelCount() const { return mFinalStream->channelCount(); } + virtual int format() const { return mFinalStream->format(); } + virtual uint32_t latency() const { return mFinalStream->latency(); } + virtual status_t setVolume(float volume) + { return mFinalStream->setVolume(volume); } + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); } + void Close(void); + +private: + AudioStreamOut *mFinalStream; + FILE *mOutFile; // output file +}; + + +class AudioDumpInterface : public AudioHardwareBase +{ + +public: + AudioDumpInterface(AudioHardwareInterface* hw); + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + virtual ~AudioDumpInterface(); + + virtual status_t initCheck() + {return mFinalInterface->initCheck();} + virtual status_t setVoiceVolume(float volume) + {return mFinalInterface->setVoiceVolume(volume);} + virtual status_t setMasterVolume(float volume) + {return mFinalInterface->setMasterVolume(volume);} + + // mic mute + virtual status_t setMicMute(bool state) + {return mFinalInterface->setMicMute(state);} + virtual status_t getMicMute(bool* state) + {return mFinalInterface->getMicMute(state);} + + virtual status_t setParameter(const char* key, const char* value) + {return mFinalInterface->setParameter(key, value);} + + virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) + {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status, acoustics);} + + virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } + +protected: + virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);} + + AudioHardwareInterface *mFinalInterface; + AudioStreamOutDump *mStreamOut; + +}; + +}; // namespace android + +#endif // ANDROID_AUDIO_DUMP_INTERFACE_H diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp new file mode 100644 index 0000000..92c40e9 --- /dev/null +++ b/libs/audioflinger/AudioFlinger.cpp @@ -0,0 +1,2474 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.cpp +** +** Copyright 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 +** +** 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 "AudioFlinger" +//#define LOG_NDEBUG 0 + +#include <math.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <utils/IServiceManager.h> +#include <utils/Log.h> +#include <utils/Parcel.h> +#include <utils/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> + +#include <cutils/properties.h> + +#include <media/AudioTrack.h> +#include <media/AudioRecord.h> + +#include <private/media/AudioTrackShared.h> + +#include <hardware_legacy/AudioHardwareInterface.h> + +#include "AudioMixer.h" +#include "AudioFlinger.h" + +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +//static const nsecs_t kStandbyTimeInNsecs = seconds(3); +static const unsigned long kBufferRecoveryInUsecs = 2000; +static const unsigned long kMaxBufferRecoveryInUsecs = 20000; +static const float MAX_GAIN = 4096.0f; + +// retry counts for buffer fill timeout +// 50 * ~20msecs = 1 second +static const int8_t kMaxTrackRetries = 50; +static const int8_t kMaxTrackStartupRetries = 50; + +static const int kStartSleepTime = 30000; +static const int kStopSleepTime = 30000; + +// Maximum number of pending buffers allocated by OutputTrack::write() +static const uint8_t kMaxOutputTrackBuffers = 5; + + +#define AUDIOFLINGER_SECURITY_ENABLED 1 + +// ---------------------------------------------------------------------------- + +static bool recordingAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO")); + if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO"))) + LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest"); + return true; +#endif +} + +static bool settingsAllowed() { +#ifndef HAVE_ANDROID_OS + return true; +#endif +#if AUDIOFLINGER_SECURITY_ENABLED + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); + if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); + return ok; +#else + if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"))) + LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest"); + return true; +#endif +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioFlinger() + : BnAudioFlinger(), + mAudioHardware(0), mA2dpAudioInterface(0), + mA2dpEnabled(false), mA2dpEnabledReq(false), + mForcedSpeakerCount(0), mForcedRoute(0), mRouteRestoreTime(0), mMusicMuteSaved(false) +{ + mHardwareStatus = AUDIO_HW_IDLE; + mAudioHardware = AudioHardwareInterface::create(); + mHardwareStatus = AUDIO_HW_INIT; + if (mAudioHardware->initCheck() == NO_ERROR) { + // open 16-bit output stream for s/w mixer + mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + status_t status; + AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + mHardwareStatus = AUDIO_HW_IDLE; + if (hwOutput) { + mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE); + } else { + LOGE("Failed to initialize hardware output stream, status: %d", status); + } + +#ifdef WITH_A2DP + // Create A2DP interface + mA2dpAudioInterface = new A2dpAudioInterface(); + AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); + if (a2dpOutput) { + mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP); + if (hwOutput) { + uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate(); + MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread, + hwOutput->sampleRate(), + AudioSystem::PCM_16_BIT, + hwOutput->channelCount(), + frameCount); + mHardwareMixerThread->setOuputTrack(a2dpOutTrack); + } + } else { + LOGE("Failed to initialize A2DP output stream, status: %d", status); + } +#endif + + // FIXME - this should come from settings + setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); + setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); + setMode(AudioSystem::MODE_NORMAL); + + setMasterVolume(1.0f); + setMasterMute(false); + + // Start record thread + mAudioRecordThread = new AudioRecordThread(mAudioHardware); + if (mAudioRecordThread != 0) { + mAudioRecordThread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); + } + } else { + LOGE("Couldn't even initialize the stubbed audio hardware!"); + } +} + +AudioFlinger::~AudioFlinger() +{ + if (mAudioRecordThread != 0) { + mAudioRecordThread->exit(); + mAudioRecordThread.clear(); + } + mHardwareMixerThread.clear(); + delete mAudioHardware; + // deleting mA2dpAudioInterface also deletes mA2dpOutput; +#ifdef WITH_A2DP + mA2dpMixerThread.clear(); + delete mA2dpAudioInterface; +#endif +} + + +#ifdef WITH_A2DP +void AudioFlinger::setA2dpEnabled(bool enable) +{ + LOGV_IF(enable, "set output to A2DP\n"); + LOGV_IF(!enable, "set output to hardware audio\n"); + + mA2dpEnabledReq = enable; + mA2dpMixerThread->wakeUp(); +} +#endif // WITH_A2DP + +bool AudioFlinger::streamForcedToSpeaker(int streamType) +{ + // NOTE that streams listed here must not be routed to A2DP by default: + // AudioSystem::routedToA2dpOutput(streamType) == false + return (streamType == AudioSystem::RING || + streamType == AudioSystem::ALARM || + streamType == AudioSystem::NOTIFICATION); +} + +status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append("Clients:\n"); + for (size_t i = 0; i < mClients.size(); ++i) { + wp<Client> wClient = mClients.valueAt(i); + if (wClient != 0) { + sp<Client> client = wClient.promote(); + if (client != 0) { + snprintf(buffer, SIZE, " pid: %d\n", client->pid()); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + + +status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Hardware status: %d\n", mHardwareStatus); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioFlinger from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::dump(int fd, const Vector<String16>& args) +{ + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + dumpPermissionDenial(fd, args); + } else { + AutoMutex lock(&mLock); + + dumpClients(fd, args); + dumpInternals(fd, args); + mHardwareMixerThread->dump(fd, args); +#ifdef WITH_A2DP + mA2dpMixerThread->dump(fd, args); +#endif + + // dump record client + if (mAudioRecordThread != 0) mAudioRecordThread->dump(fd, args); + + if (mAudioHardware) { + mAudioHardware->dumpState(fd, args); + } + } + return NO_ERROR; +} + +// IAudioFlinger interface + + +sp<IAudioTrack> AudioFlinger::createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<MixerThread::Track> track; + sp<TrackHandle> trackHandle; + sp<Client> client; + wp<Client> wclient; + status_t lStatus; + + if (streamType >= AudioSystem::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + wclient = mClients.valueFor(pid); + + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } +#ifdef WITH_A2DP + if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) { + track = mA2dpMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } else +#endif + { + track = mHardwareMixerThread->createTrack(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); + } + if (track != NULL) { + trackHandle = new TrackHandle(track); + lStatus = NO_ERROR; + } + } + +Exit: + if(status) { + *status = lStatus; + } + return trackHandle; +} + +uint32_t AudioFlinger::sampleRate(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->sampleRate(); + } +#endif + return mHardwareMixerThread->sampleRate(); +} + +int AudioFlinger::channelCount(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->channelCount(); + } +#endif + return mHardwareMixerThread->channelCount(); +} + +int AudioFlinger::format(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->format(); + } +#endif + return mHardwareMixerThread->format(); +} + +size_t AudioFlinger::frameCount(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->frameCount(); + } +#endif + return mHardwareMixerThread->frameCount(); +} + +uint32_t AudioFlinger::latency(int output) const +{ +#ifdef WITH_A2DP + if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { + return mA2dpMixerThread->latency(); + } +#endif + return mHardwareMixerThread->latency(); +} + +status_t AudioFlinger::setMasterVolume(float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + // when hw supports master volume, don't scale in sw mixer + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if (mAudioHardware->setMasterVolume(value) == NO_ERROR) { + value = 1.0f; + } + mHardwareStatus = AUDIO_HW_IDLE; + mHardwareMixerThread->setMasterVolume(value); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterVolume(value); +#endif + + return NO_ERROR; +} + +status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) +{ + status_t err = NO_ERROR; + + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < AudioSystem::MODE_CURRENT) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setRouting(%d, %u, %u)", mode, routes, mask); + return BAD_VALUE; + } + +#ifdef WITH_A2DP + LOGD("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), IPCThreadState::self()->getCallingPid()); + if (mode == AudioSystem::MODE_NORMAL && + (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { + AutoMutex lock(&mLock); + + bool enableA2dp = false; + if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { + enableA2dp = true; + } + setA2dpEnabled(enableA2dp); + LOGV("setOutput done\n"); + } +#endif + + // do nothing if only A2DP routing is affected + mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP; + if (mask) { + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_GET_ROUTING; + uint32_t r; + err = mAudioHardware->getRouting(mode, &r); + if (err == NO_ERROR) { + r = (r & ~mask) | (routes & mask); + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + mSavedRoute = r; + r |= mForcedRoute; + LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute); + } + mHardwareStatus = AUDIO_HW_SET_ROUTING; + err = mAudioHardware->setRouting(mode, r); + } + mHardwareStatus = AUDIO_HW_IDLE; + } + return err; +} + +uint32_t AudioFlinger::getRouting(int mode) const +{ + uint32_t routes = 0; + if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) { + if (mode == AudioSystem::MODE_NORMAL || + (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { + routes = mSavedRoute; + } else { + mHardwareStatus = AUDIO_HW_GET_ROUTING; + mAudioHardware->getRouting(mode, &routes); + mHardwareStatus = AUDIO_HW_IDLE; + } + } else { + LOGW("Illegal value: getRouting(%d)", mode); + } + return routes; +} + +status_t AudioFlinger::setMode(int mode) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) { + LOGW("Illegal value: setMode(%d)", mode); + return BAD_VALUE; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MODE; + status_t ret = mAudioHardware->setMode(mode); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +int AudioFlinger::getMode() const +{ + int mode = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_SET_MODE; + mAudioHardware->getMode(&mode); + mHardwareStatus = AUDIO_HW_IDLE; + return mode; +} + +status_t AudioFlinger::setMicMute(bool state) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; + status_t ret = mAudioHardware->setMicMute(state); + mHardwareStatus = AUDIO_HW_IDLE; + return ret; +} + +bool AudioFlinger::getMicMute() const +{ + bool state = AudioSystem::MODE_INVALID; + mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; + mAudioHardware->getMicMute(&state); + mHardwareStatus = AUDIO_HW_IDLE; + return state; +} + +status_t AudioFlinger::setMasterMute(bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + mHardwareMixerThread->setMasterMute(muted); +#ifdef WITH_A2DP + mA2dpMixerThread->setMasterMute(muted); +#endif + return NO_ERROR; +} + +float AudioFlinger::masterVolume() const +{ + return mHardwareMixerThread->masterVolume(); +} + +bool AudioFlinger::masterMute() const +{ + return mHardwareMixerThread->masterMute(); +} + +status_t AudioFlinger::setStreamVolume(int stream, float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + mHardwareMixerThread->setStreamVolume(stream, value); +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamVolume(stream, value); +#endif + + status_t ret = NO_ERROR; + if (stream == AudioSystem::VOICE_CALL || + stream == AudioSystem::BLUETOOTH_SCO) { + + if (stream == AudioSystem::VOICE_CALL) { + value = (float)AudioSystem::logToLinear(value)/100.0f; + } else { // (type == AudioSystem::BLUETOOTH_SCO) + value = 1.0f; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_VOICE_VOLUME; + ret = mAudioHardware->setVoiceVolume(value); + mHardwareStatus = AUDIO_HW_IDLE; + } + + return ret; +} + +status_t AudioFlinger::setStreamMute(int stream, bool muted) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + +#ifdef WITH_A2DP + mA2dpMixerThread->setStreamMute(stream, muted); +#endif + if (stream == AudioSystem::MUSIC) + { + AutoMutex lock(&mHardwareLock); + if (mForcedRoute != 0) + mMusicMuteSaved = muted; + else + mHardwareMixerThread->setStreamMute(stream, muted); + } else { + mHardwareMixerThread->setStreamMute(stream, muted); + } + + + + return NO_ERROR; +} + +float AudioFlinger::streamVolume(int stream) const +{ + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return 0.0f; + } + return mHardwareMixerThread->streamVolume(stream); +} + +bool AudioFlinger::streamMute(int stream) const +{ + if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + return true; + } + + if (stream == AudioSystem::MUSIC && mForcedRoute != 0) + { + return mMusicMuteSaved; + } + return mHardwareMixerThread->streamMute(stream); +} + +bool AudioFlinger::isMusicActive() const +{ + #ifdef WITH_A2DP + if (isA2dpEnabled()) { + return mA2dpMixerThread->isMusicActive(); + } + #endif + return mHardwareMixerThread->isMusicActive(); +} + +status_t AudioFlinger::setParameter(const char* key, const char* value) +{ + status_t result, result2; + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_PARAMETER; + + LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid()); + result = mAudioHardware->setParameter(key, value); + if (mA2dpAudioInterface) { + result2 = mA2dpAudioInterface->setParameter(key, value); + if (result2) + result = result2; + } + mHardwareStatus = AUDIO_HW_IDLE; + return result; +} + +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); +} + +void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) +{ + + LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + sp<IBinder> binder = client->asBinder(); + if (mNotificationClients.indexOf(binder) < 0) { + LOGV("Adding notification client %p", binder.get()); + binder->linkToDeath(this); + mNotificationClients.add(binder); + client->a2dpEnabledChanged(isA2dpEnabled()); + } +} + +void AudioFlinger::binderDied(const wp<IBinder>& who) { + + LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + + IBinder *binder = who.unsafe_get(); + + if (binder != NULL) { + int index = mNotificationClients.indexOf(binder); + if (index >= 0) { + LOGV("Removing notification client %p", binder); + mNotificationClients.removeAt(index); + } + } +} + +void AudioFlinger::handleOutputSwitch() +{ + if (mA2dpEnabled != mA2dpEnabledReq) + { + Mutex::Autolock _l(mLock); + + if (mA2dpEnabled != mA2dpEnabledReq) + { + mA2dpEnabled = mA2dpEnabledReq; + SortedVector < sp<MixerThread::Track> > tracks; + SortedVector < wp<MixerThread::Track> > activeTracks; + + // We hold mA2dpMixerThread mLock already + Mutex::Autolock _l(mHardwareMixerThread->mLock); + + // Transfer tracks playing on MUSIC stream from one mixer to the other + if (mA2dpEnabled) { + mHardwareMixerThread->getTracks(tracks, activeTracks); + mA2dpMixerThread->putTracks(tracks, activeTracks); + } else { + mA2dpMixerThread->getTracks(tracks, activeTracks); + mHardwareMixerThread->putTracks(tracks, activeTracks); + } + + // Notify AudioSystem of the A2DP activation/deactivation + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp<IBinder> binder = mNotificationClients.itemAt(i).promote(); + if (binder != NULL) { + LOGV("Notifying output change to client %p", binder.get()); + sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); + client->a2dpEnabledChanged(mA2dpEnabled); + } + } + + mHardwareMixerThread->wakeUp(); + } + } +} + +void AudioFlinger::removeClient(pid_t pid) +{ + LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mLock); + mClients.removeItem(pid); +} + +void AudioFlinger::wakeUp() +{ + mHardwareMixerThread->wakeUp(); +#ifdef WITH_A2DP + mA2dpMixerThread->wakeUp(); +#endif // WITH_A2DP +} + +bool AudioFlinger::isA2dpEnabled() const +{ + return mA2dpEnabledReq; +} + +void AudioFlinger::handleForcedSpeakerRoute(int command) +{ + switch(command) { + case ACTIVE_TRACK_ADDED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount++ == 0) { + mRouteRestoreTime = 0; + mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); + if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(0); + usleep(mHardwareMixerThread->latency()*1000); + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareStatus = AUDIO_HW_IDLE; + // delay track start so that audio hardware has time to siwtch routes + usleep(kStartSleepTime); + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + mAudioHardware->setMasterVolume(mHardwareMixerThread->masterVolume()); + mHardwareStatus = AUDIO_HW_IDLE; + } + mForcedRoute = AudioSystem::ROUTE_SPEAKER; + } + LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); + } + break; + case ACTIVE_TRACK_REMOVED: + { + AutoMutex lock(mHardwareLock); + if (mForcedSpeakerCount > 0){ + if (--mForcedSpeakerCount == 0) { + mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000); + } + LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount); + } else { + LOGE("mForcedSpeakerCount is already zero"); + } + } + break; + case CHECK_ROUTE_RESTORE_TIME: + case FORCE_ROUTE_RESTORE: + if (mRouteRestoreTime) { + AutoMutex lock(mHardwareLock); + if (mRouteRestoreTime && + (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) { + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved); + mForcedRoute = 0; + if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute); + mHardwareStatus = AUDIO_HW_IDLE; + LOGV("Route forced to Speaker OFF %08x", mSavedRoute); + } + mRouteRestoreTime = 0; + } + } + break; + } +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType) + : Thread(false), + mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType), + mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), + mInWrite(false) +{ + mSampleRate = output->sampleRate(); + mChannelCount = output->channelCount(); + + // FIXME - Current mixer implementation only supports stereo output + if (mChannelCount == 1) { + LOGE("Invalid audio hardware channel count"); + } + + mFormat = output->format(); + mFrameCount = output->bufferSize() / output->channelCount() / sizeof(int16_t); + mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate()); + + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + mMixBuffer = new int16_t[mFrameCount * 2]; + memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +} + +AudioFlinger::MixerThread::~MixerThread() +{ + delete [] mMixBuffer; + delete mAudioMixer; +} + +status_t AudioFlinger::MixerThread::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + dumpTracks(fd, args); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mTracks.size(); ++i) { + wp<Track> wTrack = mTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + + snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType); + result.append(buffer); + result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + for (size_t i = 0; i < mActiveTracks.size(); ++i) { + wp<Track> wTrack = mTracks[i]; + if (wTrack != 0) { + sp<Track> track = wTrack.promote(); + if (track != 0) { + track->dump(buffer, SIZE); + result.append(buffer); + } + } + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType); + result.append(buffer); + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + result.append(buffer); + snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + result.append(buffer); + snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); + result.append(buffer); + snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); + result.append(buffer); + snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); + result.append(buffer); + snprintf(buffer, SIZE, "standby: %d\n", mStandby); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// Thread virtuals +bool AudioFlinger::MixerThread::threadLoop() +{ + unsigned long sleepTime = kBufferRecoveryInUsecs; + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + size_t enabledTracks = 0; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); + nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; + +#ifdef WITH_A2DP + bool outputTrackActive = false; +#endif + + do { + enabledTracks = 0; + { // scope for the mLock + + Mutex::Autolock _l(mLock); + +#ifdef WITH_A2DP + if (mOutputType == AudioSystem::AUDIO_OUTPUT_A2DP) { + mAudioFlinger->handleOutputSwitch(); + } + if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } + } +#endif + + const SortedVector< wp<Track> >& activeTracks = mActiveTracks; + + // put audio hardware into standby after short delay + if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { + // wait until we have something to do... + LOGV("Audio hardware entering standby, output %d\n", mOutputType); +// mAudioFlinger->mHardwareStatus = AUDIO_HW_STANDBY; + if (!mStandby) { + mOutput->standby(); + mStandby = true; + } + +#ifdef WITH_A2DP + if (outputTrackActive) { + mOutputTrack->stop(); + outputTrackActive = false; + } +#endif + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE); + } +// mHardwareStatus = AUDIO_HW_IDLE; + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mWaitWorkCV.wait(mLock); + LOGV("Audio hardware exiting standby, output %d\n", mOutputType); + + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; + } + + // Forced route to speaker is handled by hardware mixer thread + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME); + } + + // find out which tracks need to be processed + size_t count = activeTracks.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + mAudioMixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + int16_t left, right; + if (track->isMuted() || mMasterMute || track->isPausing()) { + left = right = 0; + if (track->isPausing()) { + LOGV("paused(%d)", track->name()); + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = int16_t(v_clamped); + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = int16_t(v_clamped); + } + + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + int param; + if ( track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } else { + param = AudioMixer::VOLUME; + } + } else { + param = AudioMixer::RAMP_VOLUME; + } + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mAudioMixer->setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + enabledTracks++; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + LOGV("remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + tracksToRemove.add(track); + } + } + // LOGV("disable(%d)", track->name()); + mAudioMixer->disable(AudioMixer::MIXING); + } + } + + // remove all the tracks that need to be... + count = tracksToRemove.size(); + if (UNLIKELY(count)) { + for (size_t i=0 ; i<count ; i++) { + const sp<Track>& track = tracksToRemove[i]; + removeActiveTrack(track); + if (track->isTerminated()) { + mTracks.remove(track); + deleteTrackName(track->mName); + } + } + } + } + + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (!outputTrackActive) { + LOGV("starting output track in mixer for output %d", mOutputType); + mOutputTrack->start(); + outputTrackActive = true; + } + mOutputTrack->write(curBuf, mFrameCount); + } +#endif + + // output audio to hardware + mLastWriteTime = systemTime(); + mInWrite = true; + mOutput->write(curBuf, mixBufferSize); + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t temp = systemTime(); + standbyTime = temp + kStandbyTimeInNsecs; + nsecs_t delta = temp - mLastWriteTime; + if (delta > maxPeriod) { + LOGW("write blocked for %llu msecs", ns2ms(delta)); + mNumDelayedWrites++; + } + sleepTime = kBufferRecoveryInUsecs; + } else { +#ifdef WITH_A2DP + if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { + if (outputTrackActive) { + mOutputTrack->write(curBuf, 0); + if (mOutputTrack->bufferQueueEmpty()) { + mOutputTrack->stop(); + outputTrackActive = false; + } else { + standbyTime = systemTime() + kStandbyTimeInNsecs; + } + } + } +#endif + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, the audio + // hardware will zero-fill for us. + //LOGV("no buffers - usleep(%lu)", sleepTime); + usleep(sleepTime); + if (sleepTime < kMaxBufferRecoveryInUsecs) { + sleepTime += kBufferRecoveryInUsecs; + } + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + } while (true); + + return false; +} + +status_t AudioFlinger::MixerThread::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's thread ready to run for output %d", mOutputType); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType); + + run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); +} + + +sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<Track> track; + status_t lStatus; + + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (sampleRate > MAX_SAMPLE_RATE || sampleRate > mSampleRate*2) { + LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + if (mSampleRate == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + if (track->getCblk() == NULL) { + track.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + mTracks.add(track); + lStatus = NO_ERROR; + } + +Exit: + if(status) { + *status = lStatus; + } + return track; +} + +void AudioFlinger::MixerThread::getTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + size_t size = mTracks.size(); + LOGV ("MixerThread::getTracks() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size()); + for (size_t i = 0; i < size; i++) { + sp<Track> t = mTracks[i]; + if (AudioSystem::routedToA2dpOutput(t->mStreamType)) { + tracks.add(t); + int j = mActiveTracks.indexOf(t); + if (j >= 0) { + t = mActiveTracks[j].promote(); + if (t != NULL) { + activeTracks.add(t); + } + } + } + } + + size = activeTracks.size(); + for (size_t i = 0; i < size; i++) { + removeActiveTrack(activeTracks[i]); + } + + size = tracks.size(); + for (size_t i = 0; i < size; i++) { + sp<Track> t = tracks[i]; + mTracks.remove(t); + deleteTrackName(t->name()); + } +} + +void AudioFlinger::MixerThread::putTracks( + SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks) +{ + + LOGV ("MixerThread::putTracks() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size()); + + size_t size = tracks.size(); + for (size_t i = 0; i < size ; i++) { + sp<Track> t = tracks[i]; + int name = getTrackName(); + + if (name < 0) return; + + t->mName = name; + t->mMixerThread = this; + mTracks.add(t); + + int j = activeTracks.indexOf(t); + if (j >= 0) { + addActiveTrack(t); + } + } +} + +uint32_t AudioFlinger::MixerThread::sampleRate() const +{ + return mSampleRate; +} + +int AudioFlinger::MixerThread::channelCount() const +{ + return mChannelCount; +} + +int AudioFlinger::MixerThread::format() const +{ + return mFormat; +} + +size_t AudioFlinger::MixerThread::frameCount() const +{ + return mFrameCount; +} + +uint32_t AudioFlinger::MixerThread::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + +status_t AudioFlinger::MixerThread::setMasterVolume(float value) +{ + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setMasterMute(bool muted) +{ + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::MixerThread::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value) +{ + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted) +{ + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::MixerThread::streamVolume(int stream) const +{ + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::MixerThread::streamMute(int stream) const +{ + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::MixerThread::isMusicActive() const +{ + size_t count = mActiveTracks.size(); + for (size_t i = 0 ; i < count ; ++i) { + sp<Track> t = mActiveTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + if (t->mStreamType == AudioSystem::MUSIC) + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::addTrack(const sp<Track>& track) +{ + status_t status = ALREADY_EXISTS; + Mutex::Autolock _l(mLock); + + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (track->isPaused()) { + track->mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d)", track->name()); + } else { + track->mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d)", track->name()); + } + // set retry count for buffer fill + track->mRetryCount = kMaxTrackStartupRetries; + if (mActiveTracks.indexOf(track) < 0) { + // the track is newly added, make sure it fills up all its + // buffers before playing. This is to ensure the client will + // effectively get the latency it requested. + track->mFillingUpStatus = Track::FS_FILLING; + track->mResetDone = false; + addActiveTrack(track); + status = NO_ERROR; + } + + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + return status; +} + +void AudioFlinger::MixerThread::removeTrack(wp<Track> track, int name) +{ + Mutex::Autolock _l(mLock); + sp<Track> t = track.promote(); + if (t!=NULL && (t->mState <= TrackBase::STOPPED)) { + remove_track_l(track, name); + } +} + +void AudioFlinger::MixerThread::remove_track_l(wp<Track> track, int name) +{ + sp<Track> t = track.promote(); + if (t!=NULL) { + t->reset(); + } + deleteTrackName(name); + removeActiveTrack(track); + mWaitWorkCV.broadcast(); +} + +void AudioFlinger::MixerThread::destroyTrack(const sp<Track>& track) +{ + // NOTE: We're acquiring a strong reference on the track before + // acquiring the lock, this is to make sure removing it from + // mTracks won't cause the destructor to be called while the lock is + // held (note that technically, 'track' could be a reference to an item + // in mTracks, which is why we need to do this). + sp<Track> keep(track); + Mutex::Autolock _l(mLock); + track->mState = TrackBase::TERMINATED; + if (mActiveTracks.indexOf(track) < 0) { + LOGV("remove track (%d) and delete from mixer", track->name()); + mTracks.remove(track); + deleteTrackName(keep->name()); + } +} + + +void AudioFlinger::MixerThread::addActiveTrack(const wp<Track>& t) +{ + mActiveTracks.add(t); + + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp<Track> track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED); + } + } +} + +void AudioFlinger::MixerThread::removeActiveTrack(const wp<Track>& t) +{ + mActiveTracks.remove(t); + + // Force routing to speaker for certain stream types + // The forced routing to speaker is managed by hardware mixer + if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { + sp<Track> track = t.promote(); + if (track == NULL) return; + + if (streamForcedToSpeaker(track->type())) { + mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED); + } + } +} + +int AudioFlinger::MixerThread::getTrackName() +{ + return mAudioMixer->getTrackName(); +} + +void AudioFlinger::MixerThread::deleteTrackName(int name) +{ + mAudioMixer->deleteTrackName(name); +} + +size_t AudioFlinger::MixerThread::getOutputFrameCount() +{ + return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::TrackBase::TrackBase( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer) + : RefBase(), + mMixerThread(mixerThread), + mClient(client), + mStreamType(streamType), + mFrameCount(0), + mState(IDLE), + mClientTid(-1), + mFormat(format), + mFlags(flags & ~SYSTEM_FLAGS_MASK) +{ + mName = mixerThread->getTrackName(); + LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + if (mName < 0) { + LOGE("no more track names availlable"); + return; + } + + LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); + + // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); + size_t size = sizeof(audio_track_cblk_t); + size_t bufferSize = frameCount*channelCount*sizeof(int16_t); + if (sharedBuffer == 0) { + size += bufferSize; + } + + if (client != NULL) { + mCblkMemory = client->heap()->allocate(size); + if (mCblkMemory != 0) { + mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + if (sharedBuffer == 0) { + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + } else { + mBuffer = sharedBuffer->pointer(); + } + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } else { + LOGE("not enough memory for AudioTrack size=%u", size); + client->heap()->dump("AudioTrack"); + return; + } + } else { + mCblk = (audio_track_cblk_t *)(new uint8_t[size]); + if (mCblk) { // construct the shared structure in-place. + new(mCblk) audio_track_cblk_t(); + // clear all buffers + mCblk->frameCount = frameCount; + mCblk->sampleRate = sampleRate; + mCblk->channels = channelCount; + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mBufferEnd = (uint8_t *)mBuffer + bufferSize; + } + } +} + +AudioFlinger::MixerThread::TrackBase::~TrackBase() +{ + if (mCblk) { + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + } + mCblkMemory.clear(); // and free the shared memory + mClient.clear(); +} + +void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + buffer->raw = 0; + mFrameCount = buffer->frameCount; + step(); + buffer->frameCount = 0; +} + +bool AudioFlinger::MixerThread::TrackBase::step() { + bool result; + audio_track_cblk_t* cblk = this->cblk(); + + result = cblk->stepServer(mFrameCount); + if (!result) { + LOGV("stepServer failed acquiring cblk mutex"); + mFlags |= STEPSERVER_FAILED; + } + return result; +} + +void AudioFlinger::MixerThread::TrackBase::reset() { + audio_track_cblk_t* cblk = this->cblk(); + + cblk->user = 0; + cblk->server = 0; + cblk->userBase = 0; + cblk->serverBase = 0; + mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK); + LOGV("TrackBase::reset"); +} + +sp<IMemory> AudioFlinger::MixerThread::TrackBase::getCblk() const +{ + return mCblkMemory; +} + +int AudioFlinger::MixerThread::TrackBase::sampleRate() const { + return mCblk->sampleRate; +} + +int AudioFlinger::MixerThread::TrackBase::channelCount() const { + return mCblk->channels; +} + +void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { + audio_track_cblk_t* cblk = this->cblk(); + int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels; + int16_t *bufferEnd = bufferStart + frames * cblk->channels; + + // Check validity of returned pointer in case the track control block would have been corrupted. + if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd) { + LOGW("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \ + server %d, serverBase %d, user %d, userBase %d", + bufferStart, bufferEnd, mBuffer, mBufferEnd, + cblk->server, cblk->serverBase, cblk->user, cblk->userBase); + return 0; + } + + return bufferStart; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::Track::Track( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer) + : TrackBase(mixerThread, client, streamType, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) +{ + mVolume[0] = 1.0f; + mVolume[1] = 1.0f; + mMute = false; + mSharedBuffer = sharedBuffer; +} + +AudioFlinger::MixerThread::Track::~Track() +{ + wp<Track> weak(this); // never create a strong ref from the dtor + mState = TERMINATED; + mMixerThread->removeTrack(weak, mName); +} + +void AudioFlinger::MixerThread::Track::destroy() +{ + mMixerThread->destroyTrack(this); +} + +void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n", + mName - AudioMixer::TRACK0, + (mClient == NULL) ? getpid() : mClient->pid(), + mStreamType, + mFormat, + mCblk->channels, + mFrameCount, + mState, + mMute, + mFillingUpStatus, + mCblk->sampleRate, + mCblk->volume[0], + mCblk->volume[1], + mCblk->server, + mCblk->user); +} + +status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesReady; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesReady = cblk->framesReady(); + + if (LIKELY(framesReady)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd; + if (framesReq > framesReady) { + framesReq = framesReady; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +bool AudioFlinger::MixerThread::Track::isReady() const { + if (mFillingUpStatus != FS_FILLING) return true; + + if (mCblk->framesReady() >= mCblk->frameCount || + mCblk->forceReady) { + mFillingUpStatus = FS_FILLED; + mCblk->forceReady = 0; + LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType); + return true; + } + return false; +} + +status_t AudioFlinger::MixerThread::Track::start() +{ + LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + mMixerThread->addTrack(this); + return NO_ERROR; +} + +void AudioFlinger::MixerThread::Track::stop() +{ + LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState > STOPPED) { + mState = STOPPED; + // If the track is not active (PAUSED and buffers full), flush buffers + if (mMixerThread->mActiveTracks.indexOf(this) < 0) { + reset(); + } + LOGV("(> STOPPED) => STOPPED (%d)", mName); + } +} + +void AudioFlinger::MixerThread::Track::pause() +{ + LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState == ACTIVE || mState == RESUMING) { + mState = PAUSING; + LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + } +} + +void AudioFlinger::MixerThread::Track::flush() +{ + LOGV("flush(%d)", mName); + Mutex::Autolock _l(mMixerThread->mLock); + if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { + return; + } + // No point remaining in PAUSED state after a flush => go to + // STOPPED state + mState = STOPPED; + + // NOTE: reset() will reset cblk->user and cblk->server with + // the risk that at the same time, the AudioMixer is trying to read + // data. In this case, getNextBuffer() would return a NULL pointer + // as audio buffer => the AudioMixer code MUST always test that pointer + // returned by getNextBuffer() is not NULL! + reset(); +} + +void AudioFlinger::MixerThread::Track::reset() +{ + // Do not reset twice to avoid discarding data written just after a flush and before + // the audioflinger thread detects the track is stopped. + if (!mResetDone) { + TrackBase::reset(); + // Force underrun condition to avoid false underrun callback until first data is + // written to buffer + mCblk->flowControlFlag = 1; + mCblk->forceReady = 0; + mFillingUpStatus = FS_FILLING; + mResetDone = true; + } +} + +void AudioFlinger::MixerThread::Track::mute(bool muted) +{ + mMute = muted; +} + +void AudioFlinger::MixerThread::Track::setVolume(float left, float right) +{ + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::RecordTrack::RecordTrack( + const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags) + : TrackBase(mixerThread, client, streamType, sampleRate, format, + channelCount, frameCount, flags, 0), + mOverflow(false) +{ +} + +AudioFlinger::MixerThread::RecordTrack::~RecordTrack() +{ + mMixerThread->deleteTrackName(mName); +} + +status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t framesAvail; + uint32_t framesReq = buffer->frameCount; + + // Check if last stepServer failed, try to step now + if (mFlags & TrackBase::STEPSERVER_FAILED) { + if (!step()) goto getNextBuffer_exit; + LOGV("stepServer recovered"); + mFlags &= ~TrackBase::STEPSERVER_FAILED; + } + + framesAvail = cblk->framesAvailable_l(); + + if (LIKELY(framesAvail)) { + uint32_t s = cblk->server; + uint32_t bufferEnd = cblk->serverBase + cblk->frameCount; + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + if (s + framesReq > bufferEnd) { + framesReq = bufferEnd - s; + } + + buffer->raw = getBuffer(s, framesReq); + if (buffer->raw == 0) goto getNextBuffer_exit; + + buffer->frameCount = framesReq; + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +status_t AudioFlinger::MixerThread::RecordTrack::start() +{ + return mMixerThread->mAudioFlinger->startRecord(this); +} + +void AudioFlinger::MixerThread::RecordTrack::stop() +{ + mMixerThread->mAudioFlinger->stopRecord(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; +} + + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::OutputTrack::OutputTrack( + const sp<MixerThread>& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount) + : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL), + mOutputMixerThread(mixerThread) +{ + + mCblk->out = 1; + mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); + mCblk->volume[0] = mCblk->volume[1] = 0x1000; + mOutBuffer.frameCount = 0; + mCblk->bufferTimeoutMs = 10; + + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); + +} + +AudioFlinger::MixerThread::OutputTrack::~OutputTrack() +{ + stop(); +} + +status_t AudioFlinger::MixerThread::OutputTrack::start() +{ + status_t status = Track::start(); + + mRetryCount = 127; + return status; +} + +void AudioFlinger::MixerThread::OutputTrack::stop() +{ + Track::stop(); + clearBufferQueue(); + mOutBuffer.frameCount = 0; +} + +void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames) +{ + Buffer *pInBuffer; + Buffer inBuffer; + uint32_t channels = mCblk->channels; + + inBuffer.frameCount = frames; + inBuffer.i16 = data; + + if (mCblk->user == 0) { + if (mOutputMixerThread->isMusicActive()) { + mCblk->forceReady = 1; + LOGV("OutputTrack::start() force ready"); + } else if (mCblk->frameCount > frames){ + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + uint32_t startFrames = (mCblk->frameCount - frames); + LOGV("OutputTrack::start() write %d frames", startFrames); + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->frameCount = startFrames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW ("OutputTrack::write() no more buffers"); + } + } + } + + while (1) { + // First write pending buffers, then new data + if (mBufferQueue.size()) { + pInBuffer = mBufferQueue.itemAt(0); + } else { + pInBuffer = &inBuffer; + } + + if (pInBuffer->frameCount == 0) { + break; + } + + if (mOutBuffer.frameCount == 0) { + mOutBuffer.frameCount = pInBuffer->frameCount; + if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + break; + } + } + + uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; + memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); + mCblk->stepUser(outFrames); + pInBuffer->frameCount -= outFrames; + pInBuffer->i16 += outFrames * channels; + mOutBuffer.frameCount -= outFrames; + mOutBuffer.i16 += outFrames * channels; + + if (pInBuffer->frameCount == 0) { + if (mBufferQueue.size()) { + mBufferQueue.removeAt(0); + delete [] pInBuffer->mBuffer; + delete pInBuffer; + } else { + break; + } + } + } + + // If we could not write all frames, allocate a buffer and queue it for next time. + if (inBuffer.frameCount) { + if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; + pInBuffer->frameCount = inBuffer.frameCount; + pInBuffer->i16 = pInBuffer->mBuffer; + memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW("OutputTrack::write() no more buffers"); + } + } + + // Calling write() with a 0 length buffer, means that no more data will be written: + // If no more buffers are pending, fill output track buffer to make sure it is started + // by output mixer. + if (frames == 0 && mBufferQueue.size() == 0 && mCblk->user < mCblk->frameCount) { + frames = mCblk->frameCount - mCblk->user; + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->frameCount = frames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } + +} + +status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer) +{ + int active; + int timeout = 0; + status_t result; + audio_track_cblk_t* cblk = mCblk; + uint32_t framesReq = buffer->frameCount; + + LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); + buffer->frameCount = 0; + + uint32_t framesAvail = cblk->framesAvailable(); + + if (framesAvail == 0) { + return AudioTrack::NO_MORE_BUFFERS; + } + + if (framesReq > framesAvail) { + framesReq = framesAvail; + } + + uint32_t u = cblk->user; + uint32_t bufferEnd = cblk->userBase + cblk->frameCount; + + if (u + framesReq > bufferEnd) { + framesReq = bufferEnd - u; + } + + buffer->frameCount = framesReq; + buffer->raw = (void *)cblk->buffer(u); + return NO_ERROR; +} + + +void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue() +{ + size_t size = mBufferQueue.size(); + Buffer *pBuffer; + + for (size_t i = 0; i < size; i++) { + pBuffer = mBufferQueue.itemAt(i); + delete [] pBuffer->mBuffer; + delete pBuffer; + } + mBufferQueue.clear(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) + : RefBase(), + mAudioFlinger(audioFlinger), + mMemoryDealer(new MemoryDealer(1024*1024)), + mPid(pid) +{ + // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer +} + +AudioFlinger::Client::~Client() +{ + mAudioFlinger->removeClient(mPid); +} + +const sp<MemoryDealer>& AudioFlinger::Client::heap() const +{ + return mMemoryDealer; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::MixerThread::Track>& track) + : BnAudioTrack(), + mTrack(track) +{ +} + +AudioFlinger::TrackHandle::~TrackHandle() { + // just stop the track on deletion, associated resources + // will be freed from the main thread once all pending buffers have + // been played. Unless it's not in the active track list, in which + // case we free everything now... + mTrack->destroy(); +} + +status_t AudioFlinger::TrackHandle::start() { + return mTrack->start(); +} + +void AudioFlinger::TrackHandle::stop() { + mTrack->stop(); +} + +void AudioFlinger::TrackHandle::flush() { + mTrack->flush(); +} + +void AudioFlinger::TrackHandle::mute(bool e) { + mTrack->mute(e); +} + +void AudioFlinger::TrackHandle::pause() { + mTrack->pause(); +} + +void AudioFlinger::TrackHandle::setVolume(float left, float right) { + mTrack->setVolume(left, right); +} + +sp<IMemory> AudioFlinger::TrackHandle::getCblk() const { + return mTrack->getCblk(); +} + +status_t AudioFlinger::TrackHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioTrack::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +sp<IAudioRecord> AudioFlinger::openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status) +{ + sp<AudioRecordThread> thread; + sp<MixerThread::RecordTrack> recordTrack; + sp<RecordHandle> recordHandle; + sp<Client> client; + wp<Client> wclient; + AudioStreamIn* input = 0; + int inFrameCount; + size_t inputBufferSize; + status_t lStatus; + + // check calling permissions + if (!recordingAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } + + if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + lStatus = BAD_VALUE; + goto Exit; + } + + if (sampleRate > MAX_SAMPLE_RATE) { + LOGE("Sample rate out of range"); + lStatus = BAD_VALUE; + goto Exit; + } + + if (mAudioRecordThread == 0) { + LOGE("Audio record thread not started"); + lStatus = NO_INIT; + goto Exit; + } + + + // Check that audio input stream accepts requested audio parameters + inputBufferSize = mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); + if (inputBufferSize == 0) { + lStatus = BAD_VALUE; + LOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount); + goto Exit; + } + + // add client to list + { + Mutex::Autolock _l(mLock); + wclient = mClients.valueFor(pid); + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + } + + // frameCount must be a multiple of input buffer size + inFrameCount = inputBufferSize/channelCount/sizeof(short); + frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; + + // create new record track and pass to record thread + recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, streamType, sampleRate, + format, channelCount, frameCount, flags); + if (recordTrack->getCblk() == NULL) { + recordTrack.clear(); + lStatus = NO_MEMORY; + goto Exit; + } + + // return to handle to client + recordHandle = new RecordHandle(recordTrack); + lStatus = NO_ERROR; + +Exit: + if (status) { + *status = lStatus; + } + return recordHandle; +} + +status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + return mAudioRecordThread->start(recordTrack); + } + return NO_INIT; +} + +void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) { + if (mAudioRecordThread != 0) { + mAudioRecordThread->stop(recordTrack); + } +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::MixerThread::RecordTrack>& recordTrack) + : BnAudioRecord(), + mRecordTrack(recordTrack) +{ +} + +AudioFlinger::RecordHandle::~RecordHandle() { + stop(); +} + +status_t AudioFlinger::RecordHandle::start() { + LOGV("RecordHandle::start()"); + return mRecordTrack->start(); +} + +void AudioFlinger::RecordHandle::stop() { + LOGV("RecordHandle::stop()"); + mRecordTrack->stop(); +} + +sp<IMemory> AudioFlinger::RecordHandle::getCblk() const { + return mRecordTrack->getCblk(); +} + +status_t AudioFlinger::RecordHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioRecord::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware) : + mAudioHardware(audioHardware), + mActive(false) +{ +} + +AudioFlinger::AudioRecordThread::~AudioRecordThread() +{ +} + +bool AudioFlinger::AudioRecordThread::threadLoop() +{ + LOGV("AudioRecordThread: start record loop"); + AudioBufferProvider::Buffer buffer; + int inBufferSize = 0; + int inFrameCount = 0; + AudioStreamIn* input = 0; + + mActive = 0; + + // start recording + while (!exitPending()) { + if (!mActive) { + mLock.lock(); + if (!mActive && !exitPending()) { + LOGV("AudioRecordThread: loop stopping"); + if (input) { + delete input; + input = 0; + } + mRecordTrack.clear(); + mStopped.signal(); + + mWaitWorkCV.wait(mLock); + + LOGV("AudioRecordThread: loop starting"); + if (mRecordTrack != 0) { + input = mAudioHardware->openInputStream(mRecordTrack->format(), + mRecordTrack->channelCount(), + mRecordTrack->sampleRate(), + &mStartStatus, + (AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16)); + if (input != 0) { + inBufferSize = input->bufferSize(); + inFrameCount = inBufferSize/input->frameSize(); + } + } else { + mStartStatus = NO_INIT; + } + if (mStartStatus !=NO_ERROR) { + LOGW("record start failed, status %d", mStartStatus); + mActive = false; + mRecordTrack.clear(); + } + mWaitWorkCV.signal(); + } + mLock.unlock(); + } else if (mRecordTrack != 0) { + + buffer.frameCount = inFrameCount; + if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) { + LOGV("AudioRecordThread read: %d frames", buffer.frameCount); + ssize_t bytesRead = input->read(buffer.raw, inBufferSize); + if (bytesRead < 0) { + LOGE("Error reading audio input"); + sleep(1); + } + mRecordTrack->releaseBuffer(&buffer); + mRecordTrack->overflow(); + } + + // client isn't retrieving buffers fast enough + else { + if (!mRecordTrack->setOverflow()) + LOGW("AudioRecordThread: buffer overflow"); + // Release the processor for a while before asking for a new buffer. + // This will give the application more chance to read from the buffer and + // clear the overflow. + usleep(5000); + } + } + } + + + if (input) { + delete input; + } + mRecordTrack.clear(); + + return false; +} + +status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack) +{ + LOGV("AudioRecordThread::start"); + AutoMutex lock(&mLock); + mActive = true; + // If starting the active track, just reset mActive in case a stop + // was pending and exit + if (recordTrack == mRecordTrack.get()) return NO_ERROR; + + if (mRecordTrack != 0) return -EBUSY; + + mRecordTrack = recordTrack; + + // signal thread to start + LOGV("Signal record thread"); + mWaitWorkCV.signal(); + mWaitWorkCV.wait(mLock); + LOGV("Record started, status %d", mStartStatus); + return mStartStatus; +} + +void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) { + LOGV("AudioRecordThread::stop"); + AutoMutex lock(&mLock); + if (mActive && (recordTrack == mRecordTrack.get())) { + mActive = false; + mStopped.wait(mLock); + } +} + +void AudioFlinger::AudioRecordThread::exit() +{ + LOGV("AudioRecordThread::exit"); + { + AutoMutex lock(&mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + pid_t pid = 0; + + if (mRecordTrack != 0 && mRecordTrack->mClient != 0) { + snprintf(buffer, SIZE, "Record client pid: %d\n", mRecordTrack->mClient->pid()); + result.append(buffer); + } else { + result.append("No record client\n"); + } + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioFlinger::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- +void AudioFlinger::instantiate() { + defaultServiceManager()->addService( + String16("media.audio_flinger"), new AudioFlinger()); +} + +}; // namespace android diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h new file mode 100644 index 0000000..77f064b --- /dev/null +++ b/libs/audioflinger/AudioFlinger.h @@ -0,0 +1,637 @@ +/* //device/include/server/AudioFlinger/AudioFlinger.h +** +** Copyright 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 +** +** 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_FLINGER_H +#define ANDROID_AUDIO_FLINGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <media/IAudioFlinger.h> +#include <media/IAudioFlingerClient.h> +#include <media/IAudioTrack.h> +#include <media/IAudioRecord.h> +#include <media/AudioTrack.h> + +#include <utils/Atomic.h> +#include <utils/Errors.h> +#include <utils/threads.h> +#include <utils/MemoryDealer.h> +#include <utils/KeyedVector.h> +#include <utils/SortedVector.h> +#include <utils/Vector.h> + +#include <hardware_legacy/AudioHardwareInterface.h> + +#include "AudioBufferProvider.h" + +namespace android { + +class audio_track_cblk_t; +class AudioMixer; +class AudioBuffer; + + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + + +// ---------------------------------------------------------------------------- + +static const nsecs_t kStandbyTimeInNsecs = seconds(3); + +class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient +{ +public: + static void instantiate(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // IAudioFlinger interface + virtual sp<IAudioTrack> createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer, + status_t *status); + + virtual uint32_t sampleRate(int output) const; + virtual int channelCount(int output) const; + virtual int format(int output) const; + virtual size_t frameCount(int output) const; + virtual uint32_t latency(int output) const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream) const; + virtual bool streamMute(int stream) const; + + virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask); + virtual uint32_t getRouting(int mode) const; + + virtual status_t setMode(int mode); + virtual int getMode() const; + + virtual status_t setMicMute(bool state); + virtual bool getMicMute() const; + + virtual bool isMusicActive() const; + + virtual bool isA2dpEnabled() const; + + virtual status_t setParameter(const char* key, const char* value); + + virtual void registerClient(const sp<IAudioFlingerClient>& client); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + + virtual void wakeUp(); + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + enum hardware_call_state { + AUDIO_HW_IDLE = 0, + AUDIO_HW_INIT, + AUDIO_HW_OUTPUT_OPEN, + AUDIO_HW_OUTPUT_CLOSE, + AUDIO_HW_INPUT_OPEN, + AUDIO_HW_INPUT_CLOSE, + AUDIO_HW_STANDBY, + AUDIO_HW_SET_MASTER_VOLUME, + AUDIO_HW_GET_ROUTING, + AUDIO_HW_SET_ROUTING, + AUDIO_HW_GET_MODE, + AUDIO_HW_SET_MODE, + AUDIO_HW_GET_MIC_MUTE, + AUDIO_HW_SET_MIC_MUTE, + AUDIO_SET_VOICE_VOLUME, + AUDIO_SET_PARAMETER, + }; + + // record interface + virtual sp<IAudioRecord> openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + status_t *status); + + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + +private: + AudioFlinger(); + virtual ~AudioFlinger(); + + void setOutput(int outputType); + void doSetOutput(int outputType); + +#ifdef WITH_A2DP + void setA2dpEnabled(bool enable); +#endif + static bool streamForcedToSpeaker(int streamType); + + // Management of forced route to speaker for certain track types. + enum force_speaker_command { + ACTIVE_TRACK_ADDED = 0, + ACTIVE_TRACK_REMOVED, + CHECK_ROUTE_RESTORE_TIME, + FORCE_ROUTE_RESTORE + }; + void handleForcedSpeakerRoute(int command); + + // Internal dump utilites. + status_t dumpPermissionDenial(int fd, const Vector<String16>& args); + status_t dumpClients(int fd, const Vector<String16>& args); + status_t dumpInternals(int fd, const Vector<String16>& args); + + // --- Client --- + class Client : public RefBase { + public: + Client(const sp<AudioFlinger>& audioFlinger, pid_t pid); + virtual ~Client(); + const sp<MemoryDealer>& heap() const; + pid_t pid() const { return mPid; } + private: + Client(const Client&); + Client& operator = (const Client&); + sp<AudioFlinger> mAudioFlinger; + sp<MemoryDealer> mMemoryDealer; + pid_t mPid; + }; + + + class TrackHandle; + class RecordHandle; + class AudioRecordThread; + + + // --- MixerThread --- + class MixerThread : public Thread { + public: + + // --- Track --- + + // base for record and playback + class TrackBase : public AudioBufferProvider, public RefBase { + + public: + enum track_state { + IDLE, + TERMINATED, + STOPPED, + RESUMING, + ACTIVE, + PAUSING, + PAUSED + }; + + enum track_flags { + STEPSERVER_FAILED = 0x01, // StepServer could not acquire cblk->lock mutex + SYSTEM_FLAGS_MASK = 0x0000ffffUL, + // The upper 16 bits are used for track-specific flags. + }; + + TrackBase(const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags, + const sp<IMemory>& sharedBuffer); + ~TrackBase(); + + virtual status_t start() = 0; + virtual void stop() = 0; + sp<IMemory> getCblk() const; + + protected: + friend class MixerThread; + friend class RecordHandle; + friend class AudioRecordThread; + + TrackBase(const TrackBase&); + TrackBase& operator = (const TrackBase&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + audio_track_cblk_t* cblk() const { + return mCblk; + } + + int type() const { + return mStreamType; + } + + int format() const { + return mFormat; + } + + int channelCount() const ; + + int sampleRate() const; + + void* getBuffer(uint32_t offset, uint32_t frames) const; + + int name() const { + return mName; + } + + bool isStopped() const { + return mState == STOPPED; + } + + bool isTerminated() const { + return mState == TERMINATED; + } + + bool step(); + void reset(); + + sp<MixerThread> mMixerThread; + sp<Client> mClient; + sp<IMemory> mCblkMemory; + audio_track_cblk_t* mCblk; + int mStreamType; + void* mBuffer; + void* mBufferEnd; + uint32_t mFrameCount; + int mName; + // we don't really need a lock for these + int mState; + int mClientTid; + uint8_t mFormat; + uint32_t mFlags; + }; + + // playback track + class Track : public TrackBase { + public: + Track( const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer); + ~Track(); + + void dump(char* buffer, size_t size); + virtual status_t start(); + virtual void stop(); + void pause(); + + void flush(); + void destroy(); + void mute(bool); + void setVolume(float left, float right); + + protected: + friend class MixerThread; + friend class AudioFlinger; + friend class AudioFlinger::TrackHandle; + + Track(const Track&); + Track& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool isMuted() const { + return (mMute || mMixerThread->mStreamTypes[mStreamType].mute); + } + + bool isPausing() const { + return mState == PAUSING; + } + + bool isPaused() const { + return mState == PAUSED; + } + + bool isReady() const; + + void setPaused() { mState = PAUSED; } + void reset(); + + // we don't really need a lock for these + float mVolume[2]; + volatile bool mMute; + // FILLED state is used for suppressing volume ramp at begin of playing + enum {FS_FILLING, FS_FILLED, FS_ACTIVE}; + mutable uint8_t mFillingUpStatus; + int8_t mRetryCount; + sp<IMemory> mSharedBuffer; + bool mResetDone; + }; // end of Track + + // record track + class RecordTrack : public TrackBase { + public: + RecordTrack(const sp<MixerThread>& mixerThread, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags); + ~RecordTrack(); + + virtual status_t start(); + virtual void stop(); + + bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + + private: + friend class AudioFlinger; + friend class AudioFlinger::RecordHandle; + friend class AudioFlinger::AudioRecordThread; + friend class MixerThread; + + RecordTrack(const Track&); + RecordTrack& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool mOverflow; + }; + + // playback track + class OutputTrack : public Track { + public: + + class Buffer: public AudioBufferProvider::Buffer { + public: + int16_t *mBuffer; + }; + + OutputTrack( const sp<MixerThread>& mixerThread, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount); + ~OutputTrack(); + + virtual status_t start(); + virtual void stop(); + void write(int16_t* data, uint32_t frames); + bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; } + + private: + + status_t obtainBuffer(AudioBufferProvider::Buffer* buffer); + void clearBufferQueue(); + + sp<MixerThread> mOutputMixerThread; + Vector < Buffer* > mBufferQueue; + AudioBufferProvider::Buffer mOutBuffer; + uint32_t mFramesWritten; + + }; // end of OutputTrack + + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType); + virtual ~MixerThread(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // Thread virtuals + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + virtual uint32_t sampleRate() const; + virtual int channelCount() const; + virtual int format() const; + virtual size_t frameCount() const; + virtual uint32_t latency() const; + + virtual status_t setMasterVolume(float value); + virtual status_t setMasterMute(bool muted); + + virtual float masterVolume() const; + virtual bool masterMute() const; + + virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamMute(int stream, bool muted); + + virtual float streamVolume(int stream) const; + virtual bool streamMute(int stream) const; + + bool isMusicActive() const; + + + sp<Track> createTrack( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status); + + void wakeUp() { mWaitWorkCV.broadcast(); } + + void getTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks); + void putTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks); + void setOuputTrack(OutputTrack *track) { mOutputTrack = track; } + + struct stream_type_t { + stream_type_t() + : volume(1.0f), + mute(false) + { + } + float volume; + bool mute; + }; + + private: + + + friend class AudioFlinger; + friend class Track; + friend class TrackBase; + friend class RecordTrack; + + MixerThread(const Client&); + MixerThread& operator = (const MixerThread&); + + status_t addTrack(const sp<Track>& track); + void removeTrack(wp<Track> track, int name); + void remove_track_l(wp<Track> track, int name); + void destroyTrack(const sp<Track>& track); + int getTrackName(); + void deleteTrackName(int name); + void addActiveTrack(const wp<Track>& t); + void removeActiveTrack(const wp<Track>& t); + size_t getOutputFrameCount(); + + status_t dumpInternals(int fd, const Vector<String16>& args); + status_t dumpTracks(int fd, const Vector<String16>& args); + + sp<AudioFlinger> mAudioFlinger; + mutable Mutex mLock; + mutable Condition mWaitWorkCV; + SortedVector< wp<Track> > mActiveTracks; + SortedVector< sp<Track> > mTracks; + stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; + AudioMixer* mAudioMixer; + AudioStreamOut* mOutput; + int mOutputType; + uint32_t mSampleRate; + size_t mFrameCount; + int mChannelCount; + int mFormat; + int16_t* mMixBuffer; + float mMasterVolume; + bool mMasterMute; + nsecs_t mLastWriteTime; + int mNumWrites; + int mNumDelayedWrites; + bool mStandby; + bool mInWrite; + sp <OutputTrack> mOutputTrack; + }; + + + friend class AudioBuffer; + + class TrackHandle : public android::BnAudioTrack { + public: + TrackHandle(const sp<MixerThread::Track>& track); + virtual ~TrackHandle(); + virtual status_t start(); + virtual void stop(); + virtual void flush(); + virtual void mute(bool); + virtual void pause(); + virtual void setVolume(float left, float right); + virtual sp<IMemory> getCblk() const; + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + private: + sp<MixerThread::Track> mTrack; + }; + + friend class Client; + friend class MixerThread::Track; + + + void removeClient(pid_t pid); + + + + class RecordHandle : public android::BnAudioRecord { + public: + RecordHandle(const sp<MixerThread::RecordTrack>& recordTrack); + virtual ~RecordHandle(); + virtual status_t start(); + virtual void stop(); + virtual sp<IMemory> getCblk() const; + virtual status_t onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + private: + sp<MixerThread::RecordTrack> mRecordTrack; + }; + + // record thread + class AudioRecordThread : public Thread + { + public: + AudioRecordThread(AudioHardwareInterface* audioHardware); + virtual ~AudioRecordThread(); + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef() {} + + status_t start(MixerThread::RecordTrack* recordTrack); + void stop(MixerThread::RecordTrack* recordTrack); + void exit(); + status_t dump(int fd, const Vector<String16>& args); + + private: + AudioRecordThread(); + AudioHardwareInterface *mAudioHardware; + sp<MixerThread::RecordTrack> mRecordTrack; + Mutex mLock; + Condition mWaitWorkCV; + Condition mStopped; + volatile bool mActive; + status_t mStartStatus; + }; + + friend class AudioRecordThread; + friend class MixerThread; + + status_t startRecord(MixerThread::RecordTrack* recordTrack); + void stopRecord(MixerThread::RecordTrack* recordTrack); + + void handleOutputSwitch(); + + mutable Mutex mHardwareLock; + mutable Mutex mLock; + DefaultKeyedVector< pid_t, wp<Client> > mClients; + + sp<MixerThread> mA2dpMixerThread; + sp<MixerThread> mHardwareMixerThread; + AudioHardwareInterface* mAudioHardware; + AudioHardwareInterface* mA2dpAudioInterface; + sp<AudioRecordThread> mAudioRecordThread; + bool mA2dpEnabled; + bool mA2dpEnabledReq; + mutable int mHardwareStatus; + SortedVector< wp<IBinder> > mNotificationClients; + int mForcedSpeakerCount; + uint32_t mSavedRoute; + uint32_t mForcedRoute; + nsecs_t mRouteRestoreTime; + bool mMusicMuteSaved; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_FLINGER_H diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp new file mode 100644 index 0000000..62beada --- /dev/null +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -0,0 +1,313 @@ +/* +** +** Copyright 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 +** +** 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 <stdint.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sched.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#define LOG_TAG "AudioHardware" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "AudioHardwareGeneric.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static char const * const kAudioDeviceName = "/dev/eac"; + +// ---------------------------------------------------------------------------- + +AudioHardwareGeneric::AudioHardwareGeneric() + : mOutput(0), mInput(0), mFd(-1), mMicMute(false) +{ + mFd = ::open(kAudioDeviceName, O_RDWR); +} + +AudioHardwareGeneric::~AudioHardwareGeneric() +{ + if (mFd >= 0) ::close(mFd); + delete mOutput; + delete mInput; +} + +status_t AudioHardwareGeneric::initCheck() +{ + if (mFd >= 0) { + if (::access(kAudioDeviceName, O_RDWR) == NO_ERROR) + return NO_ERROR; + } + return NO_INIT; +} + +AudioStreamOut* AudioHardwareGeneric::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + AutoMutex lock(mLock); + + // only one output stream allowed + if (mOutput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); + status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mOutput = out; + } else { + delete out; + } + return mOutput; +} + +void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) { + if (out == mOutput) mOutput = 0; +} + +AudioStreamIn* AudioHardwareGeneric::openInputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) +{ + AutoMutex lock(mLock); + + // only one input stream allowed + if (mInput) { + if (status) { + *status = INVALID_OPERATION; + } + return 0; + } + + // create new output stream + AudioStreamInGeneric* in = new AudioStreamInGeneric(); + status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) { + mInput = in; + } else { + delete in; + } + return mInput; +} + +void AudioHardwareGeneric::closeInputStream(AudioStreamInGeneric* in) { + if (in == mInput) mInput = 0; +} + +status_t AudioHardwareGeneric::setVoiceVolume(float v) +{ + // Implement: set voice volume + return NO_ERROR; +} + +status_t AudioHardwareGeneric::setMasterVolume(float v) +{ + // Implement: set master volume + // return error - software mixer will handle it + return INVALID_OPERATION; +} + +status_t AudioHardwareGeneric::setMicMute(bool state) +{ + mMicMute = state; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::getMicMute(bool* state) +{ + *state = mMicMute; + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareGeneric::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmFd: %d mMicMute: %s\n", mFd, mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + if (mInput) { + mInput->dump(fd, args); + } + if (mOutput) { + mOutput->dump(fd, args); + } + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutGeneric::set( + AudioHardwareGeneric *hw, + int fd, + int format, + int channels, + uint32_t rate) +{ + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) + return BAD_VALUE; + + mAudioHardware = hw; + mFd = fd; + return NO_ERROR; +} + +AudioStreamOutGeneric::~AudioStreamOutGeneric() +{ + if (mAudioHardware) + mAudioHardware->closeOutputStream(this); +} + +ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) +{ + Mutex::Autolock _l(mLock); + return ssize_t(::write(mFd, buffer, bytes)); +} + +status_t AudioStreamOutGeneric::standby() +{ + // Implement: audio hardware to standby mode + return NO_ERROR; +} + +status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +// record functions +status_t AudioStreamInGeneric::set( + AudioHardwareGeneric *hw, + int fd, + int format, + int channels, + uint32_t rate, + AudioSystem::audio_in_acoustics acoustics) +{ + // FIXME: remove logging + LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, format, channels, rate); + // check values + if ((format != AudioSystem::PCM_16_BIT) || + (channels != channelCount()) || + (rate != sampleRate())) { + LOGE("Error opening input channel"); + return BAD_VALUE; + } + + mAudioHardware = hw; + mFd = fd; + return NO_ERROR; +} + +AudioStreamInGeneric::~AudioStreamInGeneric() +{ + // FIXME: remove logging + LOGD("AudioStreamInGeneric destructor"); + if (mAudioHardware) + mAudioHardware->closeInputStream(this); +} + +ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes) +{ + // FIXME: remove logging + LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, bytes, mFd); + AutoMutex lock(mLock); + if (mFd < 0) { + LOGE("Attempt to read from unopened device"); + return NO_INIT; + } + return ::read(mFd, buffer, bytes); +} + +status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInGeneric::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); + result.append(buffer); + snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h new file mode 100644 index 0000000..c949aa1 --- /dev/null +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -0,0 +1,141 @@ +/* +** +** Copyright 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 +** +** 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_GENERIC_H +#define ANDROID_AUDIO_HARDWARE_GENERIC_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioHardwareGeneric; + +class AudioStreamOutGeneric : public AudioStreamOut { +public: + AudioStreamOutGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamOutGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + int format, + int channelCount, + uint32_t sampleRate); + + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 20; } + virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; +}; + +class AudioStreamInGeneric : public AudioStreamIn { +public: + AudioStreamInGeneric() : mAudioHardware(0), mFd(-1) {} + virtual ~AudioStreamInGeneric(); + + virtual status_t set( + AudioHardwareGeneric *hw, + int mFd, + int format, + int channelCount, + uint32_t sampleRate, + AudioSystem::audio_in_acoustics acoustics); + + uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual int channelCount() const { return 1; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return INVALID_OPERATION; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; +}; + + +class AudioHardwareGeneric : public AudioHardwareBase +{ +public: + AudioHardwareGeneric(); + virtual ~AudioHardwareGeneric(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state); + virtual status_t getMicMute(bool* state); + + virtual status_t setParameter(const char* key, const char* value) + { return NO_ERROR; } + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + + void closeOutputStream(AudioStreamOutGeneric* out); + void closeInputStream(AudioStreamInGeneric* in); +protected: + virtual status_t doRouting() { return NO_ERROR; } + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + status_t dumpInternals(int fd, const Vector<String16>& args); + + Mutex mLock; + AudioStreamOutGeneric *mOutput; + AudioStreamInGeneric *mInput; + int mFd; + bool mMicMute; +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_GENERIC_H diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp new file mode 100644 index 0000000..ac76a19 --- /dev/null +++ b/libs/audioflinger/AudioHardwareInterface.cpp @@ -0,0 +1,247 @@ +/* +** +** Copyright 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 +** +** 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 <cutils/properties.h> +#include <string.h> +#include <unistd.h> + +#define LOG_TAG "AudioHardwareInterface" +#include <utils/Log.h> +#include <utils/String8.h> + +#include "AudioHardwareStub.h" +#include "AudioHardwareGeneric.h" + +//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file +#ifdef DUMP_FLINGER_OUT +#include "AudioDumpInterface.h" +#endif + + +// change to 1 to log routing calls +#define LOG_ROUTING_CALLS 0 + +namespace android { + +#if LOG_ROUTING_CALLS +static const char* routingModeStrings[] = +{ + "OUT OF RANGE", + "INVALID", + "CURRENT", + "NORMAL", + "RINGTONE", + "IN_CALL" +}; + +static const char* routeStrings[] = +{ + "EARPIECE ", + "SPEAKER ", + "BLUETOOTH ", + "HEADSET " + "BLUETOOTH_A2DP " +}; +static const char* routeNone = "NONE"; + +static const char* displayMode(int mode) +{ + if ((mode < -2) || (mode > 2)) + return routingModeStrings[0]; + return routingModeStrings[mode+3]; +} + +static const char* displayRoutes(uint32_t routes) +{ + static char routeStr[80]; + if (routes == 0) + return routeNone; + routeStr[0] = 0; + int bitMask = 1; + for (int i = 0; i < 4; ++i, bitMask <<= 1) { + if (routes & bitMask) { + strcat(routeStr, routeStrings[i]); + } + } + routeStr[strlen(routeStr)-1] = 0; + return routeStr; +} +#endif + +// ---------------------------------------------------------------------------- + +AudioHardwareInterface* AudioHardwareInterface::create() +{ + /* + * FIXME: This code needs to instantiate the correct audio device + * interface. For now - we use compile-time switches. + */ + AudioHardwareInterface* hw = 0; + char value[PROPERTY_VALUE_MAX]; + +#ifdef GENERIC_AUDIO + hw = new AudioHardwareGeneric(); +#else + // if running in emulation - use the emulator driver + if (property_get("ro.kernel.qemu", value, 0)) { + LOGD("Running in emulation - using generic audio driver"); + hw = new AudioHardwareGeneric(); + } + else { + LOGV("Creating Vendor Specific AudioHardware"); + hw = createAudioHardware(); + } +#endif + if (hw->initCheck() != NO_ERROR) { + LOGW("Using stubbed audio hardware. No sound will be produced."); + delete hw; + hw = new AudioHardwareStub(); + } + +#ifdef DUMP_FLINGER_OUT + // This code adds a record of buffers in a file to write calls made by AudioFlinger. + // It replaces the current AudioHardwareInterface object by an intermediate one which + // will record buffers in a file (after sending them to hardware) for testing purpose. + // This feature is enabled by defining symbol DUMP_FLINGER_OUT. + // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file. + + hw = new AudioDumpInterface(hw); // replace interface +#endif + return hw; +} + +AudioStreamOut::~AudioStreamOut() +{ +} + +AudioStreamIn::~AudioStreamIn() {} + +AudioHardwareBase::AudioHardwareBase() +{ + // force a routing update on initialization + memset(&mRoutes, 0, sizeof(mRoutes)); + mMode = 0; +} + +// generics for audio routing - the real work is done in doRouting +status_t AudioHardwareBase::setRouting(int mode, uint32_t routes) +{ +#if LOG_ROUTING_CALLS + LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes)); +#endif + if (mode == AudioSystem::MODE_CURRENT) + mode = mMode; + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + uint32_t old = mRoutes[mode]; + mRoutes[mode] = routes; + if ((mode != mMode) || (old == routes)) + return NO_ERROR; +#if LOG_ROUTING_CALLS + const char* oldRouteStr = strdup(displayRoutes(old)); + LOGD("doRouting: mode=%s, old route=[%s], new route=[%s]", + displayMode(mode), oldRouteStr, displayRoutes(routes)); + delete oldRouteStr; +#endif + return doRouting(); +} + +status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes) +{ + if (mode == AudioSystem::MODE_CURRENT) + mode = mMode; + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + *routes = mRoutes[mode]; +#if LOG_ROUTING_CALLS + LOGD("getRouting: mode=%s, routes=[%s]", + displayMode(mode), displayRoutes(*routes)); +#endif + return NO_ERROR; +} + +status_t AudioHardwareBase::setMode(int mode) +{ +#if LOG_ROUTING_CALLS + LOGD("setMode(%s)", displayMode(mode)); +#endif + if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) + return BAD_VALUE; + if (mMode == mode) + return NO_ERROR; +#if LOG_ROUTING_CALLS + LOGD("doRouting: old mode=%s, new mode=%s route=[%s]", + displayMode(mMode), displayMode(mode), displayRoutes(mRoutes[mode])); +#endif + mMode = mode; + return doRouting(); +} + +status_t AudioHardwareBase::getMode(int* mode) +{ + // Implement: set audio routing + *mode = mMode; + return NO_ERROR; +} + +status_t AudioHardwareBase::setParameter(const char* key, const char* value) +{ + // default implementation is to ignore + return NO_ERROR; +} + + +// default implementation +size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + if (sampleRate != 8000) { + 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; + } + + return 320; +} + +status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); + result.append(buffer); + for (int i = 0, n = AudioSystem::NUM_MODES; i < n; ++i) { + snprintf(buffer, SIZE, "\tmRoutes[%d]: %d\n", i, mRoutes[i]); + result.append(buffer); + } + ::write(fd, result.string(), result.size()); + dump(fd, args); // Dump the state of the concrete child. + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp new file mode 100644 index 0000000..b13cb1c --- /dev/null +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -0,0 +1,185 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.cpp +** +** Copyright 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 +** +** 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 <stdint.h> +#include <sys/types.h> + +#include <stdlib.h> +#include <unistd.h> +#include <utils/String8.h> + +#include "AudioHardwareStub.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +AudioHardwareStub::AudioHardwareStub() : mMicMute(false) +{ +} + +AudioHardwareStub::~AudioHardwareStub() +{ +} + +status_t AudioHardwareStub::initCheck() +{ + return NO_ERROR; +} + +AudioStreamOut* AudioHardwareStub::openOutputStream( + int format, int channelCount, uint32_t sampleRate, status_t *status) +{ + AudioStreamOutStub* out = new AudioStreamOutStub(); + status_t lStatus = out->set(format, channelCount, sampleRate); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return out; + delete out; + return 0; +} + +AudioStreamIn* AudioHardwareStub::openInputStream( + int format, int channelCount, uint32_t sampleRate, + status_t *status, AudioSystem::audio_in_acoustics acoustics) +{ + AudioStreamInStub* in = new AudioStreamInStub(); + status_t lStatus = in->set(format, channelCount, sampleRate, acoustics); + if (status) { + *status = lStatus; + } + if (lStatus == NO_ERROR) + return in; + delete in; + return 0; +} + +status_t AudioHardwareStub::setVoiceVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::setMasterVolume(float volume) +{ + return NO_ERROR; +} + +status_t AudioHardwareStub::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + result.append("AudioHardwareStub::dumpInternals\n"); + snprintf(buffer, SIZE, "\tmMicMute: %s\n", mMicMute? "true": "false"); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate) +{ + // fix up defaults + if (format == 0) format = AudioSystem::PCM_16_BIT; + if (channels == 0) channels = channelCount(); + if (rate == 0) rate = sampleRate(); + + if ((format == AudioSystem::PCM_16_BIT) && + (channels == channelCount()) && + (rate == sampleRate())) + return NO_ERROR; + return BAD_VALUE; +} + +ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) +{ + // fake timing for audio output + usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + return bytes; +} + +status_t AudioStreamOutStub::standby() +{ + return NO_ERROR; +} + +status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n"); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t AudioStreamInStub::set(int format, int channels, uint32_t rate, + AudioSystem::audio_in_acoustics acoustics) +{ + if ((format == AudioSystem::PCM_16_BIT) && + (channels == channelCount()) && + (rate == sampleRate())) + return NO_ERROR; + return BAD_VALUE; +} + +ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes) +{ + // fake timing for audio input + usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + memset(buffer, 0, bytes); + return bytes; +} + +status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioStreamInStub::dump\n"); + result.append(buffer); + snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); + result.append(buffer); + snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); + result.append(buffer); + snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + result.append(buffer); + snprintf(buffer, SIZE, "\tformat: %d\n", format()); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h new file mode 100644 index 0000000..d406424 --- /dev/null +++ b/libs/audioflinger/AudioHardwareStub.h @@ -0,0 +1,100 @@ +/* //device/servers/AudioFlinger/AudioHardwareStub.h +** +** Copyright 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 +** +** 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_STUB_H +#define ANDROID_AUDIO_HARDWARE_STUB_H + +#include <stdint.h> +#include <sys/types.h> + +#include <hardware_legacy/AudioHardwareBase.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioStreamOutStub : public AudioStreamOut { +public: + virtual status_t set(int format, int channelCount, uint32_t sampleRate); + virtual uint32_t sampleRate() const { return 44100; } + virtual size_t bufferSize() const { return 4096; } + virtual int channelCount() const { return 2; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual uint32_t latency() const { return 0; } + virtual status_t setVolume(float volume) { return NO_ERROR; } + virtual ssize_t write(const void* buffer, size_t bytes); + virtual status_t standby(); + virtual status_t dump(int fd, const Vector<String16>& args); +}; + +class AudioStreamInStub : public AudioStreamIn { +public: + virtual status_t set(int format, int channelCount, uint32_t sampleRate, AudioSystem::audio_in_acoustics acoustics); + virtual uint32_t sampleRate() const { return 8000; } + virtual size_t bufferSize() const { return 320; } + virtual int channelCount() const { return 1; } + virtual int format() const { return AudioSystem::PCM_16_BIT; } + virtual status_t setGain(float gain) { return NO_ERROR; } + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t standby() { return NO_ERROR; } +}; + +class AudioHardwareStub : public AudioHardwareBase +{ +public: + AudioHardwareStub(); + virtual ~AudioHardwareStub(); + virtual status_t initCheck(); + virtual status_t setVoiceVolume(float volume); + virtual status_t setMasterVolume(float volume); + + // mic mute + virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; } + virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; } + + virtual status_t setParameter(const char* key, const char* value) + { return NO_ERROR; } + + // create I/O streams + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0, + status_t *status=0); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate, + status_t *status, + AudioSystem::audio_in_acoustics acoustics); + +protected: + virtual status_t doRouting() { return NO_ERROR; } + virtual status_t dump(int fd, const Vector<String16>& args); + + bool mMicMute; +private: + status_t dumpInternals(int fd, const Vector<String16>& args); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_AUDIO_HARDWARE_STUB_H diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp new file mode 100644 index 0000000..b03467f --- /dev/null +++ b/libs/audioflinger/AudioMixer.cpp @@ -0,0 +1,913 @@ +/* //device/include/server/AudioFlinger/AudioMixer.cpp +** +** Copyright 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 +** +** 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 "AudioMixer" + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include "AudioMixer.h" + +namespace android { +// ---------------------------------------------------------------------------- + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +// ---------------------------------------------------------------------------- + +AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) + : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate) +{ + mState.enabledTracks= 0; + mState.needsChanged = 0; + mState.frameCount = frameCount; + mState.outputTemp = 0; + mState.resampleTemp = 0; + mState.hook = process__nop; + track_t* t = mState.tracks; + for (int i=0 ; i<32 ; i++) { + t->needs = 0; + t->volume[0] = UNITY_GAIN; + t->volume[1] = UNITY_GAIN; + t->volumeInc[0] = 0; + t->volumeInc[1] = 0; + t->channelCount = 2; + t->enabled = 0; + t->format = 16; + t->buffer.raw = 0; + t->bufferProvider = 0; + t->hook = 0; + t->resampler = 0; + t->sampleRate = mSampleRate; + t->in = 0; + t++; + } +} + + AudioMixer::~AudioMixer() + { + track_t* t = mState.tracks; + for (int i=0 ; i<32 ; i++) { + delete t->resampler; + t++; + } + delete [] mState.outputTemp; + delete [] mState.resampleTemp; + } + + int AudioMixer::getTrackName() + { + uint32_t names = mTrackNames; + uint32_t mask = 1; + int n = 0; + while (names & mask) { + mask <<= 1; + n++; + } + if (mask) { + LOGV("add track (%d)", n); + mTrackNames |= mask; + return TRACK0 + n; + } + return -1; + } + + void AudioMixer::invalidateState(uint32_t mask) + { + if (mask) { + mState.needsChanged |= mask; + mState.hook = process__validate; + } + } + + void AudioMixer::deleteTrackName(int name) + { + name -= TRACK0; + if (uint32_t(name) < MAX_NUM_TRACKS) { + LOGV("deleteTrackName(%d)", name); + track_t& track(mState.tracks[ name ]); + if (track.enabled != 0) { + track.enabled = 0; + invalidateState(1<<name); + } + if (track.resampler) { + // delete the resampler + delete track.resampler; + track.resampler = 0; + track.sampleRate = mSampleRate; + invalidateState(1<<name); + } + track.volumeInc[0] = 0; + track.volumeInc[1] = 0; + mTrackNames &= ~(1<<name); + } + } + +status_t AudioMixer::enable(int name) +{ + switch (name) { + case MIXING: { + if (mState.tracks[ mActiveTrack ].enabled != 1) { + mState.tracks[ mActiveTrack ].enabled = 1; + LOGV("enable(%d)", mActiveTrack); + invalidateState(1<<mActiveTrack); + } + } break; + default: + return NAME_NOT_FOUND; + } + return NO_ERROR; +} + +status_t AudioMixer::disable(int name) +{ + switch (name) { + case MIXING: { + if (mState.tracks[ mActiveTrack ].enabled != 0) { + mState.tracks[ mActiveTrack ].enabled = 0; + LOGV("disable(%d)", mActiveTrack); + invalidateState(1<<mActiveTrack); + } + } break; + default: + return NAME_NOT_FOUND; + } + return NO_ERROR; +} + +status_t AudioMixer::setActiveTrack(int track) +{ + if (uint32_t(track-TRACK0) >= MAX_NUM_TRACKS) { + return BAD_VALUE; + } + mActiveTrack = track - TRACK0; + return NO_ERROR; +} + +status_t AudioMixer::setParameter(int target, int name, int value) +{ + switch (target) { + case TRACK: + if (name == CHANNEL_COUNT) { + if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) { + if (mState.tracks[ mActiveTrack ].channelCount != value) { + mState.tracks[ mActiveTrack ].channelCount = value; + LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value); + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + } + break; + case RESAMPLE: + if (name == SAMPLE_RATE) { + if (value > 0) { + track_t& track = mState.tracks[ mActiveTrack ]; + if (track.setResampler(uint32_t(value), mSampleRate)) { + LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)", + uint32_t(value)); + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + } + break; + case RAMP_VOLUME: + case VOLUME: + if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) { + track_t& track = mState.tracks[ mActiveTrack ]; + if (track.volume[name-VOLUME0] != value) { + track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16; + track.volume[name-VOLUME0] = value; + if (target == VOLUME) { + track.prevVolume[name-VOLUME0] = value << 16; + track.volumeInc[name-VOLUME0] = 0; + } else { + int32_t d = (value<<16) - track.prevVolume[name-VOLUME0]; + int32_t volInc = d / int32_t(mState.frameCount); + track.volumeInc[name-VOLUME0] = volInc; + if (volInc == 0) { + track.prevVolume[name-VOLUME0] = value << 16; + } + } + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + break; + } + return BAD_VALUE; +} + +bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) +{ + if (value!=devSampleRate || resampler) { + if (sampleRate != value) { + sampleRate = value; + if (resampler == 0) { + resampler = AudioResampler::create( + format, channelCount, devSampleRate); + } + return true; + } + } + return false; +} + +bool AudioMixer::track_t::doesResample() const +{ + return resampler != 0; +} + +inline +void AudioMixer::track_t::adjustVolumeRamp() +{ + for (int i=0 ; i<2 ; i++) { + if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || + ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { + volumeInc[i] = 0; + prevVolume[i] = volume[i]<<16; + } + } +} + + +status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer) +{ + mState.tracks[ mActiveTrack ].bufferProvider = buffer; + return NO_ERROR; +} + + + +void AudioMixer::process(void* output) +{ + mState.hook(&mState, output); +} + + +void AudioMixer::process__validate(state_t* state, void* output) +{ + LOGW_IF(!state->needsChanged, + "in process__validate() but nothing's invalid"); + + uint32_t changed = state->needsChanged; + state->needsChanged = 0; // clear the validation flag + + // recompute which tracks are enabled / disabled + uint32_t enabled = 0; + uint32_t disabled = 0; + while (changed) { + const int i = 31 - __builtin_clz(changed); + const uint32_t mask = 1<<i; + changed &= ~mask; + track_t& t = state->tracks[i]; + (t.enabled ? enabled : disabled) |= mask; + } + state->enabledTracks &= ~disabled; + state->enabledTracks |= enabled; + + // compute everything we need... + int countActiveTracks = 0; + int all16BitsStereoNoResample = 1; + int resampling = 0; + int volumeRamp = 0; + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + + countActiveTracks++; + track_t& t = state->tracks[i]; + uint32_t n = 0; + n |= NEEDS_CHANNEL_1 + t.channelCount - 1; + n |= NEEDS_FORMAT_16; + n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; + + if (t.volumeInc[0]|t.volumeInc[1]) { + volumeRamp = 1; + } else if (!t.doesResample() && t.volumeRL == 0) { + n |= NEEDS_MUTE_ENABLED; + } + t.needs = n; + + if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) { + t.hook = track__nop; + } else { + if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + all16BitsStereoNoResample = 0; + resampling = 1; + t.hook = track__genericResample; + } else { + if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ + t.hook = track__16BitsMono; + all16BitsStereoNoResample = 0; + } + if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_2){ + t.hook = track__16BitsStereo; + } + } + } + } + + // select the processing hooks + state->hook = process__nop; + if (countActiveTracks) { + if (resampling) { + if (!state->outputTemp) { + state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; + } + if (!state->resampleTemp) { + state->resampleTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; + } + state->hook = process__genericResampling; + } else { + if (state->outputTemp) { + delete [] state->outputTemp; + state->outputTemp = 0; + } + if (state->resampleTemp) { + delete [] state->resampleTemp; + state->resampleTemp = 0; + } + state->hook = process__genericNoResampling; + if (all16BitsStereoNoResample && !volumeRamp) { + if (countActiveTracks == 1) { + state->hook = process__OneTrack16BitsStereoNoResampling; + } + } + } + } + + LOGV("mixer configuration change: %d activeTracks (%08x) " + "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d", + countActiveTracks, state->enabledTracks, + all16BitsStereoNoResample, resampling, volumeRamp); + + state->hook(state, output); + + // Now that the volume ramp has been done, set optimal state and + // track hooks for subsequent mixer process + if (countActiveTracks) { + int allMuted = 1; + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + if (!t.doesResample() && t.volumeRL == 0) + { + t.needs |= NEEDS_MUTE_ENABLED; + t.hook = track__nop; + } else { + allMuted = 0; + } + } + if (allMuted) { + state->hook = process__nop; + } else if (!resampling && all16BitsStereoNoResample) { + if (countActiveTracks == 1) { + state->hook = process__OneTrack16BitsStereoNoResampling; + } + } + } +} + +static inline +int32_t mulAdd(int16_t in, int16_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smlabb %[out], %[in], %[v], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + in * int32_t(v); +#endif +} + +static inline +int32_t mul(int16_t in, int16_t v) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smulbb %[out], %[in], %[v] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v) + : ); + return out; +#else + return in * int32_t(v); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smlabb %[out], %[inRL], %[vRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) + : ); + } else { + asm( "smlatt %[out], %[inRL], %[vRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL), [a]"r"(a) + : ); + } + return out; +#else + if (left) { + return a + int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF); + } else { + return a + int16_t(inRL>>16) * int16_t(vRL>>16); + } +#endif +} + +static inline +int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smulbb %[out], %[inRL], %[vRL] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[inRL], %[vRL] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [vRL]"r"(vRL) + : ); + } + return out; +#else + if (left) { + return int16_t(inRL&0xFFFF) * int16_t(vRL&0xFFFF); + } else { + return int16_t(inRL>>16) * int16_t(vRL>>16); + } +#endif +} + + +void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +{ + t->resampler->setSampleRate(t->sampleRate); + + // ramp gain - resample to temp buffer and scale/mix in 2nd step + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); + t->resampler->resample(temp, outFrameCount, t->bufferProvider); + volumeRampStereo(t, out, outFrameCount, temp); + } + + // constant gain + else { + t->resampler->setVolume(t->volume[0], t->volume[1]); + t->resampler->resample(out, outFrameCount, t->bufferProvider); + } +} + +void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +{ +} + +void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + // ramp volume + do { + *out++ += (vl >> 16) * (*temp++ >> 12); + *out++ += (vr >> 16) * (*temp++ >> 12); + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); +} + +void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int16_t const *in = static_cast<int16_t const *>(t->in); + + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + *out++ += (vl >> 16) * (int32_t) *in++; + *out++ += (vr >> 16) * (int32_t) *in++; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); + } + + // constant gain + else { + const uint32_t vrl = t->volumeRL; + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + out[0] = mulAddRL(1, rl, vrl, out[0]); + out[1] = mulAddRL(0, rl, vrl, out[1]); + out += 2; + } while (--frameCount); + } + t->in = in; +} + +void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +{ + int16_t const *in = static_cast<int16_t const *>(t->in); + + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + int32_t l = *in++; + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * l; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(); + } + // constant gain + else { + const int16_t vl = t->volume[0]; + const int16_t vr = t->volume[1]; + do { + int16_t l = *in++; + out[0] = mulAdd(l, vl, out[0]); + out[1] = mulAdd(l, vr, out[1]); + out += 2; + } while (--frameCount); + } + t->in = in; +} + +inline +void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) +{ + for (size_t i=0 ; i<c ; i++) { + int32_t l = *sums++; + int32_t r = *sums++; + int32_t nl = l >> 12; + int32_t nr = r >> 12; + l = clamp16(nl); + r = clamp16(nr); + *out++ = (r<<16) | (l & 0xFFFF); + } +} + +// no-op case +void AudioMixer::process__nop(state_t* state, void* output) +{ + // this assumes output 16 bits stereo, no resampling + memset(output, 0, state->frameCount*4); + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + size_t outFrames = state->frameCount; + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + if (!t.buffer.raw) break; + outFrames -= t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } + } +} + +// generic code without resampling +void AudioMixer::process__genericNoResampling(state_t* state, void* output) +{ + int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); + + // acquire each track's buffer + uint32_t enabledTracks = state->enabledTracks; + uint32_t en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + t.buffer.frameCount = state->frameCount; + t.bufferProvider->getNextBuffer(&t.buffer); + t.frameCount = t.buffer.frameCount; + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) + enabledTracks &= ~(1<<i); + } + + // this assumes output 16 bits stereo, no resampling + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + do { + memset(outTemp, 0, sizeof(outTemp)); + + en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + size_t outFrames = BLOCKSIZE; + + while (outFrames) { + size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; + if (inFrames) { + (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp); + t.frameCount -= inFrames; + outFrames -= inFrames; + } + if (t.frameCount == 0 && outFrames) { + t.bufferProvider->releaseBuffer(&t.buffer); + t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames); + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + if (t.in == NULL) { + enabledTracks &= ~(1<<i); + break; + } + t.frameCount = t.buffer.frameCount; + } + } + } + + ditherAndClamp(out, outTemp, BLOCKSIZE); + out += BLOCKSIZE; + numFrames -= BLOCKSIZE; + } while (numFrames); + + + // release each track's buffer + en = enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + t.bufferProvider->releaseBuffer(&t.buffer); + } +} + +// generic code with resampling +void AudioMixer::process__genericResampling(state_t* state, void* output) +{ + int32_t* const outTemp = state->outputTemp; + const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount; + memset(outTemp, 0, size); + + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + + uint32_t en = state->enabledTracks; + while (en) { + const int i = 31 - __builtin_clz(en); + en &= ~(1<<i); + track_t& t = state->tracks[i]; + + // this is a little goofy, on the resampling case we don't + // acquire/release the buffers because it's done by + // the resampler. + if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + (t.hook)(&t, outTemp, numFrames, state->resampleTemp); + } else { + + size_t outFrames = numFrames; + + while (outFrames) { + t.buffer.frameCount = outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) break; + + (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp); + outFrames -= t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } + } + } + + ditherAndClamp(out, outTemp, numFrames); +} + +// one track, 16 bits stereo without resampling is the most common case +void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output) +{ + const int i = 31 - __builtin_clz(state->enabledTracks); + const track_t& t = state->tracks[i]; + + AudioBufferProvider::Buffer& b(t.buffer); + + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + + const int16_t vl = t.volume[0]; + const int16_t vr = t.volume[1]; + const uint32_t vrl = t.volumeRL; + while (numFrames) { + b.frameCount = numFrames; + t.bufferProvider->getNextBuffer(&b); + int16_t const *in = b.i16; + + // in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (in == NULL) { + memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); + return; + } + size_t outFrames = b.frameCount; + + if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { + // volume is boosted, so we might need to clamp even though + // we process only one track. + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } else { + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } + numFrames -= b.frameCount; + t.bufferProvider->releaseBuffer(&b); + } +} + +// 2 tracks is also a common case +void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output) +{ + int i; + uint32_t en = state->enabledTracks; + + i = 31 - __builtin_clz(en); + const track_t& t0 = state->tracks[i]; + AudioBufferProvider::Buffer& b0(t0.buffer); + + en &= ~(1<<i); + i = 31 - __builtin_clz(en); + const track_t& t1 = state->tracks[i]; + AudioBufferProvider::Buffer& b1(t1.buffer); + + int16_t const *in0; + const int16_t vl0 = t0.volume[0]; + const int16_t vr0 = t0.volume[1]; + size_t frameCount0 = 0; + + int16_t const *in1; + const int16_t vl1 = t1.volume[0]; + const int16_t vr1 = t1.volume[1]; + size_t frameCount1 = 0; + + int32_t* out = static_cast<int32_t*>(output); + size_t numFrames = state->frameCount; + int16_t const *buff = NULL; + + + while (numFrames) { + + if (frameCount0 == 0) { + b0.frameCount = numFrames; + t0.bufferProvider->getNextBuffer(&b0); + if (b0.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in0 = buff; + b0.frameCount = numFrames; + } else { + in0 = b0.i16; + } + frameCount0 = b0.frameCount; + } + if (frameCount1 == 0) { + b1.frameCount = numFrames; + t1.bufferProvider->getNextBuffer(&b1); + if (b1.i16 == NULL) { + if (buff == NULL) { + buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + } + in1 = buff; + b1.frameCount = numFrames; + } else { + in1 = b1.i16; + } + frameCount1 = b1.frameCount; + } + + size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; + + numFrames -= outFrames; + frameCount0 -= outFrames; + frameCount1 -= outFrames; + + do { + int32_t l0 = *in0++; + int32_t r0 = *in0++; + l0 = mul(l0, vl0); + r0 = mul(r0, vr0); + int32_t l = *in1++; + int32_t r = *in1++; + l = mulAdd(l, vl1, l0) >> 12; + r = mulAdd(r, vr1, r0) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + + if (frameCount0 == 0) { + t0.bufferProvider->releaseBuffer(&b0); + } + if (frameCount1 == 0) { + t1.bufferProvider->releaseBuffer(&b1); + } + } + + if (buff != NULL) { + delete [] buff; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h new file mode 100644 index 0000000..72ca28a --- /dev/null +++ b/libs/audioflinger/AudioMixer.h @@ -0,0 +1,192 @@ +/* //device/include/server/AudioFlinger/AudioMixer.h +** +** Copyright 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 +** +** 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_MIXER_H +#define ANDROID_AUDIO_MIXER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "AudioBufferProvider.h" +#include "AudioResampler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) +#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) + +// ---------------------------------------------------------------------------- + +class AudioMixer +{ +public: + AudioMixer(size_t frameCount, uint32_t sampleRate); + + ~AudioMixer(); + + static const uint32_t MAX_NUM_TRACKS = 32; + static const uint32_t MAX_NUM_CHANNELS = 2; + + static const uint16_t UNITY_GAIN = 0x1000; + + enum { // names + + // track units (32 units) + TRACK0 = 0x1000, + + // enable/disable + MIXING = 0x2000, + + // setParameter targets + TRACK = 0x3000, + RESAMPLE = 0x3001, + RAMP_VOLUME = 0x3002, // ramp to new volume + VOLUME = 0x3003, // don't ramp + + // set Parameter names + // for target TRACK + CHANNEL_COUNT = 0x4000, + FORMAT = 0x4001, + // for TARGET RESAMPLE + SAMPLE_RATE = 0x4100, + // for TARGET VOLUME (8 channels max) + VOLUME0 = 0x4200, + VOLUME1 = 0x4201, + }; + + + int getTrackName(); + void deleteTrackName(int name); + + status_t enable(int name); + status_t disable(int name); + + status_t setActiveTrack(int track); + status_t setParameter(int target, int name, int value); + + status_t setBufferProvider(AudioBufferProvider* bufferProvider); + void process(void* output); + + uint32_t trackNames() const { return mTrackNames; } + +private: + + enum { + NEEDS_CHANNEL_COUNT__MASK = 0x00000003, + NEEDS_FORMAT__MASK = 0x000000F0, + NEEDS_MUTE__MASK = 0x00000100, + NEEDS_RESAMPLE__MASK = 0x00001000, + }; + + enum { + NEEDS_CHANNEL_1 = 0x00000000, + NEEDS_CHANNEL_2 = 0x00000001, + + NEEDS_FORMAT_16 = 0x00000010, + + NEEDS_MUTE_DISABLED = 0x00000000, + NEEDS_MUTE_ENABLED = 0x00000100, + + NEEDS_RESAMPLE_DISABLED = 0x00000000, + NEEDS_RESAMPLE_ENABLED = 0x00001000, + }; + + static inline int32_t applyVolume(int32_t in, int32_t v) { + return in * v; + } + + + struct state_t; + + typedef void (*mix_t)(state_t* state, void* output); + + static const int BLOCKSIZE = 16; // 4 cache lines + + struct track_t { + uint32_t needs; + + union { + int16_t volume[2]; // [0]3.12 fixed point + int32_t volumeRL; + }; + + int32_t prevVolume[2]; + + int32_t volumeInc[2]; + + uint16_t frameCount; + + uint8_t channelCount : 4; + uint8_t enabled : 1; + uint8_t reserved0 : 3; + uint8_t format; + + AudioBufferProvider* bufferProvider; + mutable AudioBufferProvider::Buffer buffer; + + void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp); + void const* in; // current location in buffer + + AudioResampler* resampler; + uint32_t sampleRate; + + bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); + bool doesResample() const; + void adjustVolumeRamp(); + }; + + // pad to 32-bytes to fill cache line + struct state_t { + uint32_t enabledTracks; + uint32_t needsChanged; + size_t frameCount; + mix_t hook; + int32_t *outputTemp; + int32_t *resampleTemp; + int32_t reserved[2]; + track_t tracks[32]; __attribute__((aligned(32))); + }; + + int mActiveTrack; + uint32_t mTrackNames; + const uint32_t mSampleRate; + + state_t mState __attribute__((aligned(32))); + + void invalidateState(uint32_t mask); + + static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp); + static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); + static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); + + static void process__validate(state_t* state, void* output); + static void process__nop(state_t* state, void* output); + static void process__genericNoResampling(state_t* state, void* output); + static void process__genericResampling(state_t* state, void* output); + static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output); + static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_AUDIO_MIXER_H diff --git a/libs/audioflinger/AudioResampler.cpp b/libs/audioflinger/AudioResampler.cpp new file mode 100644 index 0000000..5dabacb --- /dev/null +++ b/libs/audioflinger/AudioResampler.cpp @@ -0,0 +1,595 @@ +/* + * 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 + * + * 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 "AudioResampler" +//#define LOG_NDEBUG 0 + +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#include <cutils/log.h> +#include <cutils/properties.h> +#include "AudioResampler.h" +#include "AudioResamplerSinc.h" +#include "AudioResamplerCubic.h" + +namespace android { + +#ifdef __ARM_ARCH_5E__ // optimized asm option + #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 +#endif // __ARM_ARCH_5E__ +// ---------------------------------------------------------------------------- + +class AudioResamplerOrder1 : public AudioResampler { +public: + AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) : + AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) { + } + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + // number of bits used in interpolation multiply - 15 bits avoids overflow + static const int kNumInterpBits = 15; + + // bits to shift the phase fraction down to avoid overflow + static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; + + void init() {} + void resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + void AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); + void AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement); +#endif // ASM_ARM_RESAMP1 + + static inline int32_t Interp(int32_t x0, int32_t x1, uint32_t f) { + return x0 + (((x1 - x0) * (int32_t)(f >> kPreInterpShift)) >> kNumInterpBits); + } + static inline void Advance(size_t* index, uint32_t* frac, uint32_t inc) { + *frac += inc; + *index += (size_t)(*frac >> kNumPhaseBits); + *frac &= kPhaseMask; + } + int mX0L; + int mX0R; +}; + +// ---------------------------------------------------------------------------- +AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, + int32_t sampleRate, int quality) { + + // can only create low quality resample now + AudioResampler* resampler; + + char value[PROPERTY_VALUE_MAX]; + if (property_get("af.resampler.quality", value, 0)) { + quality = atoi(value); + LOGD("forcing AudioResampler quality to %d", quality); + } + + if (quality == DEFAULT) + quality = LOW_QUALITY; + + switch (quality) { + default: + case LOW_QUALITY: + LOGV("Create linear Resampler"); + resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); + break; + case MED_QUALITY: + LOGV("Create cubic Resampler"); + resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); + break; + case HIGH_QUALITY: + LOGV("Create sinc Resampler"); + resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate); + break; + } + + // initialize resampler + resampler->init(); + return resampler; +} + +AudioResampler::AudioResampler(int bitDepth, int inChannelCount, + int32_t sampleRate) : + mBitDepth(bitDepth), mChannelCount(inChannelCount), + mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), + mPhaseFraction(0) { + // sanity check on format + if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) { + LOGE("Unsupported sample format, %d bits, %d channels", bitDepth, + inChannelCount); + // LOG_ASSERT(0); + } + + // initialize common members + mVolume[0] = mVolume[1] = 0; + mBuffer.frameCount = 0; + + // save format for quick lookup + if (inChannelCount == 1) { + mFormat = MONO_16_BIT; + } else { + mFormat = STEREO_16_BIT; + } +} + +AudioResampler::~AudioResampler() { +} + +void AudioResampler::setSampleRate(int32_t inSampleRate) { + mInSampleRate = inSampleRate; + mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate); +} + +void AudioResampler::setVolume(int16_t left, int16_t right) { + // TODO: Implement anti-zipper filter + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + // should never happen, but we overflow if it does + // LOG_ASSERT(outFrameCount < 32767); + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resampleMono16(out, outFrameCount, provider); + break; + case 2: + resampleStereo16(out, outFrameCount, provider); + break; + } +} + +void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + + while (outputIndex < outputSampleCount) { + + // buffer is empty, fetch a new one + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) { + goto resampleStereo16_exit; + } + + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer + } + + int16_t *in = mBuffer.i16; + + // handle boundary case + while (inputIndex == 0) { + // LOGE("boundary case\n"); + out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction); + out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction); + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (outputIndex == outputSampleCount) + break; + } + + // process input samples + // LOGE("general case\n"); + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); // 2 because 2 frames per loop + maxInIdx = mBuffer.frameCount - 2; + AsmStereo16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { + out[outputIndex++] += vl * Interp(in[inputIndex*2-2], + in[inputIndex*2], phaseFraction); + out[outputIndex++] += vr * Interp(in[inputIndex*2-1], + in[inputIndex*2+1], phaseFraction); + Advance(&inputIndex, &phaseFraction, phaseIncrement); + } + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // if done with buffer, save samples + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + + // LOGE("buffer done, new input index %d", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + +resampleStereo16_exit: + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // LOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d\n", + // outFrameCount, inputIndex, phaseFraction, phaseIncrement); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + while (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) { + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; + goto resampleMono16_exit; + } + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + if (mBuffer.frameCount > inputIndex) break; + + inputIndex -= mBuffer.frameCount; + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + // mBuffer.frameCount == 0 now so we reload a new buffer + } + int16_t *in = mBuffer.i16; + + // handle boundary case + while (inputIndex == 0) { + // LOGE("boundary case\n"); + int32_t sample = Interp(mX0L, in[0], phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (outputIndex == outputSampleCount) + break; + } + + // process input samples + // LOGE("general case\n"); + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + if (inputIndex + 2 < mBuffer.frameCount) { + int32_t* maxOutPt; + int32_t maxInIdx; + + maxOutPt = out + (outputSampleCount - 2); + maxInIdx = (int32_t)mBuffer.frameCount - 2; + AsmMono16Loop(in, maxOutPt, maxInIdx, outputIndex, out, inputIndex, vl, vr, + phaseFraction, phaseIncrement); + } +#endif // ASM_ARM_RESAMP1 + + while (outputIndex < outputSampleCount && inputIndex < mBuffer.frameCount) { + int32_t sample = Interp(in[inputIndex-1], in[inputIndex], + phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + } + + + // LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // if done with buffer, save samples + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + + // LOGE("buffer done, new input index %d", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer resets the buffer frameCount + // LOG_ASSERT(mBuffer.frameCount == 0); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + +resampleMono16_exit: + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +#ifdef ASM_ARM_RESAMP1 // asm optimisation for ResamplerOrder1 + +/******************************************************************* +* +* AsmMono16Loop +* asm optimized monotonic loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define MO_PARAM5 "36" // offset of parameter 5 (outputIndex) + + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" + // get parameters + " ldr r6, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, Out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 + // r13 sp + // r14 + + // the following loop works on 2 frames + + ".Y4L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y4L02\n" + +#define MO_ONE_FRAME \ + " add r0, r1, r7, asl #1\n" /* in + inputIndex */\ + " ldrsh r4, [r0]\n" /* in[inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r0, [r0, #-2]\n" /* in[inputIndex-1] */\ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ + " sub r4, r4, r0\n" /* in[inputIndex] - in[inputIndex-1] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r0, r0, r4\n" /* x0 - (..) */\ + " mla r5, r0, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ + " mla r4, r0, r11, r4\n" /* vr*interp + out[] */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */ + + MO_ONE_FRAME // frame 1 + MO_ONE_FRAME // frame 2 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y4L01\n" + ".Y4L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" MO_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" MO_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}\n" + ); +} + +/******************************************************************* +* +* AsmStereo16Loop +* asm optimized stereo loop version; one loop is 2 frames +* Input: +* in : pointer on input samples +* maxOutPt : pointer on first not filled +* maxInIdx : index on first not used +* outputIndex : pointer on current output index +* out : pointer on output buffer +* inputIndex : pointer on current input index +* vl, vr : left and right gain +* phaseFraction : pointer on current phase fraction +* phaseIncrement +* Ouput: +* outputIndex : +* out : updated buffer +* inputIndex : index of next to use +* phaseFraction : phase fraction for next interpolation +* +*******************************************************************/ +void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, + size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, + uint32_t &phaseFraction, uint32_t phaseIncrement) +{ +#define ST_PARAM5 "40" // offset of parameter 5 (outputIndex) + asm( + "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n" + // get parameters + " ldr r6, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " ldr r6, [r6]\n" // phaseFraction + " ldr r7, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " ldr r7, [r7]\n" // inputIndex + " ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " ldr r0, [r0]\n" // outputIndex + " add r8, r0, asl #2\n" // curOut + " ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement + " ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl + " ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr + + // r0 pin, x0, Samp + + // r1 in + // r2 maxOutPt + // r3 maxInIdx + + // r4 x1, i1, i3, out1 + // r5 out0 + + // r6 frac + // r7 inputIndex + // r8 curOut + + // r9 inc + // r10 vl + // r11 vr + + // r12 temporary + // r13 sp + // r14 + + ".Y5L01:\n" + " cmp r8, r2\n" // curOut - maxCurOut + " bcs .Y5L02\n" + +#define ST_ONE_FRAME \ + " bic r6, r6, #0xC0000000\n" /* phaseFraction & ... */\ +\ + " add r0, r1, r7, asl #2\n" /* in + 2*inputIndex */\ +\ + " ldrsh r4, [r0]\n" /* in[2*inputIndex] */\ + " ldr r5, [r8]\n" /* out[outputIndex] */\ + " ldrsh r12, [r0, #-4]\n" /* in[2*inputIndex-2] */\ + " sub r4, r4, r12\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r4, r4, lsl #2\n" /* <<2 */\ + " smulwt r4, r4, r6\n" /* (x1-x0)*.. */\ + " add r12, r12, r4\n" /* x0 - (..) */\ + " mla r5, r12, r10, r5\n" /* vl*interp + out[] */\ + " ldr r4, [r8, #4]\n" /* out[outputIndex+1] */\ + " str r5, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " ldrsh r12, [r0, #+2]\n" /* in[2*inputIndex+1] */\ + " ldrsh r0, [r0, #-2]\n" /* in[2*inputIndex-1] */\ + " sub r12, r12, r0\n" /* in[2*InputIndex] - in[2*InputIndex-2] */\ + " mov r12, r12, lsl #2\n" /* <<2 */\ + " smulwt r12, r12, r6\n" /* (x1-x0)*.. */\ + " add r12, r0, r12\n" /* x0 - (..) */\ + " mla r4, r12, r11, r4\n" /* vr*interp + out[] */\ + " str r4, [r8], #4\n" /* out[outputIndex++] = ... */\ +\ + " add r6, r6, r9\n" /* phaseFraction + phaseIncrement */\ + " add r7, r7, r6, lsr #30\n" /* inputIndex + phaseFraction>>30 */ + + ST_ONE_FRAME // frame 1 + ST_ONE_FRAME // frame 1 + + " cmp r7, r3\n" // inputIndex - maxInIdx + " bcc .Y5L01\n" + ".Y5L02:\n" + + " bic r6, r6, #0xC0000000\n" // phaseFraction & ... + // save modified values + " ldr r0, [sp, #" ST_PARAM5 " + 20]\n" // &phaseFraction + " str r6, [r0]\n" // phaseFraction + " ldr r0, [sp, #" ST_PARAM5 " + 8]\n" // &inputIndex + " str r7, [r0]\n" // inputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 4]\n" // out + " sub r8, r0\n" // curOut - out + " asr r8, #2\n" // new outputIndex + " ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex + " str r8, [r0]\n" // save outputIndex + + " ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, pc}\n" + ); +} + +#endif // ASM_ARM_RESAMP1 + + +// ---------------------------------------------------------------------------- +} +; // namespace android + diff --git a/libs/audioflinger/AudioResampler.h b/libs/audioflinger/AudioResampler.h new file mode 100644 index 0000000..39656c0 --- /dev/null +++ b/libs/audioflinger/AudioResampler.h @@ -0,0 +1,93 @@ +/* + * 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 + * + * 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_RESAMPLER_H +#define ANDROID_AUDIO_RESAMPLER_H + +#include <stdint.h> +#include <sys/types.h> + +#include "AudioBufferProvider.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResampler { +public: + // Determines quality of SRC. + // LOW_QUALITY: linear interpolator (1st order) + // MED_QUALITY: cubic interpolator (3rd order) + // HIGH_QUALITY: fixed multi-tap FIR (e.g. 48KHz->44.1KHz) + // NOTE: high quality SRC will only be supported for + // certain fixed rate conversions. Sample rate cannot be + // changed dynamically. + enum src_quality { + DEFAULT=0, + LOW_QUALITY=1, + MED_QUALITY=2, + HIGH_QUALITY=3 + }; + + static AudioResampler* create(int bitDepth, int inChannelCount, + int32_t sampleRate, int quality=DEFAULT); + + virtual ~AudioResampler(); + + virtual void init() = 0; + virtual void setSampleRate(int32_t inSampleRate); + virtual void setVolume(int16_t left, int16_t right); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) = 0; + +protected: + // number of bits for phase fraction - 30 bits allows nearly 2x downsampling + static const int kNumPhaseBits = 30; + + // phase mask for fraction + static const uint32_t kPhaseMask = (1LU<<kNumPhaseBits)-1; + + // multiplier to calculate fixed point phase increment + static const double kPhaseMultiplier = 1L << kNumPhaseBits; + + enum format {MONO_16_BIT, STEREO_16_BIT}; + AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate); + + // prevent copying + AudioResampler(const AudioResampler&); + AudioResampler& operator=(const AudioResampler&); + + int32_t mBitDepth; + int32_t mChannelCount; + int32_t mSampleRate; + int32_t mInSampleRate; + AudioBufferProvider::Buffer mBuffer; + union { + int16_t mVolume[2]; + uint32_t mVolumeRL; + }; + int16_t mTargetVolume[2]; + format mFormat; + size_t mInputIndex; + int32_t mPhaseIncrement; + uint32_t mPhaseFraction; +}; + +// ---------------------------------------------------------------------------- +} +; // namespace android + +#endif // ANDROID_AUDIO_RESAMPLER_H diff --git a/libs/audioflinger/AudioResamplerCubic.cpp b/libs/audioflinger/AudioResamplerCubic.cpp new file mode 100644 index 0000000..1d247bd --- /dev/null +++ b/libs/audioflinger/AudioResamplerCubic.cpp @@ -0,0 +1,184 @@ +/* + * 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 + * + * 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 <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" +#include "AudioResamplerCubic.h" + +#define LOG_TAG "AudioSRC" + +namespace android { +// ---------------------------------------------------------------------------- + +void AudioResamplerCubic::init() { + memset(&left, 0, sizeof(state)); + memset(&right, 0, sizeof(state)); +} + +void AudioResamplerCubic::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + // should never happen, but we overflow if it does + // LOG_ASSERT(outFrameCount < 32767); + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resampleMono16(out, outFrameCount, provider); + break; + case 2: + resampleStereo16(out, outFrameCount, provider); + break; + } +} + +void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // fetch first buffer + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + return; + // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); + } + int16_t *in = mBuffer.i16; + + while (outputIndex < outputSampleCount) { + int32_t sample; + int32_t x; + + // calculate output sample + x = phaseFraction >> kPreInterpShift; + out[outputIndex++] += vl * interp(&left, x); + out[outputIndex++] += vr * interp(&right, x); + // out[outputIndex++] += vr * in[inputIndex*2]; + + // increment phase + phaseFraction += phaseIncrement; + uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); + phaseFraction &= kPhaseMask; + + // time to fetch another sample + while (indexIncrement--) { + + inputIndex++; + if (inputIndex == mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + goto save_state; // ugly, but efficient + in = mBuffer.i16; + // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount); + } + + // advance sample state + advance(&left, in[inputIndex*2]); + advance(&right, in[inputIndex*2+1]); + } + } + +save_state: + // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) { + + int32_t vl = mVolume[0]; + int32_t vr = mVolume[1]; + + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + // fetch first buffer + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + return; + // LOGW("New buffer: offset=%p, frames=%d\n", mBuffer.raw, mBuffer.frameCount); + } + int16_t *in = mBuffer.i16; + + while (outputIndex < outputSampleCount) { + int32_t sample; + int32_t x; + + // calculate output sample + x = phaseFraction >> kPreInterpShift; + sample = interp(&left, x); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + + // increment phase + phaseFraction += phaseIncrement; + uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits); + phaseFraction &= kPhaseMask; + + // time to fetch another sample + while (indexIncrement--) { + + inputIndex++; + if (inputIndex == mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + goto save_state; // ugly, but efficient + // LOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); + in = mBuffer.i16; + } + + // advance sample state + advance(&left, in[inputIndex]); + } + } + +save_state: + // LOGW("Done: index=%d, fraction=%u", inputIndex, phaseFraction); + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +// ---------------------------------------------------------------------------- +} +; // namespace android + diff --git a/libs/audioflinger/AudioResamplerCubic.h b/libs/audioflinger/AudioResamplerCubic.h new file mode 100644 index 0000000..b72b62a --- /dev/null +++ b/libs/audioflinger/AudioResamplerCubic.h @@ -0,0 +1,68 @@ +/* + * 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 + * + * 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_RESAMPLER_CUBIC_H +#define ANDROID_AUDIO_RESAMPLER_CUBIC_H + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class AudioResamplerCubic : public AudioResampler { +public: + AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) : + AudioResampler(bitDepth, inChannelCount, sampleRate) { + } + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + // number of bits used in interpolation multiply - 14 bits avoids overflow + static const int kNumInterpBits = 14; + + // bits to shift the phase fraction down to avoid overflow + static const int kPreInterpShift = kNumPhaseBits - kNumInterpBits; + typedef struct { + int32_t a, b, c, y0, y1, y2, y3; + } state; + void init(); + void resampleMono16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + void resampleStereo16(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + static inline int32_t interp(state* p, int32_t x) { + return (((((p->a * x >> 14) + p->b) * x >> 14) + p->c) * x >> 14) + p->y1; + } + static inline void advance(state* p, int16_t in) { + p->y0 = p->y1; + p->y1 = p->y2; + p->y2 = p->y3; + p->y3 = in; + p->a = (3 * (p->y1 - p->y2) - p->y0 + p->y3) >> 1; + p->b = (p->y2 << 1) + p->y0 - (((5 * p->y1 + p->y3)) >> 1); + p->c = (p->y2 - p->y0) >> 1; + } + state left, right; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_CUBIC_H*/ diff --git a/libs/audioflinger/AudioResamplerSinc.cpp b/libs/audioflinger/AudioResamplerSinc.cpp new file mode 100644 index 0000000..9e5e254 --- /dev/null +++ b/libs/audioflinger/AudioResamplerSinc.cpp @@ -0,0 +1,358 @@ +/* + * 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 + * + * 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 <string.h> +#include "AudioResamplerSinc.h" + +namespace android { +// ---------------------------------------------------------------------------- + + +/* + * These coeficients are computed with the "fir" utility found in + * tools/resampler_tools + * TODO: A good optimization would be to transpose this matrix, to take + * better advantage of the data-cache. + */ +const int32_t AudioResamplerSinc::mFirCoefsUp[] = { + 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, + 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, + 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, + 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, + 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, + 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, + 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, + 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient +}; + +/* + * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz) + * It's possible to use the above coefficient for any down-sampling + * at the expense of a slower processing loop (we can interpolate + * these coefficient from the above by "Stretching" them in time). + */ +const int32_t AudioResamplerSinc::mFirCoefsDown[] = { + 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, + 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, + 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, + 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, + 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, + 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, + 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, + 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, + 0x00000000 // this one is needed for lerping the last coefficient +}; + +// ---------------------------------------------------------------------------- + +static inline +int32_t mulRL(int left, int32_t in, uint32_t vRL) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smultb %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } + return out; +#else + if (left) { + return int16_t(in>>16) * int16_t(vRL&0xFFFF); + } else { + return int16_t(in>>16) * int16_t(vRL>>16); + } +#endif +} + +static inline +int32_t mulAdd(int16_t in, int32_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smlawb %[out], %[v], %[in], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + in * (v>>16); + // improved precision + // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + if (left) { + asm( "smlawb %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } else { + asm( "smlawt %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } + return out; +#else + if (left) { + return a + (int16_t(inRL&0xFFFF) * (v>>16)); + //improved precision + // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16); + } else { + return a + (int16_t(inRL>>16) * (v>>16)); + } +#endif +} + +// ---------------------------------------------------------------------------- + +AudioResamplerSinc::AudioResamplerSinc(int bitDepth, + int inChannelCount, int32_t sampleRate) + : AudioResampler(bitDepth, inChannelCount, sampleRate), + mState(0) +{ + /* + * Layout of the state buffer for 32 tap: + * + * "present" sample beginning of 2nd buffer + * v v + * 0 01 2 23 3 + * 0 F0 0 F0 F + * [pppppppppppppppInnnnnnnnnnnnnnnnpppppppppppppppInnnnnnnnnnnnnnnn] + * ^ ^ head + * + * p = past samples, convoluted with the (p)ositive side of sinc() + * n = future samples, convoluted with the (n)egative side of sinc() + * r = extra space for implementing the ring buffer + * + */ + + const size_t numCoefs = 2*halfNumCoefs; + const size_t stateSize = numCoefs * inChannelCount * 2; + mState = new int16_t[stateSize]; + memset(mState, 0, sizeof(int16_t)*stateSize); + mImpulse = mState + (halfNumCoefs-1)*inChannelCount; + mRingFull = mImpulse + (numCoefs+1)*inChannelCount; +} + +AudioResamplerSinc::~AudioResamplerSinc() +{ + delete [] mState; +} + +void AudioResamplerSinc::init() { +} + +void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; + + // select the appropriate resampler + switch (mChannelCount) { + case 1: + resample<1>(out, outFrameCount, provider); + break; + case 2: + resample<2>(out, outFrameCount, provider); + break; + } +} + + +template<int CHANNELS> +void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + int16_t* impulse = mImpulse; + uint32_t vRL = mVolumeRL; + size_t inputIndex = mInputIndex; + uint32_t phaseFraction = mPhaseFraction; + uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * 2; + size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + + AudioBufferProvider::Buffer& buffer(mBuffer); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + while (buffer.frameCount == 0) { + buffer.frameCount = inFrameCount; + provider->getNextBuffer(&buffer); + if (buffer.raw == NULL) { + goto resample_exit; + } + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + // read one frame + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + } else if (phaseIndex == 2) { + // read 2 frames + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + inputIndex++; + if (inputIndex >= mBuffer.frameCount) { + inputIndex -= mBuffer.frameCount; + provider->releaseBuffer(&buffer); + } else { + read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex); + } + } + } + int16_t *in = buffer.i16; + const size_t frameCount = buffer.frameCount; + + // Always read-in the first samples from the input buffer + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; + + // handle boundary case + int32_t l, r; + while (outputIndex < outputSampleCount) { + filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse); + out[outputIndex++] += 2 * mulRL(1, l, vRL); + out[outputIndex++] += 2 * mulRL(0, r, vRL); + + phaseFraction += phaseIncrement; + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex == 1) { + inputIndex++; + if (inputIndex >= frameCount) + break; // need a new buffer + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } else if(phaseIndex == 2) { // maximum value + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 2 frames needed + // read first frame + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + inputIndex++; + if (inputIndex >= frameCount) + break; // 0 frame available, 1 frame needed + // read second frame + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } + } + + // if done with buffer, save samples + if (inputIndex >= frameCount) { + inputIndex -= frameCount; + provider->releaseBuffer(&buffer); + } + } + +resample_exit: + mImpulse = impulse; + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +template<int CHANNELS> +/*** +* read() +* +* This function reads only one frame from input buffer and writes it in +* state buffer +* +**/ +void AudioResamplerSinc::read( + int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex) +{ + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + impulse += CHANNELS; + phaseFraction -= 1LU<<kNumPhaseBits; + if (impulse >= mRingFull) { + const size_t stateSize = (halfNumCoefs*2)*CHANNELS; + memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); + impulse -= stateSize; + } + int16_t* head = impulse + halfNumCoefs*CHANNELS; + head[0] = in[inputIndex*CHANNELS + 0]; + if (CHANNELS == 2) + head[1] = in[inputIndex*CHANNELS + 1]; +} + +template<int CHANNELS> +void AudioResamplerSinc::filterCoefficient( + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples) +{ + // compute the index of the coefficient on the positive side and + // negative side + uint32_t indexP = (phase & cMask) >> cShift; + uint16_t lerpP = (phase & pMask) >> pShift; + uint32_t indexN = (-phase & cMask) >> cShift; + uint16_t lerpN = (-phase & pMask) >> pShift; + if ((indexP == 0) && (lerpP == 0)) { + indexN = cMask >> cShift; + lerpN = pMask >> pShift; + } + + l = 0; + r = 0; + int32_t const* coefs = mFirCoefs; + int16_t const *sP = samples; + int16_t const *sN = samples+CHANNELS; + for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) { + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); + interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); + sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + } +} + +template<int CHANNELS> +void AudioResamplerSinc::interpolate( + int32_t& l, int32_t& r, + int32_t const* coefs, int16_t lerp, int16_t const* samples) +{ + int32_t c0 = coefs[0]; + int32_t c1 = coefs[1]; + int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); + if (CHANNELS == 2) { + uint32_t rl = *reinterpret_cast<uint32_t const*>(samples); + l = mulAddRL(1, rl, sinc, l); + r = mulAddRL(0, rl, sinc, r); + } else { + r = l = mulAdd(samples[0], sinc, l); + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/audioflinger/AudioResamplerSinc.h b/libs/audioflinger/AudioResamplerSinc.h new file mode 100644 index 0000000..e6cb90b --- /dev/null +++ b/libs/audioflinger/AudioResamplerSinc.h @@ -0,0 +1,88 @@ +/* + * 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 + * + * 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_RESAMPLER_SINC_H +#define ANDROID_AUDIO_RESAMPLER_SINC_H + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioResamplerSinc : public AudioResampler { +public: + AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate); + + ~AudioResamplerSinc(); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); +private: + void init(); + + template<int CHANNELS> + void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + + template<int CHANNELS> + inline void filterCoefficient( + int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples); + + template<int CHANNELS> + inline void interpolate( + int32_t& l, int32_t& r, + int32_t const* coefs, int16_t lerp, int16_t const* samples); + + template<int CHANNELS> + inline void read(int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex); + + int16_t *mState; + int16_t *mImpulse; + int16_t *mRingFull; + + int32_t const * mFirCoefs; + static const int32_t mFirCoefsDown[]; + static const int32_t mFirCoefsUp[]; + + // ---------------------------------------------------------------------------- + static const int32_t RESAMPLE_FIR_NUM_COEF = 8; + static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4; + + // we have 16 coefs samples per zero-crossing + static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4 + static const int cShift = kNumPhaseBits - coefsBits; // 26 + static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; // 0xf<<26 = 3c00 0000 + + // and we use 15 bits to interpolate between these samples + // this cannot change because the mul below rely on it. + static const int pLerpBits = 15; + static const int pShift = kNumPhaseBits - coefsBits - pLerpBits; // 11 + static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; // 0x7fff << 11 + + // number of zero-crossing on each side + static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_SINC_H*/ |