From 9b6599e1e10232e9c6e67e1d6d26265b8e8da885 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Mon, 29 Jul 2019 15:23:21 -0700 Subject: [PATCH 1/8] libaudioprocessing: Trivial dependency cleanups - Remove unused dependencies on libnbaio and libnblog; - Move dependencies on libaudiohal and libsonic to libaudioprocessing as AudioMixer implementation does not use them. Test: make Merged-In: I258a450725bdacb4fcf437b6f86582d51d40e622 Change-Id: I258a450725bdacb4fcf437b6f86582d51d40e622 --- media/libaudioclient/include/media/AudioMixer.h | 10 ---------- media/libaudioprocessing/Android.bp | 10 ++++++---- services/audioflinger/FastMixer.cpp | 7 +------ services/audioflinger/FastThread.cpp | 2 +- 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 783eef38ec..6a8239cf8a 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -43,10 +43,6 @@ namespace android { -namespace NBLog { -class Writer; -} // namespace NBLog - // ---------------------------------------------------------------------------- class AudioMixer @@ -188,10 +184,6 @@ public: 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: @@ -491,8 +483,6 @@ private: 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 diff --git a/media/libaudioprocessing/Android.bp b/media/libaudioprocessing/Android.bp index cb78063375..a8c44f366a 100644 --- a/media/libaudioprocessing/Android.bp +++ b/media/libaudioprocessing/Android.bp @@ -4,13 +4,9 @@ cc_defaults { export_include_dirs: ["include"], shared_libs: [ - "libaudiohal", "libaudioutils", "libcutils", "liblog", - "libnbaio", - "libnblog", - "libsonic", "libutils", "libvibrator", ], @@ -36,6 +32,12 @@ cc_library_shared { "BufferProviders.cpp", "RecordBufferConverter.cpp", ], + + shared_libs: [ + "libaudiohal", + "libsonic", + ], + whole_static_libs: ["libaudioprocessing_arm"], } diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index c5b9953ba3..3eacc8c897 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -105,13 +105,8 @@ const FastThreadState *FastMixer::poll() 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 04b32c2b8a..8b7a124c6f 100644 --- a/services/audioflinger/FastThread.cpp +++ b/services/audioflinger/FastThread.cpp @@ -124,7 +124,7 @@ bool FastThread::threadLoop() 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. From a4f00e2aeadc9dc5c2f4970cd61ace72438136d4 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 31 Jul 2019 14:53:29 -0700 Subject: [PATCH 2/8] AudioMixer: Cleanups - remove unused ARRAY_SIZE; - fix indentation for TIMESTRETCH in setParameter; - move trackNames method implementation and BLOCKSIZE constant into .cpp file; - whitespace fixes. Test: make Merged-In: Idc5da55c8c3af0d59b9a7a12d081747b0a99dafc Change-Id: Idc5da55c8c3af0d59b9a7a12d081747b0a99dafc --- .../libaudioclient/include/media/AudioMixer.h | 15 +---- media/libaudioprocessing/AudioMixer.cpp | 62 +++++++++++-------- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 6a8239cf8a..55d5127846 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include @@ -176,13 +176,7 @@ public: size_t getUnreleasedFrames(int name) const; - std::string trackNames() const { - std::stringstream ss; - for (const auto &pair : mTracks) { - ss << pair.first << " "; - } - return ss.str(); - } + std::string trackNames() const; static inline bool isValidFormat(audio_format_t format) { switch (format) { @@ -355,7 +349,7 @@ private: * 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 mAdjustChannelsBufferProvider; @@ -449,9 +443,6 @@ private: 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); diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index f7cc09620e..2d740ab68f 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "AudioMixer" //#define LOG_NDEBUG 0 +#include #include #include #include @@ -58,10 +59,6 @@ #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; @@ -79,8 +76,11 @@ static_assert(kUseNewMixer && kUseFloat, using TYPE_AUX = int32_t; // q4.27 #endif +// TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore. +static constexpr int BLOCKSIZE = 16; + // 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 { @@ -786,28 +786,28 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) } } break; - case TIMESTRETCH: - switch (param) { - case PLAYBACK_RATE: { - const AudioPlaybackRate *playbackRate = - reinterpret_cast(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); + case TIMESTRETCH: + switch (param) { + case PLAYBACK_RATE: { + const AudioPlaybackRate *playbackRate = + reinterpret_cast(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: + LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param); + } + break; default: LOG_ALWAYS_FATAL("setParameter: bad target %d", target); @@ -949,6 +949,14 @@ size_t AudioMixer::getUnreleasedFrames(int name) const return 0; } +std::string AudioMixer::trackNames() const { + std::stringstream ss; + for (const auto &pair : mTracks) { + ss << pair.first << " "; + } + return ss.str(); +} + void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider) { LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); @@ -1080,7 +1088,7 @@ void AudioMixer::process__validate() "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d", mEnabled.size(), all16BitsStereoNoResample, resampling, volumeRamp); - process(); + process(); // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process From 32f0d1689bd2eea6f43d7fb53faaedc23bd101d7 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 30 Jul 2019 14:42:32 -0700 Subject: [PATCH 3/8] libaudioprocessing: Extract vendor-available part of AudioMixer Split AudioMixer into the base part which doesn't rely on the framework and can be used in vendor code and the derived part which is intended to be used by Audioflinger. Test: A/B compare output from test scripts, manual testing on the phone Merged-In: I24c390f67f20baa8109902099359ca6e34eebcfd Change-Id: I24c390f67f20baa8109902099359ca6e34eebcfd --- media/audioserver/Android.mk | 1 + .../libaudioclient/include/media/AudioMixer.h | 328 +--- media/libaudioprocessing/Android.bp | 20 +- media/libaudioprocessing/AudioMixer.cpp | 1623 +--------------- media/libaudioprocessing/AudioMixerBase.cpp | 1692 +++++++++++++++++ .../include/media/AudioMixerBase.h | 359 ++++ services/audioflinger/Threads.cpp | 4 +- 7 files changed, 2160 insertions(+), 1867 deletions(-) create mode 100644 media/libaudioprocessing/AudioMixerBase.cpp create mode 100644 media/libaudioprocessing/include/media/AudioMixerBase.h diff --git a/media/audioserver/Android.mk b/media/audioserver/Android.mk index f5f021b076..6697cb503a 100644 --- a/media/audioserver/Android.mk +++ b/media/audioserver/Android.mk @@ -9,6 +9,7 @@ LOCAL_SHARED_LIBRARIES := \ libaaudioservice \ libaudioflinger \ libaudiopolicyservice \ + libaudioprocessing \ libbinder \ libcutils \ liblog \ diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 55d5127846..3f7cd48876 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -18,83 +18,38 @@ #ifndef ANDROID_AUDIO_MIXER_H #define ANDROID_AUDIO_MIXER_H -#include #include -#include #include #include -#include -#include #include -#include -#include -#include +#include #include -#include -#include #include // 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 { // ---------------------------------------------------------------------------- -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. @@ -127,132 +82,23 @@ public: } 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 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); + bool isValidChannelMask(audio_channel_mask_t channelMask) const override; - 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 &t = pair.second; - if (t->mKeepContractedChannels) { - t->clearContractedBuffer(); - } - } - (this->*mHook)(); - processHapticData(); - } - - size_t getUnreleasedFrames(int name) const; - - std::string trackNames() const; - - 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); @@ -263,13 +109,12 @@ private: 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(); @@ -283,51 +128,9 @@ private: 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 - 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 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. @@ -359,27 +162,10 @@ private: std::unique_ptr mPostDownmixReformatBufferProvider; std::unique_ptr 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 @@ -426,71 +212,23 @@ private: 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 - void track__Resample(TO* out, size_t frameCount, TO* temp __unused, TA* aux); - template - void track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux); }; - 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 getTrack(int name) { + return std::static_pointer_cast(mTracks[name]); } - void process__validate(); - void process__nop(); - void process__genericNoResampling(); - void process__genericResampling(); - void process__oneTrack16BitsStereoNoResampling(); - - template - void process__noResampleOneTrack(); + std::shared_ptr preCreateTrack() override; + status_t postCreateTrack(TrackBase *track) override; - void processHapticData(); + void preProcess() override; + void postProcess() override; - 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; - - 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 mOutputTemp; - std::unique_ptr 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> mGroups; - - // track names that are enabled, in increasing order (by construction). - std::vector mEnabled; - - // track smart pointers, by name, in increasing order of name. - std::map> 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 a8c44f366a..e8aa700aef 100644 --- a/media/libaudioprocessing/Android.bp +++ b/media/libaudioprocessing/Android.bp @@ -3,16 +3,13 @@ cc_defaults { export_include_dirs: ["include"], + header_libs: ["libaudioclient_headers"], + shared_libs: [ "libaudioutils", "libcutils", "liblog", "libutils", - "libvibrator", - ], - - header_libs: [ - "libbase_headers", ], cflags: [ @@ -29,24 +26,31 @@ cc_library_shared { defaults: ["libaudioprocessing_defaults"], srcs: [ + "AudioMixer.cpp", "BufferProviders.cpp", "RecordBufferConverter.cpp", ], + header_libs: [ + "libbase_headers", + ], + shared_libs: [ "libaudiohal", "libsonic", + "libvibrator", ], - whole_static_libs: ["libaudioprocessing_arm"], + 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 2d740ab68f..c0b11a41d5 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -28,9 +28,6 @@ #include #include -#include -#include - #include #include @@ -59,26 +56,6 @@ #define ALOGVV(a...) do { } while (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 - -// TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore. -static constexpr int BLOCKSIZE = 16; - // Set to default copy buffer size in frames for input processing. static constexpr size_t kCopyBufferFrameCount = 256; @@ -86,110 +63,8 @@ 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(); - { - // 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 @@ status_t AudioMixer::create( 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 = mTracks[name]; + const std::shared_ptr &track = getTrack(name); if (trackChannelMask == (track->channelMask | track->mHapticChannelMask) && mixerChannelMask == (track->mMixerChannelMask | track->mMixerHapticChannelMask)) { @@ -255,14 +130,8 @@ bool AudioMixer::setChannelMasks(int name, 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::Track::reconfigureBufferProviders() } } -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 = 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 = 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 = mTracks[name]; + const std::shared_ptr &track = getTrack(name); int valueInt = static_cast(reinterpret_cast(value)); int32_t *valueBuf = reinterpret_cast(value); @@ -670,11 +378,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) } 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(valueInt); @@ -730,61 +434,9 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) 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(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(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); - } - } + AudioMixerBase::setParameter(name, target, param, value); break; case TIMESTRETCH: switch (param) { @@ -814,43 +466,6 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) } } -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 @@ bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate) 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,92 +489,10 @@ bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate) 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; -} - -std::string AudioMixer::trackNames() const { - std::stringstream ss; - for (const auto &pair : mTracks) { - ss << pair.first << " "; - } - return ss.str(); -} - void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider) { LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); - const std::shared_ptr &track = mTracks[name]; + const std::shared_ptr &track = getTrack(name); if (track->mInputBufferProvider == bufferProvider) { return; // don't reset any buffer providers if identical. @@ -984,679 +516,6 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider 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 &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 &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 &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 &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(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(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(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(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 &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 &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 &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 &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 &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((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 &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 &t1 = mTracks[group[0]]; - - // clear temp buffer - memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount); - for (const int name : group) { - const std::shared_ptr &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 &t = mTracks[name]; - - AudioBufferProvider::Buffer& b(t->buffer); - - int32_t* out = t->mainBuffer; - float *fout = reinterpret_cast(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(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(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(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() @@ -1664,211 +523,71 @@ void AudioMixer::process__oneTrack16BitsStereoNoResampling() 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 -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(out, frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 2: - volumeRampMulti(out, frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 3: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 4: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 5: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 6: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 7: - volumeRampMulti(out, - frameCount, in, aux, vol, volinc, vola, volainc); - break; - case 8: - volumeRampMulti(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 -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(out, frameCount, in, aux, vol, vola); - break; - case 2: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 3: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 4: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 5: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 6: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 7: - volumeMulti(out, frameCount, in, aux, vol, vola); - break; - case 8: - volumeMulti(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 -void AudioMixer::Track::volumeMix(TO *out, size_t outFrames, - const TI *in, TA *aux, bool ramp) -{ - if (USEFLOATVOL) { - if (ramp) { - volumeRampMulti(mMixerChannelCount, out, outFrames, in, aux, - mPrevVolume, mVolumeInc, -#ifdef FLOAT_AUX - &mPrevAuxLevel, mAuxInc -#else - &prevAuxLevel, auxInc -#endif - ); - if (ADJUSTVOL) { - adjustVolumeRamp(aux != NULL, true); - } - } else { - volumeMulti(mMixerChannelCount, out, outFrames, in, aux, - mVolume, -#ifdef FLOAT_AUX - mAuxLevel -#else - auxLevel -#endif - ); - } - } else { - if (ramp) { - volumeRampMulti(mMixerChannelCount, out, outFrames, in, aux, - prevVolume, volumeInc, &prevAuxLevel, auxInc); - if (ADJUSTVOL) { - adjustVolumeRamp(aux != NULL); - } - } else { - volumeMulti(mMixerChannelCount, out, outFrames, in, aux, - volume, auxLevel); - } +std::shared_ptr AudioMixer::preCreateTrack() +{ + return std::make_shared(); +} + +status_t AudioMixer::postCreateTrack(TrackBase *track) +{ + Track* t = static_cast(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; } -/* 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 -void AudioMixer::process__noResampleOneTrack() +void AudioMixer::preProcess() { - ALOGVV("process__noResampleOneTrack\n"); - LOG_ALWAYS_FATAL_IF(mEnabled.size() != 1, - "%zu != 1 tracks enabled", mEnabled.size()); - const std::shared_ptr &t = mTracks[mEnabled[0]]; - const uint32_t channels = t->mMixerChannelCount; - TO* out = reinterpret_cast(t->mainBuffer); - TA* aux = reinterpret_cast(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(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::value /* USEFLOATVOL */, false /* ADJUSTVOL */> ( - out, outFrames, in, aux, ramp); - - out += outFrames * channels; - if (aux != NULL) { - aux += outFrames; + for (const auto &pair : mTracks) { + // Clear contracted buffer before processing if contracted channels are saved + const std::shared_ptr &tb = pair.second; + Track *t = static_cast(tb.get()); + if (t->mKeepContractedChannels) { + t->clearContractedBuffer(); } - numFrames -= b.frameCount; - - // release buffer - t->bufferProvider->releaseBuffer(&b); - } - if (ramp) { - t->adjustVolumeRamp(aux != NULL, is_same::value); } } -void AudioMixer::processHapticData() +void AudioMixer::postProcess() { + // 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 &t = mTracks[name]; + const std::shared_ptr &t = getTrack(name); if (t->mHapticPlaybackEnabled) { size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount; float gamma = t->getHapticScaleGamma(); @@ -1895,225 +614,5 @@ void AudioMixer::processHapticData() } } -/* 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 -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::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 -void AudioMixer::Track::track__NoResample(TO* out, size_t frameCount, TO* temp __unused, TA* aux) -{ - ALOGVV("track__NoResample\n"); - const TI *in = static_cast(mIn); - - volumeMix::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 0000000000..75c077d5e2 --- /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 +#include + +#include +#include +#include +#include + +#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::preCreateTrack() +{ + return std::make_shared(); +} + +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 &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 &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 &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 &track = mTracks[name]; + + int valueInt = static_cast(reinterpret_cast(value)); + int32_t *valueBuf = reinterpret_cast(value); + + switch (target) { + + case TRACK: + switch (param) { + case CHANNEL_MASK: { + const audio_channel_mask_t trackChannelMask = + static_cast(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(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(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(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(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(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 &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 &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 &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 &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(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(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(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(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 &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 &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 &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 &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 &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((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 &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 &t1 = mTracks[group[0]]; + + // clear temp buffer + memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount); + for (const int name : group) { + const std::shared_ptr &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 &t = mTracks[name]; + + AudioBufferProvider::Buffer& b(t->buffer); + + int32_t* out = t->mainBuffer; + float *fout = reinterpret_cast(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(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(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(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 +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(out, frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 2: + volumeRampMulti(out, frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 3: + volumeRampMulti(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 4: + volumeRampMulti(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 5: + volumeRampMulti(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 6: + volumeRampMulti(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 7: + volumeRampMulti(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 8: + volumeRampMulti(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 +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(out, frameCount, in, aux, vol, vola); + break; + case 2: + volumeMulti(out, frameCount, in, aux, vol, vola); + break; + case 3: + volumeMulti(out, frameCount, in, aux, vol, vola); + break; + case 4: + volumeMulti(out, frameCount, in, aux, vol, vola); + break; + case 5: + volumeMulti(out, frameCount, in, aux, vol, vola); + break; + case 6: + volumeMulti(out, frameCount, in, aux, vol, vola); + break; + case 7: + volumeMulti(out, frameCount, in, aux, vol, vola); + break; + case 8: + volumeMulti(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 +void AudioMixerBase::TrackBase::volumeMix(TO *out, size_t outFrames, + const TI *in, TA *aux, bool ramp) +{ + if (USEFLOATVOL) { + if (ramp) { + volumeRampMulti(mMixerChannelCount, out, outFrames, in, aux, + mPrevVolume, mVolumeInc, +#ifdef FLOAT_AUX + &mPrevAuxLevel, mAuxInc +#else + &prevAuxLevel, auxInc +#endif + ); + if (ADJUSTVOL) { + adjustVolumeRamp(aux != NULL, true); + } + } else { + volumeMulti(mMixerChannelCount, out, outFrames, in, aux, + mVolume, +#ifdef FLOAT_AUX + mAuxLevel +#else + auxLevel +#endif + ); + } + } else { + if (ramp) { + volumeRampMulti(mMixerChannelCount, out, outFrames, in, aux, + prevVolume, volumeInc, &prevAuxLevel, auxInc); + if (ADJUSTVOL) { + adjustVolumeRamp(aux != NULL); + } + } else { + volumeMulti(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 +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 &t = mTracks[mEnabled[0]]; + const uint32_t channels = t->mMixerChannelCount; + TO* out = reinterpret_cast(t->mainBuffer); + TA* aux = reinterpret_cast(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(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::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::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 +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::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 +void AudioMixerBase::TrackBase::track__NoResample( + TO* out, size_t frameCount, TO* temp __unused, TA* aux) +{ + ALOGVV("track__NoResample\n"); + const TI *in = static_cast(mIn); + + volumeMix::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 0000000000..805b6d06c4 --- /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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// 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 + 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 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 + void track__Resample(TO* out, size_t frameCount, TO* temp __unused, TA* aux); + template + 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 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 + 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 mOutputTemp; + std::unique_ptr 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> mGroups; + + // track names that are enabled, in increasing order (by construction). + std::vector mEnabled; + + // track smart pointers, by name, in increasing order of name. + std::map> mTracks; +}; + +} // namespace android + +#endif // ANDROID_AUDIO_MIXER_BASE_H diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 2e6037b03d..29ba5a7fd9 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -5289,11 +5289,11 @@ bool AudioFlinger::MixerThread::isTrackAllowed_l( 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; } From 5269849effaa40a96ccffc214efaf804f3378bd0 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 4 Sep 2019 11:38:47 -0700 Subject: [PATCH 4/8] audioflinger: Add tracing of buffer frames to PatchTrack/Record Seeing requested / obtained buffer frame counts in trace helps to identify discontinuities in software patch pipeline. There is no need to trace requested frame count for PatchRecord as it always requests as much as possible ("-1" frameCount). Bug: 117564323 Test: with software patch active, external/chromium-trace/systrace.py audio Merged-In: I78400f6cc1d1940b3290fb23bb1aacfbffe042e5 Change-Id: I78400f6cc1d1940b3290fb23bb1aacfbffe042e5 --- services/audioflinger/Tracks.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 78db80ca0a..21eba1317d 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 #include #include #include +#include #include @@ -1828,9 +1830,19 @@ status_t AudioFlinger::PlaybackThread::PatchTrack::getNextBuffer( 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; } @@ -2294,6 +2306,11 @@ status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer( 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; } From d368d9166ea7df77479a2908e3a7dbf4c5f21c12 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Sep 2019 14:59:54 -0700 Subject: [PATCH 5/8] Move PlaybackThread::Track::writeFrames to PatchRecord This code logically belongs to PatchRecord because it emulates obtaining recorded data from a client. Test: make Merged-In: Icba4e33d9d0ca57e6ad964aaf3209e7db766f284 Change-Id: Icba4e33d9d0ca57e6ad964aaf3209e7db766f284 --- services/audioflinger/PlaybackTracks.h | 2 - services/audioflinger/RecordTracks.h | 10 ++++ services/audioflinger/Tracks.cpp | 66 ++++++++++++++------------ 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index a093893fac..d9a7cd2e0a 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -266,8 +266,6 @@ protected: 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 void forEachTeePatchTrack(F f) { for (auto& tp : mTeePatches) { f(tp.patchTrack); } diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 08660dd67a..b119660756 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -136,4 +136,14 @@ public: 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); + } + +private: + /** 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 diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 21eba1317d..cbb216eee6 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -822,16 +822,9 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer( } 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); @@ -843,26 +836,6 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer( 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 @@ -2295,6 +2268,39 @@ AudioFlinger::RecordThread::PatchRecord::~PatchRecord() 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) From af28887c617eca259444f743ef83eda85d52d31e Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Sep 2019 13:05:02 -0700 Subject: [PATCH 6/8] Abstract access to HAL stream via Source in RecordThread This allows to replace direct reading from HAL with obtaining audio data from another source. It should be possible to encapsulate reading from FastCapture in the same manner, but it's not required for direct inputs. Test: make Merged-In: I3f005583410cc9c5d4b07c127d95e236abb4a85f Change-Id: I3f005583410cc9c5d4b07c127d95e236abb4a85f --- services/audioflinger/AudioFlinger.h | 19 ++++++++++++++++++- services/audioflinger/RecordTracks.h | 2 ++ services/audioflinger/Threads.cpp | 13 ++++++++++--- services/audioflinger/Threads.h | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 72e669a2a5..d639f26796 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -547,6 +547,16 @@ private: 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 @@ using effect_buffer_t = int16_t; // 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 stream; audio_input_flags_t flags; @@ -758,6 +768,13 @@ using effect_buffer_t = int16_t; AudioStreamIn(AudioHwDevice *dev, sp 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/RecordTracks.h b/services/audioflinger/RecordTracks.h index b119660756..e083b386f8 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -128,6 +128,8 @@ public: 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); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 29ba5a7fd9..2d769cfccd 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6658,6 +6658,7 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, ) : 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() @@ -7110,7 +7111,7 @@ reacquire_wakelock: } 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) { @@ -7132,7 +7133,7 @@ reacquire_wakelock: 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); @@ -7413,7 +7414,7 @@ void AudioFlinger::RecordThread::inputStandBy() 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. @@ -8398,11 +8399,17 @@ void AudioFlinger::RecordThread::addPatchTrack(const sp& record) { Mutex::Autolock _l(mLock); mTracks.add(record); + if (record->getSource()) { + mSource = record->getSource(); + } } void AudioFlinger::RecordThread::deletePatchTrack(const sp& 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 336c2b40a9..31e10a3d8f 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1645,6 +1645,7 @@ private: void checkBtNrec_l(); AudioStreamIn *mInput; + Source *mSource; SortedVector < sp > mTracks; // mActiveTracks has dual roles: it indicates the current active track(s), and // is used together with mStartStopCond to indicate start()/stop() progress From e6eb348b793736f1f4e441b53167d0c09e614f6b Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Sep 2019 14:05:29 -0700 Subject: [PATCH 7/8] Add PassthruPatchRecord for low latency software patches Implement a subclass of PatchRecord that uses PatchTrack's thread for reading from HAL. This eliminates the need for buffering that adds latency. The only modification needed for PatchTrack is to indicate unlimited amount of available frames to the playback thread. This is to prevent PatchTrack from being deactivated by DirectOutputThread due to lack of frames available. RecordThread believes it reads audio data on its thread, and manages timestamps as usual. The data that it "reads" and passes to PassthruPatchRecord is discarded by the latter. Bug: 117564323 Test: with MSD module Merged-In: I376656e3c791e91e2196331ecdf2b425697c4e18 Change-Id: I376656e3c791e91e2196331ecdf2b425697c4e18 --- services/audioflinger/PlaybackTracks.h | 2 + services/audioflinger/RecordTracks.h | 59 +++++++- services/audioflinger/TrackBase.h | 3 + services/audioflinger/Tracks.cpp | 183 +++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index d9a7cd2e0a..d0f8b170a1 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -385,6 +385,8 @@ public: 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 e083b386f8..da05dac390 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -143,9 +143,66 @@ public: return writeFrames(this, src, frameCount, frameSize); } -private: +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(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 obtainStream(sp* thread); + + PatchRecordAudioBufferProvider mPatchRecordAudioBufferProvider; + std::unique_ptr mSinkBuffer; // frame size aligned continuous buffer + std::unique_ptr 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/TrackBase.h b/services/audioflinger/TrackBase.h index 8f720b58d5..7a3bb0de7e 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -325,6 +325,7 @@ public: 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 @@ public: mPeerProxy = nullptr; } + bool producesBufferOnDemand() const override { return false; } + protected: const sp mProxy; sp mPeerReferenceHold; // keeps mPeerProxy alive during access. diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index cbb216eee6..932c32bcbc 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1785,6 +1785,15 @@ AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack() ALOGV("%s(%d)", __func__, mId); } +size_t AudioFlinger::PlaybackThread::PatchTrack::framesReady() const +{ + if (mPeerProxy && mPeerProxy->producesBufferOnDemand()) { + return std::numeric_limits::max(); + } else { + return Track::framesReady(); + } +} + status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event, audio_session_t triggerSession) { @@ -2345,6 +2354,180 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe mProxy->releaseBuffer(buffer); } +#undef LOG_TAG +#define LOG_TAG "AF::PthrPatchRecord" + +static std::unique_ptr allocAligned(size_t alignment, size_t size) +{ + void *ptr = nullptr; + (void)posix_memalign(&ptr, alignment, size); + return std::unique_ptr(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 AudioFlinger::RecordThread::PassthruPatchRecord::obtainStream( + sp* thread) +{ + *thread = mThread.promote(); + if (!*thread) return nullptr; + RecordThread *recordThread = static_cast((*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 thread; + sp 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 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 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 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 thread; + sp 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" From 48f1d4579f9fe1cd6ff672fe9daa27fec83d82c7 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 1 Oct 2019 16:52:14 -0700 Subject: [PATCH 8/8] Use PassthruPatchRecord for DIRECT to DIRECT connections When both input and output connected by a software patch are in 'DIRECT' mode (no framework processing), use PassthruPatchRecord as it helps to reduce the latency significantly by avoiding intermediate buffering. Remove 'std::nothrow' from tracks creation in PatchPanel for consistency with other code. Bug: 117564323 Test: with MSD module Merged-In: I52ec5b02a207548ebc4073c1033e396f444c041c Change-Id: I52ec5b02a207548ebc4073c1033e396f444c041c --- services/audioflinger/PatchPanel.cpp | 40 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index edb331d4ae..18cb53b1da 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -483,19 +483,6 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) // Fast mode is not available in this case. inputFlags = (audio_input_flags_t) (inputFlags & ~AUDIO_INPUT_FLAG_FAST); } - sp 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 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST); } + sp 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 tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack( + sp tempPatchTrack = new PlaybackThread::PatchTrack( mPlayback.thread().get(), streamType, sampleRate,