Merge changes I52ec5b02,I376656e3,I3f005583,Icba4e33d,I78400f6c, ...
am: d7a1506715
Change-Id: Idd3ab59ad99c450d89fd47aaa1a15681f73175ac
diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk
index f5f021b..6697cb5 100644
--- a/media/audioserver/Android.mk
+++ b/media/audioserver/Android.mk
@@ -9,6 +9,7 @@
libaaudioservice \
libaudioflinger \
libaudiopolicyservice \
+ libaudioprocessing \
libbinder \
libcutils \
liblog \
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 783eef3..3f7cd48 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -18,87 +18,38 @@
#ifndef ANDROID_AUDIO_MIXER_H
#define ANDROID_AUDIO_MIXER_H
-#include <map>
#include <pthread.h>
-#include <sstream>
#include <stdint.h>
#include <sys/types.h>
-#include <unordered_map>
-#include <vector>
#include <android/os/IExternalVibratorService.h>
-#include <media/AudioBufferProvider.h>
-#include <media/AudioResampler.h>
-#include <media/AudioResamplerPublic.h>
+#include <media/AudioMixerBase.h>
#include <media/BufferProviders.h>
-#include <system/audio.h>
-#include <utils/Compat.h>
#include <utils/threads.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
-#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
-
-// This must match frameworks/av/services/audioflinger/Configuration.h
-#define FLOAT_AUX
+#define MAX_GAIN_INT AudioMixerBase::UNITY_GAIN_INT
namespace android {
-namespace NBLog {
-class Writer;
-} // namespace NBLog
-
// ----------------------------------------------------------------------------
-class AudioMixer
+// AudioMixer extends AudioMixerBase by adding support for down- and up-mixing
+// and time stretch that are implemented via Effects HAL, and adding support
+// for haptic channels which depends on Vibrator service. This is the version
+// that is used by Audioflinger.
+
+class AudioMixer : public AudioMixerBase
{
public:
- // Do not change these unless underlying code changes.
- // This mixer has a hard-coded upper limit of 8 channels for output.
- static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
- static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2; // stereo volume only
// maximum number of channels supported for the content
static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;
- static const uint16_t UNITY_GAIN_INT = 0x1000;
- static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;
-
- enum { // names
- // setParameter targets
- TRACK = 0x3000,
- RESAMPLE = 0x3001,
- RAMP_VOLUME = 0x3002, // ramp to new volume
- VOLUME = 0x3003, // don't ramp
- TIMESTRETCH = 0x3004,
-
- // set Parameter names
- // for target TRACK
- CHANNEL_MASK = 0x4000,
- FORMAT = 0x4001,
- MAIN_BUFFER = 0x4002,
- AUX_BUFFER = 0x4003,
- DOWNMIX_TYPE = 0X4004,
- MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
- MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
+ enum { // extension of AudioMixerBase parameters
+ DOWNMIX_TYPE = 0x4004,
// for haptic
HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not.
HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data.
- // for target RESAMPLE
- SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
- // parameter 'value' is the new sample rate in Hz.
- // Only creates a sample rate converter the first time that
- // the track sample rate is different from the mix sample rate.
- // If the new sample rate is the same as the mix sample rate,
- // and a sample rate converter already exists,
- // then the sample rate converter remains present but is a no-op.
- RESET = 0x4101, // Reset sample rate converter without changing sample rate.
- // This clears out the resampler's input buffer.
- REMOVE = 0x4102, // Remove the sample rate converter on this track name;
- // the track is restored to the mix sample rate.
- // for target RAMP_VOLUME and VOLUME (8 channels max)
- // FIXME use float for these 3 to improve the dynamic range
- VOLUME0 = 0x4200,
- VOLUME1 = 0x4201,
- AUXLEVEL = 0x4210,
// for target TIMESTRETCH
PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name;
// parameter 'value' is a pointer to the new playback rate.
@@ -131,142 +82,23 @@
}
AudioMixer(size_t frameCount, uint32_t sampleRate)
- : mSampleRate(sampleRate)
- , mFrameCount(frameCount) {
+ : AudioMixerBase(frameCount, sampleRate) {
pthread_once(&sOnceControl, &sInitRoutine);
}
- // Create a new track in the mixer.
- //
- // \param name a unique user-provided integer associated with the track.
- // If name already exists, the function will abort.
- // \param channelMask output channel mask.
- // \param format PCM format
- // \param sessionId Session id for the track. Tracks with the same
- // session id will be submixed together.
- //
- // \return OK on success.
- // BAD_VALUE if the format does not satisfy isValidFormat()
- // or the channelMask does not satisfy isValidChannelMask().
- status_t create(
- int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId);
+ bool isValidChannelMask(audio_channel_mask_t channelMask) const override;
- bool exists(int name) const {
- return mTracks.count(name) > 0;
- }
-
- // Free an allocated track by name.
- void destroy(int name);
-
- // Enable or disable an allocated track by name
- void enable(int name);
- void disable(int name);
-
- void setParameter(int name, int target, int param, void *value);
-
- void setBufferProvider(int name, AudioBufferProvider* bufferProvider);
-
- void process() {
- for (const auto &pair : mTracks) {
- // Clear contracted buffer before processing if contracted channels are saved
- const std::shared_ptr<Track> &t = pair.second;
- if (t->mKeepContractedChannels) {
- t->clearContractedBuffer();
- }
- }
- (this->*mHook)();
- processHapticData();
- }
-
- size_t getUnreleasedFrames(int name) const;
-
- std::string trackNames() const {
- std::stringstream ss;
- for (const auto &pair : mTracks) {
- ss << pair.first << " ";
- }
- return ss.str();
- }
-
- void setNBLogWriter(NBLog::Writer *logWriter) {
- mNBLogWriter = logWriter;
- }
-
- static inline bool isValidFormat(audio_format_t format) {
- switch (format) {
- case AUDIO_FORMAT_PCM_8_BIT:
- case AUDIO_FORMAT_PCM_16_BIT:
- case AUDIO_FORMAT_PCM_24_BIT_PACKED:
- case AUDIO_FORMAT_PCM_32_BIT:
- case AUDIO_FORMAT_PCM_FLOAT:
- return true;
- default:
- return false;
- }
- }
-
- static inline bool isValidChannelMask(audio_channel_mask_t channelMask) {
- return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
- }
+ void setParameter(int name, int target, int param, void *value) override;
+ void setBufferProvider(int name, AudioBufferProvider* bufferProvider);
private:
- /* For multi-format functions (calls template functions
- * in AudioMixerOps.h). The template parameters are as follows:
- *
- * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * USEFLOATVOL (set to true if float volume is used)
- * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27)
- */
-
- enum {
- // FIXME this representation permits up to 8 channels
- NEEDS_CHANNEL_COUNT__MASK = 0x00000007,
- };
-
- enum {
- NEEDS_CHANNEL_1 = 0x00000000, // mono
- NEEDS_CHANNEL_2 = 0x00000001, // stereo
-
- // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT
-
- NEEDS_MUTE = 0x00000100,
- NEEDS_RESAMPLE = 0x00001000,
- NEEDS_AUX = 0x00010000,
- };
-
- // hook types
- enum {
- PROCESSTYPE_NORESAMPLEONETRACK, // others set elsewhere
- };
-
- enum {
- TRACKTYPE_NOP,
- TRACKTYPE_RESAMPLE,
- TRACKTYPE_NORESAMPLE,
- TRACKTYPE_NORESAMPLEMONO,
- };
-
- // process hook functionality
- using process_hook_t = void(AudioMixer::*)();
-
- struct Track;
- using hook_t = void(Track::*)(int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
-
- struct Track {
- Track()
- : bufferProvider(nullptr)
- {
- // TODO: move additional initialization here.
- }
+ struct Track : public TrackBase {
+ Track() : TrackBase() {}
~Track()
{
- // bufferProvider, mInputBufferProvider need not be deleted.
- mResampler.reset(nullptr);
+ // mInputBufferProvider need not be deleted.
// Ensure the order of destruction of buffer providers as they
// release the upstream provider in the destructor.
mTimestretchBufferProvider.reset(nullptr);
@@ -277,13 +109,12 @@
mAdjustChannelsBufferProvider.reset(nullptr);
}
- bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
- bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
- bool doesResample() const { return mResampler.get() != nullptr; }
- void resetResampler() { if (mResampler.get() != nullptr) mResampler->reset(); }
- void adjustVolumeRamp(bool aux, bool useFloat = false);
- size_t getUnreleasedFrames() const { return mResampler.get() != nullptr ?
- mResampler->getUnreleasedFrames() : 0; };
+ uint32_t getOutputChannelCount() override {
+ return mDownmixerBufferProvider.get() != nullptr ? mMixerChannelCount : channelCount;
+ }
+ uint32_t getMixerChannelCount() override {
+ return mMixerChannelCount + mMixerHapticChannelCount;
+ }
status_t prepareForDownmix();
void unprepareForDownmix();
@@ -297,51 +128,9 @@
bool setPlaybackRate(const AudioPlaybackRate &playbackRate);
void reconfigureBufferProviders();
- static hook_t getTrackHook(int trackType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
-
- void track__nop(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
-
- template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
- typename TO, typename TI, typename TA>
- void volumeMix(TO *out, size_t outFrames, const TI *in, TA *aux, bool ramp);
-
- uint32_t needs;
-
- // TODO: Eventually remove legacy integer volume settings
- union {
- int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
- int32_t volumeRL;
- };
-
- int32_t prevVolume[MAX_NUM_VOLUMES];
- int32_t volumeInc[MAX_NUM_VOLUMES];
- int32_t auxInc;
- int32_t prevAuxLevel;
- int16_t auxLevel; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
-
- uint16_t frameCount;
-
- uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
- uint8_t unused_padding; // formerly format, was always 16
- uint16_t enabled; // actually bool
- audio_channel_mask_t channelMask;
-
- // actual buffer provider used by the track hooks, see DownmixerBufferProvider below
- // for how the Track buffer provider is wrapped by another one when dowmixing is required
- AudioBufferProvider* bufferProvider;
-
- mutable AudioBufferProvider::Buffer buffer; // 8 bytes
-
- hook_t hook;
- const void *mIn; // current location in buffer
-
- std::unique_ptr<AudioResampler> mResampler;
- uint32_t sampleRate;
- int32_t* mainBuffer;
- int32_t* auxBuffer;
-
/* Buffer providers are constructed to translate the track input data as needed.
+ * See DownmixerBufferProvider below for how the Track buffer provider
+ * is wrapped by another one when dowmixing is required.
*
* TODO: perhaps make a single PlaybackConverterProvider class to move
* all pre-mixer track buffer conversions outside the AudioMixer class.
@@ -363,7 +152,7 @@
* the downmixer requirements to the mixer engine input requirements.
* 7) mTimestretchBufferProvider: Adds timestretching for playback rate
*/
- AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
+ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
// TODO: combine mAdjustChannelsBufferProvider and
// mContractChannelsNonDestructiveBufferProvider
std::unique_ptr<PassthruBufferProvider> mAdjustChannelsBufferProvider;
@@ -373,27 +162,10 @@
std::unique_ptr<PassthruBufferProvider> mPostDownmixReformatBufferProvider;
std::unique_ptr<PassthruBufferProvider> mTimestretchBufferProvider;
- int32_t sessionId;
-
- audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
- audio_format_t mFormat; // input track format
- audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
- // each track must be converted to this format.
audio_format_t mDownmixRequiresFormat; // required downmixer format
// AUDIO_FORMAT_PCM_16_BIT if 16 bit necessary
// AUDIO_FORMAT_INVALID if no required format
- float mVolume[MAX_NUM_VOLUMES]; // floating point set volume
- float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
- float mVolumeInc[MAX_NUM_VOLUMES]; // floating point volume increment
-
- float mAuxLevel; // floating point set aux level
- float mPrevAuxLevel; // floating point prev aux level
- float mAuxInc; // floating point aux increment
-
- audio_channel_mask_t mMixerChannelMask;
- uint32_t mMixerChannelCount;
-
AudioPlaybackRate mPlaybackRate;
// Haptic
@@ -440,76 +212,23 @@
return 0.0f;
}
}
-
- private:
- // hooks
- void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
- void track__16BitsStereo(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
- void track__16BitsMono(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
-
- void volumeRampStereo(int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
- void volumeStereo(int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
-
- // multi-format track hooks
- template <int MIXTYPE, typename TO, typename TI, typename TA>
- void track__Resample(TO* out, size_t frameCount, TO* temp __unused, TA* aux);
- template <int MIXTYPE, typename TO, typename TI, typename TA>
- void track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux);
};
- // TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore.
- static constexpr int BLOCKSIZE = 16;
-
- bool setChannelMasks(int name,
- audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);
-
- // Called when track info changes and a new process hook should be determined.
- void invalidate() {
- mHook = &AudioMixer::process__validate;
+ inline std::shared_ptr<Track> getTrack(int name) {
+ return std::static_pointer_cast<Track>(mTracks[name]);
}
- void process__validate();
- void process__nop();
- void process__genericNoResampling();
- void process__genericResampling();
- void process__oneTrack16BitsStereoNoResampling();
+ std::shared_ptr<TrackBase> preCreateTrack() override;
+ status_t postCreateTrack(TrackBase *track) override;
- template <int MIXTYPE, typename TO, typename TI, typename TA>
- void process__noResampleOneTrack();
+ void preProcess() override;
+ void postProcess() override;
- void processHapticData();
-
- static process_hook_t getProcessHook(int processType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
-
- static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
- void *in, audio_format_t mixerInFormat, size_t sampleCount);
+ bool setChannelMasks(int name,
+ audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) override;
static void sInitRoutine();
- // initialization constants
- const uint32_t mSampleRate;
- const size_t mFrameCount;
-
- NBLog::Writer *mNBLogWriter = nullptr; // associated NBLog::Writer
-
- process_hook_t mHook = &AudioMixer::process__nop; // one of process__*, never nullptr
-
- // the size of the type (int32_t) should be the largest of all types supported
- // by the mixer.
- std::unique_ptr<int32_t[]> mOutputTemp;
- std::unique_ptr<int32_t[]> mResampleTemp;
-
- // track names grouped by main buffer, in no particular order of main buffer.
- // however names for a particular main buffer are in order (by construction).
- std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
-
- // track names that are enabled, in increasing order (by construction).
- std::vector<int /* name */> mEnabled;
-
- // track smart pointers, by name, in increasing order of name.
- std::map<int /* name */, std::shared_ptr<Track>> mTracks;
-
static pthread_once_t sOnceControl; // initialized in constructor by first new
};
diff --git a/media/libaudioprocessing/Android.bp b/media/libaudioprocessing/Android.bp
index cb78063..e8aa700 100644
--- a/media/libaudioprocessing/Android.bp
+++ b/media/libaudioprocessing/Android.bp
@@ -3,20 +3,13 @@
export_include_dirs: ["include"],
+ header_libs: ["libaudioclient_headers"],
+
shared_libs: [
- "libaudiohal",
"libaudioutils",
"libcutils",
"liblog",
- "libnbaio",
- "libnblog",
- "libsonic",
"libutils",
- "libvibrator",
- ],
-
- header_libs: [
- "libbase_headers",
],
cflags: [
@@ -33,18 +26,31 @@
defaults: ["libaudioprocessing_defaults"],
srcs: [
+ "AudioMixer.cpp",
"BufferProviders.cpp",
"RecordBufferConverter.cpp",
],
- whole_static_libs: ["libaudioprocessing_arm"],
+
+ header_libs: [
+ "libbase_headers",
+ ],
+
+ shared_libs: [
+ "libaudiohal",
+ "libsonic",
+ "libvibrator",
+ ],
+
+ whole_static_libs: ["libaudioprocessing_base"],
}
cc_library_static {
- name: "libaudioprocessing_arm",
+ name: "libaudioprocessing_base",
defaults: ["libaudioprocessing_defaults"],
+ vendor_available: true,
srcs: [
- "AudioMixer.cpp",
+ "AudioMixerBase.cpp",
"AudioResampler.cpp",
"AudioResamplerCubic.cpp",
"AudioResamplerSinc.cpp",
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index f7cc096..c0b11a4 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "AudioMixer"
//#define LOG_NDEBUG 0
+#include <sstream>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
@@ -27,9 +28,6 @@
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <cutils/compiler.h>
-#include <utils/Debug.h>
-
#include <system/audio.h>
#include <audio_utils/primitives.h>
@@ -58,138 +56,15 @@
#define ALOGVV(a...) do { } while (0)
#endif
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
-#endif
-
-// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
-// original code will be used for stereo sinks, the new mixer for multichannel.
-static constexpr bool kUseNewMixer = true;
-
-// Set kUseFloat to true to allow floating input into the mixer engine.
-// If kUseNewMixer is false, this is ignored or may be overridden internally
-// because of downmix/upmix support.
-static constexpr bool kUseFloat = true;
-
-#ifdef FLOAT_AUX
-using TYPE_AUX = float;
-static_assert(kUseNewMixer && kUseFloat,
- "kUseNewMixer and kUseFloat must be true for FLOAT_AUX option");
-#else
-using TYPE_AUX = int32_t; // q4.27
-#endif
-
// Set to default copy buffer size in frames for input processing.
-static const size_t kCopyBufferFrameCount = 256;
+static constexpr size_t kCopyBufferFrameCount = 256;
namespace android {
// ----------------------------------------------------------------------------
-static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) {
- return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
-}
-
-status_t AudioMixer::create(
- int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
-{
- LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);
-
- if (!isValidChannelMask(channelMask)) {
- ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
- return BAD_VALUE;
- }
- if (!isValidFormat(format)) {
- ALOGE("%s invalid format: %#x", __func__, format);
- return BAD_VALUE;
- }
-
- auto t = std::make_shared<Track>();
- {
- // TODO: move initialization to the Track constructor.
- // assume default parameters for the track, except where noted below
- t->needs = 0;
-
- // Integer volume.
- // Currently integer volume is kept for the legacy integer mixer.
- // Will be removed when the legacy mixer path is removed.
- t->volume[0] = 0;
- t->volume[1] = 0;
- t->prevVolume[0] = 0 << 16;
- t->prevVolume[1] = 0 << 16;
- t->volumeInc[0] = 0;
- t->volumeInc[1] = 0;
- t->auxLevel = 0;
- t->auxInc = 0;
- t->prevAuxLevel = 0;
-
- // Floating point volume.
- t->mVolume[0] = 0.f;
- t->mVolume[1] = 0.f;
- t->mPrevVolume[0] = 0.f;
- t->mPrevVolume[1] = 0.f;
- t->mVolumeInc[0] = 0.;
- t->mVolumeInc[1] = 0.;
- t->mAuxLevel = 0.;
- t->mAuxInc = 0.;
- t->mPrevAuxLevel = 0.;
-
- // no initialization needed
- // t->frameCount
- t->mHapticChannelMask = channelMask & AUDIO_CHANNEL_HAPTIC_ALL;
- t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask);
- channelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL;
- t->channelCount = audio_channel_count_from_out_mask(channelMask);
- t->enabled = false;
- ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
- "Non-stereo channel mask: %d\n", channelMask);
- t->channelMask = channelMask;
- t->sessionId = sessionId;
- // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
- t->bufferProvider = NULL;
- t->buffer.raw = NULL;
- // no initialization needed
- // t->buffer.frameCount
- t->hook = NULL;
- t->mIn = NULL;
- t->sampleRate = mSampleRate;
- // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
- t->mainBuffer = NULL;
- t->auxBuffer = NULL;
- t->mInputBufferProvider = NULL;
- t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
- t->mFormat = format;
- t->mMixerInFormat = selectMixerInFormat(format);
- t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required
- t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
- AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
- t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
- t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
- // haptic
- t->mHapticPlaybackEnabled = false;
- t->mHapticIntensity = HAPTIC_SCALE_NONE;
- t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
- t->mMixerHapticChannelCount = 0;
- t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
- t->mAdjustOutChannelCount = t->channelCount + t->mMixerHapticChannelCount;
- t->mAdjustNonDestructiveInChannelCount = t->mAdjustOutChannelCount;
- t->mAdjustNonDestructiveOutChannelCount = t->channelCount;
- t->mKeepContractedChannels = false;
- // Check the downmixing (or upmixing) requirements.
- status_t status = t->prepareForDownmix();
- if (status != OK) {
- ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
- return BAD_VALUE;
- }
- // prepareForDownmix() may change mDownmixRequiresFormat
- ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
- t->prepareForReformat();
- t->prepareForAdjustChannelsNonDestructive(mFrameCount);
- t->prepareForAdjustChannels();
-
- mTracks[name] = t;
- return OK;
- }
+bool AudioMixer::isValidChannelMask(audio_channel_mask_t channelMask) const {
+ return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
}
// Called when channel masks have changed for a track name
@@ -198,7 +73,7 @@
bool AudioMixer::setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) {
LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
- const std::shared_ptr<Track> &track = mTracks[name];
+ const std::shared_ptr<Track> &track = getTrack(name);
if (trackChannelMask == (track->channelMask | track->mHapticChannelMask)
&& mixerChannelMask == (track->mMixerChannelMask | track->mMixerHapticChannelMask)) {
@@ -255,14 +130,8 @@
track->prepareForAdjustChannelsNonDestructive(mFrameCount);
track->prepareForAdjustChannels();
- if (track->mResampler.get() != nullptr) {
- // resampler channels may have changed.
- const uint32_t resetToSampleRate = track->sampleRate;
- track->mResampler.reset(nullptr);
- track->sampleRate = mSampleRate; // without resampler, track rate is device sample rate.
- // recreate the resampler with updated format, channels, saved sampleRate.
- track->setResampler(resetToSampleRate /*trackSampleRate*/, mSampleRate /*devSampleRate*/);
- }
+ // Resampler channels may have changed.
+ track->recreateResampler(mSampleRate);
return true;
}
@@ -477,171 +346,10 @@
}
}
-void AudioMixer::destroy(int name)
-{
- LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
- ALOGV("deleteTrackName(%d)", name);
-
- if (mTracks[name]->enabled) {
- invalidate();
- }
- mTracks.erase(name); // deallocate track
-}
-
-void AudioMixer::enable(int name)
-{
- LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
- const std::shared_ptr<Track> &track = mTracks[name];
-
- if (!track->enabled) {
- track->enabled = true;
- ALOGV("enable(%d)", name);
- invalidate();
- }
-}
-
-void AudioMixer::disable(int name)
-{
- LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
- const std::shared_ptr<Track> &track = mTracks[name];
-
- if (track->enabled) {
- track->enabled = false;
- ALOGV("disable(%d)", name);
- invalidate();
- }
-}
-
-/* Sets the volume ramp variables for the AudioMixer.
- *
- * The volume ramp variables are used to transition from the previous
- * volume to the set volume. ramp controls the duration of the transition.
- * Its value is typically one state framecount period, but may also be 0,
- * meaning "immediate."
- *
- * FIXME: 1) Volume ramp is enabled only if there is a nonzero integer increment
- * even if there is a nonzero floating point increment (in that case, the volume
- * change is immediate). This restriction should be changed when the legacy mixer
- * is removed (see #2).
- * FIXME: 2) Integer volume variables are used for Legacy mixing and should be removed
- * when no longer needed.
- *
- * @param newVolume set volume target in floating point [0.0, 1.0].
- * @param ramp number of frames to increment over. if ramp is 0, the volume
- * should be set immediately. Currently ramp should not exceed 65535 (frames).
- * @param pIntSetVolume pointer to the U4.12 integer target volume, set on return.
- * @param pIntPrevVolume pointer to the U4.28 integer previous volume, set on return.
- * @param pIntVolumeInc pointer to the U4.28 increment per output audio frame, set on return.
- * @param pSetVolume pointer to the float target volume, set on return.
- * @param pPrevVolume pointer to the float previous volume, set on return.
- * @param pVolumeInc pointer to the float increment per output audio frame, set on return.
- * @return true if the volume has changed, false if volume is same.
- */
-static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
- int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
- float *pSetVolume, float *pPrevVolume, float *pVolumeInc) {
- // check floating point volume to see if it is identical to the previously
- // set volume.
- // We do not use a tolerance here (and reject changes too small)
- // as it may be confusing to use a different value than the one set.
- // If the resulting volume is too small to ramp, it is a direct set of the volume.
- if (newVolume == *pSetVolume) {
- return false;
- }
- if (newVolume < 0) {
- newVolume = 0; // should not have negative volumes
- } else {
- switch (fpclassify(newVolume)) {
- case FP_SUBNORMAL:
- case FP_NAN:
- newVolume = 0;
- break;
- case FP_ZERO:
- break; // zero volume is fine
- case FP_INFINITE:
- // Infinite volume could be handled consistently since
- // floating point math saturates at infinities,
- // but we limit volume to unity gain float.
- // ramp = 0; break;
- //
- newVolume = AudioMixer::UNITY_GAIN_FLOAT;
- break;
- case FP_NORMAL:
- default:
- // Floating point does not have problems with overflow wrap
- // that integer has. However, we limit the volume to
- // unity gain here.
- // TODO: Revisit the volume limitation and perhaps parameterize.
- if (newVolume > AudioMixer::UNITY_GAIN_FLOAT) {
- newVolume = AudioMixer::UNITY_GAIN_FLOAT;
- }
- break;
- }
- }
-
- // set floating point volume ramp
- if (ramp != 0) {
- // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there
- // is no computational mismatch; hence equality is checked here.
- ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
- " prev:%f set_to:%f", *pPrevVolume, *pSetVolume);
- const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal
- // could be inf, cannot be nan, subnormal
- const float maxv = std::max(newVolume, *pPrevVolume);
-
- if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan)
- && maxv + inc != maxv) { // inc must make forward progress
- *pVolumeInc = inc;
- // ramp is set now.
- // Note: if newVolume is 0, then near the end of the ramp,
- // it may be possible that the ramped volume may be subnormal or
- // temporarily negative by a small amount or subnormal due to floating
- // point inaccuracies.
- } else {
- ramp = 0; // ramp not allowed
- }
- }
-
- // compute and check integer volume, no need to check negative values
- // The integer volume is limited to "unity_gain" to avoid wrapping and other
- // audio artifacts, so it never reaches the range limit of U4.28.
- // We safely use signed 16 and 32 bit integers here.
- const float scaledVolume = newVolume * AudioMixer::UNITY_GAIN_INT; // not neg, subnormal, nan
- const int32_t intVolume = (scaledVolume >= (float)AudioMixer::UNITY_GAIN_INT) ?
- AudioMixer::UNITY_GAIN_INT : (int32_t)scaledVolume;
-
- // set integer volume ramp
- if (ramp != 0) {
- // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28.
- // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there
- // is no computational mismatch; hence equality is checked here.
- ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished,"
- " prev:%d set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16);
- const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp;
-
- if (inc != 0) { // inc must make forward progress
- *pIntVolumeInc = inc;
- } else {
- ramp = 0; // ramp not allowed
- }
- }
-
- // if no ramp, or ramp not allowed, then clear float and integer increments
- if (ramp == 0) {
- *pVolumeInc = 0;
- *pPrevVolume = newVolume;
- *pIntVolumeInc = 0;
- *pIntPrevVolume = intVolume << 16;
- }
- *pSetVolume = newVolume;
- *pIntSetVolume = intVolume;
- return true;
-}
-
void AudioMixer::setParameter(int name, int target, int param, void *value)
{
LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
- const std::shared_ptr<Track> &track = mTracks[name];
+ const std::shared_ptr<Track> &track = getTrack(name);
int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
@@ -670,11 +378,7 @@
}
break;
case AUX_BUFFER:
- if (track->auxBuffer != valueBuf) {
- track->auxBuffer = valueBuf;
- ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
- invalidate();
- }
+ AudioMixerBase::setParameter(name, target, param, value);
break;
case FORMAT: {
audio_format_t format = static_cast<audio_format_t>(valueInt);
@@ -730,127 +434,38 @@
break;
case RESAMPLE:
- switch (param) {
- case SAMPLE_RATE:
- ALOG_ASSERT(valueInt > 0, "bad sample rate %d", valueInt);
- if (track->setResampler(uint32_t(valueInt), mSampleRate)) {
- ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
- uint32_t(valueInt));
- invalidate();
- }
- break;
- case RESET:
- track->resetResampler();
- invalidate();
- break;
- case REMOVE:
- track->mResampler.reset(nullptr);
- track->sampleRate = mSampleRate;
- invalidate();
- break;
- default:
- LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param);
- }
- break;
-
case RAMP_VOLUME:
case VOLUME:
+ AudioMixerBase::setParameter(name, target, param, value);
+ break;
+ case TIMESTRETCH:
switch (param) {
- case AUXLEVEL:
- if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
- target == RAMP_VOLUME ? mFrameCount : 0,
- &track->auxLevel, &track->prevAuxLevel, &track->auxInc,
- &track->mAuxLevel, &track->mPrevAuxLevel, &track->mAuxInc)) {
- ALOGV("setParameter(%s, AUXLEVEL: %04x)",
- target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track->auxLevel);
- invalidate();
+ case PLAYBACK_RATE: {
+ const AudioPlaybackRate *playbackRate =
+ reinterpret_cast<AudioPlaybackRate*>(value);
+ ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate),
+ "bad parameters speed %f, pitch %f",
+ playbackRate->mSpeed, playbackRate->mPitch);
+ if (track->setPlaybackRate(*playbackRate)) {
+ ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
+ "%f %f %d %d",
+ playbackRate->mSpeed,
+ playbackRate->mPitch,
+ playbackRate->mStretchMode,
+ playbackRate->mFallbackMode);
+ // invalidate(); (should not require reconfigure)
}
- break;
+ } break;
default:
- if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
- if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
- target == RAMP_VOLUME ? mFrameCount : 0,
- &track->volume[param - VOLUME0],
- &track->prevVolume[param - VOLUME0],
- &track->volumeInc[param - VOLUME0],
- &track->mVolume[param - VOLUME0],
- &track->mPrevVolume[param - VOLUME0],
- &track->mVolumeInc[param - VOLUME0])) {
- ALOGV("setParameter(%s, VOLUME%d: %04x)",
- target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
- track->volume[param - VOLUME0]);
- invalidate();
- }
- } else {
- LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
- }
+ LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
}
break;
- case TIMESTRETCH:
- switch (param) {
- case PLAYBACK_RATE: {
- const AudioPlaybackRate *playbackRate =
- reinterpret_cast<AudioPlaybackRate*>(value);
- ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate),
- "bad parameters speed %f, pitch %f",
- playbackRate->mSpeed, playbackRate->mPitch);
- if (track->setPlaybackRate(*playbackRate)) {
- ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
- "%f %f %d %d",
- playbackRate->mSpeed,
- playbackRate->mPitch,
- playbackRate->mStretchMode,
- playbackRate->mFallbackMode);
- // invalidate(); (should not require reconfigure)
- }
- } break;
- default:
- LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
- }
- break;
default:
LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
}
}
-bool AudioMixer::Track::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate)
-{
- if (trackSampleRate != devSampleRate || mResampler.get() != nullptr) {
- if (sampleRate != trackSampleRate) {
- sampleRate = trackSampleRate;
- if (mResampler.get() == nullptr) {
- ALOGV("Creating resampler from track %d Hz to device %d Hz",
- trackSampleRate, devSampleRate);
- AudioResampler::src_quality quality;
- // force lowest quality level resampler if use case isn't music or video
- // FIXME this is flawed for dynamic sample rates, as we choose the resampler
- // quality level based on the initial ratio, but that could change later.
- // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
- if (isMusicRate(trackSampleRate)) {
- quality = AudioResampler::DEFAULT_QUALITY;
- } else {
- quality = AudioResampler::DYN_LOW_QUALITY;
- }
-
- // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
- // but if none exists, it is the channel count (1 for mono).
- const int resamplerChannelCount = mDownmixerBufferProvider.get() != nullptr
- ? mMixerChannelCount : channelCount;
- ALOGVV("Creating resampler:"
- " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n",
- mMixerInFormat, resamplerChannelCount, devSampleRate, quality);
- mResampler.reset(AudioResampler::create(
- mMixerInFormat,
- resamplerChannelCount,
- devSampleRate, quality));
- }
- return true;
- }
- }
- return false;
-}
-
bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate)
{
if ((mTimestretchBufferProvider.get() == nullptr &&
@@ -863,8 +478,7 @@
if (mTimestretchBufferProvider.get() == nullptr) {
// TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
// but if none exists, it is the channel count (1 for mono).
- const int timestretchChannelCount = mDownmixerBufferProvider.get() != nullptr
- ? mMixerChannelCount : channelCount;
+ const int timestretchChannelCount = getOutputChannelCount();
mTimestretchBufferProvider.reset(new TimestretchBufferProvider(timestretchChannelCount,
mMixerInFormat, sampleRate, playbackRate));
reconfigureBufferProviders();
@@ -875,84 +489,10 @@
return true;
}
-/* Checks to see if the volume ramp has completed and clears the increment
- * variables appropriately.
- *
- * FIXME: There is code to handle int/float ramp variable switchover should it not
- * complete within a mixer buffer processing call, but it is preferred to avoid switchover
- * due to precision issues. The switchover code is included for legacy code purposes
- * and can be removed once the integer volume is removed.
- *
- * It is not sufficient to clear only the volumeInc integer variable because
- * if one channel requires ramping, all channels are ramped.
- *
- * There is a bit of duplicated code here, but it keeps backward compatibility.
- */
-inline void AudioMixer::Track::adjustVolumeRamp(bool aux, bool useFloat)
-{
- if (useFloat) {
- for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
- if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) ||
- (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) {
- volumeInc[i] = 0;
- prevVolume[i] = volume[i] << 16;
- mVolumeInc[i] = 0.;
- mPrevVolume[i] = mVolume[i];
- } else {
- //ALOGV("ramp: %f %f %f", mVolume[i], mPrevVolume[i], mVolumeInc[i]);
- prevVolume[i] = u4_28_from_float(mPrevVolume[i]);
- }
- }
- } else {
- for (uint32_t i = 0; i < MAX_NUM_VOLUMES; 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;
- mVolumeInc[i] = 0.;
- mPrevVolume[i] = mVolume[i];
- } else {
- //ALOGV("ramp: %d %d %d", volume[i] << 16, prevVolume[i], volumeInc[i]);
- mPrevVolume[i] = float_from_u4_28(prevVolume[i]);
- }
- }
- }
-
- if (aux) {
-#ifdef FLOAT_AUX
- if (useFloat) {
- if ((mAuxInc > 0.f && mPrevAuxLevel + mAuxInc >= mAuxLevel) ||
- (mAuxInc < 0.f && mPrevAuxLevel + mAuxInc <= mAuxLevel)) {
- auxInc = 0;
- prevAuxLevel = auxLevel << 16;
- mAuxInc = 0.f;
- mPrevAuxLevel = mAuxLevel;
- }
- } else
-#endif
- if ((auxInc > 0 && ((prevAuxLevel + auxInc) >> 16) >= auxLevel) ||
- (auxInc < 0 && ((prevAuxLevel + auxInc) >> 16) <= auxLevel)) {
- auxInc = 0;
- prevAuxLevel = auxLevel << 16;
- mAuxInc = 0.f;
- mPrevAuxLevel = mAuxLevel;
- }
- }
-}
-
-size_t AudioMixer::getUnreleasedFrames(int name) const
-{
- const auto it = mTracks.find(name);
- if (it != mTracks.end()) {
- return it->second->getUnreleasedFrames();
- }
- return 0;
-}
-
void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
{
LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
- const std::shared_ptr<Track> &track = mTracks[name];
+ const std::shared_ptr<Track> &track = getTrack(name);
if (track->mInputBufferProvider == bufferProvider) {
return; // don't reset any buffer providers if identical.
@@ -976,679 +516,6 @@
track->reconfigureBufferProviders();
}
-void AudioMixer::process__validate()
-{
- // TODO: fix all16BitsStereNoResample logic to
- // either properly handle muted tracks (it should ignore them)
- // or remove altogether as an obsolete optimization.
- bool all16BitsStereoNoResample = true;
- bool resampling = false;
- bool volumeRamp = false;
-
- mEnabled.clear();
- mGroups.clear();
- for (const auto &pair : mTracks) {
- const int name = pair.first;
- const std::shared_ptr<Track> &t = pair.second;
- if (!t->enabled) continue;
-
- mEnabled.emplace_back(name); // we add to mEnabled in order of name.
- mGroups[t->mainBuffer].emplace_back(name); // mGroups also in order of name.
-
- uint32_t n = 0;
- // FIXME can overflow (mask is only 3 bits)
- n |= NEEDS_CHANNEL_1 + t->channelCount - 1;
- if (t->doesResample()) {
- n |= NEEDS_RESAMPLE;
- }
- if (t->auxLevel != 0 && t->auxBuffer != NULL) {
- n |= NEEDS_AUX;
- }
-
- if (t->volumeInc[0]|t->volumeInc[1]) {
- volumeRamp = true;
- } else if (!t->doesResample() && t->volumeRL == 0) {
- n |= NEEDS_MUTE;
- }
- t->needs = n;
-
- if (n & NEEDS_MUTE) {
- t->hook = &Track::track__nop;
- } else {
- if (n & NEEDS_AUX) {
- all16BitsStereoNoResample = false;
- }
- if (n & NEEDS_RESAMPLE) {
- all16BitsStereoNoResample = false;
- resampling = true;
- t->hook = Track::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
- t->mMixerInFormat, t->mMixerFormat);
- ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
- "Track %d needs downmix + resample", name);
- } else {
- if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
- t->hook = Track::getTrackHook(
- (t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK
- && t->channelMask == AUDIO_CHANNEL_OUT_MONO)
- ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
- t->mMixerChannelCount,
- t->mMixerInFormat, t->mMixerFormat);
- all16BitsStereoNoResample = false;
- }
- if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
- t->hook = Track::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
- t->mMixerInFormat, t->mMixerFormat);
- ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
- "Track %d needs downmix", name);
- }
- }
- }
- }
-
- // select the processing hooks
- mHook = &AudioMixer::process__nop;
- if (mEnabled.size() > 0) {
- if (resampling) {
- if (mOutputTemp.get() == nullptr) {
- mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
- }
- if (mResampleTemp.get() == nullptr) {
- mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
- }
- mHook = &AudioMixer::process__genericResampling;
- } else {
- // we keep temp arrays around.
- mHook = &AudioMixer::process__genericNoResampling;
- if (all16BitsStereoNoResample && !volumeRamp) {
- if (mEnabled.size() == 1) {
- const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
- if ((t->needs & NEEDS_MUTE) == 0) {
- // The check prevents a muted track from acquiring a process hook.
- //
- // This is dangerous if the track is MONO as that requires
- // special case handling due to implicit channel duplication.
- // Stereo or Multichannel should actually be fine here.
- mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
- t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
- }
- }
- }
- }
- }
-
- ALOGV("mixer configuration change: %zu "
- "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d",
- mEnabled.size(), all16BitsStereoNoResample, resampling, volumeRamp);
-
- process();
-
- // Now that the volume ramp has been done, set optimal state and
- // track hooks for subsequent mixer process
- if (mEnabled.size() > 0) {
- bool allMuted = true;
-
- for (const int name : mEnabled) {
- const std::shared_ptr<Track> &t = mTracks[name];
- if (!t->doesResample() && t->volumeRL == 0) {
- t->needs |= NEEDS_MUTE;
- t->hook = &Track::track__nop;
- } else {
- allMuted = false;
- }
- }
- if (allMuted) {
- mHook = &AudioMixer::process__nop;
- } else if (all16BitsStereoNoResample) {
- if (mEnabled.size() == 1) {
- //const int i = 31 - __builtin_clz(enabledTracks);
- const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
- // Muted single tracks handled by allMuted above.
- mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
- t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
- }
- }
- }
-}
-
-void AudioMixer::Track::track__genericResample(
- int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
-{
- ALOGVV("track__genericResample\n");
- mResampler->setSampleRate(sampleRate);
-
- // ramp gain - resample to temp buffer and scale/mix in 2nd step
- if (aux != NULL) {
- // always resample with unity gain when sending to auxiliary buffer to be able
- // to apply send level after resampling
- mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
- memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(int32_t));
- mResampler->resample(temp, outFrameCount, bufferProvider);
- if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
- volumeRampStereo(out, outFrameCount, temp, aux);
- } else {
- volumeStereo(out, outFrameCount, temp, aux);
- }
- } else {
- if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
- mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
- memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
- mResampler->resample(temp, outFrameCount, bufferProvider);
- volumeRampStereo(out, outFrameCount, temp, aux);
- }
-
- // constant gain
- else {
- mResampler->setVolume(mVolume[0], mVolume[1]);
- mResampler->resample(out, outFrameCount, bufferProvider);
- }
- }
-}
-
-void AudioMixer::Track::track__nop(int32_t* out __unused,
- size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
-{
-}
-
-void AudioMixer::Track::volumeRampStereo(
- int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
-{
- int32_t vl = prevVolume[0];
- int32_t vr = prevVolume[1];
- const int32_t vlInc = volumeInc[0];
- const int32_t vrInc = volumeInc[1];
-
- //ALOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
-
- // ramp volume
- if (CC_UNLIKELY(aux != NULL)) {
- int32_t va = prevAuxLevel;
- const int32_t vaInc = auxInc;
- int32_t l;
- int32_t r;
-
- do {
- l = (*temp++ >> 12);
- r = (*temp++ >> 12);
- *out++ += (vl >> 16) * l;
- *out++ += (vr >> 16) * r;
- *aux++ += (va >> 17) * (l + r);
- vl += vlInc;
- vr += vrInc;
- va += vaInc;
- } while (--frameCount);
- prevAuxLevel = va;
- } else {
- do {
- *out++ += (vl >> 16) * (*temp++ >> 12);
- *out++ += (vr >> 16) * (*temp++ >> 12);
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
- }
- prevVolume[0] = vl;
- prevVolume[1] = vr;
- adjustVolumeRamp(aux != NULL);
-}
-
-void AudioMixer::Track::volumeStereo(
- int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
-{
- const int16_t vl = volume[0];
- const int16_t vr = volume[1];
-
- if (CC_UNLIKELY(aux != NULL)) {
- const int16_t va = auxLevel;
- do {
- int16_t l = (int16_t)(*temp++ >> 12);
- int16_t r = (int16_t)(*temp++ >> 12);
- out[0] = mulAdd(l, vl, out[0]);
- int16_t a = (int16_t)(((int32_t)l + r) >> 1);
- out[1] = mulAdd(r, vr, out[1]);
- out += 2;
- aux[0] = mulAdd(a, va, aux[0]);
- aux++;
- } while (--frameCount);
- } else {
- do {
- int16_t l = (int16_t)(*temp++ >> 12);
- int16_t r = (int16_t)(*temp++ >> 12);
- out[0] = mulAdd(l, vl, out[0]);
- out[1] = mulAdd(r, vr, out[1]);
- out += 2;
- } while (--frameCount);
- }
-}
-
-void AudioMixer::Track::track__16BitsStereo(
- int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
-{
- ALOGVV("track__16BitsStereo\n");
- const int16_t *in = static_cast<const int16_t *>(mIn);
-
- if (CC_UNLIKELY(aux != NULL)) {
- int32_t l;
- int32_t r;
- // ramp gain
- if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
- int32_t vl = prevVolume[0];
- int32_t vr = prevVolume[1];
- int32_t va = prevAuxLevel;
- const int32_t vlInc = volumeInc[0];
- const int32_t vrInc = volumeInc[1];
- const int32_t vaInc = auxInc;
- // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
-
- do {
- l = (int32_t)*in++;
- r = (int32_t)*in++;
- *out++ += (vl >> 16) * l;
- *out++ += (vr >> 16) * r;
- *aux++ += (va >> 17) * (l + r);
- vl += vlInc;
- vr += vrInc;
- va += vaInc;
- } while (--frameCount);
-
- prevVolume[0] = vl;
- prevVolume[1] = vr;
- prevAuxLevel = va;
- adjustVolumeRamp(true);
- }
-
- // constant gain
- else {
- const uint32_t vrl = volumeRL;
- const int16_t va = (int16_t)auxLevel;
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
- int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
- in += 2;
- out[0] = mulAddRL(1, rl, vrl, out[0]);
- out[1] = mulAddRL(0, rl, vrl, out[1]);
- out += 2;
- aux[0] = mulAdd(a, va, aux[0]);
- aux++;
- } while (--frameCount);
- }
- } else {
- // ramp gain
- if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
- int32_t vl = prevVolume[0];
- int32_t vr = prevVolume[1];
- const int32_t vlInc = volumeInc[0];
- const int32_t vrInc = volumeInc[1];
-
- // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
-
- do {
- *out++ += (vl >> 16) * (int32_t) *in++;
- *out++ += (vr >> 16) * (int32_t) *in++;
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
-
- prevVolume[0] = vl;
- prevVolume[1] = vr;
- adjustVolumeRamp(false);
- }
-
- // constant gain
- else {
- const uint32_t vrl = volumeRL;
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
- in += 2;
- out[0] = mulAddRL(1, rl, vrl, out[0]);
- out[1] = mulAddRL(0, rl, vrl, out[1]);
- out += 2;
- } while (--frameCount);
- }
- }
- mIn = in;
-}
-
-void AudioMixer::Track::track__16BitsMono(
- int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
-{
- ALOGVV("track__16BitsMono\n");
- const int16_t *in = static_cast<int16_t const *>(mIn);
-
- if (CC_UNLIKELY(aux != NULL)) {
- // ramp gain
- if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
- int32_t vl = prevVolume[0];
- int32_t vr = prevVolume[1];
- int32_t va = prevAuxLevel;
- const int32_t vlInc = volumeInc[0];
- const int32_t vrInc = volumeInc[1];
- const int32_t vaInc = auxInc;
-
- // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
-
- do {
- int32_t l = *in++;
- *out++ += (vl >> 16) * l;
- *out++ += (vr >> 16) * l;
- *aux++ += (va >> 16) * l;
- vl += vlInc;
- vr += vrInc;
- va += vaInc;
- } while (--frameCount);
-
- prevVolume[0] = vl;
- prevVolume[1] = vr;
- prevAuxLevel = va;
- adjustVolumeRamp(true);
- }
- // constant gain
- else {
- const int16_t vl = volume[0];
- const int16_t vr = volume[1];
- const int16_t va = (int16_t)auxLevel;
- do {
- int16_t l = *in++;
- out[0] = mulAdd(l, vl, out[0]);
- out[1] = mulAdd(l, vr, out[1]);
- out += 2;
- aux[0] = mulAdd(l, va, aux[0]);
- aux++;
- } while (--frameCount);
- }
- } else {
- // ramp gain
- if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
- int32_t vl = prevVolume[0];
- int32_t vr = prevVolume[1];
- const int32_t vlInc = volumeInc[0];
- const int32_t vrInc = volumeInc[1];
-
- // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
- // t, vlInc/65536.0f, vl/65536.0f, volume[0],
- // (vl + vlInc*frameCount)/65536.0f, frameCount);
-
- do {
- int32_t l = *in++;
- *out++ += (vl >> 16) * l;
- *out++ += (vr >> 16) * l;
- vl += vlInc;
- vr += vrInc;
- } while (--frameCount);
-
- prevVolume[0] = vl;
- prevVolume[1] = vr;
- adjustVolumeRamp(false);
- }
- // constant gain
- else {
- const int16_t vl = volume[0];
- const int16_t vr = volume[1];
- do {
- int16_t l = *in++;
- out[0] = mulAdd(l, vl, out[0]);
- out[1] = mulAdd(l, vr, out[1]);
- out += 2;
- } while (--frameCount);
- }
- }
- mIn = in;
-}
-
-// no-op case
-void AudioMixer::process__nop()
-{
- ALOGVV("process__nop\n");
-
- for (const auto &pair : mGroups) {
- // process by group of tracks with same output buffer to
- // avoid multiple memset() on same buffer
- const auto &group = pair.second;
-
- const std::shared_ptr<Track> &t = mTracks[group[0]];
- memset(t->mainBuffer, 0,
- mFrameCount * audio_bytes_per_frame(
- t->mMixerChannelCount + t->mMixerHapticChannelCount, t->mMixerFormat));
-
- // now consume data
- for (const int name : group) {
- const std::shared_ptr<Track> &t = mTracks[name];
- size_t outFrames = mFrameCount;
- while (outFrames) {
- t->buffer.frameCount = outFrames;
- t->bufferProvider->getNextBuffer(&t->buffer);
- if (t->buffer.raw == NULL) break;
- outFrames -= t->buffer.frameCount;
- t->bufferProvider->releaseBuffer(&t->buffer);
- }
- }
- }
-}
-
-// generic code without resampling
-void AudioMixer::process__genericNoResampling()
-{
- ALOGVV("process__genericNoResampling\n");
- int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
-
- for (const auto &pair : mGroups) {
- // process by group of tracks with same output main buffer to
- // avoid multiple memset() on same buffer
- const auto &group = pair.second;
-
- // acquire buffer
- for (const int name : group) {
- const std::shared_ptr<Track> &t = mTracks[name];
- t->buffer.frameCount = mFrameCount;
- t->bufferProvider->getNextBuffer(&t->buffer);
- t->frameCount = t->buffer.frameCount;
- t->mIn = t->buffer.raw;
- }
-
- int32_t *out = (int *)pair.first;
- size_t numFrames = 0;
- do {
- const size_t frameCount = std::min((size_t)BLOCKSIZE, mFrameCount - numFrames);
- memset(outTemp, 0, sizeof(outTemp));
- for (const int name : group) {
- const std::shared_ptr<Track> &t = mTracks[name];
- int32_t *aux = NULL;
- if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
- aux = t->auxBuffer + numFrames;
- }
- for (int outFrames = frameCount; outFrames > 0; ) {
- // t->in == nullptr can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t->mIn == nullptr) {
- break;
- }
- size_t inFrames = (t->frameCount > outFrames)?outFrames:t->frameCount;
- if (inFrames > 0) {
- (t.get()->*t->hook)(
- outTemp + (frameCount - outFrames) * t->mMixerChannelCount,
- inFrames, mResampleTemp.get() /* naked ptr */, aux);
- t->frameCount -= inFrames;
- outFrames -= inFrames;
- if (CC_UNLIKELY(aux != NULL)) {
- aux += inFrames;
- }
- }
- if (t->frameCount == 0 && outFrames) {
- t->bufferProvider->releaseBuffer(&t->buffer);
- t->buffer.frameCount = (mFrameCount - numFrames) -
- (frameCount - outFrames);
- t->bufferProvider->getNextBuffer(&t->buffer);
- t->mIn = t->buffer.raw;
- if (t->mIn == nullptr) {
- break;
- }
- t->frameCount = t->buffer.frameCount;
- }
- }
- }
-
- const std::shared_ptr<Track> &t1 = mTracks[group[0]];
- convertMixerFormat(out, t1->mMixerFormat, outTemp, t1->mMixerInFormat,
- frameCount * t1->mMixerChannelCount);
- // TODO: fix ugly casting due to choice of out pointer type
- out = reinterpret_cast<int32_t*>((uint8_t*)out
- + frameCount * t1->mMixerChannelCount
- * audio_bytes_per_sample(t1->mMixerFormat));
- numFrames += frameCount;
- } while (numFrames < mFrameCount);
-
- // release each track's buffer
- for (const int name : group) {
- const std::shared_ptr<Track> &t = mTracks[name];
- t->bufferProvider->releaseBuffer(&t->buffer);
- }
- }
-}
-
-// generic code with resampling
-void AudioMixer::process__genericResampling()
-{
- ALOGVV("process__genericResampling\n");
- int32_t * const outTemp = mOutputTemp.get(); // naked ptr
- size_t numFrames = mFrameCount;
-
- for (const auto &pair : mGroups) {
- const auto &group = pair.second;
- const std::shared_ptr<Track> &t1 = mTracks[group[0]];
-
- // clear temp buffer
- memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);
- for (const int name : group) {
- const std::shared_ptr<Track> &t = mTracks[name];
- int32_t *aux = NULL;
- if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
- aux = t->auxBuffer;
- }
-
- // this is a little goofy, on the resampling case we don't
- // acquire/release the buffers because it's done by
- // the resampler.
- if (t->needs & NEEDS_RESAMPLE) {
- (t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);
- } else {
-
- size_t outFrames = 0;
-
- while (outFrames < numFrames) {
- t->buffer.frameCount = numFrames - outFrames;
- t->bufferProvider->getNextBuffer(&t->buffer);
- t->mIn = t->buffer.raw;
- // t->mIn == nullptr can happen if the track was flushed just after having
- // been enabled for mixing.
- if (t->mIn == nullptr) break;
-
- (t.get()->*t->hook)(
- outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
- mResampleTemp.get() /* naked ptr */,
- aux != nullptr ? aux + outFrames : nullptr);
- outFrames += t->buffer.frameCount;
-
- t->bufferProvider->releaseBuffer(&t->buffer);
- }
- }
- }
- convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,
- outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount);
- }
-}
-
-// one track, 16 bits stereo without resampling is the most common case
-void AudioMixer::process__oneTrack16BitsStereoNoResampling()
-{
- ALOGVV("process__oneTrack16BitsStereoNoResampling\n");
- LOG_ALWAYS_FATAL_IF(mEnabled.size() != 0,
- "%zu != 1 tracks enabled", mEnabled.size());
- const int name = mEnabled[0];
- const std::shared_ptr<Track> &t = mTracks[name];
-
- AudioBufferProvider::Buffer& b(t->buffer);
-
- int32_t* out = t->mainBuffer;
- float *fout = reinterpret_cast<float*>(out);
- size_t numFrames = mFrameCount;
-
- const int16_t vl = t->volume[0];
- const int16_t vr = t->volume[1];
- const uint32_t vrl = t->volumeRL;
- while (numFrames) {
- b.frameCount = numFrames;
- t->bufferProvider->getNextBuffer(&b);
- const int16_t *in = b.i16;
-
- // in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (in == NULL || (((uintptr_t)in) & 3)) {
- if ( AUDIO_FORMAT_PCM_FLOAT == t->mMixerFormat ) {
- memset((char*)fout, 0, numFrames
- * t->mMixerChannelCount * audio_bytes_per_sample(t->mMixerFormat));
- } else {
- memset((char*)out, 0, numFrames
- * t->mMixerChannelCount * audio_bytes_per_sample(t->mMixerFormat));
- }
- ALOGE_IF((((uintptr_t)in) & 3),
- "process__oneTrack16BitsStereoNoResampling: misaligned buffer"
- " %p track %d, channels %d, needs %08x, volume %08x vfl %f vfr %f",
- in, name, t->channelCount, t->needs, vrl, t->mVolume[0], t->mVolume[1]);
- return;
- }
- size_t outFrames = b.frameCount;
-
- switch (t->mMixerFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
- in += 2;
- int32_t l = mulRL(1, rl, vrl);
- int32_t r = mulRL(0, rl, vrl);
- *fout++ = float_from_q4_27(l);
- *fout++ = float_from_q4_27(r);
- // Note: In case of later int16_t sink output,
- // conversion and clamping is done by memcpy_to_i16_from_float().
- } while (--outFrames);
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) {
- // volume is boosted, so we might need to clamp even though
- // we process only one track.
- do {
- uint32_t rl = *reinterpret_cast<const uint32_t *>(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<const uint32_t *>(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);
- }
- break;
- default:
- LOG_ALWAYS_FATAL("bad mixer format: %d", t->mMixerFormat);
- }
- numFrames -= b.frameCount;
- t->bufferProvider->releaseBuffer(&b);
- }
-}
-
/*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT;
/*static*/ void AudioMixer::sInitRoutine()
@@ -1656,211 +523,71 @@
DownmixerBufferProvider::init(); // for the downmixer
}
-/* TODO: consider whether this level of optimization is necessary.
- * Perhaps just stick with a single for loop.
- */
-
-// Needs to derive a compile time constant (constexpr). Could be targeted to go
-// to a MONOVOL mixtype based on MAX_NUM_VOLUMES, but that's an unnecessary complication.
-#define MIXTYPE_MONOVOL(mixtype) ((mixtype) == MIXTYPE_MULTI ? MIXTYPE_MULTI_MONOVOL : \
- (mixtype) == MIXTYPE_MULTI_SAVEONLY ? MIXTYPE_MULTI_SAVEONLY_MONOVOL : (mixtype))
-
-/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27) or float
- */
-template <int MIXTYPE,
- typename TO, typename TI, typename TV, typename TA, typename TAV>
-static void volumeRampMulti(uint32_t channels, TO* out, size_t frameCount,
- const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
+std::shared_ptr<AudioMixerBase::TrackBase> AudioMixer::preCreateTrack()
{
- switch (channels) {
- case 1:
- volumeRampMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 2:
- volumeRampMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 3:
- volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out,
- frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 4:
- volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out,
- frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 5:
- volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out,
- frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 6:
- volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out,
- frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 7:
- volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out,
- frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- case 8:
- volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out,
- frameCount, in, aux, vol, volinc, vola, volainc);
- break;
- }
+ return std::make_shared<Track>();
}
-/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27) or float
- */
-template <int MIXTYPE,
- typename TO, typename TI, typename TV, typename TA, typename TAV>
-static void volumeMulti(uint32_t channels, TO* out, size_t frameCount,
- const TI* in, TA* aux, const TV *vol, TAV vola)
+status_t AudioMixer::postCreateTrack(TrackBase *track)
{
- switch (channels) {
- case 1:
- volumeMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, vola);
- break;
- case 2:
- volumeMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, vola);
- break;
- case 3:
- volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out, frameCount, in, aux, vol, vola);
- break;
- case 4:
- volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out, frameCount, in, aux, vol, vola);
- break;
- case 5:
- volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out, frameCount, in, aux, vol, vola);
- break;
- case 6:
- volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out, frameCount, in, aux, vol, vola);
- break;
- case 7:
- volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out, frameCount, in, aux, vol, vola);
- break;
- case 8:
- volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out, frameCount, in, aux, vol, vola);
- break;
+ Track* t = static_cast<Track*>(track);
+
+ audio_channel_mask_t channelMask = t->channelMask;
+ t->mHapticChannelMask = channelMask & AUDIO_CHANNEL_HAPTIC_ALL;
+ t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask);
+ channelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL;
+ t->channelCount = audio_channel_count_from_out_mask(channelMask);
+ ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
+ "Non-stereo channel mask: %d\n", channelMask);
+ t->channelMask = channelMask;
+ t->mInputBufferProvider = NULL;
+ t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required
+ t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
+ // haptic
+ t->mHapticPlaybackEnabled = false;
+ t->mHapticIntensity = HAPTIC_SCALE_NONE;
+ t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
+ t->mMixerHapticChannelCount = 0;
+ t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
+ t->mAdjustOutChannelCount = t->channelCount + t->mMixerHapticChannelCount;
+ t->mAdjustNonDestructiveInChannelCount = t->mAdjustOutChannelCount;
+ t->mAdjustNonDestructiveOutChannelCount = t->channelCount;
+ t->mKeepContractedChannels = false;
+ // Check the downmixing (or upmixing) requirements.
+ status_t status = t->prepareForDownmix();
+ if (status != OK) {
+ ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
+ return BAD_VALUE;
}
+ // prepareForDownmix() may change mDownmixRequiresFormat
+ ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
+ t->prepareForReformat();
+ t->prepareForAdjustChannelsNonDestructive(mFrameCount);
+ t->prepareForAdjustChannels();
+ return OK;
}
-/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * USEFLOATVOL (set to true if float volume is used)
- * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27) or float
- */
-template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
- typename TO, typename TI, typename TA>
-void AudioMixer::Track::volumeMix(TO *out, size_t outFrames,
- const TI *in, TA *aux, bool ramp)
+void AudioMixer::preProcess()
{
- if (USEFLOATVOL) {
- if (ramp) {
- volumeRampMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
- mPrevVolume, mVolumeInc,
-#ifdef FLOAT_AUX
- &mPrevAuxLevel, mAuxInc
-#else
- &prevAuxLevel, auxInc
-#endif
- );
- if (ADJUSTVOL) {
- adjustVolumeRamp(aux != NULL, true);
- }
- } else {
- volumeMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
- mVolume,
-#ifdef FLOAT_AUX
- mAuxLevel
-#else
- auxLevel
-#endif
- );
- }
- } else {
- if (ramp) {
- volumeRampMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
- prevVolume, volumeInc, &prevAuxLevel, auxInc);
- if (ADJUSTVOL) {
- adjustVolumeRamp(aux != NULL);
- }
- } else {
- volumeMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
- volume, auxLevel);
+ for (const auto &pair : mTracks) {
+ // Clear contracted buffer before processing if contracted channels are saved
+ const std::shared_ptr<TrackBase> &tb = pair.second;
+ Track *t = static_cast<Track*>(tb.get());
+ if (t->mKeepContractedChannels) {
+ t->clearContractedBuffer();
}
}
}
-/* This process hook is called when there is a single track without
- * aux buffer, volume ramp, or resampling.
- * TODO: Update the hook selection: this can properly handle aux and ramp.
- *
- * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27)
- */
-template <int MIXTYPE, typename TO, typename TI, typename TA>
-void AudioMixer::process__noResampleOneTrack()
+void AudioMixer::postProcess()
{
- ALOGVV("process__noResampleOneTrack\n");
- LOG_ALWAYS_FATAL_IF(mEnabled.size() != 1,
- "%zu != 1 tracks enabled", mEnabled.size());
- const std::shared_ptr<Track> &t = mTracks[mEnabled[0]];
- const uint32_t channels = t->mMixerChannelCount;
- TO* out = reinterpret_cast<TO*>(t->mainBuffer);
- TA* aux = reinterpret_cast<TA*>(t->auxBuffer);
- const bool ramp = t->needsRamp();
-
- for (size_t numFrames = mFrameCount; numFrames > 0; ) {
- AudioBufferProvider::Buffer& b(t->buffer);
- // get input buffer
- b.frameCount = numFrames;
- t->bufferProvider->getNextBuffer(&b);
- const TI *in = reinterpret_cast<TI*>(b.raw);
-
- // in == NULL can happen if the track was flushed just after having
- // been enabled for mixing.
- if (in == NULL || (((uintptr_t)in) & 3)) {
- memset(out, 0, numFrames
- * channels * audio_bytes_per_sample(t->mMixerFormat));
- ALOGE_IF((((uintptr_t)in) & 3), "process__noResampleOneTrack: bus error: "
- "buffer %p track %p, channels %d, needs %#x",
- in, &t, t->channelCount, t->needs);
- return;
- }
-
- const size_t outFrames = b.frameCount;
- t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
- out, outFrames, in, aux, ramp);
-
- out += outFrames * channels;
- if (aux != NULL) {
- aux += outFrames;
- }
- numFrames -= b.frameCount;
-
- // release buffer
- t->bufferProvider->releaseBuffer(&b);
- }
- if (ramp) {
- t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
- }
-}
-
-void AudioMixer::processHapticData()
-{
+ // Process haptic data.
// Need to keep consistent with VibrationEffect.scale(int, float, int)
for (const auto &pair : mGroups) {
// process by group of tracks with same output main buffer.
const auto &group = pair.second;
for (const int name : group) {
- const std::shared_ptr<Track> &t = mTracks[name];
+ const std::shared_ptr<Track> &t = getTrack(name);
if (t->mHapticPlaybackEnabled) {
size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
float gamma = t->getHapticScaleGamma();
@@ -1887,225 +614,5 @@
}
}
-/* This track hook is called to do resampling then mixing,
- * pulling from the track's upstream AudioBufferProvider.
- *
- * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27) or float
- */
-template <int MIXTYPE, typename TO, typename TI, typename TA>
-void AudioMixer::Track::track__Resample(TO* out, size_t outFrameCount, TO* temp, TA* aux)
-{
- ALOGVV("track__Resample\n");
- mResampler->setSampleRate(sampleRate);
- const bool ramp = needsRamp();
- if (ramp || aux != NULL) {
- // if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
- // if aux != NULL: resample with unity gain to temp buffer then apply send level.
-
- mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
- memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
- mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);
-
- volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
- out, outFrameCount, temp, aux, ramp);
-
- } else { // constant volume gain
- mResampler->setVolume(mVolume[0], mVolume[1]);
- mResampler->resample((int32_t*)out, outFrameCount, bufferProvider);
- }
-}
-
-/* This track hook is called to mix a track, when no resampling is required.
- * The input buffer should be present in in.
- *
- * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
- * TO: int32_t (Q4.27) or float
- * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
- * TA: int32_t (Q4.27) or float
- */
-template <int MIXTYPE, typename TO, typename TI, typename TA>
-void AudioMixer::Track::track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux)
-{
- ALOGVV("track__NoResample\n");
- const TI *in = static_cast<const TI *>(mIn);
-
- volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
- out, frameCount, in, aux, needsRamp());
-
- // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
- // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
- in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * mMixerChannelCount;
- mIn = in;
-}
-
-/* The Mixer engine generates either int32_t (Q4_27) or float data.
- * We use this function to convert the engine buffers
- * to the desired mixer output format, either int16_t (Q.15) or float.
- */
-/* static */
-void AudioMixer::convertMixerFormat(void *out, audio_format_t mixerOutFormat,
- void *in, audio_format_t mixerInFormat, size_t sampleCount)
-{
- switch (mixerInFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- switch (mixerOutFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- memcpy(out, in, sampleCount * sizeof(float)); // MEMCPY. TODO optimize out
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- memcpy_to_i16_from_float((int16_t*)out, (float*)in, sampleCount);
- break;
- default:
- LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
- break;
- }
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- switch (mixerOutFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- memcpy_to_float_from_q4_27((float*)out, (const int32_t*)in, sampleCount);
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- memcpy_to_i16_from_q4_27((int16_t*)out, (const int32_t*)in, sampleCount);
- break;
- default:
- LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
- break;
- }
- break;
- default:
- LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
- break;
- }
-}
-
-/* Returns the proper track hook to use for mixing the track into the output buffer.
- */
-/* static */
-AudioMixer::hook_t AudioMixer::Track::getTrackHook(int trackType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
-{
- if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
- switch (trackType) {
- case TRACKTYPE_NOP:
- return &Track::track__nop;
- case TRACKTYPE_RESAMPLE:
- return &Track::track__genericResample;
- case TRACKTYPE_NORESAMPLEMONO:
- return &Track::track__16BitsMono;
- case TRACKTYPE_NORESAMPLE:
- return &Track::track__16BitsStereo;
- default:
- LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
- break;
- }
- }
- LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
- switch (trackType) {
- case TRACKTYPE_NOP:
- return &Track::track__nop;
- case TRACKTYPE_RESAMPLE:
- switch (mixerInFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- return (AudioMixer::hook_t) &Track::track__Resample<
- MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
- case AUDIO_FORMAT_PCM_16_BIT:
- return (AudioMixer::hook_t) &Track::track__Resample<
- MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- default:
- LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
- break;
- }
- break;
- case TRACKTYPE_NORESAMPLEMONO:
- switch (mixerInFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- return (AudioMixer::hook_t) &Track::track__NoResample<
- MIXTYPE_MONOEXPAND, float /*TO*/, float /*TI*/, TYPE_AUX>;
- case AUDIO_FORMAT_PCM_16_BIT:
- return (AudioMixer::hook_t) &Track::track__NoResample<
- MIXTYPE_MONOEXPAND, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- default:
- LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
- break;
- }
- break;
- case TRACKTYPE_NORESAMPLE:
- switch (mixerInFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- return (AudioMixer::hook_t) &Track::track__NoResample<
- MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
- case AUDIO_FORMAT_PCM_16_BIT:
- return (AudioMixer::hook_t) &Track::track__NoResample<
- MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- default:
- LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
- break;
- }
- break;
- default:
- LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
- break;
- }
- return NULL;
-}
-
-/* Returns the proper process hook for mixing tracks. Currently works only for
- * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling.
- *
- * TODO: Due to the special mixing considerations of duplicating to
- * a stereo output track, the input track cannot be MONO. This should be
- * prevented by the caller.
- */
-/* static */
-AudioMixer::process_hook_t AudioMixer::getProcessHook(
- int processType, uint32_t channelCount,
- audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
-{
- if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
- LOG_ALWAYS_FATAL("bad processType: %d", processType);
- return NULL;
- }
- if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
- return &AudioMixer::process__oneTrack16BitsStereoNoResampling;
- }
- LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
- switch (mixerInFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- switch (mixerOutFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- return &AudioMixer::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
- case AUDIO_FORMAT_PCM_16_BIT:
- return &AudioMixer::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
- default:
- LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
- break;
- }
- break;
- case AUDIO_FORMAT_PCM_16_BIT:
- switch (mixerOutFormat) {
- case AUDIO_FORMAT_PCM_FLOAT:
- return &AudioMixer::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- case AUDIO_FORMAT_PCM_16_BIT:
- return &AudioMixer::process__noResampleOneTrack<
- MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
- default:
- LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
- break;
- }
- break;
- default:
- LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
- break;
- }
- return NULL;
-}
-
// ----------------------------------------------------------------------------
} // namespace android
diff --git a/media/libaudioprocessing/AudioMixerBase.cpp b/media/libaudioprocessing/AudioMixerBase.cpp
new file mode 100644
index 0000000..75c077d
--- /dev/null
+++ b/media/libaudioprocessing/AudioMixerBase.cpp
@@ -0,0 +1,1692 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioMixer"
+//#define LOG_NDEBUG 0
+
+#include <sstream>
+#include <string.h>
+
+#include <audio_utils/primitives.h>
+#include <cutils/compiler.h>
+#include <media/AudioMixerBase.h>
+#include <utils/Log.h>
+
+#include "AudioMixerOps.h"
+
+// The FCC_2 macro refers to the Fixed Channel Count of 2 for the legacy integer mixer.
+#ifndef FCC_2
+#define FCC_2 2
+#endif
+
+// Look for MONO_HACK for any Mono hack involving legacy mono channel to
+// stereo channel conversion.
+
+/* VERY_VERY_VERBOSE_LOGGING will show exactly which process hook and track hook is
+ * being used. This is a considerable amount of log spam, so don't enable unless you
+ * are verifying the hook based code.
+ */
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+//define ALOGVV printf // for test-mixer.cpp
+#else
+#define ALOGVV(a...) do { } while (0)
+#endif
+
+// TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore.
+static constexpr int BLOCKSIZE = 16;
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+bool AudioMixerBase::isValidFormat(audio_format_t format) const
+{
+ switch (format) {
+ case AUDIO_FORMAT_PCM_8_BIT:
+ case AUDIO_FORMAT_PCM_16_BIT:
+ case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_PCM_32_BIT:
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool AudioMixerBase::isValidChannelMask(audio_channel_mask_t channelMask) const
+{
+ return audio_channel_count_from_out_mask(channelMask) <= MAX_NUM_CHANNELS;
+}
+
+std::shared_ptr<AudioMixerBase::TrackBase> AudioMixerBase::preCreateTrack()
+{
+ return std::make_shared<TrackBase>();
+}
+
+status_t AudioMixerBase::create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
+{
+ LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);
+
+ if (!isValidChannelMask(channelMask)) {
+ ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
+ return BAD_VALUE;
+ }
+ if (!isValidFormat(format)) {
+ ALOGE("%s invalid format: %#x", __func__, format);
+ return BAD_VALUE;
+ }
+
+ auto t = preCreateTrack();
+ {
+ // TODO: move initialization to the Track constructor.
+ // assume default parameters for the track, except where noted below
+ t->needs = 0;
+
+ // Integer volume.
+ // Currently integer volume is kept for the legacy integer mixer.
+ // Will be removed when the legacy mixer path is removed.
+ t->volume[0] = 0;
+ t->volume[1] = 0;
+ t->prevVolume[0] = 0 << 16;
+ t->prevVolume[1] = 0 << 16;
+ t->volumeInc[0] = 0;
+ t->volumeInc[1] = 0;
+ t->auxLevel = 0;
+ t->auxInc = 0;
+ t->prevAuxLevel = 0;
+
+ // Floating point volume.
+ t->mVolume[0] = 0.f;
+ t->mVolume[1] = 0.f;
+ t->mPrevVolume[0] = 0.f;
+ t->mPrevVolume[1] = 0.f;
+ t->mVolumeInc[0] = 0.;
+ t->mVolumeInc[1] = 0.;
+ t->mAuxLevel = 0.;
+ t->mAuxInc = 0.;
+ t->mPrevAuxLevel = 0.;
+
+ // no initialization needed
+ // t->frameCount
+ t->channelCount = audio_channel_count_from_out_mask(channelMask);
+ t->enabled = false;
+ ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
+ "Non-stereo channel mask: %d\n", channelMask);
+ t->channelMask = channelMask;
+ t->sessionId = sessionId;
+ // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
+ t->bufferProvider = NULL;
+ t->buffer.raw = NULL;
+ // no initialization needed
+ // t->buffer.frameCount
+ t->hook = NULL;
+ t->mIn = NULL;
+ t->sampleRate = mSampleRate;
+ // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
+ t->mainBuffer = NULL;
+ t->auxBuffer = NULL;
+ t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
+ t->mFormat = format;
+ t->mMixerInFormat = kUseFloat && kUseNewMixer ?
+ AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
+ t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
+ AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
+ t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
+ status_t status = postCreateTrack(t.get());
+ if (status != OK) return status;
+ mTracks[name] = t;
+ return OK;
+ }
+}
+
+// Called when channel masks have changed for a track name
+bool AudioMixerBase::setChannelMasks(int name,
+ audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask)
+{
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
+ const std::shared_ptr<TrackBase> &track = mTracks[name];
+
+ if (trackChannelMask == track->channelMask && mixerChannelMask == track->mMixerChannelMask) {
+ return false; // no need to change
+ }
+ // always recompute for both channel masks even if only one has changed.
+ const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask);
+ const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask);
+
+ ALOG_ASSERT(trackChannelCount && mixerChannelCount);
+ track->channelMask = trackChannelMask;
+ track->channelCount = trackChannelCount;
+ track->mMixerChannelMask = mixerChannelMask;
+ track->mMixerChannelCount = mixerChannelCount;
+
+ // Resampler channels may have changed.
+ track->recreateResampler(mSampleRate);
+ return true;
+}
+
+void AudioMixerBase::destroy(int name)
+{
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
+ ALOGV("deleteTrackName(%d)", name);
+
+ if (mTracks[name]->enabled) {
+ invalidate();
+ }
+ mTracks.erase(name); // deallocate track
+}
+
+void AudioMixerBase::enable(int name)
+{
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
+ const std::shared_ptr<TrackBase> &track = mTracks[name];
+
+ if (!track->enabled) {
+ track->enabled = true;
+ ALOGV("enable(%d)", name);
+ invalidate();
+ }
+}
+
+void AudioMixerBase::disable(int name)
+{
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
+ const std::shared_ptr<TrackBase> &track = mTracks[name];
+
+ if (track->enabled) {
+ track->enabled = false;
+ ALOGV("disable(%d)", name);
+ invalidate();
+ }
+}
+
+/* Sets the volume ramp variables for the AudioMixer.
+ *
+ * The volume ramp variables are used to transition from the previous
+ * volume to the set volume. ramp controls the duration of the transition.
+ * Its value is typically one state framecount period, but may also be 0,
+ * meaning "immediate."
+ *
+ * FIXME: 1) Volume ramp is enabled only if there is a nonzero integer increment
+ * even if there is a nonzero floating point increment (in that case, the volume
+ * change is immediate). This restriction should be changed when the legacy mixer
+ * is removed (see #2).
+ * FIXME: 2) Integer volume variables are used for Legacy mixing and should be removed
+ * when no longer needed.
+ *
+ * @param newVolume set volume target in floating point [0.0, 1.0].
+ * @param ramp number of frames to increment over. if ramp is 0, the volume
+ * should be set immediately. Currently ramp should not exceed 65535 (frames).
+ * @param pIntSetVolume pointer to the U4.12 integer target volume, set on return.
+ * @param pIntPrevVolume pointer to the U4.28 integer previous volume, set on return.
+ * @param pIntVolumeInc pointer to the U4.28 increment per output audio frame, set on return.
+ * @param pSetVolume pointer to the float target volume, set on return.
+ * @param pPrevVolume pointer to the float previous volume, set on return.
+ * @param pVolumeInc pointer to the float increment per output audio frame, set on return.
+ * @return true if the volume has changed, false if volume is same.
+ */
+static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
+ int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
+ float *pSetVolume, float *pPrevVolume, float *pVolumeInc) {
+ // check floating point volume to see if it is identical to the previously
+ // set volume.
+ // We do not use a tolerance here (and reject changes too small)
+ // as it may be confusing to use a different value than the one set.
+ // If the resulting volume is too small to ramp, it is a direct set of the volume.
+ if (newVolume == *pSetVolume) {
+ return false;
+ }
+ if (newVolume < 0) {
+ newVolume = 0; // should not have negative volumes
+ } else {
+ switch (fpclassify(newVolume)) {
+ case FP_SUBNORMAL:
+ case FP_NAN:
+ newVolume = 0;
+ break;
+ case FP_ZERO:
+ break; // zero volume is fine
+ case FP_INFINITE:
+ // Infinite volume could be handled consistently since
+ // floating point math saturates at infinities,
+ // but we limit volume to unity gain float.
+ // ramp = 0; break;
+ //
+ newVolume = AudioMixerBase::UNITY_GAIN_FLOAT;
+ break;
+ case FP_NORMAL:
+ default:
+ // Floating point does not have problems with overflow wrap
+ // that integer has. However, we limit the volume to
+ // unity gain here.
+ // TODO: Revisit the volume limitation and perhaps parameterize.
+ if (newVolume > AudioMixerBase::UNITY_GAIN_FLOAT) {
+ newVolume = AudioMixerBase::UNITY_GAIN_FLOAT;
+ }
+ break;
+ }
+ }
+
+ // set floating point volume ramp
+ if (ramp != 0) {
+ // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there
+ // is no computational mismatch; hence equality is checked here.
+ ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
+ " prev:%f set_to:%f", *pPrevVolume, *pSetVolume);
+ const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal
+ // could be inf, cannot be nan, subnormal
+ const float maxv = std::max(newVolume, *pPrevVolume);
+
+ if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan)
+ && maxv + inc != maxv) { // inc must make forward progress
+ *pVolumeInc = inc;
+ // ramp is set now.
+ // Note: if newVolume is 0, then near the end of the ramp,
+ // it may be possible that the ramped volume may be subnormal or
+ // temporarily negative by a small amount or subnormal due to floating
+ // point inaccuracies.
+ } else {
+ ramp = 0; // ramp not allowed
+ }
+ }
+
+ // compute and check integer volume, no need to check negative values
+ // The integer volume is limited to "unity_gain" to avoid wrapping and other
+ // audio artifacts, so it never reaches the range limit of U4.28.
+ // We safely use signed 16 and 32 bit integers here.
+ const float scaledVolume = newVolume * AudioMixerBase::UNITY_GAIN_INT; // not neg, subnormal, nan
+ const int32_t intVolume = (scaledVolume >= (float)AudioMixerBase::UNITY_GAIN_INT) ?
+ AudioMixerBase::UNITY_GAIN_INT : (int32_t)scaledVolume;
+
+ // set integer volume ramp
+ if (ramp != 0) {
+ // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28.
+ // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there
+ // is no computational mismatch; hence equality is checked here.
+ ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished,"
+ " prev:%d set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16);
+ const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp;
+
+ if (inc != 0) { // inc must make forward progress
+ *pIntVolumeInc = inc;
+ } else {
+ ramp = 0; // ramp not allowed
+ }
+ }
+
+ // if no ramp, or ramp not allowed, then clear float and integer increments
+ if (ramp == 0) {
+ *pVolumeInc = 0;
+ *pPrevVolume = newVolume;
+ *pIntVolumeInc = 0;
+ *pIntPrevVolume = intVolume << 16;
+ }
+ *pSetVolume = newVolume;
+ *pIntSetVolume = intVolume;
+ return true;
+}
+
+void AudioMixerBase::setParameter(int name, int target, int param, void *value)
+{
+ LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
+ const std::shared_ptr<TrackBase> &track = mTracks[name];
+
+ int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
+ int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
+
+ switch (target) {
+
+ case TRACK:
+ switch (param) {
+ case CHANNEL_MASK: {
+ const audio_channel_mask_t trackChannelMask =
+ static_cast<audio_channel_mask_t>(valueInt);
+ if (setChannelMasks(name, trackChannelMask, track->mMixerChannelMask)) {
+ ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
+ invalidate();
+ }
+ } break;
+ case MAIN_BUFFER:
+ if (track->mainBuffer != valueBuf) {
+ track->mainBuffer = valueBuf;
+ ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
+ invalidate();
+ }
+ break;
+ case AUX_BUFFER:
+ if (track->auxBuffer != valueBuf) {
+ track->auxBuffer = valueBuf;
+ ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
+ invalidate();
+ }
+ break;
+ case FORMAT: {
+ audio_format_t format = static_cast<audio_format_t>(valueInt);
+ if (track->mFormat != format) {
+ ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
+ track->mFormat = format;
+ ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
+ invalidate();
+ }
+ } break;
+ case MIXER_FORMAT: {
+ audio_format_t format = static_cast<audio_format_t>(valueInt);
+ if (track->mMixerFormat != format) {
+ track->mMixerFormat = format;
+ ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
+ }
+ } break;
+ case MIXER_CHANNEL_MASK: {
+ const audio_channel_mask_t mixerChannelMask =
+ static_cast<audio_channel_mask_t>(valueInt);
+ if (setChannelMasks(name, track->channelMask, mixerChannelMask)) {
+ ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
+ invalidate();
+ }
+ } break;
+ default:
+ LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
+ }
+ break;
+
+ case RESAMPLE:
+ switch (param) {
+ case SAMPLE_RATE:
+ ALOG_ASSERT(valueInt > 0, "bad sample rate %d", valueInt);
+ if (track->setResampler(uint32_t(valueInt), mSampleRate)) {
+ ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
+ uint32_t(valueInt));
+ invalidate();
+ }
+ break;
+ case RESET:
+ track->resetResampler();
+ invalidate();
+ break;
+ case REMOVE:
+ track->mResampler.reset(nullptr);
+ track->sampleRate = mSampleRate;
+ invalidate();
+ break;
+ default:
+ LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param);
+ }
+ break;
+
+ case RAMP_VOLUME:
+ case VOLUME:
+ switch (param) {
+ case AUXLEVEL:
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mFrameCount : 0,
+ &track->auxLevel, &track->prevAuxLevel, &track->auxInc,
+ &track->mAuxLevel, &track->mPrevAuxLevel, &track->mAuxInc)) {
+ ALOGV("setParameter(%s, AUXLEVEL: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track->auxLevel);
+ invalidate();
+ }
+ break;
+ default:
+ if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
+ if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
+ target == RAMP_VOLUME ? mFrameCount : 0,
+ &track->volume[param - VOLUME0],
+ &track->prevVolume[param - VOLUME0],
+ &track->volumeInc[param - VOLUME0],
+ &track->mVolume[param - VOLUME0],
+ &track->mPrevVolume[param - VOLUME0],
+ &track->mVolumeInc[param - VOLUME0])) {
+ ALOGV("setParameter(%s, VOLUME%d: %04x)",
+ target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
+ track->volume[param - VOLUME0]);
+ invalidate();
+ }
+ } else {
+ LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
+ }
+ }
+ break;
+
+ default:
+ LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
+ }
+}
+
+bool AudioMixerBase::TrackBase::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate)
+{
+ if (trackSampleRate != devSampleRate || mResampler.get() != nullptr) {
+ if (sampleRate != trackSampleRate) {
+ sampleRate = trackSampleRate;
+ if (mResampler.get() == nullptr) {
+ ALOGV("Creating resampler from track %d Hz to device %d Hz",
+ trackSampleRate, devSampleRate);
+ AudioResampler::src_quality quality;
+ // force lowest quality level resampler if use case isn't music or video
+ // FIXME this is flawed for dynamic sample rates, as we choose the resampler
+ // quality level based on the initial ratio, but that could change later.
+ // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
+ if (isMusicRate(trackSampleRate)) {
+ quality = AudioResampler::DEFAULT_QUALITY;
+ } else {
+ quality = AudioResampler::DYN_LOW_QUALITY;
+ }
+
+ // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
+ // but if none exists, it is the channel count (1 for mono).
+ const int resamplerChannelCount = getOutputChannelCount();
+ ALOGVV("Creating resampler:"
+ " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n",
+ mMixerInFormat, resamplerChannelCount, devSampleRate, quality);
+ mResampler.reset(AudioResampler::create(
+ mMixerInFormat,
+ resamplerChannelCount,
+ devSampleRate, quality));
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Checks to see if the volume ramp has completed and clears the increment
+ * variables appropriately.
+ *
+ * FIXME: There is code to handle int/float ramp variable switchover should it not
+ * complete within a mixer buffer processing call, but it is preferred to avoid switchover
+ * due to precision issues. The switchover code is included for legacy code purposes
+ * and can be removed once the integer volume is removed.
+ *
+ * It is not sufficient to clear only the volumeInc integer variable because
+ * if one channel requires ramping, all channels are ramped.
+ *
+ * There is a bit of duplicated code here, but it keeps backward compatibility.
+ */
+void AudioMixerBase::TrackBase::adjustVolumeRamp(bool aux, bool useFloat)
+{
+ if (useFloat) {
+ for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) {
+ if ((mVolumeInc[i] > 0 && mPrevVolume[i] + mVolumeInc[i] >= mVolume[i]) ||
+ (mVolumeInc[i] < 0 && mPrevVolume[i] + mVolumeInc[i] <= mVolume[i])) {
+ volumeInc[i] = 0;
+ prevVolume[i] = volume[i] << 16;
+ mVolumeInc[i] = 0.;
+ mPrevVolume[i] = mVolume[i];
+ } else {
+ //ALOGV("ramp: %f %f %f", mVolume[i], mPrevVolume[i], mVolumeInc[i]);
+ prevVolume[i] = u4_28_from_float(mPrevVolume[i]);
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < MAX_NUM_VOLUMES; 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;
+ mVolumeInc[i] = 0.;
+ mPrevVolume[i] = mVolume[i];
+ } else {
+ //ALOGV("ramp: %d %d %d", volume[i] << 16, prevVolume[i], volumeInc[i]);
+ mPrevVolume[i] = float_from_u4_28(prevVolume[i]);
+ }
+ }
+ }
+
+ if (aux) {
+#ifdef FLOAT_AUX
+ if (useFloat) {
+ if ((mAuxInc > 0.f && mPrevAuxLevel + mAuxInc >= mAuxLevel) ||
+ (mAuxInc < 0.f && mPrevAuxLevel + mAuxInc <= mAuxLevel)) {
+ auxInc = 0;
+ prevAuxLevel = auxLevel << 16;
+ mAuxInc = 0.f;
+ mPrevAuxLevel = mAuxLevel;
+ }
+ } else
+#endif
+ if ((auxInc > 0 && ((prevAuxLevel + auxInc) >> 16) >= auxLevel) ||
+ (auxInc < 0 && ((prevAuxLevel + auxInc) >> 16) <= auxLevel)) {
+ auxInc = 0;
+ prevAuxLevel = auxLevel << 16;
+ mAuxInc = 0.f;
+ mPrevAuxLevel = mAuxLevel;
+ }
+ }
+}
+
+void AudioMixerBase::TrackBase::recreateResampler(uint32_t devSampleRate)
+{
+ if (mResampler.get() != nullptr) {
+ const uint32_t resetToSampleRate = sampleRate;
+ mResampler.reset(nullptr);
+ sampleRate = devSampleRate; // without resampler, track rate is device sample rate.
+ // recreate the resampler with updated format, channels, saved sampleRate.
+ setResampler(resetToSampleRate /*trackSampleRate*/, devSampleRate);
+ }
+}
+
+size_t AudioMixerBase::getUnreleasedFrames(int name) const
+{
+ const auto it = mTracks.find(name);
+ if (it != mTracks.end()) {
+ return it->second->getUnreleasedFrames();
+ }
+ return 0;
+}
+
+std::string AudioMixerBase::trackNames() const
+{
+ std::stringstream ss;
+ for (const auto &pair : mTracks) {
+ ss << pair.first << " ";
+ }
+ return ss.str();
+}
+
+void AudioMixerBase::process__validate()
+{
+ // TODO: fix all16BitsStereNoResample logic to
+ // either properly handle muted tracks (it should ignore them)
+ // or remove altogether as an obsolete optimization.
+ bool all16BitsStereoNoResample = true;
+ bool resampling = false;
+ bool volumeRamp = false;
+
+ mEnabled.clear();
+ mGroups.clear();
+ for (const auto &pair : mTracks) {
+ const int name = pair.first;
+ const std::shared_ptr<TrackBase> &t = pair.second;
+ if (!t->enabled) continue;
+
+ mEnabled.emplace_back(name); // we add to mEnabled in order of name.
+ mGroups[t->mainBuffer].emplace_back(name); // mGroups also in order of name.
+
+ uint32_t n = 0;
+ // FIXME can overflow (mask is only 3 bits)
+ n |= NEEDS_CHANNEL_1 + t->channelCount - 1;
+ if (t->doesResample()) {
+ n |= NEEDS_RESAMPLE;
+ }
+ if (t->auxLevel != 0 && t->auxBuffer != NULL) {
+ n |= NEEDS_AUX;
+ }
+
+ if (t->volumeInc[0]|t->volumeInc[1]) {
+ volumeRamp = true;
+ } else if (!t->doesResample() && t->volumeRL == 0) {
+ n |= NEEDS_MUTE;
+ }
+ t->needs = n;
+
+ if (n & NEEDS_MUTE) {
+ t->hook = &TrackBase::track__nop;
+ } else {
+ if (n & NEEDS_AUX) {
+ all16BitsStereoNoResample = false;
+ }
+ if (n & NEEDS_RESAMPLE) {
+ all16BitsStereoNoResample = false;
+ resampling = true;
+ t->hook = TrackBase::getTrackHook(TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
+ t->mMixerInFormat, t->mMixerFormat);
+ ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
+ "Track %d needs downmix + resample", name);
+ } else {
+ if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
+ t->hook = TrackBase::getTrackHook(
+ (t->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO // TODO: MONO_HACK
+ && t->channelMask == AUDIO_CHANNEL_OUT_MONO)
+ ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
+ t->mMixerChannelCount,
+ t->mMixerInFormat, t->mMixerFormat);
+ all16BitsStereoNoResample = false;
+ }
+ if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
+ t->hook = TrackBase::getTrackHook(TRACKTYPE_NORESAMPLE, t->mMixerChannelCount,
+ t->mMixerInFormat, t->mMixerFormat);
+ ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
+ "Track %d needs downmix", name);
+ }
+ }
+ }
+ }
+
+ // select the processing hooks
+ mHook = &AudioMixerBase::process__nop;
+ if (mEnabled.size() > 0) {
+ if (resampling) {
+ if (mOutputTemp.get() == nullptr) {
+ mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
+ }
+ if (mResampleTemp.get() == nullptr) {
+ mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
+ }
+ mHook = &AudioMixerBase::process__genericResampling;
+ } else {
+ // we keep temp arrays around.
+ mHook = &AudioMixerBase::process__genericNoResampling;
+ if (all16BitsStereoNoResample && !volumeRamp) {
+ if (mEnabled.size() == 1) {
+ const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
+ if ((t->needs & NEEDS_MUTE) == 0) {
+ // The check prevents a muted track from acquiring a process hook.
+ //
+ // This is dangerous if the track is MONO as that requires
+ // special case handling due to implicit channel duplication.
+ // Stereo or Multichannel should actually be fine here.
+ mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
+ t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
+ }
+ }
+ }
+ }
+ }
+
+ ALOGV("mixer configuration change: %zu "
+ "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d",
+ mEnabled.size(), all16BitsStereoNoResample, resampling, volumeRamp);
+
+ process();
+
+ // Now that the volume ramp has been done, set optimal state and
+ // track hooks for subsequent mixer process
+ if (mEnabled.size() > 0) {
+ bool allMuted = true;
+
+ for (const int name : mEnabled) {
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+ if (!t->doesResample() && t->volumeRL == 0) {
+ t->needs |= NEEDS_MUTE;
+ t->hook = &TrackBase::track__nop;
+ } else {
+ allMuted = false;
+ }
+ }
+ if (allMuted) {
+ mHook = &AudioMixerBase::process__nop;
+ } else if (all16BitsStereoNoResample) {
+ if (mEnabled.size() == 1) {
+ //const int i = 31 - __builtin_clz(enabledTracks);
+ const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
+ // Muted single tracks handled by allMuted above.
+ mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
+ t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat);
+ }
+ }
+ }
+}
+
+void AudioMixerBase::TrackBase::track__genericResample(
+ int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
+{
+ ALOGVV("track__genericResample\n");
+ mResampler->setSampleRate(sampleRate);
+
+ // ramp gain - resample to temp buffer and scale/mix in 2nd step
+ if (aux != NULL) {
+ // always resample with unity gain when sending to auxiliary buffer to be able
+ // to apply send level after resampling
+ mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
+ memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(int32_t));
+ mResampler->resample(temp, outFrameCount, bufferProvider);
+ if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
+ volumeRampStereo(out, outFrameCount, temp, aux);
+ } else {
+ volumeStereo(out, outFrameCount, temp, aux);
+ }
+ } else {
+ if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
+ mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
+ memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
+ mResampler->resample(temp, outFrameCount, bufferProvider);
+ volumeRampStereo(out, outFrameCount, temp, aux);
+ }
+
+ // constant gain
+ else {
+ mResampler->setVolume(mVolume[0], mVolume[1]);
+ mResampler->resample(out, outFrameCount, bufferProvider);
+ }
+ }
+}
+
+void AudioMixerBase::TrackBase::track__nop(int32_t* out __unused,
+ size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
+{
+}
+
+void AudioMixerBase::TrackBase::volumeRampStereo(
+ int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+{
+ int32_t vl = prevVolume[0];
+ int32_t vr = prevVolume[1];
+ const int32_t vlInc = volumeInc[0];
+ const int32_t vrInc = volumeInc[1];
+
+ //ALOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ // ramp volume
+ if (CC_UNLIKELY(aux != NULL)) {
+ int32_t va = prevAuxLevel;
+ const int32_t vaInc = auxInc;
+ int32_t l;
+ int32_t r;
+
+ do {
+ l = (*temp++ >> 12);
+ r = (*temp++ >> 12);
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * r;
+ *aux++ += (va >> 17) * (l + r);
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+ prevAuxLevel = va;
+ } else {
+ do {
+ *out++ += (vl >> 16) * (*temp++ >> 12);
+ *out++ += (vr >> 16) * (*temp++ >> 12);
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+ }
+ prevVolume[0] = vl;
+ prevVolume[1] = vr;
+ adjustVolumeRamp(aux != NULL);
+}
+
+void AudioMixerBase::TrackBase::volumeStereo(
+ int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+{
+ const int16_t vl = volume[0];
+ const int16_t vr = volume[1];
+
+ if (CC_UNLIKELY(aux != NULL)) {
+ const int16_t va = auxLevel;
+ do {
+ int16_t l = (int16_t)(*temp++ >> 12);
+ int16_t r = (int16_t)(*temp++ >> 12);
+ out[0] = mulAdd(l, vl, out[0]);
+ int16_t a = (int16_t)(((int32_t)l + r) >> 1);
+ out[1] = mulAdd(r, vr, out[1]);
+ out += 2;
+ aux[0] = mulAdd(a, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ } else {
+ do {
+ int16_t l = (int16_t)(*temp++ >> 12);
+ int16_t r = (int16_t)(*temp++ >> 12);
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(r, vr, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+}
+
+void AudioMixerBase::TrackBase::track__16BitsStereo(
+ int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
+{
+ ALOGVV("track__16BitsStereo\n");
+ const int16_t *in = static_cast<const int16_t *>(mIn);
+
+ if (CC_UNLIKELY(aux != NULL)) {
+ int32_t l;
+ int32_t r;
+ // ramp gain
+ if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
+ int32_t vl = prevVolume[0];
+ int32_t vr = prevVolume[1];
+ int32_t va = prevAuxLevel;
+ const int32_t vlInc = volumeInc[0];
+ const int32_t vrInc = volumeInc[1];
+ const int32_t vaInc = auxInc;
+ // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ l = (int32_t)*in++;
+ r = (int32_t)*in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * r;
+ *aux++ += (va >> 17) * (l + r);
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+
+ prevVolume[0] = vl;
+ prevVolume[1] = vr;
+ prevAuxLevel = va;
+ adjustVolumeRamp(true);
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = volumeRL;
+ const int16_t va = (int16_t)auxLevel;
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ aux[0] = mulAdd(a, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ }
+ } else {
+ // ramp gain
+ if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
+ int32_t vl = prevVolume[0];
+ int32_t vr = prevVolume[1];
+ const int32_t vlInc = volumeInc[0];
+ const int32_t vrInc = volumeInc[1];
+
+ // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ *out++ += (vl >> 16) * (int32_t) *in++;
+ *out++ += (vr >> 16) * (int32_t) *in++;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ prevVolume[0] = vl;
+ prevVolume[1] = vr;
+ adjustVolumeRamp(false);
+ }
+
+ // constant gain
+ else {
+ const uint32_t vrl = volumeRL;
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ in += 2;
+ out[0] = mulAddRL(1, rl, vrl, out[0]);
+ out[1] = mulAddRL(0, rl, vrl, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+ }
+ mIn = in;
+}
+
+void AudioMixerBase::TrackBase::track__16BitsMono(
+ int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
+{
+ ALOGVV("track__16BitsMono\n");
+ const int16_t *in = static_cast<int16_t const *>(mIn);
+
+ if (CC_UNLIKELY(aux != NULL)) {
+ // ramp gain
+ if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
+ int32_t vl = prevVolume[0];
+ int32_t vr = prevVolume[1];
+ int32_t va = prevAuxLevel;
+ const int32_t vlInc = volumeInc[0];
+ const int32_t vrInc = volumeInc[1];
+ const int32_t vaInc = auxInc;
+
+ // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ *aux++ += (va >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ va += vaInc;
+ } while (--frameCount);
+
+ prevVolume[0] = vl;
+ prevVolume[1] = vr;
+ prevAuxLevel = va;
+ adjustVolumeRamp(true);
+ }
+ // constant gain
+ else {
+ const int16_t vl = volume[0];
+ const int16_t vr = volume[1];
+ const int16_t va = (int16_t)auxLevel;
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ aux[0] = mulAdd(l, va, aux[0]);
+ aux++;
+ } while (--frameCount);
+ }
+ } else {
+ // ramp gain
+ if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
+ int32_t vl = prevVolume[0];
+ int32_t vr = prevVolume[1];
+ const int32_t vlInc = volumeInc[0];
+ const int32_t vrInc = volumeInc[1];
+
+ // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
+ // t, vlInc/65536.0f, vl/65536.0f, volume[0],
+ // (vl + vlInc*frameCount)/65536.0f, frameCount);
+
+ do {
+ int32_t l = *in++;
+ *out++ += (vl >> 16) * l;
+ *out++ += (vr >> 16) * l;
+ vl += vlInc;
+ vr += vrInc;
+ } while (--frameCount);
+
+ prevVolume[0] = vl;
+ prevVolume[1] = vr;
+ adjustVolumeRamp(false);
+ }
+ // constant gain
+ else {
+ const int16_t vl = volume[0];
+ const int16_t vr = volume[1];
+ do {
+ int16_t l = *in++;
+ out[0] = mulAdd(l, vl, out[0]);
+ out[1] = mulAdd(l, vr, out[1]);
+ out += 2;
+ } while (--frameCount);
+ }
+ }
+ mIn = in;
+}
+
+// no-op case
+void AudioMixerBase::process__nop()
+{
+ ALOGVV("process__nop\n");
+
+ for (const auto &pair : mGroups) {
+ // process by group of tracks with same output buffer to
+ // avoid multiple memset() on same buffer
+ const auto &group = pair.second;
+
+ const std::shared_ptr<TrackBase> &t = mTracks[group[0]];
+ memset(t->mainBuffer, 0,
+ mFrameCount * audio_bytes_per_frame(t->getMixerChannelCount(), t->mMixerFormat));
+
+ // now consume data
+ for (const int name : group) {
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+ size_t outFrames = mFrameCount;
+ while (outFrames) {
+ t->buffer.frameCount = outFrames;
+ t->bufferProvider->getNextBuffer(&t->buffer);
+ if (t->buffer.raw == NULL) break;
+ outFrames -= t->buffer.frameCount;
+ t->bufferProvider->releaseBuffer(&t->buffer);
+ }
+ }
+ }
+}
+
+// generic code without resampling
+void AudioMixerBase::process__genericNoResampling()
+{
+ ALOGVV("process__genericNoResampling\n");
+ int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
+
+ for (const auto &pair : mGroups) {
+ // process by group of tracks with same output main buffer to
+ // avoid multiple memset() on same buffer
+ const auto &group = pair.second;
+
+ // acquire buffer
+ for (const int name : group) {
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+ t->buffer.frameCount = mFrameCount;
+ t->bufferProvider->getNextBuffer(&t->buffer);
+ t->frameCount = t->buffer.frameCount;
+ t->mIn = t->buffer.raw;
+ }
+
+ int32_t *out = (int *)pair.first;
+ size_t numFrames = 0;
+ do {
+ const size_t frameCount = std::min((size_t)BLOCKSIZE, mFrameCount - numFrames);
+ memset(outTemp, 0, sizeof(outTemp));
+ for (const int name : group) {
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+ int32_t *aux = NULL;
+ if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
+ aux = t->auxBuffer + numFrames;
+ }
+ for (int outFrames = frameCount; outFrames > 0; ) {
+ // t->in == nullptr can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t->mIn == nullptr) {
+ break;
+ }
+ size_t inFrames = (t->frameCount > outFrames)?outFrames:t->frameCount;
+ if (inFrames > 0) {
+ (t.get()->*t->hook)(
+ outTemp + (frameCount - outFrames) * t->mMixerChannelCount,
+ inFrames, mResampleTemp.get() /* naked ptr */, aux);
+ t->frameCount -= inFrames;
+ outFrames -= inFrames;
+ if (CC_UNLIKELY(aux != NULL)) {
+ aux += inFrames;
+ }
+ }
+ if (t->frameCount == 0 && outFrames) {
+ t->bufferProvider->releaseBuffer(&t->buffer);
+ t->buffer.frameCount = (mFrameCount - numFrames) -
+ (frameCount - outFrames);
+ t->bufferProvider->getNextBuffer(&t->buffer);
+ t->mIn = t->buffer.raw;
+ if (t->mIn == nullptr) {
+ break;
+ }
+ t->frameCount = t->buffer.frameCount;
+ }
+ }
+ }
+
+ const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]];
+ convertMixerFormat(out, t1->mMixerFormat, outTemp, t1->mMixerInFormat,
+ frameCount * t1->mMixerChannelCount);
+ // TODO: fix ugly casting due to choice of out pointer type
+ out = reinterpret_cast<int32_t*>((uint8_t*)out
+ + frameCount * t1->mMixerChannelCount
+ * audio_bytes_per_sample(t1->mMixerFormat));
+ numFrames += frameCount;
+ } while (numFrames < mFrameCount);
+
+ // release each track's buffer
+ for (const int name : group) {
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+ t->bufferProvider->releaseBuffer(&t->buffer);
+ }
+ }
+}
+
+// generic code with resampling
+void AudioMixerBase::process__genericResampling()
+{
+ ALOGVV("process__genericResampling\n");
+ int32_t * const outTemp = mOutputTemp.get(); // naked ptr
+ size_t numFrames = mFrameCount;
+
+ for (const auto &pair : mGroups) {
+ const auto &group = pair.second;
+ const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]];
+
+ // clear temp buffer
+ memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);
+ for (const int name : group) {
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+ int32_t *aux = NULL;
+ if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
+ aux = t->auxBuffer;
+ }
+
+ // this is a little goofy, on the resampling case we don't
+ // acquire/release the buffers because it's done by
+ // the resampler.
+ if (t->needs & NEEDS_RESAMPLE) {
+ (t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);
+ } else {
+
+ size_t outFrames = 0;
+
+ while (outFrames < numFrames) {
+ t->buffer.frameCount = numFrames - outFrames;
+ t->bufferProvider->getNextBuffer(&t->buffer);
+ t->mIn = t->buffer.raw;
+ // t->mIn == nullptr can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t->mIn == nullptr) break;
+
+ (t.get()->*t->hook)(
+ outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
+ mResampleTemp.get() /* naked ptr */,
+ aux != nullptr ? aux + outFrames : nullptr);
+ outFrames += t->buffer.frameCount;
+
+ t->bufferProvider->releaseBuffer(&t->buffer);
+ }
+ }
+ }
+ convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,
+ outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount);
+ }
+}
+
+// one track, 16 bits stereo without resampling is the most common case
+void AudioMixerBase::process__oneTrack16BitsStereoNoResampling()
+{
+ ALOGVV("process__oneTrack16BitsStereoNoResampling\n");
+ LOG_ALWAYS_FATAL_IF(mEnabled.size() != 0,
+ "%zu != 1 tracks enabled", mEnabled.size());
+ const int name = mEnabled[0];
+ const std::shared_ptr<TrackBase> &t = mTracks[name];
+
+ AudioBufferProvider::Buffer& b(t->buffer);
+
+ int32_t* out = t->mainBuffer;
+ float *fout = reinterpret_cast<float*>(out);
+ size_t numFrames = mFrameCount;
+
+ const int16_t vl = t->volume[0];
+ const int16_t vr = t->volume[1];
+ const uint32_t vrl = t->volumeRL;
+ while (numFrames) {
+ b.frameCount = numFrames;
+ t->bufferProvider->getNextBuffer(&b);
+ const int16_t *in = b.i16;
+
+ // in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (in == NULL || (((uintptr_t)in) & 3)) {
+ if ( AUDIO_FORMAT_PCM_FLOAT == t->mMixerFormat ) {
+ memset((char*)fout, 0, numFrames
+ * t->mMixerChannelCount * audio_bytes_per_sample(t->mMixerFormat));
+ } else {
+ memset((char*)out, 0, numFrames
+ * t->mMixerChannelCount * audio_bytes_per_sample(t->mMixerFormat));
+ }
+ ALOGE_IF((((uintptr_t)in) & 3),
+ "process__oneTrack16BitsStereoNoResampling: misaligned buffer"
+ " %p track %d, channels %d, needs %08x, volume %08x vfl %f vfr %f",
+ in, name, t->channelCount, t->needs, vrl, t->mVolume[0], t->mVolume[1]);
+ return;
+ }
+ size_t outFrames = b.frameCount;
+
+ switch (t->mMixerFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
+ in += 2;
+ int32_t l = mulRL(1, rl, vrl);
+ int32_t r = mulRL(0, rl, vrl);
+ *fout++ = float_from_q4_27(l);
+ *fout++ = float_from_q4_27(r);
+ // Note: In case of later int16_t sink output,
+ // conversion and clamping is done by memcpy_to_i16_from_float().
+ } while (--outFrames);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) {
+ // volume is boosted, so we might need to clamp even though
+ // we process only one track.
+ do {
+ uint32_t rl = *reinterpret_cast<const uint32_t *>(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<const uint32_t *>(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);
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixer format: %d", t->mMixerFormat);
+ }
+ numFrames -= b.frameCount;
+ t->bufferProvider->releaseBuffer(&b);
+ }
+}
+
+/* TODO: consider whether this level of optimization is necessary.
+ * Perhaps just stick with a single for loop.
+ */
+
+// Needs to derive a compile time constant (constexpr). Could be targeted to go
+// to a MONOVOL mixtype based on MAX_NUM_VOLUMES, but that's an unnecessary complication.
+#define MIXTYPE_MONOVOL(mixtype) ((mixtype) == MIXTYPE_MULTI ? MIXTYPE_MULTI_MONOVOL : \
+ (mixtype) == MIXTYPE_MULTI_SAVEONLY ? MIXTYPE_MULTI_SAVEONLY_MONOVOL : (mixtype))
+
+/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27) or float
+ */
+template <int MIXTYPE,
+ typename TO, typename TI, typename TV, typename TA, typename TAV>
+static void volumeRampMulti(uint32_t channels, TO* out, size_t frameCount,
+ const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc)
+{
+ switch (channels) {
+ case 1:
+ volumeRampMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 2:
+ volumeRampMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 3:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 4:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 5:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 6:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 7:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ case 8:
+ volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out,
+ frameCount, in, aux, vol, volinc, vola, volainc);
+ break;
+ }
+}
+
+/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27) or float
+ */
+template <int MIXTYPE,
+ typename TO, typename TI, typename TV, typename TA, typename TAV>
+static void volumeMulti(uint32_t channels, TO* out, size_t frameCount,
+ const TI* in, TA* aux, const TV *vol, TAV vola)
+{
+ switch (channels) {
+ case 1:
+ volumeMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 2:
+ volumeMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 3:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 4:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 5:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 6:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 7:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out, frameCount, in, aux, vol, vola);
+ break;
+ case 8:
+ volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out, frameCount, in, aux, vol, vola);
+ break;
+ }
+}
+
+/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * USEFLOATVOL (set to true if float volume is used)
+ * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27) or float
+ */
+template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
+ typename TO, typename TI, typename TA>
+void AudioMixerBase::TrackBase::volumeMix(TO *out, size_t outFrames,
+ const TI *in, TA *aux, bool ramp)
+{
+ if (USEFLOATVOL) {
+ if (ramp) {
+ volumeRampMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+ mPrevVolume, mVolumeInc,
+#ifdef FLOAT_AUX
+ &mPrevAuxLevel, mAuxInc
+#else
+ &prevAuxLevel, auxInc
+#endif
+ );
+ if (ADJUSTVOL) {
+ adjustVolumeRamp(aux != NULL, true);
+ }
+ } else {
+ volumeMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+ mVolume,
+#ifdef FLOAT_AUX
+ mAuxLevel
+#else
+ auxLevel
+#endif
+ );
+ }
+ } else {
+ if (ramp) {
+ volumeRampMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+ prevVolume, volumeInc, &prevAuxLevel, auxInc);
+ if (ADJUSTVOL) {
+ adjustVolumeRamp(aux != NULL);
+ }
+ } else {
+ volumeMulti<MIXTYPE>(mMixerChannelCount, out, outFrames, in, aux,
+ volume, auxLevel);
+ }
+ }
+}
+
+/* This process hook is called when there is a single track without
+ * aux buffer, volume ramp, or resampling.
+ * TODO: Update the hook selection: this can properly handle aux and ramp.
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
+ */
+template <int MIXTYPE, typename TO, typename TI, typename TA>
+void AudioMixerBase::process__noResampleOneTrack()
+{
+ ALOGVV("process__noResampleOneTrack\n");
+ LOG_ALWAYS_FATAL_IF(mEnabled.size() != 1,
+ "%zu != 1 tracks enabled", mEnabled.size());
+ const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
+ const uint32_t channels = t->mMixerChannelCount;
+ TO* out = reinterpret_cast<TO*>(t->mainBuffer);
+ TA* aux = reinterpret_cast<TA*>(t->auxBuffer);
+ const bool ramp = t->needsRamp();
+
+ for (size_t numFrames = mFrameCount; numFrames > 0; ) {
+ AudioBufferProvider::Buffer& b(t->buffer);
+ // get input buffer
+ b.frameCount = numFrames;
+ t->bufferProvider->getNextBuffer(&b);
+ const TI *in = reinterpret_cast<TI*>(b.raw);
+
+ // in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (in == NULL || (((uintptr_t)in) & 3)) {
+ memset(out, 0, numFrames
+ * channels * audio_bytes_per_sample(t->mMixerFormat));
+ ALOGE_IF((((uintptr_t)in) & 3), "process__noResampleOneTrack: bus error: "
+ "buffer %p track %p, channels %d, needs %#x",
+ in, &t, t->channelCount, t->needs);
+ return;
+ }
+
+ const size_t outFrames = b.frameCount;
+ t->volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, false /* ADJUSTVOL */> (
+ out, outFrames, in, aux, ramp);
+
+ out += outFrames * channels;
+ if (aux != NULL) {
+ aux += outFrames;
+ }
+ numFrames -= b.frameCount;
+
+ // release buffer
+ t->bufferProvider->releaseBuffer(&b);
+ }
+ if (ramp) {
+ t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
+ }
+}
+
+/* This track hook is called to do resampling then mixing,
+ * pulling from the track's upstream AudioBufferProvider.
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27) or float
+ */
+template <int MIXTYPE, typename TO, typename TI, typename TA>
+void AudioMixerBase::TrackBase::track__Resample(TO* out, size_t outFrameCount, TO* temp, TA* aux)
+{
+ ALOGVV("track__Resample\n");
+ mResampler->setSampleRate(sampleRate);
+ const bool ramp = needsRamp();
+ if (ramp || aux != NULL) {
+ // if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
+ // if aux != NULL: resample with unity gain to temp buffer then apply send level.
+
+ mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
+ memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
+ mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);
+
+ volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
+ out, outFrameCount, temp, aux, ramp);
+
+ } else { // constant volume gain
+ mResampler->setVolume(mVolume[0], mVolume[1]);
+ mResampler->resample((int32_t*)out, outFrameCount, bufferProvider);
+ }
+}
+
+/* This track hook is called to mix a track, when no resampling is required.
+ * The input buffer should be present in in.
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27) or float
+ */
+template <int MIXTYPE, typename TO, typename TI, typename TA>
+void AudioMixerBase::TrackBase::track__NoResample(
+ TO* out, size_t frameCount, TO* temp __unused, TA* aux)
+{
+ ALOGVV("track__NoResample\n");
+ const TI *in = static_cast<const TI *>(mIn);
+
+ volumeMix<MIXTYPE, is_same<TI, float>::value /* USEFLOATVOL */, true /* ADJUSTVOL */>(
+ out, frameCount, in, aux, needsRamp());
+
+ // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
+ // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
+ in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * mMixerChannelCount;
+ mIn = in;
+}
+
+/* The Mixer engine generates either int32_t (Q4_27) or float data.
+ * We use this function to convert the engine buffers
+ * to the desired mixer output format, either int16_t (Q.15) or float.
+ */
+/* static */
+void AudioMixerBase::convertMixerFormat(void *out, audio_format_t mixerOutFormat,
+ void *in, audio_format_t mixerInFormat, size_t sampleCount)
+{
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ memcpy(out, in, sampleCount * sizeof(float)); // MEMCPY. TODO optimize out
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ memcpy_to_i16_from_float((int16_t*)out, (float*)in, sampleCount);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ memcpy_to_float_from_q4_27((float*)out, (const int32_t*)in, sampleCount);
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ memcpy_to_i16_from_q4_27((int16_t*)out, (const int32_t*)in, sampleCount);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+}
+
+/* Returns the proper track hook to use for mixing the track into the output buffer.
+ */
+/* static */
+AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, uint32_t channelCount,
+ audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
+{
+ if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ switch (trackType) {
+ case TRACKTYPE_NOP:
+ return &TrackBase::track__nop;
+ case TRACKTYPE_RESAMPLE:
+ return &TrackBase::track__genericResample;
+ case TRACKTYPE_NORESAMPLEMONO:
+ return &TrackBase::track__16BitsMono;
+ case TRACKTYPE_NORESAMPLE:
+ return &TrackBase::track__16BitsStereo;
+ default:
+ LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
+ break;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
+ switch (trackType) {
+ case TRACKTYPE_NOP:
+ return &TrackBase::track__nop;
+ case TRACKTYPE_RESAMPLE:
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+ MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
+ MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ break;
+ case TRACKTYPE_NORESAMPLEMONO:
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+ MIXTYPE_MONOEXPAND, float /*TO*/, float /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+ MIXTYPE_MONOEXPAND, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ break;
+ case TRACKTYPE_NORESAMPLE:
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+ MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
+ MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
+ break;
+ }
+ return NULL;
+}
+
+/* Returns the proper process hook for mixing tracks. Currently works only for
+ * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling.
+ *
+ * TODO: Due to the special mixing considerations of duplicating to
+ * a stereo output track, the input track cannot be MONO. This should be
+ * prevented by the caller.
+ */
+/* static */
+AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
+ int processType, uint32_t channelCount,
+ audio_format_t mixerInFormat, audio_format_t mixerOutFormat)
+{
+ if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
+ LOG_ALWAYS_FATAL("bad processType: %d", processType);
+ return NULL;
+ }
+ if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
+ return &AudioMixerBase::process__oneTrack16BitsStereoNoResampling;
+ }
+ LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
+ switch (mixerInFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, float /*TO*/, float /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, float /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ switch (mixerOutFormat) {
+ case AUDIO_FORMAT_PCM_FLOAT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, float /*TO*/, int16_t /*TI*/, TYPE_AUX>;
+ case AUDIO_FORMAT_PCM_16_BIT:
+ return &AudioMixerBase::process__noResampleOneTrack<
+ MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
+ break;
+ }
+ break;
+ default:
+ LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
+ break;
+ }
+ return NULL;
+}
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/media/libaudioprocessing/include/media/AudioMixerBase.h b/media/libaudioprocessing/include/media/AudioMixerBase.h
new file mode 100644
index 0000000..805b6d0
--- /dev/null
+++ b/media/libaudioprocessing/include/media/AudioMixerBase.h
@@ -0,0 +1,359 @@
+/*
+**
+** Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_MIXER_BASE_H
+#define ANDROID_AUDIO_MIXER_BASE_H
+
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <media/AudioBufferProvider.h>
+#include <media/AudioResampler.h>
+#include <media/AudioResamplerPublic.h>
+#include <system/audio.h>
+#include <utils/Compat.h>
+
+// This must match frameworks/av/services/audioflinger/Configuration.h
+// when used with the Audio Framework.
+#define FLOAT_AUX
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+// AudioMixerBase is functional on its own if only mixing and resampling
+// is needed.
+
+class AudioMixerBase
+{
+public:
+ // Do not change these unless underlying code changes.
+ // This mixer has a hard-coded upper limit of 8 channels for output.
+ static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
+ static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2; // stereo volume only
+
+ static const uint16_t UNITY_GAIN_INT = 0x1000;
+ static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;
+
+ enum { // names
+ // setParameter targets
+ TRACK = 0x3000,
+ RESAMPLE = 0x3001,
+ RAMP_VOLUME = 0x3002, // ramp to new volume
+ VOLUME = 0x3003, // don't ramp
+ TIMESTRETCH = 0x3004,
+
+ // set Parameter names
+ // for target TRACK
+ CHANNEL_MASK = 0x4000,
+ FORMAT = 0x4001,
+ MAIN_BUFFER = 0x4002,
+ AUX_BUFFER = 0x4003,
+ // 0x4004 reserved
+ MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
+ // for target RESAMPLE
+ SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
+ // parameter 'value' is the new sample rate in Hz.
+ // Only creates a sample rate converter the first time that
+ // the track sample rate is different from the mix sample rate.
+ // If the new sample rate is the same as the mix sample rate,
+ // and a sample rate converter already exists,
+ // then the sample rate converter remains present but is a no-op.
+ RESET = 0x4101, // Reset sample rate converter without changing sample rate.
+ // This clears out the resampler's input buffer.
+ REMOVE = 0x4102, // Remove the sample rate converter on this track name;
+ // the track is restored to the mix sample rate.
+ // for target RAMP_VOLUME and VOLUME (8 channels max)
+ // FIXME use float for these 3 to improve the dynamic range
+ VOLUME0 = 0x4200,
+ VOLUME1 = 0x4201,
+ AUXLEVEL = 0x4210,
+ };
+
+ AudioMixerBase(size_t frameCount, uint32_t sampleRate)
+ : mSampleRate(sampleRate)
+ , mFrameCount(frameCount) {
+ }
+
+ virtual ~AudioMixerBase() {}
+
+ virtual bool isValidFormat(audio_format_t format) const;
+ virtual bool isValidChannelMask(audio_channel_mask_t channelMask) const;
+
+ // Create a new track in the mixer.
+ //
+ // \param name a unique user-provided integer associated with the track.
+ // If name already exists, the function will abort.
+ // \param channelMask output channel mask.
+ // \param format PCM format
+ // \param sessionId Session id for the track. Tracks with the same
+ // session id will be submixed together.
+ //
+ // \return OK on success.
+ // BAD_VALUE if the format does not satisfy isValidFormat()
+ // or the channelMask does not satisfy isValidChannelMask().
+ status_t create(
+ int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId);
+
+ bool exists(int name) const {
+ return mTracks.count(name) > 0;
+ }
+
+ // Free an allocated track by name.
+ void destroy(int name);
+
+ // Enable or disable an allocated track by name
+ void enable(int name);
+ void disable(int name);
+
+ virtual void setParameter(int name, int target, int param, void *value);
+
+ void process() {
+ preProcess();
+ (this->*mHook)();
+ postProcess();
+ }
+
+ size_t getUnreleasedFrames(int name) const;
+
+ std::string trackNames() const;
+
+ protected:
+ // Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
+ // original code will be used for stereo sinks, the new mixer for everything else.
+ static constexpr bool kUseNewMixer = true;
+
+ // Set kUseFloat to true to allow floating input into the mixer engine.
+ // If kUseNewMixer is false, this is ignored or may be overridden internally
+ static constexpr bool kUseFloat = true;
+
+#ifdef FLOAT_AUX
+ using TYPE_AUX = float;
+ static_assert(kUseNewMixer && kUseFloat,
+ "kUseNewMixer and kUseFloat must be true for FLOAT_AUX option");
+#else
+ using TYPE_AUX = int32_t; // q4.27
+#endif
+
+ /* For multi-format functions (calls template functions
+ * in AudioMixerOps.h). The template parameters are as follows:
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * USEFLOATVOL (set to true if float volume is used)
+ * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
+ */
+
+ enum {
+ // FIXME this representation permits up to 8 channels
+ NEEDS_CHANNEL_COUNT__MASK = 0x00000007,
+ };
+
+ enum {
+ NEEDS_CHANNEL_1 = 0x00000000, // mono
+ NEEDS_CHANNEL_2 = 0x00000001, // stereo
+
+ // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT
+
+ NEEDS_MUTE = 0x00000100,
+ NEEDS_RESAMPLE = 0x00001000,
+ NEEDS_AUX = 0x00010000,
+ };
+
+ // hook types
+ enum {
+ PROCESSTYPE_NORESAMPLEONETRACK, // others set elsewhere
+ };
+
+ enum {
+ TRACKTYPE_NOP,
+ TRACKTYPE_RESAMPLE,
+ TRACKTYPE_NORESAMPLE,
+ TRACKTYPE_NORESAMPLEMONO,
+ };
+
+ // process hook functionality
+ using process_hook_t = void(AudioMixerBase::*)();
+
+ struct TrackBase;
+ using hook_t = void(TrackBase::*)(
+ int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
+
+ struct TrackBase {
+ TrackBase()
+ : bufferProvider(nullptr)
+ {
+ // TODO: move additional initialization here.
+ }
+ virtual ~TrackBase() {}
+
+ virtual uint32_t getOutputChannelCount() { return channelCount; }
+ virtual uint32_t getMixerChannelCount() { return mMixerChannelCount; }
+
+ bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
+ bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
+ bool doesResample() const { return mResampler.get() != nullptr; }
+ void recreateResampler(uint32_t devSampleRate);
+ void resetResampler() { if (mResampler.get() != nullptr) mResampler->reset(); }
+ void adjustVolumeRamp(bool aux, bool useFloat = false);
+ size_t getUnreleasedFrames() const { return mResampler.get() != nullptr ?
+ mResampler->getUnreleasedFrames() : 0; };
+
+ static hook_t getTrackHook(int trackType, uint32_t channelCount,
+ audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+
+ void track__nop(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+
+ template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL,
+ typename TO, typename TI, typename TA>
+ void volumeMix(TO *out, size_t outFrames, const TI *in, TA *aux, bool ramp);
+
+ uint32_t needs;
+
+ // TODO: Eventually remove legacy integer volume settings
+ union {
+ int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
+ int32_t volumeRL;
+ };
+
+ int32_t prevVolume[MAX_NUM_VOLUMES];
+ int32_t volumeInc[MAX_NUM_VOLUMES];
+ int32_t auxInc;
+ int32_t prevAuxLevel;
+ int16_t auxLevel; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
+
+ uint16_t frameCount;
+
+ uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
+ uint8_t unused_padding; // formerly format, was always 16
+ uint16_t enabled; // actually bool
+ audio_channel_mask_t channelMask;
+
+ // actual buffer provider used by the track hooks
+ AudioBufferProvider* bufferProvider;
+
+ mutable AudioBufferProvider::Buffer buffer; // 8 bytes
+
+ hook_t hook;
+ const void *mIn; // current location in buffer
+
+ std::unique_ptr<AudioResampler> mResampler;
+ uint32_t sampleRate;
+ int32_t* mainBuffer;
+ int32_t* auxBuffer;
+
+ int32_t sessionId;
+
+ audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ audio_format_t mFormat; // input track format
+ audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
+ // each track must be converted to this format.
+
+ float mVolume[MAX_NUM_VOLUMES]; // floating point set volume
+ float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
+ float mVolumeInc[MAX_NUM_VOLUMES]; // floating point volume increment
+
+ float mAuxLevel; // floating point set aux level
+ float mPrevAuxLevel; // floating point prev aux level
+ float mAuxInc; // floating point aux increment
+
+ audio_channel_mask_t mMixerChannelMask;
+ uint32_t mMixerChannelCount;
+
+ protected:
+
+ // hooks
+ void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ void track__16BitsStereo(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ void track__16BitsMono(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+
+ void volumeRampStereo(int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+ void volumeStereo(int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
+
+ // multi-format track hooks
+ template <int MIXTYPE, typename TO, typename TI, typename TA>
+ void track__Resample(TO* out, size_t frameCount, TO* temp __unused, TA* aux);
+ template <int MIXTYPE, typename TO, typename TI, typename TA>
+ void track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux);
+ };
+
+ // preCreateTrack must create an instance of a proper TrackBase descendant.
+ // postCreateTrack is called after filling out fields of TrackBase. It can
+ // abort track creation by returning non-OK status. See the implementation
+ // of create() for details.
+ virtual std::shared_ptr<TrackBase> preCreateTrack();
+ virtual status_t postCreateTrack(TrackBase *track __unused) { return OK; }
+
+ // preProcess is called before the process hook, postProcess after,
+ // see the implementation of process() method.
+ virtual void preProcess() {}
+ virtual void postProcess() {}
+
+ virtual bool setChannelMasks(int name,
+ audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);
+
+ // Called when track info changes and a new process hook should be determined.
+ void invalidate() {
+ mHook = &AudioMixerBase::process__validate;
+ }
+
+ void process__validate();
+ void process__nop();
+ void process__genericNoResampling();
+ void process__genericResampling();
+ void process__oneTrack16BitsStereoNoResampling();
+
+ template <int MIXTYPE, typename TO, typename TI, typename TA>
+ void process__noResampleOneTrack();
+
+ static process_hook_t getProcessHook(int processType, uint32_t channelCount,
+ audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
+
+ static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
+ void *in, audio_format_t mixerInFormat, size_t sampleCount);
+
+ // initialization constants
+ const uint32_t mSampleRate;
+ const size_t mFrameCount;
+
+ process_hook_t mHook = &AudioMixerBase::process__nop; // one of process__*, never nullptr
+
+ // the size of the type (int32_t) should be the largest of all types supported
+ // by the mixer.
+ std::unique_ptr<int32_t[]> mOutputTemp;
+ std::unique_ptr<int32_t[]> mResampleTemp;
+
+ // track names grouped by main buffer, in no particular order of main buffer.
+ // however names for a particular main buffer are in order (by construction).
+ std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
+
+ // track names that are enabled, in increasing order (by construction).
+ std::vector<int /* name */> mEnabled;
+
+ // track smart pointers, by name, in increasing order of name.
+ std::map<int /* name */, std::shared_ptr<TrackBase>> mTracks;
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_MIXER_BASE_H
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 72e669a..d639f26 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -547,6 +547,16 @@
bool mute;
};
+ // Abstraction for the Audio Source for the RecordThread (HAL or PassthruPatchRecord).
+ struct Source
+ {
+ virtual ~Source() = default;
+ // The following methods have the same signatures as in StreamHalInterface.
+ virtual status_t read(void *buffer, size_t bytes, size_t *read) = 0;
+ virtual status_t getCapturePosition(int64_t *frames, int64_t *time) = 0;
+ virtual status_t standby() = 0;
+ };
+
// --- PlaybackThread ---
#ifdef FLOAT_EFFECT_CHAIN
#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_FLOAT
@@ -749,7 +759,7 @@
// For emphasis, we could also make all pointers to them be "const *",
// but that would clutter the code unnecessarily.
- struct AudioStreamIn {
+ struct AudioStreamIn : public Source {
AudioHwDevice* const audioHwDev;
sp<StreamInHalInterface> stream;
audio_input_flags_t flags;
@@ -758,6 +768,13 @@
AudioStreamIn(AudioHwDevice *dev, sp<StreamInHalInterface> in, audio_input_flags_t flags) :
audioHwDev(dev), stream(in), flags(flags) {}
+ status_t read(void *buffer, size_t bytes, size_t *read) override {
+ return stream->read(buffer, bytes, read);
+ }
+ status_t getCapturePosition(int64_t *frames, int64_t *time) override {
+ return stream->getCapturePosition(frames, time);
+ }
+ status_t standby() override { return stream->standby(); }
};
struct TeePatch {
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index c5b9953..3eacc8c 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -105,13 +105,8 @@
return mSQ.poll();
}
-void FastMixer::setNBLogWriter(NBLog::Writer *logWriter)
+void FastMixer::setNBLogWriter(NBLog::Writer *logWriter __unused)
{
- // FIXME If mMixer is set or changed prior to this, we don't inform correctly.
- // Should cache logWriter and re-apply it at the assignment to mMixer.
- if (mMixer != NULL) {
- mMixer->setNBLogWriter(logWriter);
- }
}
void FastMixer::onIdle()
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 04b32c2..8b7a124 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -124,7 +124,7 @@
mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState;
tlNBLogWriter = next->mNBLogWriter != NULL ?
next->mNBLogWriter : mDummyNBLogWriter.get();
- setNBLogWriter(tlNBLogWriter); // FastMixer informs its AudioMixer, FastCapture ignores
+ setNBLogWriter(tlNBLogWriter); // This is used for debugging only
// We want to always have a valid reference to the previous (non-idle) state.
// However, the state queue only guarantees access to current and previous states.
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index edb331d..18cb53b 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -483,19 +483,6 @@
// Fast mode is not available in this case.
inputFlags = (audio_input_flags_t) (inputFlags & ~AUDIO_INPUT_FLAG_FAST);
}
- sp<RecordThread::PatchRecord> tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord(
- mRecord.thread().get(),
- sampleRate,
- inChannelMask,
- format,
- frameCount,
- NULL,
- (size_t)0 /* bufferSize */,
- inputFlags);
- status = mRecord.checkTrack(tempRecordTrack.get());
- if (status != NO_ERROR) {
- return status;
- }
audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE;
@@ -512,9 +499,34 @@
outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
+ sp<RecordThread::PatchRecord> tempRecordTrack;
+ if ((inputFlags & AUDIO_INPUT_FLAG_DIRECT) && (outputFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
+ tempRecordTrack = new RecordThread::PassthruPatchRecord(
+ mRecord.thread().get(),
+ sampleRate,
+ inChannelMask,
+ format,
+ frameCount,
+ inputFlags);
+ } else {
+ tempRecordTrack = new RecordThread::PatchRecord(
+ mRecord.thread().get(),
+ sampleRate,
+ inChannelMask,
+ format,
+ frameCount,
+ nullptr,
+ (size_t)0 /* bufferSize */,
+ inputFlags);
+ }
+ status = mRecord.checkTrack(tempRecordTrack.get());
+ if (status != NO_ERROR) {
+ return status;
+ }
+
// create a special playback track to render to playback thread.
// this track is given the same buffer as the PatchRecord buffer
- sp<PlaybackThread::PatchTrack> tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack(
+ sp<PlaybackThread::PatchTrack> tempPatchTrack = new PlaybackThread::PatchTrack(
mPlayback.thread().get(),
streamType,
sampleRate,
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a093893..d0f8b17 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -266,8 +266,6 @@
private:
void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
- /** Write the source data in the buffer provider. @return written frame count. */
- size_t writeFrames(AudioBufferProvider* dest, const void* src, size_t frameCount);
template <class F>
void forEachTeePatchTrack(F f) {
for (auto& tp : mTeePatches) { f(tp.patchTrack); }
@@ -387,6 +385,8 @@
const Timeout& timeout = {});
virtual ~PatchTrack();
+ size_t framesReady() const override;
+
virtual status_t start(AudioSystem::sync_event_t event =
AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 08660dd..da05dac 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -128,6 +128,8 @@
const Timeout& timeout = {});
virtual ~PatchRecord();
+ virtual Source* getSource() { return nullptr; }
+
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
@@ -136,4 +138,71 @@
virtual status_t obtainBuffer(Proxy::Buffer *buffer,
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer *buffer);
+
+ size_t writeFrames(const void* src, size_t frameCount, size_t frameSize) {
+ return writeFrames(this, src, frameCount, frameSize);
+ }
+
+protected:
+ /** Write the source data into the buffer provider. @return written frame count. */
+ static size_t writeFrames(AudioBufferProvider* dest, const void* src,
+ size_t frameCount, size_t frameSize);
+
}; // end of PatchRecord
+
+class PassthruPatchRecord : public PatchRecord, public Source {
+public:
+ PassthruPatchRecord(RecordThread *recordThread,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_format_t format,
+ size_t frameCount,
+ audio_input_flags_t flags);
+
+ Source* getSource() override { return static_cast<Source*>(this); }
+
+ // Source interface
+ status_t read(void *buffer, size_t bytes, size_t *read) override;
+ status_t getCapturePosition(int64_t *frames, int64_t *time) override;
+ status_t standby() override;
+
+ // AudioBufferProvider interface
+ // This interface is used by RecordThread to pass the data obtained
+ // from HAL or other source to the client. PassthruPatchRecord receives
+ // the data in 'obtainBuffer' so these calls are stubbed out.
+ status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
+ void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
+
+ // PatchProxyBufferProvider interface
+ // This interface is used from DirectOutputThread to acquire data from HAL.
+ bool producesBufferOnDemand() const override { return true; }
+ status_t obtainBuffer(Proxy::Buffer *buffer, const struct timespec *timeOut = nullptr) override;
+ void releaseBuffer(Proxy::Buffer *buffer) override;
+
+private:
+ // This is to use with PatchRecord::writeFrames
+ struct PatchRecordAudioBufferProvider : public AudioBufferProvider {
+ explicit PatchRecordAudioBufferProvider(PassthruPatchRecord& passthru) :
+ mPassthru(passthru) {}
+ status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override {
+ return mPassthru.PatchRecord::getNextBuffer(buffer);
+ }
+ void releaseBuffer(AudioBufferProvider::Buffer* buffer) override {
+ return mPassthru.PatchRecord::releaseBuffer(buffer);
+ }
+ private:
+ PassthruPatchRecord& mPassthru;
+ };
+
+ sp<StreamInHalInterface> obtainStream(sp<ThreadBase>* thread);
+
+ PatchRecordAudioBufferProvider mPatchRecordAudioBufferProvider;
+ std::unique_ptr<void, decltype(free)*> mSinkBuffer; // frame size aligned continuous buffer
+ std::unique_ptr<void, decltype(free)*> mStubBuffer; // buffer used for AudioBufferProvider
+ size_t mUnconsumedFrames = 0;
+ std::mutex mReadLock;
+ std::condition_variable mReadCV;
+ size_t mReadBytes = 0; // GUARDED_BY(mReadLock)
+ status_t mReadError = NO_ERROR; // GUARDED_BY(mReadLock)
+ int64_t mLastReadFrames = 0; // accessed on RecordThread only
+};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3f4a24e..ea9e70a 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -5301,11 +5301,11 @@
return false;
}
// Check validity as we don't call AudioMixer::create() here.
- if (!AudioMixer::isValidFormat(format)) {
+ if (!mAudioMixer->isValidFormat(format)) {
ALOGW("%s: invalid format: %#x", __func__, format);
return false;
}
- if (!AudioMixer::isValidChannelMask(channelMask)) {
+ if (!mAudioMixer->isValidChannelMask(channelMask)) {
ALOGW("%s: invalid channelMask: %#x", __func__, channelMask);
return false;
}
@@ -6670,6 +6670,7 @@
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
mInput(input),
+ mSource(mInput),
mActiveTracks(&this->mLocalLog),
mRsmpInBuffer(NULL),
// mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
@@ -7122,7 +7123,7 @@
} else {
ATRACE_BEGIN("read");
size_t bytesRead;
- status_t result = mInput->stream->read(
+ status_t result = mSource->read(
(uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);
ATRACE_END();
if (result < 0) {
@@ -7144,7 +7145,7 @@
int64_t position, time;
if (mStandby) {
mTimestampVerifier.discontinuity();
- } else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR
+ } else if (mSource->getCapturePosition(&position, &time) == NO_ERROR
&& time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) {
mTimestampVerifier.add(position, time, mSampleRate);
@@ -7425,7 +7426,7 @@
sq->end(false /*didModify*/);
}
}
- status_t result = mInput->stream->standby();
+ status_t result = mSource->standby();
ALOGE_IF(result != OK, "Error when putting input stream into standby: %d", result);
// If going into standby, flush the pipe source.
@@ -8410,11 +8411,17 @@
{
Mutex::Autolock _l(mLock);
mTracks.add(record);
+ if (record->getSource()) {
+ mSource = record->getSource();
+ }
}
void AudioFlinger::RecordThread::deletePatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
+ if (mSource == record->getSource()) {
+ mSource = mInput;
+ }
destroyTrack_l(record);
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index fc8aa13..acb1370 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1646,6 +1646,7 @@
void checkBtNrec_l();
AudioStreamIn *mInput;
+ Source *mSource;
SortedVector < sp<RecordTrack> > mTracks;
// mActiveTracks has dual roles: it indicates the current active track(s), and
// is used together with mStartStopCond to indicate start()/stop() progress
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 8f720b5..7a3bb0d 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -325,6 +325,7 @@
virtual ~PatchProxyBufferProvider() {}
+ virtual bool producesBufferOnDemand() const = 0;
virtual status_t obtainBuffer(Proxy::Buffer* buffer,
const struct timespec *requested = NULL) = 0;
virtual void releaseBuffer(Proxy::Buffer* buffer) = 0;
@@ -347,6 +348,8 @@
mPeerProxy = nullptr;
}
+ bool producesBufferOnDemand() const override { return false; }
+
protected:
const sp<ClientProxy> mProxy;
sp<RefBase> mPeerReferenceHold; // keeps mPeerProxy alive during access.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 78db80c..932c32b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -18,12 +18,14 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_AUDIO
#include "Configuration.h"
#include <linux/futex.h>
#include <math.h>
#include <sys/syscall.h>
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <private/media/AudioTrackShared.h>
@@ -820,16 +822,9 @@
}
for (auto& teePatch : mTeePatches) {
RecordThread::PatchRecord* patchRecord = teePatch.patchRecord.get();
-
- size_t framesWritten = writeFrames(patchRecord, sourceBuffer.i8, frameCount);
- // On buffer wrap, the buffer frame count will be less than requested,
- // when this happens a second buffer needs to be used to write the leftover audio
- size_t framesLeft = frameCount - framesWritten;
- if (framesWritten != 0 && framesLeft != 0) {
- framesWritten +=
- writeFrames(patchRecord, sourceBuffer.i8 + framesWritten * mFrameSize, framesLeft);
- framesLeft = frameCount - framesWritten;
- }
+ const size_t framesWritten = patchRecord->writeFrames(
+ sourceBuffer.i8, frameCount, mFrameSize);
+ const size_t framesLeft = frameCount - framesWritten;
ALOGW_IF(framesLeft != 0, "%s(%d) PatchRecord %d can not provide big enough "
"buffer %zu/%zu, dropping %zu frames", __func__, mId, patchRecord->mId,
framesWritten, frameCount, framesLeft);
@@ -841,26 +836,6 @@
spent.count(), mTeePatches.size());
}
-size_t AudioFlinger::PlaybackThread::Track::writeFrames(AudioBufferProvider* dest,
- const void* src,
- size_t frameCount) {
- AudioBufferProvider::Buffer patchBuffer;
- patchBuffer.frameCount = frameCount;
- auto status = dest->getNextBuffer(&patchBuffer);
- if (status != NO_ERROR) {
- ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
- __func__, status, strerror(-status));
- return 0;
- }
- ALOG_ASSERT(patchBuffer.frameCount <= frameCount);
- memcpy(patchBuffer.raw, src, patchBuffer.frameCount * mFrameSize);
- auto framesWritten = patchBuffer.frameCount;
- dest->releaseBuffer(&patchBuffer);
- return framesWritten;
-}
-
-// releaseBuffer() is not overridden
-
// ExtendedAudioBufferProvider interface
// framesReady() may return an approximation of the number of frames if called
@@ -1810,6 +1785,15 @@
ALOGV("%s(%d)", __func__, mId);
}
+size_t AudioFlinger::PlaybackThread::PatchTrack::framesReady() const
+{
+ if (mPeerProxy && mPeerProxy->producesBufferOnDemand()) {
+ return std::numeric_limits<size_t>::max();
+ } else {
+ return Track::framesReady();
+ }
+}
+
status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event,
audio_session_t triggerSession)
{
@@ -1828,9 +1812,19 @@
ALOG_ASSERT(mPeerProxy != 0, "%s(%d): called without peer proxy", __func__, mId);
Proxy::Buffer buf;
buf.mFrameCount = buffer->frameCount;
+ if (ATRACE_ENABLED()) {
+ std::string traceName("PTnReq");
+ traceName += std::to_string(id());
+ ATRACE_INT(traceName.c_str(), buf.mFrameCount);
+ }
status_t status = mPeerProxy->obtainBuffer(&buf, &mPeerTimeout);
ALOGV_IF(status != NO_ERROR, "%s(%d): getNextBuffer status %d", __func__, mId, status);
buffer->frameCount = buf.mFrameCount;
+ if (ATRACE_ENABLED()) {
+ std::string traceName("PTnObt");
+ traceName += std::to_string(id());
+ ATRACE_INT(traceName.c_str(), buf.mFrameCount);
+ }
if (buf.mFrameCount == 0) {
return WOULD_BLOCK;
}
@@ -2283,6 +2277,39 @@
ALOGV("%s(%d)", __func__, mId);
}
+static size_t writeFramesHelper(
+ AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize)
+{
+ AudioBufferProvider::Buffer patchBuffer;
+ patchBuffer.frameCount = frameCount;
+ auto status = dest->getNextBuffer(&patchBuffer);
+ if (status != NO_ERROR) {
+ ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
+ __func__, status, strerror(-status));
+ return 0;
+ }
+ ALOG_ASSERT(patchBuffer.frameCount <= frameCount);
+ memcpy(patchBuffer.raw, src, patchBuffer.frameCount * frameSize);
+ size_t framesWritten = patchBuffer.frameCount;
+ dest->releaseBuffer(&patchBuffer);
+ return framesWritten;
+}
+
+// static
+size_t AudioFlinger::RecordThread::PatchRecord::writeFrames(
+ AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize)
+{
+ size_t framesWritten = writeFramesHelper(dest, src, frameCount, frameSize);
+ // On buffer wrap, the buffer frame count will be less than requested,
+ // when this happens a second buffer needs to be used to write the leftover audio
+ const size_t framesLeft = frameCount - framesWritten;
+ if (framesWritten != 0 && framesLeft != 0) {
+ framesWritten += writeFramesHelper(dest, (const char*)src + framesWritten * frameSize,
+ framesLeft, frameSize);
+ }
+ return framesWritten;
+}
+
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
@@ -2294,6 +2321,11 @@
ALOGV_IF(status != NO_ERROR,
"%s(%d): mPeerProxy->obtainBuffer status %d", __func__, mId, status);
buffer->frameCount = buf.mFrameCount;
+ if (ATRACE_ENABLED()) {
+ std::string traceName("PRnObt");
+ traceName += std::to_string(id());
+ ATRACE_INT(traceName.c_str(), buf.mFrameCount);
+ }
if (buf.mFrameCount == 0) {
return WOULD_BLOCK;
}
@@ -2322,6 +2354,180 @@
mProxy->releaseBuffer(buffer);
}
+#undef LOG_TAG
+#define LOG_TAG "AF::PthrPatchRecord"
+
+static std::unique_ptr<void, decltype(free)*> allocAligned(size_t alignment, size_t size)
+{
+ void *ptr = nullptr;
+ (void)posix_memalign(&ptr, alignment, size);
+ return std::unique_ptr<void, decltype(free)*>(ptr, free);
+}
+
+AudioFlinger::RecordThread::PassthruPatchRecord::PassthruPatchRecord(
+ RecordThread *recordThread,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_format_t format,
+ size_t frameCount,
+ audio_input_flags_t flags)
+ : PatchRecord(recordThread, sampleRate, channelMask, format, frameCount,
+ nullptr /*buffer*/, 0 /*bufferSize*/, flags),
+ mPatchRecordAudioBufferProvider(*this),
+ mSinkBuffer(allocAligned(32, mFrameCount * mFrameSize)),
+ mStubBuffer(allocAligned(32, mFrameCount * mFrameSize))
+{
+ memset(mStubBuffer.get(), 0, mFrameCount * mFrameSize);
+}
+
+sp<StreamInHalInterface> AudioFlinger::RecordThread::PassthruPatchRecord::obtainStream(
+ sp<ThreadBase>* thread)
+{
+ *thread = mThread.promote();
+ if (!*thread) return nullptr;
+ RecordThread *recordThread = static_cast<RecordThread*>((*thread).get());
+ Mutex::Autolock _l(recordThread->mLock);
+ return recordThread->mInput ? recordThread->mInput->stream : nullptr;
+}
+
+// PatchProxyBufferProvider methods are called on DirectOutputThread
+status_t AudioFlinger::RecordThread::PassthruPatchRecord::obtainBuffer(
+ Proxy::Buffer* buffer, const struct timespec* timeOut)
+{
+ if (mUnconsumedFrames) {
+ buffer->mFrameCount = std::min(buffer->mFrameCount, mUnconsumedFrames);
+ // mUnconsumedFrames is decreased in releaseBuffer to use actual frame consumption figure.
+ return PatchRecord::obtainBuffer(buffer, timeOut);
+ }
+
+ // Otherwise, execute a read from HAL and write into the buffer.
+ nsecs_t startTimeNs = 0;
+ if (timeOut && (timeOut->tv_sec != 0 || timeOut->tv_nsec != 0) && timeOut->tv_sec != INT_MAX) {
+ // Will need to correct timeOut by elapsed time.
+ startTimeNs = systemTime();
+ }
+ const size_t framesToRead = std::min(buffer->mFrameCount, mFrameCount);
+ buffer->mFrameCount = 0;
+ buffer->mRaw = nullptr;
+ sp<ThreadBase> thread;
+ sp<StreamInHalInterface> stream = obtainStream(&thread);
+ if (!stream) return NO_INIT; // If there is no stream, RecordThread is not reading.
+
+ status_t result = NO_ERROR;
+ struct timespec newTimeOut = *timeOut;
+ size_t bytesRead = 0;
+ {
+ ATRACE_NAME("read");
+ result = stream->read(mSinkBuffer.get(), framesToRead * mFrameSize, &bytesRead);
+ if (result != NO_ERROR) goto stream_error;
+ if (bytesRead == 0) return NO_ERROR;
+ }
+
+ {
+ std::lock_guard<std::mutex> lock(mReadLock);
+ mReadBytes += bytesRead;
+ mReadError = NO_ERROR;
+ }
+ mReadCV.notify_one();
+ // writeFrames handles wraparound and should write all the provided frames.
+ // If it couldn't, there is something wrong with the client/server buffer of the software patch.
+ buffer->mFrameCount = writeFrames(
+ &mPatchRecordAudioBufferProvider,
+ mSinkBuffer.get(), bytesRead / mFrameSize, mFrameSize);
+ ALOGW_IF(buffer->mFrameCount < bytesRead / mFrameSize,
+ "Lost %zu frames obtained from HAL", bytesRead / mFrameSize - buffer->mFrameCount);
+ mUnconsumedFrames = buffer->mFrameCount;
+ // Correct newTimeOut by elapsed time.
+ if (startTimeNs) {
+ nsecs_t newTimeOutNs =
+ audio_utils_ns_from_timespec(&newTimeOut) - (systemTime() - startTimeNs);
+ if (newTimeOutNs < 0) newTimeOutNs = 0;
+ newTimeOut.tv_sec = newTimeOutNs / NANOS_PER_SECOND;
+ newTimeOut.tv_nsec = newTimeOutNs - newTimeOut.tv_sec * NANOS_PER_SECOND;
+ }
+ return PatchRecord::obtainBuffer(buffer, &newTimeOut);
+
+stream_error:
+ stream->standby();
+ {
+ std::lock_guard<std::mutex> lock(mReadLock);
+ mReadError = result;
+ }
+ mReadCV.notify_one();
+ return result;
+}
+
+void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(Proxy::Buffer* buffer)
+{
+ if (buffer->mFrameCount <= mUnconsumedFrames) {
+ mUnconsumedFrames -= buffer->mFrameCount;
+ } else {
+ ALOGW("Write side has consumed more frames than we had: %zu > %zu",
+ buffer->mFrameCount, mUnconsumedFrames);
+ mUnconsumedFrames = 0;
+ }
+ PatchRecord::releaseBuffer(buffer);
+}
+
+// AudioBufferProvider and Source methods are called on RecordThread
+// 'read' emulates actual audio data with 0's. This is OK as 'getNextBuffer'
+// and 'releaseBuffer' are stubbed out and ignore their input.
+// It's not possible to retrieve actual data here w/o blocking 'obtainBuffer'
+// until we copy it.
+status_t AudioFlinger::RecordThread::PassthruPatchRecord::read(
+ void* buffer, size_t bytes, size_t* read)
+{
+ bytes = std::min(bytes, mFrameCount * mFrameSize);
+ {
+ std::unique_lock<std::mutex> lock(mReadLock);
+ mReadCV.wait(lock, [&]{ return mReadError != NO_ERROR || mReadBytes != 0; });
+ if (mReadError != NO_ERROR) {
+ mLastReadFrames = 0;
+ return mReadError;
+ }
+ *read = std::min(bytes, mReadBytes);
+ mReadBytes -= *read;
+ }
+ mLastReadFrames = *read / mFrameSize;
+ memset(buffer, 0, *read);
+ return 0;
+}
+
+status_t AudioFlinger::RecordThread::PassthruPatchRecord::getCapturePosition(
+ int64_t* frames, int64_t* time)
+{
+ sp<ThreadBase> thread;
+ sp<StreamInHalInterface> stream = obtainStream(&thread);
+ return stream ? stream->getCapturePosition(frames, time) : NO_INIT;
+}
+
+status_t AudioFlinger::RecordThread::PassthruPatchRecord::standby()
+{
+ // RecordThread issues 'standby' command in two major cases:
+ // 1. Error on read--this case is handled in 'obtainBuffer'.
+ // 2. Track is stopping--as PassthruPatchRecord assumes continuous
+ // output, this can only happen when the software patch
+ // is being torn down. In this case, the RecordThread
+ // will terminate and close the HAL stream.
+ return 0;
+}
+
+// As the buffer gets filled in obtainBuffer, here we only simulate data consumption.
+status_t AudioFlinger::RecordThread::PassthruPatchRecord::getNextBuffer(
+ AudioBufferProvider::Buffer* buffer)
+{
+ buffer->frameCount = mLastReadFrames;
+ buffer->raw = buffer->frameCount != 0 ? mStubBuffer.get() : nullptr;
+ return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(
+ AudioBufferProvider::Buffer* buffer)
+{
+ buffer->frameCount = 0;
+ buffer->raw = nullptr;
+}
+
// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::MmapTrack"