summaryrefslogtreecommitdiffstats
path: root/libs/audioflinger
diff options
context:
space:
mode:
Diffstat (limited to 'libs/audioflinger')
-rw-r--r--libs/audioflinger/A2dpAudioInterface.cpp186
-rw-r--r--libs/audioflinger/A2dpAudioInterface.h107
-rw-r--r--libs/audioflinger/Android.mk7
-rw-r--r--libs/audioflinger/AudioDumpInterface.cpp49
-rw-r--r--libs/audioflinger/AudioDumpInterface.h53
-rw-r--r--libs/audioflinger/AudioFlinger.cpp710
-rw-r--r--libs/audioflinger/AudioFlinger.h101
-rw-r--r--libs/audioflinger/AudioHardwareGeneric.cpp46
-rw-r--r--libs/audioflinger/AudioHardwareGeneric.h30
-rw-r--r--libs/audioflinger/AudioHardwareInterface.cpp59
-rw-r--r--libs/audioflinger/AudioHardwareStub.cpp36
-rw-r--r--libs/audioflinger/AudioHardwareStub.h28
-rw-r--r--libs/audioflinger/AudioMixer.cpp278
-rw-r--r--libs/audioflinger/AudioMixer.h18
-rw-r--r--libs/audioflinger/AudioResampler.cpp350
-rw-r--r--libs/audioflinger/AudioResamplerCubic.cpp18
-rw-r--r--libs/audioflinger/AudioResamplerSinc.cpp292
-rw-r--r--libs/audioflinger/AudioResamplerSinc.h23
18 files changed, 1678 insertions, 713 deletions
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp
new file mode 100644
index 0000000..d54795c
--- /dev/null
+++ b/libs/audioflinger/A2dpAudioInterface.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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)
+{
+ if (status)
+ *status = -1;
+ return NULL;
+}
+
+status_t A2dpAudioInterface::standby()
+{
+ return 0;
+}
+
+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);
+ 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), mStartCount(0), mRetryCount(0), mData(NULL),
+ mInitialized(false), mBufferRemaining(0)
+{
+}
+
+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()
+{
+ if (mData)
+ a2dp_cleanup(mData);
+}
+
+ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
+{
+ if (!mInitialized) {
+ int ret = a2dp_init("00:00:00:00:00:00", 44100, 2, &mData);
+ if (ret)
+ return ret;
+ mInitialized = true;
+ }
+
+ size_t remaining = bytes;
+ while (remaining > 0) {
+ int written = a2dp_write(mData, buffer, remaining);
+ remaining -= written;
+ buffer = ((char *)buffer) + written;
+ }
+
+ return bytes;
+}
+
+status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
+{
+ return 0;
+}
+
+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..03bf933
--- /dev/null
+++ b/libs/audioflinger/A2dpAudioInterface.h
@@ -0,0 +1,107 @@
+/*
+ * 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/AudioHardwareBase.h>
+
+
+namespace android {
+
+class A2dpAudioInterface : public AudioHardwareBase
+{
+ class A2dpAudioStreamOut;
+
+public:
+ A2dpAudioInterface();
+ virtual ~A2dpAudioInterface();
+ virtual status_t initCheck();
+ virtual status_t standby();
+
+ virtual status_t setVoiceVolume(float volume);
+ virtual status_t setMasterVolume(float volume);
+
+ // mic mute
+ virtual status_t setMicMute(bool state);
+ virtual status_t getMicMute(bool* state);
+
+ // 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);
+
+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; }
+ // must be 32-bit aligned - driver only seems to like 4800
+ virtual size_t bufferSize() const { return 5120; }
+ 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 INVALID_OPERATION; }
+ virtual ssize_t write(const void* buffer, size_t bytes);
+ status_t standby();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ private:
+ int mFd;
+ int mStartCount;
+ int mRetryCount;
+ void* mData;
+ bool mInitialized;
+
+#define kBufferSize 50000
+ char mBuffer[kBufferSize];
+ int mBufferRemaining;
+ };
+
+ Mutex mLock;
+ A2dpAudioStreamOut* mOutput;
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // A2DP_AUDIO_HARDWARE_H
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index a9cb303..d16e3e1 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -45,9 +45,12 @@ endif
LOCAL_MODULE:= libaudioflinger
-ifeq ($(TARGET_ARCH),arm) # not simulator
- LOCAL_CFLAGS += -DWITH_BLUETOOTH
+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/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp
index 5ff2f18..8eee9cc 100644
--- a/libs/audioflinger/AudioDumpInterface.cpp
+++ b/libs/audioflinger/AudioDumpInterface.cpp
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -28,6 +28,8 @@
namespace android {
+bool gFirst = true; // true if first write after a standby
+
// ----------------------------------------------------------------------------
AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
@@ -40,17 +42,25 @@ AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
}
+AudioDumpInterface::~AudioDumpInterface()
+{
+ if(mFinalInterface) delete mFinalInterface;
+ if(mStreamOut) delete mStreamOut;
+}
+
+
status_t AudioDumpInterface::standby()
{
if(mStreamOut) mStreamOut->Close();
+ gFirst = true;
return mFinalInterface->standby();
}
AudioStreamOut* AudioDumpInterface::openOutputStream(
- int format, int channelCount, uint32_t sampleRate)
+ int format, int channelCount, uint32_t sampleRate, status_t *status)
{
- AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate);
+ AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status);
if(outFinal) {
mStreamOut = new AudioStreamOutDump(outFinal);
@@ -69,13 +79,26 @@ AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* 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) {
- mOutFile = fopen(FLINGER_DUMP_NAME, "ab");
+ 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);
diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h
index 732b97d..a65e56a 100644
--- a/libs/audioflinger/AudioDumpInterface.h
+++ b/libs/audioflinger/AudioDumpInterface.h
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -21,33 +21,35 @@
#include <stdint.h>
#include <sys/types.h>
-#include <hardware/AudioHardwareInterface.h>
+#include <hardware/AudioHardwareBase.h>
namespace android {
-#define FLINGER_DUMP_NAME "/tmp/FlingerOut.pcm" // name of file used for dump
+#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 dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); }
void Close(void);
private:
- AudioStreamOut *mFinalStream;
- FILE *mOutFile; // output file
+ AudioStreamOut *mFinalStream;
+ FILE *mOutFile; // output file
};
-class AudioDumpInterface : public AudioHardwareInterface
+class AudioDumpInterface : public AudioHardwareBase
{
public:
@@ -56,10 +58,10 @@ public:
virtual AudioStreamOut* openOutputStream(
int format=0,
int channelCount=0,
- uint32_t sampleRate=0);
+ uint32_t sampleRate=0,
+ status_t *status=0);
+ virtual ~AudioDumpInterface();
- virtual ~AudioDumpInterface()
- {delete mFinalInterface;}
virtual status_t initCheck()
{return mFinalInterface->initCheck();}
virtual status_t setVoiceVolume(float volume)
@@ -67,13 +69,6 @@ public:
virtual status_t setMasterVolume(float volume)
{return mFinalInterface->setMasterVolume(volume);}
- virtual status_t setRouting(int mode, uint32_t routes)
- {return mFinalInterface->setRouting(mode, routes);}
- virtual status_t getRouting(int mode, uint32_t* routes)
- {return mFinalInterface->getRouting(mode, routes);}
- virtual status_t getMode(int* mode)
- {return mFinalInterface->getMode(mode);}
-
// mic mute
virtual status_t setMicMute(bool state)
{return mFinalInterface->setMicMute(state);}
@@ -83,17 +78,17 @@ public:
virtual status_t setParameter(const char* key, const char* value)
{return mFinalInterface->setParameter(key, value);}
- virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate)
- {return mFinalInterface->openInputStream( format, channelCount, sampleRate);}
+ virtual AudioStreamIn* openInputStream( int format, int channelCount, uint32_t sampleRate, status_t *status)
+ {return mFinalInterface->openInputStream( format, channelCount, sampleRate, status);}
virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
protected:
- virtual status_t doRouting() {return 0;}
-
+ virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);}
+
AudioHardwareInterface *mFinalInterface;
AudioStreamOutDump *mStreamOut;
-
+
};
}; // namespace android
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index fb21629..53b18ad 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -31,6 +31,8 @@
#include <utils/String16.h>
#include <utils/threads.h>
+#include <cutils/properties.h>
+
#include <media/AudioTrack.h>
#include <media/AudioRecord.h>
@@ -41,9 +43,13 @@
#include "AudioMixer.h"
#include "AudioFlinger.h"
+#ifdef WITH_A2DP
+#include "A2dpAudioInterface.h"
+#endif
+
namespace android {
-static const nsecs_t kStandbyTimeInNsecs = seconds(3);
+//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;
@@ -93,8 +99,9 @@ static bool settingsAllowed() {
AudioFlinger::AudioFlinger()
: BnAudioFlinger(), Thread(false),
- mMasterVolume(0), mMasterMute(true),
- mAudioMixer(0), mAudioHardware(0), mOutput(0), mAudioRecordThread(0),
+ mMasterVolume(0), mMasterMute(true), mHardwareAudioMixer(0), mA2dpAudioMixer(0),
+ mAudioMixer(0), mAudioHardware(0), mA2dpAudioInterface(0),
+ mHardwareOutput(0), mA2dpOutput(0), mOutput(0), mAudioRecordThread(0),
mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0),
mMixBuffer(0), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0),
mStandby(false), mInWrite(false)
@@ -105,17 +112,14 @@ AudioFlinger::AudioFlinger()
if (mAudioHardware->initCheck() == NO_ERROR) {
// open 16-bit output stream for s/w mixer
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- mOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT);
+ status_t status;
+ mHardwareOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status);
mHardwareStatus = AUDIO_HW_IDLE;
- if (mOutput) {
- mSampleRate = mOutput->sampleRate();
- mChannelCount = mOutput->channelCount();
- mFormat = mOutput->format();
- mMixBufferSize = mOutput->bufferSize();
- mFrameCount = mMixBufferSize / mChannelCount / sizeof(int16_t);
- mMixBuffer = new int16_t[mFrameCount * mChannelCount];
- memset(mMixBuffer, 0, mMixBufferSize);
- mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+ if (mHardwareOutput) {
+ mSampleRate = mHardwareOutput->sampleRate();
+ mHardwareAudioMixer = new AudioMixer(getOutputFrameCount(mHardwareOutput), mSampleRate);
+ setOutput(mHardwareOutput);
+
// FIXME - this should come from settings
setMasterVolume(1.0f);
setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL);
@@ -124,20 +128,87 @@ AudioFlinger::AudioFlinger()
setMode(AudioSystem::MODE_NORMAL);
mMasterMute = false;
} else {
- LOGE("Failed to initialize output stream");
+ LOGE("Failed to initialize output stream, status: %d", status);
}
- } else {
+
+#ifdef WITH_A2DP
+ // Create A2DP interface
+ mA2dpAudioInterface = new A2dpAudioInterface();
+ mA2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status);
+ mA2dpAudioMixer = new AudioMixer(getOutputFrameCount(mA2dpOutput), mA2dpOutput->sampleRate());
+
+ // create a buffer big enough for both hardware and A2DP audio output.
+ size_t hwFrameCount = getOutputFrameCount(mHardwareOutput);
+ size_t a2dpFrameCount = getOutputFrameCount(mA2dpOutput);
+ size_t frameCount = (hwFrameCount > a2dpFrameCount ? hwFrameCount : a2dpFrameCount);
+#else
+ size_t frameCount = getOutputFrameCount(mHardwareOutput);
+#endif
+ // FIXME - Current mixer implementation only supports stereo output: Always
+ // Allocate a stereo buffer even if HW output is mono.
+ mMixBuffer = new int16_t[frameCount * 2];
+ memset(mMixBuffer, 0, frameCount * 2 * sizeof(int16_t));
+
+ // 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!");
}
+
+ char value[PROPERTY_VALUE_MAX];
+ // FIXME: What property should this be???
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ mMasterMute = true;
+ }
}
AudioFlinger::~AudioFlinger()
{
+ if (mAudioRecordThread != 0) {
+ mAudioRecordThread->exit();
+ mAudioRecordThread.clear();
+ }
delete mOutput;
+ delete mA2dpOutput;
delete mAudioHardware;
+ delete mA2dpAudioInterface;
delete [] mMixBuffer;
- delete mAudioMixer;
- mAudioRecordThread.clear();
+ delete mHardwareAudioMixer;
+ delete mA2dpAudioMixer;
+}
+
+void AudioFlinger::setOutput(AudioStreamOut* output)
+{
+ // lock on mOutputLock to prevent threadLoop() from starving us
+ Mutex::Autolock _l2(mOutputLock);
+
+ // to synchronize with threadLoop()
+ Mutex::Autolock _l(mLock);
+
+ if (mOutput != output) {
+ 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 = getOutputFrameCount(output);
+
+ mAudioMixer = (output == mA2dpOutput ? mA2dpAudioMixer : mHardwareAudioMixer);
+ mOutput = output;
+ }
+}
+
+size_t AudioFlinger::getOutputFrameCount(AudioStreamOut* output)
+{
+ return output->bufferSize() / output->channelCount() / sizeof(int16_t);
}
status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
@@ -201,8 +272,8 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
-
- snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", audioMixer().trackNames());
+
+ snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", audioMixer()->trackNames());
result.append(buffer);
snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
result.append(buffer);
@@ -254,17 +325,26 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
// Thread virtuals
bool AudioFlinger::threadLoop()
{
- nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2;
unsigned long sleepTime = kBufferRecoveryInUsecs;
- const size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t);
int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
- size_t enabledTracks;
+ size_t enabledTracks = 0;
nsecs_t standbyTime = systemTime();
+ AudioMixer* mixer = 0;
+ size_t frameCount = 0;
+ int channelCount = 0;
+ uint32_t sampleRate = 0;
+ AudioStreamOut* output = 0;
do {
enabledTracks = 0;
- { // scope for the lock
+ { // scope for the mLock
+
+ // locking briefly on the secondary mOutputLock is necessary to avoid
+ // having this thread starve the thread that called setOutput()
+ mOutputLock.lock();
+ mOutputLock.unlock();
+
Mutex::Autolock _l(mLock);
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
@@ -286,6 +366,15 @@ bool AudioFlinger::threadLoop()
continue;
}
+ // get active mixer and output parameter while the lock is held and keep them
+ // consistent till the next loop.
+
+ mixer = audioMixer();
+ frameCount = mFrameCount;
+ channelCount = mChannelCount;
+ sampleRate = mSampleRate;
+ output = mOutput;
+
// find out which tracks need to be processed
size_t count = activeTracks.size();
for (size_t i=0 ; i<count ; i++) {
@@ -294,13 +383,11 @@ bool AudioFlinger::threadLoop()
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
- uint32_t u = cblk->user;
- uint32_t s = cblk->server;
// The first time a track is added we wait
// for all its buffers to be filled before processing it
- audioMixer().setActiveTrack(track->name());
- if ((u > s) && (track->isReady(u, s) || track->isStopped()) &&
+ mixer->setActiveTrack(track->name());
+ if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
!track->isPaused())
{
//LOGD("u=%08x, s=%08x [OK]", u, s);
@@ -325,9 +412,8 @@ bool AudioFlinger::threadLoop()
}
// XXX: these things DON'T need to be done each time
- AudioMixer& mixer(audioMixer());
- mixer.setBufferProvider(track);
- mixer.enable(AudioMixer::MIXING);
+ mixer->setBufferProvider(track);
+ mixer->enable(AudioMixer::MIXING);
int param;
if ( track->mFillingUpStatus == Track::FS_FILLED) {
@@ -342,15 +428,15 @@ bool AudioFlinger::threadLoop()
} else {
param = AudioMixer::RAMP_VOLUME;
}
- mixer.setParameter(param, AudioMixer::VOLUME0, left);
- mixer.setParameter(param, AudioMixer::VOLUME1, right);
- mixer.setParameter(
+ mixer->setParameter(param, AudioMixer::VOLUME0, left);
+ mixer->setParameter(param, AudioMixer::VOLUME1, right);
+ mixer->setParameter(
AudioMixer::TRACK,
AudioMixer::FORMAT, track->format());
- mixer.setParameter(
+ mixer->setParameter(
AudioMixer::TRACK,
AudioMixer::CHANNEL_COUNT, track->channelCount());
- mixer.setParameter(
+ mixer->setParameter(
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
int(cblk->sampleRate));
@@ -361,8 +447,7 @@ bool AudioFlinger::threadLoop()
} else {
//LOGD("u=%08x, s=%08x [NOT READY]", u, s);
if (track->isStopped()) {
- track->mFillingUpStatus = Track::FS_FILLING;
- track->mFlags = 0;
+ track->reset();
}
if (track->isTerminated() || track->isStopped() || track->isPaused()) {
// We have consumed all the buffers of this track.
@@ -378,7 +463,7 @@ bool AudioFlinger::threadLoop()
}
}
// LOGV("disable(%d)", track->name());
- audioMixer().disable(AudioMixer::MIXING);
+ mixer->disable(AudioMixer::MIXING);
}
}
@@ -390,26 +475,27 @@ bool AudioFlinger::threadLoop()
mActiveTracks.remove(track);
if (track->isTerminated()) {
mTracks.remove(track);
- audioMixer().deleteTrackName(track->mName);
+ mixer->deleteTrackName(track->mName);
}
}
- }
- }
-
+ }
+ }
if (LIKELY(enabledTracks)) {
// mix buffers...
- audioMixer().process(curBuf);
+ mixer->process(curBuf);
// output audio to hardware
mLastWriteTime = systemTime();
mInWrite = true;
- mOutput->write(curBuf, mixBufferSize);
+ size_t mixBufferSize = frameCount*channelCount*sizeof(int16_t);
+ output->write(curBuf, mixBufferSize);
mNumWrites++;
mInWrite = false;
mStandby = false;
nsecs_t temp = systemTime();
standbyTime = temp + kStandbyTimeInNsecs;
nsecs_t delta = temp - mLastWriteTime;
+ nsecs_t maxPeriod = seconds(frameCount) / sampleRate * 2;
if (delta > maxPeriod) {
LOGW("write blocked for %llu msecs", ns2ms(delta));
mNumDelayedWrites++;
@@ -458,43 +544,60 @@ sp<IAudioTrack> AudioFlinger::createTrack(
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- uint32_t flags)
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer,
+ status_t *status)
{
+ sp<Track> track;
+ sp<TrackHandle> trackHandle;
+ sp<Client> client;
+ wp<Client> wclient;
+ status_t lStatus;
+
if (streamType >= AudioTrack::NUM_STREAM_TYPES) {
LOGE("invalid stream type");
- return NULL;
+ lStatus = BAD_VALUE;
+ goto Exit;
}
- if (sampleRate > MAX_SAMPLE_RATE) {
+ // 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", sampleRate);
- return NULL;
+ lStatus = BAD_VALUE;
+ goto Exit;
}
- sp<Track> track;
- sp<TrackHandle> trackHandle;
- Mutex::Autolock _l(mLock);
+ {
+ Mutex::Autolock _l(mLock);
- if (mSampleRate == 0) {
- LOGE("Audio driver not initialized.");
- return trackHandle;
- }
+ if (mSampleRate == 0) {
+ LOGE("Audio driver not initialized.");
+ lStatus = NO_INIT;
+ goto Exit;
+ }
- sp<Client> client;
- wp<Client> wclient = mClients.valueFor(pid);
+ wclient = mClients.valueFor(pid);
- if (wclient != NULL) {
- client = wclient.promote();
- } else {
- client = new Client(this, pid);
- mClients.add(pid, client);
+ if (wclient != NULL) {
+ client = wclient.promote();
+ } else {
+ client = new Client(this, pid);
+ mClients.add(pid, client);
+ }
+
+ track = new Track(this, client, streamType, sampleRate, format,
+ channelCount, frameCount, sharedBuffer);
+ mTracks.add(track);
+ trackHandle = new TrackHandle(track);
+
+ lStatus = NO_ERROR;
}
- // FIXME: Buffer size should be based on sample rate for consistent latency
- track = new Track(this, client, streamType, sampleRate, format,
- channelCount, bufferCount, channelCount == 1 ? mMixBufferSize>>1 : mMixBufferSize);
- mTracks.add(track);
- trackHandle = new TrackHandle(track);
+Exit:
+ if(status) {
+ *status = lStatus;
+ }
return trackHandle;
}
@@ -518,6 +621,16 @@ size_t AudioFlinger::frameCount() const
return mFrameCount;
}
+uint32_t AudioFlinger::latency() const
+{
+ if (mOutput) {
+ return mOutput->latency();
+ }
+ else {
+ return 0;
+ }
+}
+
status_t AudioFlinger::setMasterVolume(float value)
{
// check calling permissions
@@ -549,6 +662,21 @@ status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask)
return BAD_VALUE;
}
+#ifdef WITH_A2DP
+ LOGD("setRouting %d %d %d\n", mode, routes, mask);
+ if (mode == AudioSystem::MODE_NORMAL &&
+ (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) {
+ if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) {
+ LOGD("set output to A2DP\n");
+ setOutput(mA2dpOutput);
+ } else {
+ LOGD("set output to hardware audio\n");
+ setOutput(mHardwareOutput);
+ }
+ LOGD("setOutput done\n");
+ }
+#endif
+
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_ROUTING;
uint32_t r;
@@ -656,7 +784,7 @@ status_t AudioFlinger::setStreamVolume(int stream, float value)
if (uint32_t(stream) >= AudioTrack::NUM_STREAM_TYPES) {
return BAD_VALUE;
}
-
+
mStreamTypes[stream].volume = value;
status_t ret = NO_ERROR;
if (stream == AudioTrack::VOICE_CALL) {
@@ -750,6 +878,7 @@ status_t AudioFlinger::addTrack(const sp<Track>& track)
// buffers before playing. This is to ensure the client will
// effectively get the latency it requested.
track->mFillingUpStatus = Track::FS_FILLING;
+ track->mResetDone = false;
mActiveTracks.add(track);
return NO_ERROR;
}
@@ -771,7 +900,7 @@ void AudioFlinger::remove_track_l(wp<Track> track, int name)
if (t!=NULL) {
t->reset();
}
- audioMixer().deleteTrackName(name);
+ audioMixer()->deleteTrackName(name);
mActiveTracks.remove(track);
mWaitWorkCV.broadcast();
}
@@ -789,7 +918,7 @@ void AudioFlinger::destroyTrack(const sp<Track>& track)
if (mActiveTracks.indexOf(track) < 0) {
LOGV("remove track (%d) and delete from mixer", track->name());
mTracks.remove(track);
- audioMixer().deleteTrackName(keep->name());
+ audioMixer()->deleteTrackName(keep->name());
}
}
@@ -823,38 +952,53 @@ AudioFlinger::TrackBase::TrackBase(
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- int bufferSize)
+ int frameCount,
+ const sp<IMemory>& sharedBuffer)
: RefBase(),
mAudioFlinger(audioFlinger),
mClient(client),
mStreamType(streamType),
- mFormat(format),
- mChannelCount(channelCount),
- mBufferCount(bufferCount),
- mFlags(0),
- mBufferSize(bufferSize),
+ mFrameCount(0),
mState(IDLE),
- mClientTid(-1)
+ mClientTid(-1),
+ mFormat(format),
+ mFlags(0)
{
- mName = audioFlinger->audioMixer().getTrackName();
+ mName = audioFlinger->audioMixer()->getTrackName();
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) + bufferCount * bufferSize;
+ size_t size = sizeof(audio_track_cblk_t);
+ size_t bufferSize = frameCount*channelCount*sizeof(int16_t);
+ if (sharedBuffer == 0) {
+ size += bufferSize;
+ }
+
mCblkMemory = client->heap()->allocate(size);
if (mCblkMemory != 0) {
mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
if (mCblk) { // construct the shared structure in-place.
new(mCblk) audio_track_cblk_t();
// clear all buffers
- mCblk->size = bufferSize;
+ mCblk->frameCount = frameCount;
mCblk->sampleRate = sampleRate;
- mBuffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- memset(mBuffers, 0, bufferCount * bufferSize);
+ 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);
@@ -873,15 +1017,16 @@ AudioFlinger::TrackBase::~TrackBase()
void AudioFlinger::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
buffer->raw = 0;
- buffer->frameCount = 0;
+ mFrameCount = buffer->frameCount;
step();
+ buffer->frameCount = 0;
}
bool AudioFlinger::TrackBase::step() {
bool result;
audio_track_cblk_t* cblk = this->cblk();
-
- result = cblk->stepServer(bufferCount());
+
+ result = cblk->stepServer(mFrameCount);
if (!result) {
LOGV("stepServer failed acquiring cblk mutex");
mFlags |= STEPSERVER_FAILED;
@@ -894,7 +1039,10 @@ void AudioFlinger::TrackBase::reset() {
cblk->user = 0;
cblk->server = 0;
- mFlags = 0;
+ cblk->userBase = 0;
+ cblk->serverBase = 0;
+ mFlags = 0;
+ LOGV("TrackBase::reset");
}
sp<IMemory> AudioFlinger::TrackBase::getCblk() const
@@ -906,6 +1054,27 @@ int AudioFlinger::TrackBase::sampleRate() const {
return mCblk->sampleRate;
}
+int AudioFlinger::TrackBase::channelCount() const {
+ return mCblk->channels;
+}
+
+void* AudioFlinger::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::Track::Track(
@@ -915,13 +1084,14 @@ AudioFlinger::Track::Track(
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- int bufferSize)
- : TrackBase(audioFlinger, client, streamType, sampleRate, format, channelCount, bufferCount, bufferSize)
+ int frameCount,
+ const sp<IMemory>& sharedBuffer)
+ : TrackBase(audioFlinger, client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer)
{
mVolume[0] = 1.0f;
mVolume[1] = 1.0f;
mMute = false;
+ mSharedBuffer = sharedBuffer;
}
AudioFlinger::Track::~Track()
@@ -943,8 +1113,8 @@ void AudioFlinger::Track::dump(char* buffer, size_t size)
mClient->pid(),
mStreamType,
mFormat,
- mChannelCount,
- mBufferCount,
+ mCblk->channels,
+ mFrameCount,
mState,
mMute,
mFillingUpStatus,
@@ -958,36 +1128,50 @@ void AudioFlinger::Track::dump(char* buffer, size_t size)
status_t AudioFlinger::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
audio_track_cblk_t* cblk = this->cblk();
- uint32_t u = cblk->user;
- uint32_t s = cblk->server;
-
- // Check if last stepServer failed, try to step now
+ 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;
}
- if (LIKELY(u > s)) {
- int index = s & audio_track_cblk_t::BUFFER_MASK;
- buffer->raw = getBuffer(index);
- buffer->frameCount = mAudioFlinger->frameCount();
- return NO_ERROR;
+ 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::Track::isReady(uint32_t u, int32_t s) const {
+bool AudioFlinger::Track::isReady() const {
if (mFillingUpStatus != FS_FILLING) return true;
- const uint32_t u_seq = u & audio_track_cblk_t::SEQUENCE_MASK;
- const uint32_t u_buf = u & audio_track_cblk_t::BUFFER_MASK;
- const uint32_t s_seq = s & audio_track_cblk_t::SEQUENCE_MASK;
- const uint32_t s_buf = s & audio_track_cblk_t::BUFFER_MASK;
- if (u_seq > s_seq && u_buf == s_buf) {
+
+ if (mCblk->framesReady() >= mCblk->frameCount ||
+ mCblk->forceReady) {
mFillingUpStatus = FS_FILLED;
+ mCblk->forceReady = 0;
return true;
}
return false;
@@ -1006,7 +1190,7 @@ void AudioFlinger::Track::stop()
Mutex::Autolock _l(mAudioFlinger->mLock);
if (mState > STOPPED) {
mState = STOPPED;
- // If the track is not active (PAUSED and buffers full), flush buffers
+ // If the track is not active (PAUSED and buffers full), flush buffers
if (mAudioFlinger->mActiveTracks.indexOf(this) < 0) {
reset();
}
@@ -1038,15 +1222,24 @@ void AudioFlinger::Track::flush()
// 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!
+ // as audio buffer => the AudioMixer code MUST always test that pointer
+ // returned by getNextBuffer() is not NULL!
reset();
}
void AudioFlinger::Track::reset()
{
- TrackBase::reset();
- mFillingUpStatus = FS_FILLING;
+ // 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::Track::mute(bool muted)
@@ -1112,26 +1305,15 @@ status_t AudioFlinger::TrackHandle::onTransact(
// ----------------------------------------------------------------------------
-sp<AudioFlinger::AudioRecordThread> AudioFlinger::audioRecordThread()
-{
- Mutex::Autolock _l(mLock);
- return mAudioRecordThread;
-}
-
-void AudioFlinger::endRecord()
-{
- Mutex::Autolock _l(mLock);
- mAudioRecordThread.clear();
-}
-
sp<IAudioRecord> AudioFlinger::openRecord(
pid_t pid,
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- uint32_t flags)
+ int frameCount,
+ uint32_t flags,
+ status_t *status)
{
sp<AudioRecordThread> thread;
sp<RecordTrack> recordTrack;
@@ -1139,46 +1321,46 @@ sp<IAudioRecord> AudioFlinger::openRecord(
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 (mSampleRate == 0) {
LOGE("Audio driver not initialized");
+ lStatus = NO_INIT;
goto Exit;
}
- // Create audio thread - take mutex to prevent race condition
- {
- Mutex::Autolock _l(mLock);
- if (mAudioRecordThread != 0) {
- LOGE("Record channel already open");
- goto Exit;
- }
- thread = new AudioRecordThread(this);
- mAudioRecordThread = thread;
- }
- // It's safe to release the mutex here since the client doesn't get a
- // handle until we return from this call
-
- // open driver, initialize h/w
- input = mAudioHardware->openInputStream(
- AudioSystem::PCM_16_BIT, channelCount, sampleRate);
- if (!input) {
- LOGE("Error opening input stream");
- mAudioRecordThread.clear();
+ 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;
}
@@ -1194,37 +1376,38 @@ sp<IAudioRecord> AudioFlinger::openRecord(
}
}
+ // 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 RecordTrack(this, client, streamType, sampleRate,
- format, channelCount, bufferCount, input->bufferSize());
-
- // spin up record thread
- thread->open(recordTrack, input);
- thread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO);
+ format, channelCount, frameCount);
// return to handle to client
recordHandle = new RecordHandle(recordTrack);
+ lStatus = NO_ERROR;
Exit:
+ if (status) {
+ *status = lStatus;
+ }
return recordHandle;
}
-status_t AudioFlinger::startRecord() {
- sp<AudioRecordThread> t = audioRecordThread();
- if (t == 0) return NO_INIT;
- return t->start();
+status_t AudioFlinger::startRecord(RecordTrack* recordTrack) {
+ if (mAudioRecordThread != 0) {
+ return mAudioRecordThread->start(recordTrack);
+ }
+ return NO_INIT;
}
-void AudioFlinger::stopRecord() {
- sp<AudioRecordThread> t = audioRecordThread();
- if (t != 0) t->stop();
+void AudioFlinger::stopRecord(RecordTrack* recordTrack) {
+ if (mAudioRecordThread != 0) {
+ mAudioRecordThread->stop(recordTrack);
+ }
}
-void AudioFlinger::exitRecord()
-{
- sp<AudioRecordThread> t = audioRecordThread();
- if (t != 0) t->exit();
-}
// ----------------------------------------------------------------------------
@@ -1235,55 +1418,69 @@ AudioFlinger::RecordTrack::RecordTrack(
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- int bufferSize)
+ int frameCount)
: TrackBase(audioFlinger, client, streamType, sampleRate, format,
- channelCount, bufferCount, bufferSize),
+ channelCount, frameCount, 0),
mOverflow(false)
{
}
AudioFlinger::RecordTrack::~RecordTrack()
{
- mAudioFlinger->audioMixer().deleteTrackName(mName);
- mAudioFlinger->exitRecord();
+ mAudioFlinger->audioMixer()->deleteTrackName(mName);
}
status_t AudioFlinger::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
- audio_track_cblk_t* cblk = this->cblk();
- const uint32_t u_seq = cblk->user & audio_track_cblk_t::SEQUENCE_MASK;
- const uint32_t u_buf = cblk->user & audio_track_cblk_t::BUFFER_MASK;
- const uint32_t s_seq = cblk->server & audio_track_cblk_t::SEQUENCE_MASK;
- const uint32_t s_buf = cblk->server & audio_track_cblk_t::BUFFER_MASK;
-
- // Check if last stepServer failed, try to step now
- if (mFlags & TrackBase::STEPSERVER_FAILED) {
- if (!step()) goto getNextBuffer_exit;
- LOGV("stepServer recovered");
- mFlags &= ~TrackBase::STEPSERVER_FAILED;
- }
+ 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;
+ }
- if (LIKELY(s_seq == u_seq || s_buf != u_buf)) {
- buffer->raw = getBuffer(s_buf);
- buffer->frameCount = mAudioFlinger->frameCount();
- return NO_ERROR;
- }
+ framesAvail = cblk->framesAvailable_l();
-getNextBuffer_exit:
- buffer->raw = 0;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
+ 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::RecordTrack::start()
{
- return mAudioFlinger->startRecord();
+ return mAudioFlinger->startRecord(this);
}
void AudioFlinger::RecordTrack::stop()
{
- mAudioFlinger->stopRecord();
+ mAudioFlinger->stopRecord(this);
+ TrackBase::reset();
+ // Force overerrun condition to avoid false overrun callback until first data is
+ // read from buffer
+ mCblk->flowControlFlag = 1;
}
// ----------------------------------------------------------------------------
@@ -1294,7 +1491,9 @@ AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordTrack>& re
{
}
-AudioFlinger::RecordHandle::~RecordHandle() {}
+AudioFlinger::RecordHandle::~RecordHandle() {
+ stop();
+}
status_t AudioFlinger::RecordHandle::start() {
LOGV("RecordHandle::start()");
@@ -1318,10 +1517,8 @@ status_t AudioFlinger::RecordHandle::onTransact(
// ----------------------------------------------------------------------------
-AudioFlinger::AudioRecordThread::AudioRecordThread(const sp<AudioFlinger>& audioFlinger) :
- mAudioFlinger(audioFlinger),
- mRecordTrack(0),
- mInput(0),
+AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware) :
+ mAudioHardware(audioHardware),
mActive(false)
{
}
@@ -1333,108 +1530,123 @@ 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();
+
mWaitWorkCV.wait(mLock);
+
LOGV("AudioRecordThread: loop starting");
+ if (mRecordTrack != 0) {
+ input = mAudioHardware->openInputStream(mRecordTrack->format(),
+ mRecordTrack->channelCount(),
+ mRecordTrack->sampleRate(),
+ &mStartStatus);
+ 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 {
- // promote strong ref so track isn't deleted while we access it
- sp<RecordTrack> t = mRecordTrack.promote();
+ } else if (mRecordTrack != 0){
- // if we lose the weak reference, client is gone.
- if (t == 0) {
- LOGV("AudioRecordThread: client deleted track");
- break;
- }
-
- if (LIKELY(t->getNextBuffer(&mBuffer) == NO_ERROR)) {
- if (mInput->read(mBuffer.raw, t->mBufferSize) < 0) {
+ buffer.frameCount = inFrameCount;
+ if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ LOGV("AudioRecordThread read: %d frames", buffer.frameCount);
+ if (input->read(buffer.raw, inBufferSize) < 0) {
LOGE("Error reading audio input");
sleep(1);
}
- t->releaseBuffer(&mBuffer);
+ mRecordTrack->releaseBuffer(&buffer);
+ mRecordTrack->overflow();
}
// client isn't retrieving buffers fast enough
else {
- if (!t->setOverflow())
+ 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);
}
}
- };
-
- // close hardware
- close();
+ }
- // delete this object - no more data references after this call
- mAudioFlinger->endRecord();
- return false;
-}
-status_t AudioFlinger::AudioRecordThread::open(const sp<RecordTrack>& recordTrack, AudioStreamIn *input) {
- LOGV("AudioRecordThread::open");
- // check for record channel already open
- AutoMutex lock(&mLock);
- if (mRecordTrack != NULL) {
- LOGE("Record channel already open");
- return ALREADY_EXISTS;
+ if (input) {
+ delete input;
}
- mRecordTrack = recordTrack;
- mInput = input;
- return NO_ERROR;
+ mRecordTrack.clear();
+
+ return false;
}
-status_t AudioFlinger::AudioRecordThread::start()
+status_t AudioFlinger::AudioRecordThread::start(RecordTrack* recordTrack)
{
LOGV("AudioRecordThread::start");
AutoMutex lock(&mLock);
- if (mActive) return -EBUSY;
+ 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;
- sp<RecordTrack> t = mRecordTrack.promote();
- if (t == 0) return UNKNOWN_ERROR;
+ mRecordTrack = recordTrack;
// signal thread to start
LOGV("Signal record thread");
- mActive = true;
mWaitWorkCV.signal();
- return NO_ERROR;
+ mWaitWorkCV.wait(mLock);
+ LOGV("Record started, status %d", mStartStatus);
+ return mStartStatus;
}
-void AudioFlinger::AudioRecordThread::stop() {
+void AudioFlinger::AudioRecordThread::stop(RecordTrack* recordTrack) {
LOGV("AudioRecordThread::stop");
AutoMutex lock(&mLock);
- if (mActive) {
+ if (mActive && (recordTrack == mRecordTrack.get())) {
mActive = false;
- mWaitWorkCV.signal();
}
}
void AudioFlinger::AudioRecordThread::exit()
{
LOGV("AudioRecordThread::exit");
- AutoMutex lock(&mLock);
- requestExit();
- mWaitWorkCV.signal();
+ {
+ AutoMutex lock(&mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
}
-status_t AudioFlinger::AudioRecordThread::close()
-{
- LOGV("AudioRecordThread::close");
- AutoMutex lock(&mLock);
- if (!mInput) return NO_INIT;
- delete mInput;
- mInput = 0;
- return NO_ERROR;
-}
-
status_t AudioFlinger::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 8c02617..d9f7b49 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -43,13 +43,17 @@ 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, protected Thread
{
public:
@@ -69,13 +73,16 @@ public:
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- uint32_t flags);
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer,
+ status_t *status);
virtual uint32_t sampleRate() const;
virtual int channelCount() const;
virtual int format() const;
virtual size_t frameCount() const;
+ virtual size_t latency() const;
virtual status_t setMasterVolume(float value);
virtual status_t setMasterMute(bool muted);
@@ -128,8 +135,9 @@ public:
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- uint32_t flags);
+ int frameCount,
+ uint32_t flags,
+ status_t *status);
virtual status_t onTransact(
uint32_t code,
@@ -141,12 +149,15 @@ private:
AudioFlinger();
virtual ~AudioFlinger();
+ void setOutput(AudioStreamOut* output);
+ size_t getOutputFrameCount(AudioStreamOut* output);
+
// Internal dump utilites.
status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
status_t dumpClients(int fd, const Vector<String16>& args);
status_t dumpTracks(int fd, const Vector<String16>& args);
status_t dumpInternals(int fd, const Vector<String16>& args);
-
+
// --- Client ---
class Client : public RefBase {
public:
@@ -183,17 +194,17 @@ private:
};
enum track_flags {
- STEPSERVER_FAILED = 0x01 // StepServer could not acquire cblk->lock mutex
+ STEPSERVER_FAILED = 0x01 // StepServer could not acquire cblk->lock mutex
};
-
+
TrackBase( const sp<AudioFlinger>& audioFlinger,
const sp<Client>& client,
int streamType,
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- int bufferSize);
+ int frameCount,
+ const sp<IMemory>& sharedBuffer);
~TrackBase();
virtual status_t start() = 0;
@@ -203,6 +214,7 @@ private:
protected:
friend class AudioFlinger;
friend class RecordHandle;
+ friend class AudioRecordThread;
TrackBase(const TrackBase&);
TrackBase& operator = (const TrackBase&);
@@ -222,19 +234,11 @@ private:
return mFormat;
}
- int channelCount() const {
- return mChannelCount;
- }
-
- int bufferCount() const {
- return mBufferCount;
- }
+ int channelCount() const ;
int sampleRate() const;
- void* getBuffer(int n) const {
- return (char*)mBuffers + n * mBufferSize;
- }
+ void* getBuffer(uint32_t offset, uint32_t frames) const;
int name() const {
return mName;
@@ -256,16 +260,15 @@ private:
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
int mStreamType;
- uint8_t mFormat;
- uint8_t mChannelCount;
- uint8_t mBufferCount;
- uint8_t mFlags;
- void* mBuffers;
- size_t mBufferSize;
+ 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;
+ uint8_t mFlags;
};
// playback track
@@ -277,8 +280,8 @@ private:
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- int bufferSize);
+ int frameCount,
+ const sp<IMemory>& sharedBuffer);
~Track();
void dump(char* buffer, size_t size);
@@ -312,7 +315,7 @@ private:
return mState == PAUSED;
}
- bool isReady(uint32_t u, int32_t s) const;
+ bool isReady() const;
void setPaused() { mState = PAUSED; }
void reset();
@@ -324,6 +327,8 @@ private:
enum {FS_FILLING, FS_FILLED, FS_ACTIVE};
mutable uint8_t mFillingUpStatus;
int8_t mRetryCount;
+ sp<IMemory> mSharedBuffer;
+ bool mResetDone;
}; // end of Track
friend class AudioBuffer;
@@ -366,8 +371,8 @@ private:
void remove_track_l(wp<Track> track, int name);
void destroyTrack(const sp<Track>& track);
- AudioMixer& audioMixer() {
- return *mAudioMixer;
+ AudioMixer* audioMixer() {
+ return mAudioMixer;
}
// record track
@@ -379,8 +384,7 @@ private:
uint32_t sampleRate,
int format,
int channelCount,
- int bufferCount,
- int bufferSize);
+ int frameCount);
~RecordTrack();
virtual status_t start();
@@ -419,43 +423,34 @@ private:
class AudioRecordThread : public Thread
{
public:
- AudioRecordThread(const sp<AudioFlinger>& audioFlinger);
+ AudioRecordThread(AudioHardwareInterface* audioHardware);
virtual ~AudioRecordThread();
virtual bool threadLoop();
virtual status_t readyToRun() { return NO_ERROR; }
virtual void onFirstRef() {}
- status_t open(const sp<RecordTrack>& recordTrack, AudioStreamIn *input);
- status_t start();
- void stop();
- status_t close();
+ status_t start(RecordTrack* recordTrack);
+ void stop(RecordTrack* recordTrack);
void exit();
-
- bool isOpen() { return bool(mRecordTrack != NULL); }
private:
AudioRecordThread();
- sp<AudioFlinger> mAudioFlinger;
- wp<RecordTrack> mRecordTrack;
- AudioStreamIn* mInput;
+ AudioHardwareInterface *mAudioHardware;
+ sp<RecordTrack> mRecordTrack;
Mutex mLock;
Condition mWaitWorkCV;
- AudioBufferProvider::Buffer mBuffer;
volatile bool mActive;
+ status_t mStartStatus;
};
friend class AudioRecordThread;
- sp<AudioRecordThread> audioRecordThread();
- void endRecord();
- status_t startRecord();
- void stopRecord();
- void exitRecord();
-
- AudioHardwareInterface* audioHardware() { return mAudioHardware; }
+ status_t startRecord(RecordTrack* recordTrack);
+ void stopRecord(RecordTrack* recordTrack);
mutable Mutex mHardwareLock;
mutable Mutex mLock;
+ mutable Mutex mOutputLock;
mutable Condition mWaitWorkCV;
DefaultKeyedVector< pid_t, wp<Client> > mClients;
SortedVector< wp<Track> > mActiveTracks;
@@ -465,15 +460,19 @@ private:
bool mMasterMute;
stream_type_t mStreamTypes[AudioTrack::NUM_STREAM_TYPES];
+ AudioMixer* mHardwareAudioMixer;
+ AudioMixer* mA2dpAudioMixer;
AudioMixer* mAudioMixer;
AudioHardwareInterface* mAudioHardware;
+ AudioHardwareInterface* mA2dpAudioInterface;
+ AudioStreamOut* mHardwareOutput;
+ AudioStreamOut* mA2dpOutput;
AudioStreamOut* mOutput;
sp<AudioRecordThread> mAudioRecordThread;
uint32_t mSampleRate;
size_t mFrameCount;
int mChannelCount;
int mFormat;
- int mMixBufferSize;
int16_t* mMixBuffer;
mutable int mHardwareStatus;
nsecs_t mLastWriteTime;
diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp
index b1e5b7f..e6a163b 100644
--- a/libs/audioflinger/AudioHardwareGeneric.cpp
+++ b/libs/audioflinger/AudioHardwareGeneric.cpp
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -68,16 +68,25 @@ status_t AudioHardwareGeneric::standby()
}
AudioStreamOut* AudioHardwareGeneric::openOutputStream(
- int format, int channelCount, uint32_t sampleRate)
+ int format, int channelCount, uint32_t sampleRate, status_t *status)
{
AutoMutex lock(mLock);
// only one output stream allowed
- if (mOutput) return 0;
+ if (mOutput) {
+ if (status) {
+ *status = INVALID_OPERATION;
+ }
+ return 0;
+ }
// create new output stream
AudioStreamOutGeneric* out = new AudioStreamOutGeneric();
- if (out->set(this, mFd, format, channelCount, sampleRate) == NO_ERROR) {
+ status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR) {
mOutput = out;
} else {
delete out;
@@ -90,16 +99,25 @@ void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) {
}
AudioStreamIn* AudioHardwareGeneric::openInputStream(
- int format, int channelCount, uint32_t sampleRate)
+ int format, int channelCount, uint32_t sampleRate, status_t *status)
{
AutoMutex lock(mLock);
// only one input stream allowed
- if (mInput) return 0;
+ if (mInput) {
+ if (status) {
+ *status = INVALID_OPERATION;
+ }
+ return 0;
+ }
// create new output stream
AudioStreamInGeneric* in = new AudioStreamInGeneric();
- if (in->set(this, mFd, format, channelCount, sampleRate) == NO_ERROR) {
+ status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR) {
mInput = in;
} else {
delete in;
diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h
index 10cc45d..a2342cd 100644
--- a/libs/audioflinger/AudioHardwareGeneric.h
+++ b/libs/audioflinger/AudioHardwareGeneric.h
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -23,7 +23,7 @@
#include <utils/threads.h>
-#include <hardware/AudioHardwareInterface.h>
+#include <hardware/AudioHardwareBase.h>
namespace android {
@@ -47,6 +47,7 @@ public:
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 INVALID_OPERATION; }
virtual ssize_t write(const void* buffer, size_t bytes);
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -76,6 +77,7 @@ public:
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;
@@ -84,7 +86,7 @@ private:
};
-class AudioHardwareGeneric : public AudioHardwareInterface
+class AudioHardwareGeneric : public AudioHardwareBase
{
public:
AudioHardwareGeneric();
@@ -105,12 +107,14 @@ public:
virtual AudioStreamOut* openOutputStream(
int format=0,
int channelCount=0,
- uint32_t sampleRate=0);
+ uint32_t sampleRate=0,
+ status_t *status=0);
virtual AudioStreamIn* openInputStream(
int format,
int channelCount,
- uint32_t sampleRate);
+ uint32_t sampleRate,
+ status_t *status);
void closeOutputStream(AudioStreamOutGeneric* out);
void closeInputStream(AudioStreamInGeneric* in);
@@ -120,7 +124,7 @@ protected:
private:
status_t dumpInternals(int fd, const Vector<String16>& args);
-
+
Mutex mLock;
AudioStreamOutGeneric *mOutput;
AudioStreamInGeneric *mInput;
diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp
index 7387b3d..ac76a19 100644
--- a/libs/audioflinger/AudioHardwareInterface.cpp
+++ b/libs/audioflinger/AudioHardwareInterface.cpp
@@ -26,7 +26,7 @@
#include "AudioHardwareStub.h"
#include "AudioHardwareGeneric.h"
-// #define DUMP_FLINGER_OUT // if defined allows recording samples in a file
+//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file
#ifdef DUMP_FLINGER_OUT
#include "AudioDumpInterface.h"
#endif
@@ -54,6 +54,7 @@ static const char* routeStrings[] =
"SPEAKER ",
"BLUETOOTH ",
"HEADSET "
+ "BLUETOOTH_A2DP "
};
static const char* routeNone = "NONE";
@@ -115,24 +116,10 @@ AudioHardwareInterface* AudioHardwareInterface::create()
// This code adds a record of buffers in a file to write calls made by AudioFlinger.
// It replaces the current AudioHardwareInterface object by an intermediate one which
// will record buffers in a file (after sending them to hardware) for testing purpose.
- // This feature is enabled by defining symbol DUMP_FLINGER_OUT and setting environement
- // "audioflinger.dump = 1". The output file is "tmp/FlingerOut.pcm". Pause are not recorded
- // in the file.
+ // This feature is enabled by defining symbol DUMP_FLINGER_OUT.
+ // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file.
- // read dump mode
- property_get("audioflinger.dump", value, "0");
- switch(value[0]) {
- case '1':
- LOGV("Dump mode");
- hw = new AudioDumpInterface(hw); // replace interface
- return hw;
- break;
- case '0':
- default:
- LOGV("No Dump mode");
- return hw;
- break;
- }
+ hw = new AudioDumpInterface(hw); // replace interface
#endif
return hw;
}
@@ -143,7 +130,7 @@ AudioStreamOut::~AudioStreamOut()
AudioStreamIn::~AudioStreamIn() {}
-AudioHardwareInterface::AudioHardwareInterface()
+AudioHardwareBase::AudioHardwareBase()
{
// force a routing update on initialization
memset(&mRoutes, 0, sizeof(mRoutes));
@@ -151,7 +138,7 @@ AudioHardwareInterface::AudioHardwareInterface()
}
// generics for audio routing - the real work is done in doRouting
-status_t AudioHardwareInterface::setRouting(int mode, uint32_t routes)
+status_t AudioHardwareBase::setRouting(int mode, uint32_t routes)
{
#if LOG_ROUTING_CALLS
LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes));
@@ -173,7 +160,7 @@ status_t AudioHardwareInterface::setRouting(int mode, uint32_t routes)
return doRouting();
}
-status_t AudioHardwareInterface::getRouting(int mode, uint32_t* routes)
+status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes)
{
if (mode == AudioSystem::MODE_CURRENT)
mode = mMode;
@@ -187,7 +174,7 @@ status_t AudioHardwareInterface::getRouting(int mode, uint32_t* routes)
return NO_ERROR;
}
-status_t AudioHardwareInterface::setMode(int mode)
+status_t AudioHardwareBase::setMode(int mode)
{
#if LOG_ROUTING_CALLS
LOGD("setMode(%s)", displayMode(mode));
@@ -204,25 +191,45 @@ status_t AudioHardwareInterface::setMode(int mode)
return doRouting();
}
-status_t AudioHardwareInterface::getMode(int* mode)
+status_t AudioHardwareBase::getMode(int* mode)
{
// Implement: set audio routing
*mode = mMode;
return NO_ERROR;
}
-status_t AudioHardwareInterface::setParameter(const char* key, const char* value)
+status_t AudioHardwareBase::setParameter(const char* key, const char* value)
{
// default implementation is to ignore
return NO_ERROR;
}
-status_t AudioHardwareInterface::dumpState(int fd, const Vector<String16>& args)
+
+// 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, "AudioHardwareInterface::dumpState\n");
+ snprintf(buffer, SIZE, "AudioHardwareBase::dumpState\n");
result.append(buffer);
snprintf(buffer, SIZE, "\tmMode: %d\n", mMode);
result.append(buffer);
diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp
index 0046db8..d309902 100644
--- a/libs/audioflinger/AudioHardwareStub.cpp
+++ b/libs/audioflinger/AudioHardwareStub.cpp
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -47,20 +47,28 @@ status_t AudioHardwareStub::standby()
}
AudioStreamOut* AudioHardwareStub::openOutputStream(
- int format, int channelCount, uint32_t sampleRate)
+ int format, int channelCount, uint32_t sampleRate, status_t *status)
{
AudioStreamOutStub* out = new AudioStreamOutStub();
- if (out->set(format, channelCount, sampleRate) == NO_ERROR)
+ 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)
+ int format, int channelCount, uint32_t sampleRate, status_t *status)
{
AudioStreamInStub* in = new AudioStreamInStub();
- if (in->set(format, channelCount, sampleRate) == NO_ERROR)
+ status_t lStatus = in->set(format, channelCount, sampleRate);
+ if (status) {
+ *status = lStatus;
+ }
+ if (lStatus == NO_ERROR)
return in;
delete in;
return 0;
@@ -102,7 +110,7 @@ status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate)
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()))
@@ -129,7 +137,7 @@ status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args)
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
::write(fd, result.string(), result.size());
- return NO_ERROR;
+ return NO_ERROR;
}
// ----------------------------------------------------------------------------
diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h
index 1a61552..5316d60 100644
--- a/libs/audioflinger/AudioHardwareStub.h
+++ b/libs/audioflinger/AudioHardwareStub.h
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -21,7 +21,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <hardware/AudioHardwareInterface.h>
+#include <hardware/AudioHardwareBase.h>
namespace android {
@@ -34,6 +34,7 @@ public:
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 dump(int fd, const Vector<String16>& args);
@@ -49,9 +50,10 @@ public:
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 AudioHardwareInterface
+class AudioHardwareStub : public AudioHardwareBase
{
public:
AudioHardwareStub();
@@ -72,12 +74,14 @@ public:
virtual AudioStreamOut* openOutputStream(
int format=0,
int channelCount=0,
- uint32_t sampleRate=0);
+ uint32_t sampleRate=0,
+ status_t *status=0);
virtual AudioStreamIn* openInputStream(
int format,
int channelCount,
- uint32_t sampleRate);
+ uint32_t sampleRate,
+ status_t *status);
protected:
virtual status_t doRouting() { return NO_ERROR; }
diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp
index 9f1b17f..b03467f 100644
--- a/libs/audioflinger/AudioMixer.cpp
+++ b/libs/audioflinger/AudioMixer.cpp
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -247,8 +247,8 @@ inline
void AudioMixer::track_t::adjustVolumeRamp()
{
for (int i=0 ; i<2 ; i++) {
- if (((volumeInc[i]>0) && ((prevVolume[i]>>16) >= volume[i])) ||
- ((volumeInc[i]<0) && ((prevVolume[i]>>16) <= volume[i]))) {
+ 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;
}
@@ -307,7 +307,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
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) {
@@ -370,7 +370,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
state->hook(state, output);
- // Now that the volume ramp has been done, set optimal state and
+ // Now that the volume ramp has been done, set optimal state and
// track hooks for subsequent mixer process
if (countActiveTracks) {
int allMuted = 1;
@@ -397,7 +397,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
}
}
-static inline
+static inline
int32_t mulAdd(int16_t in, int16_t v, int32_t a)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -412,7 +412,7 @@ int32_t mulAdd(int16_t in, int16_t v, int32_t a)
#endif
}
-static inline
+static inline
int32_t mul(int16_t in, int16_t v)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -427,7 +427,7 @@ int32_t mul(int16_t in, int16_t v)
#endif
}
-static inline
+static inline
int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -453,7 +453,7 @@ int32_t mulAddRL(int left, uint32_t inRL, uint32_t vRL, int32_t a)
#endif
}
-static inline
+static inline
int32_t mulRL(int left, uint32_t inRL, uint32_t vRL)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -513,7 +513,7 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i
//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);
@@ -548,7 +548,7 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
vl += vlInc;
vr += vrInc;
} while (--frameCount);
-
+
t->prevVolume[0] = vl;
t->prevVolume[1] = vr;
t->adjustVolumeRamp();
@@ -590,7 +590,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
vl += vlInc;
vr += vrInc;
} while (--frameCount);
-
+
t->prevVolume[0] = vl;
t->prevVolume[1] = vr;
t->adjustVolumeRamp();
@@ -609,7 +609,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
t->in = in;
}
-inline
+inline
void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c)
{
for (size_t i=0 ; i<c ; i++) {
@@ -633,8 +633,12 @@ void AudioMixer::process__nop(state_t* state, void* output)
const int i = 31 - __builtin_clz(en);
en &= ~(1<<i);
track_t& t = state->tracks[i];
- t.bufferProvider->getNextBuffer(&t.buffer);
- if (t.buffer.raw) {
+ 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);
}
}
@@ -652,12 +656,14 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output)
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);
+ enabledTracks &= ~(1<<i);
}
// this assumes output 16 bits stereo, no resampling
@@ -671,12 +677,31 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output)
const int i = 31 - __builtin_clz(en);
en &= ~(1<<i);
track_t& t = state->tracks[i];
- (t.hook)(&t, outTemp, BLOCKSIZE, state->resampleTemp);
+ 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);
@@ -713,12 +738,19 @@ void AudioMixer::process__genericResampling(state_t* state, void* output)
if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
(t.hook)(&t, outTemp, numFrames, state->resampleTemp);
} else {
- t.bufferProvider->getNextBuffer(&t.buffer);
- t.in = t.buffer.raw;
- // t.in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t.in) {
- (t.hook)(&t, outTemp, numFrames, state->resampleTemp);
+
+ 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);
}
}
@@ -734,45 +766,51 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void*
const track_t& t = state->tracks[i];
AudioBufferProvider::Buffer& b(t.buffer);
- t.bufferProvider->getNextBuffer(&b);
- int16_t const *in = t.buffer.i16;
-
- // in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (in == NULL) {
- memset(output, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t));
- return;
- }
-
+
int32_t* out = static_cast<int32_t*>(output);
size_t numFrames = state->frameCount;
+
const int16_t vl = t.volume[0];
const int16_t vr = t.volume[1];
const uint32_t vrl = t.volumeRL;
- if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
- // volume is boosted, so we might need to clamp even though
- // we process only one track.
- do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
- in += 2;
- int32_t l = mulRL(1, rl, vrl) >> 12;
- int32_t r = mulRL(0, rl, vrl) >> 12;
- // clamping...
- l = clamp16(l);
- r = clamp16(r);
- *out++ = (r<<16) | (l & 0xFFFF);
- } while (--numFrames);
- } else {
- do {
- uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
- in += 2;
- int32_t l = mulRL(1, rl, vrl) >> 12;
- int32_t r = mulRL(0, rl, vrl) >> 12;
- *out++ = (r<<16) | (l & 0xFFFF);
- } while (--numFrames);
- }
+ while (numFrames) {
+ b.frameCount = numFrames;
+ t.bufferProvider->getNextBuffer(&b);
+ int16_t const *in = b.i16;
- t.bufferProvider->releaseBuffer(&b);
+ // 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
@@ -784,71 +822,89 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
i = 31 - __builtin_clz(en);
const track_t& t0 = state->tracks[i];
AudioBufferProvider::Buffer& b0(t0.buffer);
- t0.bufferProvider->getNextBuffer(&b0);
en &= ~(1<<i);
i = 31 - __builtin_clz(en);
const track_t& t1 = state->tracks[i];
AudioBufferProvider::Buffer& b1(t1.buffer);
- t1.bufferProvider->getNextBuffer(&b1);
-
+
int16_t const *in0;
const int16_t vl0 = t0.volume[0];
const int16_t vr0 = t0.volume[1];
+ size_t frameCount0 = 0;
+
int16_t const *in1;
const int16_t vl1 = t1.volume[0];
const int16_t vr1 = t1.volume[1];
- size_t numFrames = state->frameCount;
+ size_t frameCount1 = 0;
+
int32_t* out = static_cast<int32_t*>(output);
-
- // t0/1.buffer.i16 == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t0.buffer.i16 != NULL) {
- in0 = t0.buffer.i16;
- if (t1.buffer.i16 != NULL) {
- in1 = t1.buffer.i16;
- } else {
- in1 = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
- memset((void *)in1, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t));
+ 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;
}
- } else {
- in0 = new int16_t[MAX_NUM_CHANNELS * state->frameCount];
- memset((void *)in0, 0, state->frameCount*MAX_NUM_CHANNELS*sizeof(int16_t));
- if (t1.buffer.i16 != NULL) {
- in1 = t1.buffer.i16;
- } else {
- in1 = in0;
+ 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;
}
- }
-
- do {
- int32_t l0 = *in0++;
- int32_t r0 = *in0++;
- l0 = mul(l0, vl0);
- r0 = mul(r0, vr0);
- int32_t l = *in1++;
- int32_t r = *in1++;
- l = mulAdd(l, vl1, l0) >> 12;
- r = mulAdd(r, vr1, r0) >> 12;
- // clamping...
- l = clamp16(l);
- r = clamp16(r);
- *out++ = (r<<16) | (l & 0xFFFF);
- } while (--numFrames);
+
+ size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
-
- if (t0.buffer.i16 != NULL) {
- t0.bufferProvider->releaseBuffer(&b0);
- if (t1.buffer.i16 != NULL) {
- t1.bufferProvider->releaseBuffer(&b1);
- } else {
- delete [] in1;
+ 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);
}
- } else {
- delete [] in0;
- if (t1.buffer.i16 != NULL) {
+ if (frameCount1 == 0) {
t1.bufferProvider->releaseBuffer(&b1);
}
+ }
+
+ if (buff != NULL) {
+ delete [] buff;
}
}
diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h
index 9ca109f..72ca28a 100644
--- a/libs/audioflinger/AudioMixer.h
+++ b/libs/audioflinger/AudioMixer.h
@@ -2,16 +2,16 @@
**
** 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
+** 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
+** 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
+** 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.
*/
@@ -130,7 +130,7 @@ private:
int32_t volumeInc[2];
- uint16_t reserved;
+ uint16_t frameCount;
uint8_t channelCount : 4;
uint8_t enabled : 1;
diff --git a/libs/audioflinger/AudioResampler.cpp b/libs/audioflinger/AudioResampler.cpp
index c93ead3..5dabacb 100644
--- a/libs/audioflinger/AudioResampler.cpp
+++ b/libs/audioflinger/AudioResampler.cpp
@@ -14,17 +14,23 @@
* 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 {
@@ -46,6 +52,15 @@ private:
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);
}
@@ -73,20 +88,23 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
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;
@@ -103,10 +121,10 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
inChannelCount);
// LOG_ASSERT(0);
}
-
+
// initialize common members
mVolume[0] = mVolume[1] = 0;
- mBuffer.raw = NULL;
+ mBuffer.frameCount = 0;
// save format for quick lookup
if (inChannelCount == 1) {
@@ -160,19 +178,31 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
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);
+ // outFrameCount, inputIndex, phaseFraction, phaseIncrement);
while (outputIndex < outputSampleCount) {
// buffer is empty, fetch a new one
- if (mBuffer.raw == NULL) {
+ while (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer);
- if (mBuffer.raw == NULL)
- break;
+ 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
@@ -187,34 +217,47 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount,
// process input samples
// LOGE("general case\n");
- while (outputIndex < outputSampleCount) {
+
+#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);
- if (inputIndex >= mBuffer.frameCount)
- break;
}
+
// LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
// if done with buffer, save samples
if (inputIndex >= mBuffer.frameCount) {
inputIndex -= mBuffer.frameCount;
- // LOGE("buffer done, new input index", inputIndex);
+ // 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 NULLS the buffer pointer
- // LOG_ASSERT(mBuffer.raw == NULL);
+ // 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;
@@ -231,18 +274,27 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
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
- if (mBuffer.raw == NULL) {
+ while (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer);
- if (mBuffer.raw == NULL)
- break;
+ 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;
@@ -259,38 +311,284 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount,
// process input samples
// LOGE("general case\n");
- while (outputIndex < outputSampleCount) {
+
+#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);
- if (inputIndex >= mBuffer.frameCount)
- break;
}
+
+
// LOGE("loop done - outputIndex=%d, inputIndex=%d\n", outputIndex, inputIndex);
// if done with buffer, save samples
if (inputIndex >= mBuffer.frameCount) {
inputIndex -= mBuffer.frameCount;
- // LOGE("buffer done, new input index", inputIndex);
+ // LOGE("buffer done, new input index %d", inputIndex);
mX0L = mBuffer.i16[mBuffer.frameCount-1];
provider->releaseBuffer(&mBuffer);
- // verify that the releaseBuffer NULLS the buffer pointer
- // LOG_ASSERT(mBuffer.raw == NULL);
+ // 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/AudioResamplerCubic.cpp b/libs/audioflinger/AudioResamplerCubic.cpp
index 4f437bf..1d247bd 100644
--- a/libs/audioflinger/AudioResamplerCubic.cpp
+++ b/libs/audioflinger/AudioResamplerCubic.cpp
@@ -60,9 +60,11 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
-
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
// fetch first buffer
- if (mBuffer.raw == NULL) {
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer);
if (mBuffer.raw == NULL)
return;
@@ -79,7 +81,7 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
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);
@@ -92,6 +94,7 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount,
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
@@ -122,9 +125,11 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
uint32_t phaseIncrement = mPhaseIncrement;
size_t outputIndex = 0;
size_t outputSampleCount = outFrameCount * 2;
-
+ size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate;
+
// fetch first buffer
- if (mBuffer.raw == NULL) {
+ if (mBuffer.frameCount == 0) {
+ mBuffer.frameCount = inFrameCount;
provider->getNextBuffer(&mBuffer);
if (mBuffer.raw == NULL)
return;
@@ -141,7 +146,7 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
sample = interp(&left, x);
out[outputIndex++] += vl * sample;
out[outputIndex++] += vr * sample;
-
+
// increment phase
phaseFraction += phaseIncrement;
uint32_t indexIncrement = (phaseFraction >> kNumPhaseBits);
@@ -154,6 +159,7 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount,
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
diff --git a/libs/audioflinger/AudioResamplerSinc.cpp b/libs/audioflinger/AudioResamplerSinc.cpp
index e710d16..9e5e254 100644
--- a/libs/audioflinger/AudioResamplerSinc.cpp
+++ b/libs/audioflinger/AudioResamplerSinc.cpp
@@ -25,18 +25,18 @@ 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.
+ * 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
+ 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
};
/*
@@ -46,20 +46,20 @@ const int32_t AudioResamplerSinc::mFirCoefsUp[] = {
* 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
+ 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
+static inline
int32_t mulRL(int left, int32_t in, uint32_t vRL)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -85,7 +85,7 @@ int32_t mulRL(int left, int32_t in, uint32_t vRL)
#endif
}
-static inline
+static inline
int32_t mulAdd(int16_t in, int32_t v, int32_t a)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -95,12 +95,14 @@ int32_t mulAdd(int16_t in, int32_t v, int32_t a)
: [in]"%r"(in), [v]"r"(v), [a]"r"(a)
: );
return out;
-#else
- return a + ((in * int32_t(v))>>16);
+#else
+ return a + in * (v>>16);
+ // improved precision
+ // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16);
#endif
}
-static inline
+static inline
int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
{
#if defined(__arm__) && !defined(__thumb__)
@@ -119,9 +121,11 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
return out;
#else
if (left) {
- return a + ((int16_t(inRL&0xFFFF) * int32_t(v))>>16);
+ 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) * int32_t(v))>>16);
+ return a + (int16_t(inRL>>16) * (v>>16));
}
#endif
}
@@ -129,37 +133,37 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
// ----------------------------------------------------------------------------
AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
- int inChannelCount, int32_t sampleRate)
- : AudioResampler(bitDepth, inChannelCount, sampleRate),
- mState(0)
+ 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
- *
- */
+ /*
+ * 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;
+ 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;
+ delete [] mState;
}
void AudioResamplerSinc::init() {
@@ -168,9 +172,9 @@ void AudioResamplerSinc::init() {
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
- mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
+ mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
- // select the appropriate resampler
+ // select the appropriate resampler
switch (mChannelCount) {
case 1:
resample<1>(out, outFrameCount, provider);
@@ -193,43 +197,68 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
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
- if (buffer.raw == NULL) {
+ while (buffer.frameCount == 0) {
+ buffer.frameCount = inFrameCount;
provider->getNextBuffer(&buffer);
- if (buffer.raw == NULL)
- break;
- const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
- if (phaseIndex) {
- read<CHANNELS>(impulse, phaseFraction, buffer.i16, inputIndex);
+ 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;
+ 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];
+ // 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;
+ int32_t l, r;
while (outputIndex < outputSampleCount) {
- filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse);
- out[outputIndex++] = mulRL(1, l, vRL);
- out[outputIndex++] = mulRL(0, r, vRL);
+ 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) {
- inputIndex += phaseIndex;
- if (inputIndex >= frameCount)
- break;
- read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
- }
+ 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
@@ -239,80 +268,89 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
}
}
+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)
+ int16_t*& impulse, uint32_t& phaseFraction,
+ int16_t const* in, size_t inputIndex)
{
- // read new samples into the ring buffer
- while (phaseFraction >> kNumPhaseBits) {
- const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
- impulse += CHANNELS;
- phaseFraction -= 1LU<<kNumPhaseBits;
- if (impulse >= mRingFull) {
- const size_t stateSize = (halfNumCoefs*2)*CHANNELS;
- memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
- impulse -= stateSize;
- }
- int16_t* head = impulse + halfNumCoefs*CHANNELS;
- head[0] = in[inputIndex*CHANNELS + 0];
- if (CHANNELS == 2)
- head[1] = in[inputIndex*CHANNELS + 1];
- }
+ const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
+ impulse += CHANNELS;
+ phaseFraction -= 1LU<<kNumPhaseBits;
+ if (impulse >= mRingFull) {
+ const size_t stateSize = (halfNumCoefs*2)*CHANNELS;
+ memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
+ impulse -= stateSize;
+ }
+ int16_t* head = impulse + halfNumCoefs*CHANNELS;
+ head[0] = in[inputIndex*CHANNELS + 0];
+ if (CHANNELS == 2)
+ head[1] = in[inputIndex*CHANNELS + 1];
}
template<int CHANNELS>
void AudioResamplerSinc::filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, int16_t const *samples)
-{
- // compute the index of the coefficient on the positive side and
- // negative side
- uint32_t indexP = (phase & cMask) >> cShift;
- uint16_t lerpP = (phase & pMask) >> pShift;
- uint32_t indexN = (-phase & cMask) >> cShift;
- uint16_t lerpN = (-phase & pMask) >> pShift;
-
- l = 0;
- r = 0;
- int32_t const* coefs = mFirCoefs;
- int16_t const *sP = samples;
- int16_t const *sN = samples+CHANNELS;
- for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) {
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits;
+ 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;
+ 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;
+ 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;
- }
+ 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 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);
- }
+ 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);
+ }
}
// ----------------------------------------------------------------------------
diff --git a/libs/audioflinger/AudioResamplerSinc.h b/libs/audioflinger/AudioResamplerSinc.h
index 89b9577..e6cb90b 100644
--- a/libs/audioflinger/AudioResamplerSinc.h
+++ b/libs/audioflinger/AudioResamplerSinc.h
@@ -24,19 +24,20 @@
#include "AudioResampler.h"
namespace android {
+
// ----------------------------------------------------------------------------
class AudioResamplerSinc : public AudioResampler {
public:
- AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate);
+ AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate);
+
+ ~AudioResamplerSinc();
- ~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);
@@ -52,12 +53,12 @@ private:
template<int CHANNELS>
inline void read(int16_t*& impulse, uint32_t& phaseFraction,
- int16_t const* in, size_t inputIndex);
+ 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[];
@@ -67,15 +68,15 @@ private:
static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4;
// we have 16 coefs samples per zero-crossing
- static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS;
- static const int cShift = kNumPhaseBits - coefsBits;
- static const uint32_t cMask = ((1<<coefsBits)-1) << cShift;
+ 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;
- static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift;
+ 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;