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 783eef38ec..3f7cd48876 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -18,87 +18,38 @@ #ifndef ANDROID_AUDIO_MIXER_H #define ANDROID_AUDIO_MIXER_H -#include #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 { -namespace NBLog { -class Writer; -} // namespace NBLog - // ---------------------------------------------------------------------------- -class AudioMixer +// AudioMixer extends AudioMixerBase by adding support for down- and up-mixing +// and time stretch that are implemented via Effects HAL, and adding support +// for haptic channels which depends on Vibrator service. This is the version +// that is used by Audioflinger. + +class AudioMixer : public AudioMixerBase { public: - // Do not change these unless underlying code changes. - // This mixer has a hard-coded upper limit of 8 channels for output. - static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8; - static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2; // stereo volume only // maximum number of channels supported for the content static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX; - static const uint16_t UNITY_GAIN_INT = 0x1000; - static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f; - - enum { // names - // setParameter targets - TRACK = 0x3000, - RESAMPLE = 0x3001, - RAMP_VOLUME = 0x3002, // ramp to new volume - VOLUME = 0x3003, // don't ramp - TIMESTRETCH = 0x3004, - - // set Parameter names - // for target TRACK - CHANNEL_MASK = 0x4000, - FORMAT = 0x4001, - MAIN_BUFFER = 0x4002, - AUX_BUFFER = 0x4003, - DOWNMIX_TYPE = 0X4004, - MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT) - MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output + enum { // extension of AudioMixerBase parameters + DOWNMIX_TYPE = 0x4004, // for haptic HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not. HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data. - // for target RESAMPLE - SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; - // parameter 'value' is the new sample rate in Hz. - // Only creates a sample rate converter the first time that - // the track sample rate is different from the mix sample rate. - // If the new sample rate is the same as the mix sample rate, - // and a sample rate converter already exists, - // then the sample rate converter remains present but is a no-op. - RESET = 0x4101, // Reset sample rate converter without changing sample rate. - // This clears out the resampler's input buffer. - REMOVE = 0x4102, // Remove the sample rate converter on this track name; - // the track is restored to the mix sample rate. - // for target RAMP_VOLUME and VOLUME (8 channels max) - // FIXME use float for these 3 to improve the dynamic range - VOLUME0 = 0x4200, - VOLUME1 = 0x4201, - AUXLEVEL = 0x4210, // for target TIMESTRETCH PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name; // parameter 'value' is a pointer to the new playback rate. @@ -131,142 +82,23 @@ 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); - - 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 { - std::stringstream ss; - for (const auto &pair : mTracks) { - ss << pair.first << " "; - } - return ss.str(); - } - - void setNBLogWriter(NBLog::Writer *logWriter) { - mNBLogWriter = logWriter; - } - - static inline bool isValidFormat(audio_format_t format) { - switch (format) { - case AUDIO_FORMAT_PCM_8_BIT: - case AUDIO_FORMAT_PCM_16_BIT: - case AUDIO_FORMAT_PCM_24_BIT_PACKED: - case AUDIO_FORMAT_PCM_32_BIT: - case AUDIO_FORMAT_PCM_FLOAT: - return true; - default: - return false; - } - } + bool isValidChannelMask(audio_channel_mask_t channelMask) const override; - static inline bool isValidChannelMask(audio_channel_mask_t channelMask) { - return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible. - } + void setParameter(int name, int target, int param, void *value) override; + void setBufferProvider(int name, AudioBufferProvider* bufferProvider); private: - /* For multi-format functions (calls template functions - * in AudioMixerOps.h). The template parameters are as follows: - * - * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) - * USEFLOATVOL (set to true if float volume is used) - * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards) - * TO: int32_t (Q4.27) or float - * TI: int32_t (Q4.27) or int16_t (Q0.15) or float - * TA: int32_t (Q4.27) - */ - - enum { - // FIXME this representation permits up to 8 channels - NEEDS_CHANNEL_COUNT__MASK = 0x00000007, - }; - - enum { - NEEDS_CHANNEL_1 = 0x00000000, // mono - NEEDS_CHANNEL_2 = 0x00000001, // stereo - - // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT - - NEEDS_MUTE = 0x00000100, - NEEDS_RESAMPLE = 0x00001000, - NEEDS_AUX = 0x00010000, - }; - - // hook types - enum { - PROCESSTYPE_NORESAMPLEONETRACK, // others set elsewhere - }; - - enum { - TRACKTYPE_NOP, - TRACKTYPE_RESAMPLE, - TRACKTYPE_NORESAMPLE, - TRACKTYPE_NORESAMPLEMONO, - }; - - // process hook functionality - using process_hook_t = void(AudioMixer::*)(); - - struct Track; - using hook_t = void(Track::*)(int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); - - struct Track { - Track() - : bufferProvider(nullptr) - { - // TODO: move additional initialization here. - } + struct Track : public TrackBase { + Track() : TrackBase() {} ~Track() { - // bufferProvider, mInputBufferProvider need not be deleted. - mResampler.reset(nullptr); + // mInputBufferProvider need not be deleted. // Ensure the order of destruction of buffer providers as they // release the upstream provider in the destructor. mTimestretchBufferProvider.reset(nullptr); @@ -277,13 +109,12 @@ 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(); @@ -297,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. @@ -363,7 +152,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; @@ -373,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 @@ -440,76 +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); }; - // TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore. - static constexpr int BLOCKSIZE = 16; - - bool setChannelMasks(int name, - audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask); - - // Called when track info changes and a new process hook should be determined. - void invalidate() { - mHook = &AudioMixer::process__validate; + inline std::shared_ptr 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; - - NBLog::Writer *mNBLogWriter = nullptr; // associated NBLog::Writer - - process_hook_t mHook = &AudioMixer::process__nop; // one of process__*, never nullptr - - // the size of the type (int32_t) should be the largest of all types supported - // by the mixer. - std::unique_ptr 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 cb78063375..e8aa700aef 100644 --- a/media/libaudioprocessing/Android.bp +++ b/media/libaudioprocessing/Android.bp @@ -3,20 +3,13 @@ cc_defaults { export_include_dirs: ["include"], + header_libs: ["libaudioclient_headers"], + shared_libs: [ - "libaudiohal", "libaudioutils", "libcutils", "liblog", - "libnbaio", - "libnblog", - "libsonic", "libutils", - "libvibrator", - ], - - header_libs: [ - "libbase_headers", ], cflags: [ @@ -33,18 +26,31 @@ cc_library_shared { defaults: ["libaudioprocessing_defaults"], srcs: [ + "AudioMixer.cpp", "BufferProviders.cpp", "RecordBufferConverter.cpp", ], - whole_static_libs: ["libaudioprocessing_arm"], + + header_libs: [ + "libbase_headers", + ], + + shared_libs: [ + "libaudiohal", + "libsonic", + "libvibrator", + ], + + whole_static_libs: ["libaudioprocessing_base"], } cc_library_static { - name: "libaudioprocessing_arm", + name: "libaudioprocessing_base", defaults: ["libaudioprocessing_defaults"], + vendor_available: true, srcs: [ - "AudioMixer.cpp", + "AudioMixerBase.cpp", "AudioResampler.cpp", "AudioResamplerCubic.cpp", "AudioResamplerSinc.cpp", diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index f7cc09620e..c0b11a41d5 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 @@ -27,9 +28,6 @@ #include #include -#include -#include - #include #include @@ -58,138 +56,15 @@ #define ALOGVV(a...) do { } while (0) #endif -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) -#endif - -// Set kUseNewMixer to true to use the new mixer engine always. Otherwise the -// original code will be used for stereo sinks, the new mixer for multichannel. -static constexpr bool kUseNewMixer = true; - -// Set kUseFloat to true to allow floating input into the mixer engine. -// If kUseNewMixer is false, this is ignored or may be overridden internally -// because of downmix/upmix support. -static constexpr bool kUseFloat = true; - -#ifdef FLOAT_AUX -using TYPE_AUX = float; -static_assert(kUseNewMixer && kUseFloat, - "kUseNewMixer and kUseFloat must be true for FLOAT_AUX option"); -#else -using TYPE_AUX = int32_t; // q4.27 -#endif - // Set to default copy buffer size in frames for input processing. -static const size_t kCopyBufferFrameCount = 256; +static constexpr size_t kCopyBufferFrameCount = 256; namespace android { // ---------------------------------------------------------------------------- -static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) { - return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; -} - -status_t AudioMixer::create( - int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId) -{ - LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name); - - if (!isValidChannelMask(channelMask)) { - ALOGE("%s invalid channelMask: %#x", __func__, channelMask); - return BAD_VALUE; - } - if (!isValidFormat(format)) { - ALOGE("%s invalid format: %#x", __func__, format); - return BAD_VALUE; - } - - auto t = std::make_shared(); - { - // 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,127 +434,38 @@ 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: + AudioMixerBase::setParameter(name, target, param, value); + break; + case TIMESTRETCH: 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; + 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: - 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); - } + LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param); } 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); - } - break; default: LOG_ALWAYS_FATAL("setParameter: bad target %d", target); } } -bool AudioMixer::Track::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate) -{ - if (trackSampleRate != devSampleRate || mResampler.get() != nullptr) { - if (sampleRate != trackSampleRate) { - sampleRate = trackSampleRate; - if (mResampler.get() == nullptr) { - ALOGV("Creating resampler from track %d Hz to device %d Hz", - trackSampleRate, devSampleRate); - AudioResampler::src_quality quality; - // force lowest quality level resampler if use case isn't music or video - // FIXME this is flawed for dynamic sample rates, as we choose the resampler - // quality level based on the initial ratio, but that could change later. - // Should have a way to distinguish tracks with static ratios vs. dynamic ratios. - if (isMusicRate(trackSampleRate)) { - quality = AudioResampler::DEFAULT_QUALITY; - } else { - quality = AudioResampler::DYN_LOW_QUALITY; - } - - // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer - // but if none exists, it is the channel count (1 for mono). - const int resamplerChannelCount = mDownmixerBufferProvider.get() != nullptr - ? mMixerChannelCount : channelCount; - ALOGVV("Creating resampler:" - " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n", - mMixerInFormat, resamplerChannelCount, devSampleRate, quality); - mResampler.reset(AudioResampler::create( - mMixerInFormat, - resamplerChannelCount, - devSampleRate, quality)); - } - return true; - } - } - return false; -} - bool AudioMixer::Track::setPlaybackRate(const AudioPlaybackRate &playbackRate) { if ((mTimestretchBufferProvider.get() == nullptr && @@ -863,8 +478,7 @@ 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,84 +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; -} - 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. @@ -976,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() @@ -1656,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(); @@ -1887,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/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/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. 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, diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index a093893fac..d0f8b170a1 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); } @@ -387,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 08660dd67a..da05dac390 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); @@ -136,4 +138,71 @@ 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); + } + +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/Threads.cpp b/services/audioflinger/Threads.cpp index 2e6037b03d..2d769cfccd 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; } @@ -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 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 78db80ca0a..932c32bcbc 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 @@ -820,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); @@ -841,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 @@ -1810,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) { @@ -1828,9 +1812,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; } @@ -2283,6 +2277,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) @@ -2294,6 +2321,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; } @@ -2322,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"