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