diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch) | |
tree | 35051494d2af230dce54d6b31c6af8fc24091316 /libs/audioflinger | |
download | frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2 |
Initial Contribution
Diffstat (limited to 'libs/audioflinger')
-rw-r--r-- | libs/audioflinger/Android.mk | 53 | ||||
-rw-r--r-- | libs/audioflinger/AudioBufferProvider.h | 47 | ||||
-rw-r--r-- | libs/audioflinger/AudioDumpInterface.cpp | 94 | ||||
-rw-r--r-- | libs/audioflinger/AudioDumpInterface.h | 101 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.cpp | 1450 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.h | 490 | ||||
-rw-r--r-- | libs/audioflinger/AudioHardwareGeneric.cpp | 293 | ||||
-rw-r--r-- | libs/audioflinger/AudioHardwareGeneric.h | 135 | ||||
-rw-r--r-- | libs/audioflinger/AudioHardwareInterface.cpp | 240 | ||||
-rw-r--r-- | libs/audioflinger/AudioHardwareStub.cpp | 175 | ||||
-rw-r--r-- | libs/audioflinger/AudioHardwareStub.h | 95 | ||||
-rw-r--r-- | libs/audioflinger/AudioMixer.cpp | 857 | ||||
-rw-r--r-- | libs/audioflinger/AudioMixer.h | 192 | ||||
-rw-r--r-- | libs/audioflinger/AudioResampler.cpp | 297 | ||||
-rw-r--r-- | libs/audioflinger/AudioResampler.h | 93 | ||||
-rw-r--r-- | libs/audioflinger/AudioResamplerCubic.cpp | 178 | ||||
-rw-r--r-- | libs/audioflinger/AudioResamplerCubic.h | 68 | ||||
-rw-r--r-- | libs/audioflinger/AudioResamplerSinc.cpp | 320 | ||||
-rw-r--r-- | libs/audioflinger/AudioResamplerSinc.h | 87 |
19 files changed, 5265 insertions, 0 deletions
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk new file mode 100644 index 0000000..a9cb303 --- /dev/null +++ b/libs/audioflinger/Android.mk @@ -0,0 +1,53 @@ +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 + +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 + +ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) + LOCAL_STATIC_LIBRARIES += libaudiointerface +else + LOCAL_SHARED_LIBRARIES += libaudio +endif + +LOCAL_MODULE:= libaudioflinger + +ifeq ($(TARGET_ARCH),arm) # not simulator + LOCAL_CFLAGS += -DWITH_BLUETOOTH + LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs) +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..5ff2f18 --- /dev/null +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -0,0 +1,94 @@ +/* //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 { + +// ---------------------------------------------------------------------------- + +AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) +{ + if(hw == 0) { + LOGE("Dump construct hw = 0"); + } + mFinalInterface = hw; + mStreamOut = 0; +} + + +status_t AudioDumpInterface::standby() +{ + if(mStreamOut) mStreamOut->Close(); + return mFinalInterface->standby(); +} + + +AudioStreamOut* AudioDumpInterface::openOutputStream( + int format, int channelCount, uint32_t sampleRate) +{ + AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate); + + if(outFinal) { + mStreamOut = new AudioStreamOutDump(outFinal); + return mStreamOut; + } else { + LOGE("Dump outFinal=0"); + return 0; + } +} + +// ---------------------------------------------------------------------------- + +AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* finalStream) +{ + mFinalStream = finalStream; + mOutFile = 0; +} + +ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) +{ + ssize_t ret; + + ret = mFinalStream->write(buffer, bytes); + if(!mOutFile) { + mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + } + if (mOutFile) { + fwrite(buffer, bytes, 1, mOutFile); + } + return ret; +} + +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..732b97d --- /dev/null +++ b/libs/audioflinger/AudioDumpInterface.h @@ -0,0 +1,101 @@ +/* //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/AudioHardwareInterface.h> + +namespace android { + +#define FLINGER_DUMP_NAME "/tmp/FlingerOut.pcm" // name of file used for dump + +class AudioStreamOutDump : public AudioStreamOut { +public: + AudioStreamOutDump( AudioStreamOut* FinalStream); + 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 status_t setVolume(float volume) + { return mFinalStream->setVolume(volume); } + 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 AudioHardwareInterface +{ + +public: + AudioDumpInterface(AudioHardwareInterface* hw); + virtual status_t standby(); + virtual AudioStreamOut* openOutputStream( + int format=0, + int channelCount=0, + uint32_t sampleRate=0); + + virtual ~AudioDumpInterface() + {delete mFinalInterface;} + 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);} + + virtual status_t setRouting(int mode, uint32_t routes) + {return mFinalInterface->setRouting(mode, routes);} + virtual status_t getRouting(int mode, uint32_t* routes) + {return mFinalInterface->getRouting(mode, routes);} + virtual status_t getMode(int* mode) + {return mFinalInterface->getMode(mode);} + + // 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) + {return mFinalInterface->openInputStream( format, channelCount, sampleRate);} + + virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } + +protected: + virtual status_t doRouting() {return 0;} + + 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..fb21629 --- /dev/null +++ b/libs/audioflinger/AudioFlinger.cpp @@ -0,0 +1,1450 @@ +/* //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 <media/AudioTrack.h> +#include <media/AudioRecord.h> + +#include <private/media/AudioTrackShared.h> + +#include <hardware/AudioHardwareInterface.h> + +#include "AudioMixer.h" +#include "AudioFlinger.h" + +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; + +#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(), Thread(false), + mMasterVolume(0), mMasterMute(true), + mAudioMixer(0), mAudioHardware(0), mOutput(0), mAudioRecordThread(0), + mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), + mMixBuffer(0), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), + mStandby(false), mInWrite(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; + mOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT); + mHardwareStatus = AUDIO_HW_IDLE; + if (mOutput) { + mSampleRate = mOutput->sampleRate(); + mChannelCount = mOutput->channelCount(); + mFormat = mOutput->format(); + mMixBufferSize = mOutput->bufferSize(); + mFrameCount = mMixBufferSize / mChannelCount / sizeof(int16_t); + mMixBuffer = new int16_t[mFrameCount * mChannelCount]; + memset(mMixBuffer, 0, mMixBufferSize); + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + // FIXME - this should come from settings + setMasterVolume(1.0f); + 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); + mMasterMute = false; + } else { + LOGE("Failed to initialize output stream"); + } + } else { + LOGE("Couldn't even initialize the stubbed audio hardware!"); + } +} + +AudioFlinger::~AudioFlinger() +{ + delete mOutput; + delete mAudioHardware; + delete [] mMixBuffer; + delete mAudioMixer; + mAudioRecordThread.clear(); +} + +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::dumpTracks(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + result.append("Tracks:\n"); + 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); + } + } + } + + result.append("Active Tracks:\n"); + 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::dumpInternals(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", audioMixer().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); + 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); + dumpTracks(fd, args); + dumpInternals(fd, args); + if (mAudioHardware) { + mAudioHardware->dumpState(fd, args); + } + } + return NO_ERROR; +} + +// Thread virtuals +bool AudioFlinger::threadLoop() +{ + nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; + unsigned long sleepTime = kBufferRecoveryInUsecs; + const size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + size_t enabledTracks; + nsecs_t standbyTime = systemTime(); + + do { + enabledTracks = 0; + { // scope for the lock + Mutex::Autolock _l(mLock); + 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\n"); + mHardwareStatus = AUDIO_HW_STANDBY; + if (!mStandby) { + mAudioHardware->standby(); + mStandby = true; + } + 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\n"); + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; + } + + // 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(); + uint32_t u = cblk->user; + uint32_t s = cblk->server; + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + audioMixer().setActiveTrack(track->name()); + if ((u > s) && (track->isReady(u, s) || track->isStopped()) && + !track->isPaused()) + { + //LOGD("u=%08x, s=%08x [OK]", u, s); + + // 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 + AudioMixer& mixer(audioMixer()); + mixer.setBufferProvider(track); + mixer.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; + } + mixer.setParameter(param, AudioMixer::VOLUME0, left); + mixer.setParameter(param, AudioMixer::VOLUME1, right); + mixer.setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mixer.setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mixer.setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + enabledTracks++; + } else { + //LOGD("u=%08x, s=%08x [NOT READY]", u, s); + if (track->isStopped()) { + track->mFillingUpStatus = Track::FS_FILLING; + track->mFlags = 0; + } + 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()); + audioMixer().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]; + mActiveTracks.remove(track); + if (track->isTerminated()) { + mTracks.remove(track); + audioMixer().deleteTrackName(track->mName); + } + } + } + } + + if (LIKELY(enabledTracks)) { + // mix buffers... + audioMixer().process(curBuf); + + // 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 { + // 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::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's main thread ready to run."); + return NO_ERROR; +} + +void AudioFlinger::onFirstRef() +{ + run("AudioFlinger", ANDROID_PRIORITY_URGENT_AUDIO); +} + +// IAudioFlinger interface +sp<IAudioTrack> AudioFlinger::createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) +{ + if (streamType >= AudioTrack::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + return NULL; + } + + if (sampleRate > MAX_SAMPLE_RATE) { + LOGE("Sample rate out of range: %d", sampleRate); + return NULL; + } + + sp<Track> track; + sp<TrackHandle> trackHandle; + Mutex::Autolock _l(mLock); + + if (mSampleRate == 0) { + LOGE("Audio driver not initialized."); + return trackHandle; + } + + sp<Client> client; + wp<Client> wclient = mClients.valueFor(pid); + + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + + // FIXME: Buffer size should be based on sample rate for consistent latency + track = new Track(this, client, streamType, sampleRate, format, + channelCount, bufferCount, channelCount == 1 ? mMixBufferSize>>1 : mMixBufferSize); + mTracks.add(track); + trackHandle = new TrackHandle(track); + return trackHandle; +} + +uint32_t AudioFlinger::sampleRate() const +{ + return mSampleRate; +} + +int AudioFlinger::channelCount() const +{ + return mChannelCount; +} + +int AudioFlinger::format() const +{ + return mFormat; +} + +size_t AudioFlinger::frameCount() const +{ + return mFrameCount; +} + +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) { + mMasterVolume = 1.0f; + } + else { + mMasterVolume = value; + } + mHardwareStatus = AUDIO_HW_IDLE; + return NO_ERROR; +} + +status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) +{ + // 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; + } + + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_HW_GET_ROUTING; + uint32_t r; + uint32_t err = mAudioHardware->getRouting(mode, &r); + if (err == NO_ERROR) { + r = (r & ~mask) | (routes & mask); + 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)) { + 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; + } + + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::setStreamVolume(int stream, float value) +{ + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + mStreamTypes[stream].volume = value; + status_t ret = NO_ERROR; + if (stream == AudioTrack::VOICE_CALL) { + 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) >= AudioTrack::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::streamVolume(int stream) const +{ + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + return 0.0f; + } + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::streamMute(int stream) const +{ + if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) { + return true; + } + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::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 == AudioTrack::MUSIC) + return true; + } + return false; +} + +status_t AudioFlinger::setParameter(const char* key, const char* value) +{ + status_t result; + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_PARAMETER; + result = mAudioHardware->setParameter(key, value); + mHardwareStatus = AUDIO_HW_IDLE; + return result; +} + +void AudioFlinger::removeClient(pid_t pid) +{ + Mutex::Autolock _l(mLock); + mClients.removeItem(pid); +} + +status_t AudioFlinger::addTrack(const sp<Track>& track) +{ + 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; + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + 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; + mActiveTracks.add(track); + return NO_ERROR; + } + return ALREADY_EXISTS; +} + +void AudioFlinger::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::remove_track_l(wp<Track> track, int name) +{ + sp<Track> t = track.promote(); + if (t!=NULL) { + t->reset(); + } + audioMixer().deleteTrackName(name); + mActiveTracks.remove(track); + mWaitWorkCV.broadcast(); +} + +void AudioFlinger::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); + audioMixer().deleteTrackName(keep->name()); + } +} + +// ---------------------------------------------------------------------------- + +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::TrackBase::TrackBase( + const sp<AudioFlinger>& audioFlinger, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + int bufferSize) + : RefBase(), + mAudioFlinger(audioFlinger), + mClient(client), + mStreamType(streamType), + mFormat(format), + mChannelCount(channelCount), + mBufferCount(bufferCount), + mFlags(0), + mBufferSize(bufferSize), + mState(IDLE), + mClientTid(-1) +{ + mName = audioFlinger->audioMixer().getTrackName(); + if (mName < 0) { + LOGE("no more track names availlable"); + return; + } + + // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); + size_t size = sizeof(audio_track_cblk_t) + bufferCount * bufferSize; + 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->size = bufferSize; + mCblk->sampleRate = sampleRate; + mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffers, 0, bufferCount * bufferSize); + } + } else { + LOGE("not enough memory for AudioTrack size=%u", size); + client->heap()->dump("AudioTrack"); + return; + } +} + +AudioFlinger::TrackBase::~TrackBase() +{ + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + mCblkMemory.clear(); // and free the shared memory + mClient.clear(); +} + +void AudioFlinger::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + buffer->raw = 0; + buffer->frameCount = 0; + step(); +} + +bool AudioFlinger::TrackBase::step() { + bool result; + audio_track_cblk_t* cblk = this->cblk(); + + result = cblk->stepServer(bufferCount()); + if (!result) { + LOGV("stepServer failed acquiring cblk mutex"); + mFlags |= STEPSERVER_FAILED; + } + return result; +} + +void AudioFlinger::TrackBase::reset() { + audio_track_cblk_t* cblk = this->cblk(); + + cblk->user = 0; + cblk->server = 0; + mFlags = 0; +} + +sp<IMemory> AudioFlinger::TrackBase::getCblk() const +{ + return mCblkMemory; +} + +int AudioFlinger::TrackBase::sampleRate() const { + return mCblk->sampleRate; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::Track::Track( + const sp<AudioFlinger>& audioFlinger, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + int bufferSize) + : TrackBase(audioFlinger, client, streamType, sampleRate, format, channelCount, bufferCount, bufferSize) +{ + mVolume[0] = 1.0f; + mVolume[1] = 1.0f; + mMute = false; +} + +AudioFlinger::Track::~Track() +{ + wp<Track> weak(this); // never create a strong ref from the dtor + mState = TERMINATED; + mAudioFlinger->removeTrack(weak, mName); +} + +void AudioFlinger::Track::destroy() +{ + mAudioFlinger->destroyTrack(this); +} + +void AudioFlinger::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->pid(), + mStreamType, + mFormat, + mChannelCount, + mBufferCount, + mState, + mMute, + mFillingUpStatus, + mCblk->sampleRate, + mCblk->volume[0], + mCblk->volume[1], + mCblk->server, + mCblk->user); +} + +status_t AudioFlinger::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + uint32_t u = cblk->user; + uint32_t s = cblk->server; + + // 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; + } + + if (LIKELY(u > s)) { + int index = s & audio_track_cblk_t::BUFFER_MASK; + buffer->raw = getBuffer(index); + buffer->frameCount = mAudioFlinger->frameCount(); + return NO_ERROR; + } +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +bool AudioFlinger::Track::isReady(uint32_t u, int32_t s) const { + if (mFillingUpStatus != FS_FILLING) return true; + const uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK; + const uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK; + const uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK; + const uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK; + if (u_seq > s_seq && u_buf == s_buf) { + mFillingUpStatus = FS_FILLED; + return true; + } + return false; +} + +status_t AudioFlinger::Track::start() +{ + LOGV("start(%d)", mName); + mAudioFlinger->addTrack(this); + return NO_ERROR; +} + +void AudioFlinger::Track::stop() +{ + LOGV("stop(%d)", mName); + Mutex::Autolock _l(mAudioFlinger->mLock); + if (mState > STOPPED) { + mState = STOPPED; + // If the track is not active (PAUSED and buffers full), flush buffers + if (mAudioFlinger->mActiveTracks.indexOf(this) < 0) { + reset(); + } + LOGV("(> STOPPED) => STOPPED (%d)", mName); + } +} + +void AudioFlinger::Track::pause() +{ + LOGV("pause(%d)", mName); + Mutex::Autolock _l(mAudioFlinger->mLock); + if (mState == ACTIVE || mState == RESUMING) { + mState = PAUSING; + LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + } +} + +void AudioFlinger::Track::flush() +{ + LOGV("flush(%d)", mName); + Mutex::Autolock _l(mAudioFlinger->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::Track::reset() +{ + TrackBase::reset(); + mFillingUpStatus = FS_FILLING; +} + +void AudioFlinger::Track::mute(bool muted) +{ + mMute = muted; +} + +void AudioFlinger::Track::setVolume(float left, float right) +{ + mVolume[0] = left; + mVolume[1] = right; +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::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<AudioFlinger::AudioRecordThread> AudioFlinger::audioRecordThread() +{ + Mutex::Autolock _l(mLock); + return mAudioRecordThread; +} + +void AudioFlinger::endRecord() +{ + Mutex::Autolock _l(mLock); + mAudioRecordThread.clear(); +} + +sp<IAudioRecord> AudioFlinger::openRecord( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags) +{ + sp<AudioRecordThread> thread; + sp<RecordTrack> recordTrack; + sp<RecordHandle> recordHandle; + sp<Client> client; + wp<Client> wclient; + AudioStreamIn* input = 0; + + // check calling permissions + if (!recordingAllowed()) { + goto Exit; + } + + if (uint32_t(streamType) >= AudioRecord::NUM_STREAM_TYPES) { + LOGE("invalid stream type"); + goto Exit; + } + + if (sampleRate > MAX_SAMPLE_RATE) { + LOGE("Sample rate out of range"); + goto Exit; + } + + if (mSampleRate == 0) { + LOGE("Audio driver not initialized"); + goto Exit; + } + + // Create audio thread - take mutex to prevent race condition + { + Mutex::Autolock _l(mLock); + if (mAudioRecordThread != 0) { + LOGE("Record channel already open"); + goto Exit; + } + thread = new AudioRecordThread(this); + mAudioRecordThread = thread; + } + // It's safe to release the mutex here since the client doesn't get a + // handle until we return from this call + + // open driver, initialize h/w + input = mAudioHardware->openInputStream( + AudioSystem::PCM_16_BIT, channelCount, sampleRate); + if (!input) { + LOGE("Error opening input stream"); + mAudioRecordThread.clear(); + 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); + } + } + + // create new record track and pass to record thread + recordTrack = new RecordTrack(this, client, streamType, sampleRate, + format, channelCount, bufferCount, input->bufferSize()); + + // spin up record thread + thread->open(recordTrack, input); + thread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); + + // return to handle to client + recordHandle = new RecordHandle(recordTrack); + +Exit: + return recordHandle; +} + +status_t AudioFlinger::startRecord() { + sp<AudioRecordThread> t = audioRecordThread(); + if (t == 0) return NO_INIT; + return t->start(); +} + +void AudioFlinger::stopRecord() { + sp<AudioRecordThread> t = audioRecordThread(); + if (t != 0) t->stop(); +} + +void AudioFlinger::exitRecord() +{ + sp<AudioRecordThread> t = audioRecordThread(); + if (t != 0) t->exit(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordTrack::RecordTrack( + const sp<AudioFlinger>& audioFlinger, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + int bufferSize) + : TrackBase(audioFlinger, client, streamType, sampleRate, format, + channelCount, bufferCount, bufferSize), + mOverflow(false) +{ +} + +AudioFlinger::RecordTrack::~RecordTrack() +{ + mAudioFlinger->audioMixer().deleteTrackName(mName); + mAudioFlinger->exitRecord(); +} + +status_t AudioFlinger::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + audio_track_cblk_t* cblk = this->cblk(); + const uint32_t u_seq = cblk->user & audio_track_cblk_t::SEQUENCE_MASK; + const uint32_t u_buf = cblk->user & audio_track_cblk_t::BUFFER_MASK; + const uint32_t s_seq = cblk->server & audio_track_cblk_t::SEQUENCE_MASK; + const uint32_t s_buf = cblk->server & audio_track_cblk_t::BUFFER_MASK; + + // 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; + } + + if (LIKELY(s_seq == u_seq || s_buf != u_buf)) { + buffer->raw = getBuffer(s_buf); + buffer->frameCount = mAudioFlinger->frameCount(); + return NO_ERROR; + } + +getNextBuffer_exit: + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; +} + +status_t AudioFlinger::RecordTrack::start() +{ + return mAudioFlinger->startRecord(); +} + +void AudioFlinger::RecordTrack::stop() +{ + mAudioFlinger->stopRecord(); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordTrack>& recordTrack) + : BnAudioRecord(), + mRecordTrack(recordTrack) +{ +} + +AudioFlinger::RecordHandle::~RecordHandle() {} + +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(const sp<AudioFlinger>& audioFlinger) : + mAudioFlinger(audioFlinger), + mRecordTrack(0), + mInput(0), + mActive(false) +{ +} + +AudioFlinger::AudioRecordThread::~AudioRecordThread() +{ +} + +bool AudioFlinger::AudioRecordThread::threadLoop() +{ + LOGV("AudioRecordThread: start record loop"); + + // start recording + while (!exitPending()) { + if (!mActive) { + mLock.lock(); + if (!mActive && !exitPending()) { + LOGV("AudioRecordThread: loop stopping"); + mWaitWorkCV.wait(mLock); + LOGV("AudioRecordThread: loop starting"); + } + mLock.unlock(); + } else { + // promote strong ref so track isn't deleted while we access it + sp<RecordTrack> t = mRecordTrack.promote(); + + // if we lose the weak reference, client is gone. + if (t == 0) { + LOGV("AudioRecordThread: client deleted track"); + break; + } + + if (LIKELY(t->getNextBuffer(&mBuffer) == NO_ERROR)) { + if (mInput->read(mBuffer.raw, t->mBufferSize) < 0) { + LOGE("Error reading audio input"); + sleep(1); + } + t->releaseBuffer(&mBuffer); + } + + // client isn't retrieving buffers fast enough + else { + if (!t->setOverflow()) + LOGW("AudioRecordThread: buffer overflow"); + } + } + }; + + // close hardware + close(); + + // delete this object - no more data references after this call + mAudioFlinger->endRecord(); + return false; +} + +status_t AudioFlinger::AudioRecordThread::open(const sp<RecordTrack>& recordTrack, AudioStreamIn *input) { + LOGV("AudioRecordThread::open"); + // check for record channel already open + AutoMutex lock(&mLock); + if (mRecordTrack != NULL) { + LOGE("Record channel already open"); + return ALREADY_EXISTS; + } + mRecordTrack = recordTrack; + mInput = input; + return NO_ERROR; +} + +status_t AudioFlinger::AudioRecordThread::start() +{ + LOGV("AudioRecordThread::start"); + AutoMutex lock(&mLock); + if (mActive) return -EBUSY; + + sp<RecordTrack> t = mRecordTrack.promote(); + if (t == 0) return UNKNOWN_ERROR; + + // signal thread to start + LOGV("Signal record thread"); + mActive = true; + mWaitWorkCV.signal(); + return NO_ERROR; +} + +void AudioFlinger::AudioRecordThread::stop() { + LOGV("AudioRecordThread::stop"); + AutoMutex lock(&mLock); + if (mActive) { + mActive = false; + mWaitWorkCV.signal(); + } +} + +void AudioFlinger::AudioRecordThread::exit() +{ + LOGV("AudioRecordThread::exit"); + AutoMutex lock(&mLock); + requestExit(); + mWaitWorkCV.signal(); +} + + +status_t AudioFlinger::AudioRecordThread::close() +{ + LOGV("AudioRecordThread::close"); + AutoMutex lock(&mLock); + if (!mInput) return NO_INIT; + delete mInput; + mInput = 0; + 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..8c02617 --- /dev/null +++ b/libs/audioflinger/AudioFlinger.h @@ -0,0 +1,490 @@ +/* //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/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 <hardware/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 )) + +// ---------------------------------------------------------------------------- + +class AudioFlinger : public BnAudioFlinger, protected Thread +{ +public: + static void instantiate(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // Thread virtuals + virtual bool threadLoop(); + virtual status_t readyToRun(); + virtual void onFirstRef(); + + // IAudioFlinger interface + virtual sp<IAudioTrack> createTrack( + pid_t pid, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + uint32_t flags); + + virtual uint32_t sampleRate() const; + virtual int channelCount() const; + virtual int format() const; + virtual size_t frameCount() 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 status_t setParameter(const char* key, const char* value); + + 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 bufferCount, + uint32_t flags); + + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + +private: + AudioFlinger(); + virtual ~AudioFlinger(); + + // Internal dump utilites. + status_t dumpPermissionDenial(int fd, const Vector<String16>& args); + status_t dumpClients(int fd, const Vector<String16>& args); + status_t dumpTracks(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; + }; + + + // --- Track --- + class TrackHandle; + class RecordHandle; + class AudioRecordThread; + + // 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 + }; + + TrackBase( const sp<AudioFlinger>& audioFlinger, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + int bufferSize); + ~TrackBase(); + + virtual status_t start() = 0; + virtual void stop() = 0; + sp<IMemory> getCblk() const; + + protected: + friend class AudioFlinger; + friend class RecordHandle; + + 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 { + return mChannelCount; + } + + int bufferCount() const { + return mBufferCount; + } + + int sampleRate() const; + + void* getBuffer(int n) const { + return (char*)mBuffers + n * mBufferSize; + } + + int name() const { + return mName; + } + + bool isStopped() const { + return mState == STOPPED; + } + + bool isTerminated() const { + return mState == TERMINATED; + } + + bool step(); + void reset(); + + sp<AudioFlinger> mAudioFlinger; + sp<Client> mClient; + sp<IMemory> mCblkMemory; + audio_track_cblk_t* mCblk; + int mStreamType; + uint8_t mFormat; + uint8_t mChannelCount; + uint8_t mBufferCount; + uint8_t mFlags; + void* mBuffers; + size_t mBufferSize; + int mName; + // we don't really need a lock for these + int mState; + int mClientTid; + }; + + // playback track + class Track : public TrackBase { + public: + Track( const sp<AudioFlinger>& audioFlinger, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + int bufferSize); + ~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); + + private: + friend class AudioFlinger; + friend class TrackHandle; + + Track(const Track&); + Track& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool isMuted() const { + return mMute; + } + + bool isPausing() const { + return mState == PAUSING; + } + + bool isPaused() const { + return mState == PAUSED; + } + + bool isReady(uint32_t u, int32_t s) 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; + }; // end of Track + + friend class AudioBuffer; + + class TrackHandle : public android::BnAudioTrack { + public: + TrackHandle(const sp<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<Track> mTrack; + }; + + struct stream_type_t { + stream_type_t() + : volume(1.0f), + mute(false) + { + } + float volume; + bool mute; + }; + + friend class Client; + friend class Track; + + + void removeClient(pid_t pid); + + 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); + + AudioMixer& audioMixer() { + return *mAudioMixer; + } + + // record track + class RecordTrack : public TrackBase { + public: + RecordTrack( const sp<AudioFlinger>& audioFlinger, + const sp<Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int bufferCount, + int bufferSize); + ~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 RecordHandle; + friend class AudioRecordThread; + + RecordTrack(const Track&); + RecordTrack& operator = (const Track&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool mOverflow; + }; + + class RecordHandle : public android::BnAudioRecord { + public: + RecordHandle(const sp<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<RecordTrack> mRecordTrack; + }; + + // record thread + class AudioRecordThread : public Thread + { + public: + AudioRecordThread(const sp<AudioFlinger>& audioFlinger); + virtual ~AudioRecordThread(); + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef() {} + + status_t open(const sp<RecordTrack>& recordTrack, AudioStreamIn *input); + status_t start(); + void stop(); + status_t close(); + void exit(); + + bool isOpen() { return bool(mRecordTrack != NULL); } + + private: + AudioRecordThread(); + sp<AudioFlinger> mAudioFlinger; + wp<RecordTrack> mRecordTrack; + AudioStreamIn* mInput; + Mutex mLock; + Condition mWaitWorkCV; + AudioBufferProvider::Buffer mBuffer; + volatile bool mActive; + }; + + friend class AudioRecordThread; + + sp<AudioRecordThread> audioRecordThread(); + void endRecord(); + status_t startRecord(); + void stopRecord(); + void exitRecord(); + + AudioHardwareInterface* audioHardware() { return mAudioHardware; } + + mutable Mutex mHardwareLock; + mutable Mutex mLock; + mutable Condition mWaitWorkCV; + DefaultKeyedVector< pid_t, wp<Client> > mClients; + SortedVector< wp<Track> > mActiveTracks; + SortedVector< sp<Track> > mTracks; + float mMasterVolume; + uint32_t mMasterRouting; + bool mMasterMute; + stream_type_t mStreamTypes[AudioTrack::NUM_STREAM_TYPES]; + + AudioMixer* mAudioMixer; + AudioHardwareInterface* mAudioHardware; + AudioStreamOut* mOutput; + sp<AudioRecordThread> mAudioRecordThread; + uint32_t mSampleRate; + size_t mFrameCount; + int mChannelCount; + int mFormat; + int mMixBufferSize; + int16_t* mMixBuffer; + mutable int mHardwareStatus; + nsecs_t mLastWriteTime; + int mNumWrites; + int mNumDelayedWrites; + bool mStandby; + bool mInWrite; +}; + +// ---------------------------------------------------------------------------- + +}; // 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..b1e5b7f --- /dev/null +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -0,0 +1,293 @@ +/* +** +** 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; +} + +status_t AudioHardwareGeneric::standby() +{ + // Implement: audio hardware to standby mode + return NO_ERROR; +} + +AudioStreamOut* AudioHardwareGeneric::openOutputStream( + int format, int channelCount, uint32_t sampleRate) +{ + AutoMutex lock(mLock); + + // only one output stream allowed + if (mOutput) return 0; + + // create new output stream + AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); + if (out->set(this, mFd, format, channelCount, sampleRate) == 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) +{ + AutoMutex lock(mLock); + + // only one input stream allowed + if (mInput) return 0; + + // create new output stream + AudioStreamInGeneric* in = new AudioStreamInGeneric(); + if (in->set(this, mFd, format, channelCount, sampleRate) == 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::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) +{ + // 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..10cc45d --- /dev/null +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -0,0 +1,135 @@ +/* +** +** 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/AudioHardwareInterface.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 status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual ssize_t write(const void* buffer, size_t bytes); + 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); + + 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); + +private: + AudioHardwareGeneric *mAudioHardware; + Mutex mLock; + int mFd; +}; + + +class AudioHardwareGeneric : public AudioHardwareInterface +{ +public: + AudioHardwareGeneric(); + virtual ~AudioHardwareGeneric(); + virtual status_t initCheck(); + virtual status_t standby(); + 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); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate); + + 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..7387b3d --- /dev/null +++ b/libs/audioflinger/AudioHardwareInterface.cpp @@ -0,0 +1,240 @@ +/* +** +** 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 " +}; +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 and setting environement + // "audioflinger.dump = 1". The output file is "tmp/FlingerOut.pcm". Pause are not recorded + // in the file. + + // read dump mode + property_get("audioflinger.dump", value, "0"); + switch(value[0]) { + case '1': + LOGV("Dump mode"); + hw = new AudioDumpInterface(hw); // replace interface + return hw; + break; + case '0': + default: + LOGV("No Dump mode"); + return hw; + break; + } +#endif + return hw; +} + +AudioStreamOut::~AudioStreamOut() +{ +} + +AudioStreamIn::~AudioStreamIn() {} + +AudioHardwareInterface::AudioHardwareInterface() +{ + // 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 AudioHardwareInterface::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 AudioHardwareInterface::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 AudioHardwareInterface::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 AudioHardwareInterface::getMode(int* mode) +{ + // Implement: set audio routing + *mode = mMode; + return NO_ERROR; +} + +status_t AudioHardwareInterface::setParameter(const char* key, const char* value) +{ + // default implementation is to ignore + return NO_ERROR; +} + +status_t AudioHardwareInterface::dumpState(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "AudioHardwareInterface::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..0046db8 --- /dev/null +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -0,0 +1,175 @@ +/* //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; +} + +status_t AudioHardwareStub::standby() +{ + return NO_ERROR; +} + +AudioStreamOut* AudioHardwareStub::openOutputStream( + int format, int channelCount, uint32_t sampleRate) +{ + AudioStreamOutStub* out = new AudioStreamOutStub(); + if (out->set(format, channelCount, sampleRate) == NO_ERROR) + return out; + delete out; + return 0; +} + +AudioStreamIn* AudioHardwareStub::openInputStream( + int format, int channelCount, uint32_t sampleRate) +{ + AudioStreamInStub* in = new AudioStreamInStub(); + if (in->set(format, channelCount, sampleRate) == 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::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) +{ + 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..1a61552 --- /dev/null +++ b/libs/audioflinger/AudioHardwareStub.h @@ -0,0 +1,95 @@ +/* //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/AudioHardwareInterface.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 status_t setVolume(float volume) { return NO_ERROR; } + virtual ssize_t write(const void* buffer, size_t bytes); + 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); + 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); +}; + +class AudioHardwareStub : public AudioHardwareInterface +{ +public: + AudioHardwareStub(); + virtual ~AudioHardwareStub(); + virtual status_t initCheck(); + virtual status_t standby(); + 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); + + virtual AudioStreamIn* openInputStream( + int format, + int channelCount, + uint32_t sampleRate); + +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..9f1b17f --- /dev/null +++ b/libs/audioflinger/AudioMixer.cpp @@ -0,0 +1,857 @@ +/* //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]>>16) >= volume[i])) || + ((volumeInc[i]<0) && ((prevVolume[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]; + t.bufferProvider->getNextBuffer(&t.buffer); + if (t.buffer.raw) { + 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.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) + 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]; + (t.hook)(&t, outTemp, BLOCKSIZE, state->resampleTemp); + } + + 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 { + 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) { + (t.hook)(&t, outTemp, numFrames, state->resampleTemp); + 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); + t.bufferProvider->getNextBuffer(&b); + int16_t const *in = t.buffer.i16; + + // in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (in == NULL) { + memset(output, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t)); + return; + } + + 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; + 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 (--numFrames); + } 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 (--numFrames); + } + + 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); + t0.bufferProvider->getNextBuffer(&b0); + + en &= ~(1<<i); + i = 31 - __builtin_clz(en); + const track_t& t1 = state->tracks[i]; + AudioBufferProvider::Buffer& b1(t1.buffer); + t1.bufferProvider->getNextBuffer(&b1); + + int16_t const *in0; + const int16_t vl0 = t0.volume[0]; + const int16_t vr0 = t0.volume[1]; + int16_t const *in1; + const int16_t vl1 = t1.volume[0]; + const int16_t vr1 = t1.volume[1]; + size_t numFrames = state->frameCount; + int32_t* out = static_cast<int32_t*>(output); + + // t0/1.buffer.i16 == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t0.buffer.i16 != NULL) { + in0 = t0.buffer.i16; + if (t1.buffer.i16 != NULL) { + in1 = t1.buffer.i16; + } else { + in1 = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + memset((void *)in1, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t)); + } + } else { + in0 = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; + memset((void *)in0, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t)); + if (t1.buffer.i16 != NULL) { + in1 = t1.buffer.i16; + } else { + in1 = in0; + } + } + + 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 (--numFrames); + + + if (t0.buffer.i16 != NULL) { + t0.bufferProvider->releaseBuffer(&b0); + if (t1.buffer.i16 != NULL) { + t1.bufferProvider->releaseBuffer(&b1); + } else { + delete [] in1; + } + } else { + delete [] in0; + if (t1.buffer.i16 != NULL) { + t1.bufferProvider->releaseBuffer(&b1); + } + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h new file mode 100644 index 0000000..9ca109f --- /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 reserved; + + 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..c93ead3 --- /dev/null +++ b/libs/audioflinger/AudioResampler.cpp @@ -0,0 +1,297 @@ +/* + * 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 <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 { +// ---------------------------------------------------------------------------- + +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); + 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: + resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); + break; + case MED_QUALITY: + resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); + break; + case HIGH_QUALITY: + 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.raw = NULL; + + // 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; + + // 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 + if (mBuffer.raw == NULL) { + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + break; + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + } + 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"); + while (outputIndex < outputSampleCount) { + 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); + if (inputIndex >= mBuffer.frameCount) + break; + } + // 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", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount*2-2]; + mX0R = mBuffer.i16[mBuffer.frameCount*2-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer NULLS the buffer pointer + // LOG_ASSERT(mBuffer.raw == NULL); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // 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; + + // 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 + if (mBuffer.raw == NULL) { + provider->getNextBuffer(&mBuffer); + if (mBuffer.raw == NULL) + break; + // LOGE("New buffer fetched: %d frames\n", mBuffer.frameCount); + } + 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"); + while (outputIndex < outputSampleCount) { + int32_t sample = Interp(in[inputIndex-1], in[inputIndex], + phaseFraction); + out[outputIndex++] += vl * sample; + out[outputIndex++] += vr * sample; + Advance(&inputIndex, &phaseFraction, phaseIncrement); + if (inputIndex >= mBuffer.frameCount) + break; + } + // 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", inputIndex); + + mX0L = mBuffer.i16[mBuffer.frameCount-1]; + provider->releaseBuffer(&mBuffer); + + // verify that the releaseBuffer NULLS the buffer pointer + // LOG_ASSERT(mBuffer.raw == NULL); + } + } + + // LOGE("output buffer full - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex); + + // save state + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +// ---------------------------------------------------------------------------- +} +; // 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..4f437bf --- /dev/null +++ b/libs/audioflinger/AudioResamplerCubic.cpp @@ -0,0 +1,178 @@ +/* + * 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; + + // fetch first buffer + if (mBuffer.raw == NULL) { + 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); + 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; + + // fetch first buffer + if (mBuffer.raw == NULL) { + 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); + 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..e710d16 --- /dev/null +++ b/libs/audioflinger/AudioResamplerSinc.cpp @@ -0,0 +1,320 @@ +/* + * 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 * int32_t(v))>>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) * int32_t(v))>>16); + } else { + return a + ((int16_t(inRL>>16) * int32_t(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; + + AudioBufferProvider::Buffer& buffer(mBuffer); + while (outputIndex < outputSampleCount) { + // buffer is empty, fetch a new one + if (buffer.raw == NULL) { + provider->getNextBuffer(&buffer); + if (buffer.raw == NULL) + break; + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex) { + 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++] = mulRL(1, l, vRL); + out[outputIndex++] = mulRL(0, r, vRL); + + phaseFraction += phaseIncrement; + const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; + if (phaseIndex) { + inputIndex += phaseIndex; + if (inputIndex >= frameCount) + break; + read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + } + } + + // if done with buffer, save samples + if (inputIndex >= frameCount) { + inputIndex -= frameCount; + provider->releaseBuffer(&buffer); + } + } + + mImpulse = impulse; + mInputIndex = inputIndex; + mPhaseFraction = phaseFraction; +} + +template<int CHANNELS> +void AudioResamplerSinc::read( + int16_t*& impulse, uint32_t& phaseFraction, + int16_t const* in, size_t inputIndex) +{ + // read new samples into the ring buffer + while (phaseFraction >> kNumPhaseBits) { + 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; + + 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..89b9577 --- /dev/null +++ b/libs/audioflinger/AudioResamplerSinc.h @@ -0,0 +1,87 @@ +/* + * 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; + static const int cShift = kNumPhaseBits - coefsBits; + static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; + + // 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; + static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; + + // number of zero-crossing on each side + static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_SINC_H*/ |