From 3fae037db39ff89707c3a58ef1419229a00ad71d Mon Sep 17 00:00:00 2001 From: Richard Folke Tullberg Date: Fri, 13 Jan 2017 09:04:25 +0100 Subject: [PATCH] Add master audio balance Test: Change Balance through Settings, play audio Bug: 28390736 Co-author: Ed Savage-Jones Change-Id: I0169b436ccbaa5628584d9f4954dd7c76d021aae --- media/libaudioclient/AudioSystem.cpp | 14 +++++ media/libaudioclient/IAudioFlinger.cpp | 45 ++++++++++++++++ .../include/media/AudioSystem.h | 3 ++ .../include/media/IAudioFlinger.h | 3 ++ services/audioflinger/AudioFlinger.cpp | 46 +++++++++++++++++ services/audioflinger/AudioFlinger.h | 6 +++ services/audioflinger/FastMixer.cpp | 12 +++-- services/audioflinger/FastMixer.h | 8 ++- services/audioflinger/Threads.cpp | 51 +++++++++++++++++-- services/audioflinger/Threads.h | 19 +++++++ 10 files changed, 198 insertions(+), 9 deletions(-) diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp index 4c762eddcf..896198bfb2 100644 --- a/media/libaudioclient/AudioSystem.cpp +++ b/media/libaudioclient/AudioSystem.cpp @@ -1281,6 +1281,20 @@ status_t AudioSystem::getMasterMono(bool *mono) return aps->getMasterMono(mono); } +status_t AudioSystem::setMasterBalance(float balance) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->setMasterBalance(balance); +} + +status_t AudioSystem::getMasterBalance(float *balance) +{ + const sp& af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + return af->getMasterBalance(balance); +} + float AudioSystem::getStreamVolumeDB(audio_stream_type_t stream, int index, audio_devices_t device) { const sp& aps = AudioSystem::get_audio_policy_service(); diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp index 00678c2371..825cd4ec3d 100644 --- a/media/libaudioclient/IAudioFlinger.cpp +++ b/media/libaudioclient/IAudioFlinger.cpp @@ -87,6 +87,8 @@ enum { SYSTEM_READY, FRAME_COUNT_HAL, GET_MICROPHONES, + SET_MASTER_BALANCE, + GET_MASTER_BALANCE, }; #define MAX_ITEMS_PER_LIST 1024 @@ -242,6 +244,34 @@ public: return reply.readInt32(); } + status_t setMasterBalance(float balance) override + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + data.writeFloat(balance); + status_t status = remote()->transact(SET_MASTER_BALANCE, data, &reply); + if (status != NO_ERROR) { + return status; + } + return reply.readInt32(); + } + + status_t getMasterBalance(float *balance) const override + { + Parcel data, reply; + data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_BALANCE, data, &reply); + if (status != NO_ERROR) { + return status; + } + status = (status_t)reply.readInt32(); + if (status != NO_ERROR) { + return status; + } + *balance = reply.readFloat(); + return NO_ERROR; + } + virtual status_t setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { @@ -1050,6 +1080,21 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( masterMute() ); return NO_ERROR; } break; + case SET_MASTER_BALANCE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + reply->writeInt32( setMasterBalance(data.readFloat()) ); + return NO_ERROR; + } break; + case GET_MASTER_BALANCE: { + CHECK_INTERFACE(IAudioFlinger, data, reply); + float f; + const status_t status = getMasterBalance(&f); + reply->writeInt32((int32_t)status); + if (status == NO_ERROR) { + (void)reply->writeFloat(f); + } + return NO_ERROR; + } break; case SET_STREAM_VOLUME: { CHECK_INTERFACE(IAudioFlinger, data, reply); int stream = data.readInt32(); diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h index a208602a32..1fb7add602 100644 --- a/media/libaudioclient/include/media/AudioSystem.h +++ b/media/libaudioclient/include/media/AudioSystem.h @@ -339,6 +339,9 @@ public: static status_t setMasterMono(bool mono); static status_t getMasterMono(bool *mono); + static status_t setMasterBalance(float balance); + static status_t getMasterBalance(float *balance); + static float getStreamVolumeDB( audio_stream_type_t stream, int index, audio_devices_t device); diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h index a34b207cc6..ef0ed0c560 100644 --- a/media/libaudioclient/include/media/IAudioFlinger.h +++ b/media/libaudioclient/include/media/IAudioFlinger.h @@ -359,6 +359,9 @@ public: virtual float masterVolume() const = 0; virtual bool masterMute() const = 0; + virtual status_t setMasterBalance(float balance) = 0; + virtual status_t getMasterBalance(float *balance) const = 0; + /* set/get stream type state. This will probably be used by * the preference panel, mostly. */ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 0d6ef46b4c..b8307ce1b3 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -897,6 +897,40 @@ status_t AudioFlinger::setMasterVolume(float value) return NO_ERROR; } +status_t AudioFlinger::setMasterBalance(float balance) +{ + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + + // check range + if (isnan(balance) || fabs(balance) > 1.f) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + // short cut. + if (mMasterBalance == balance) return NO_ERROR; + + mMasterBalance = balance; + + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->isDuplicating()) { + continue; + } + mPlaybackThreads.valueAt(i)->setMasterBalance(balance); + } + + return NO_ERROR; +} + status_t AudioFlinger::setMode(audio_mode_t mode) { status_t ret = initCheck(); @@ -1036,6 +1070,13 @@ float AudioFlinger::masterVolume() const return masterVolume_l(); } +status_t AudioFlinger::getMasterBalance(float *balance) const +{ + Mutex::Autolock _l(mLock); + *balance = getMasterBalance_l(); + return NO_ERROR; // if called through binder, may return a transactional error +} + bool AudioFlinger::masterMute() const { Mutex::Autolock _l(mLock); @@ -1047,6 +1088,11 @@ float AudioFlinger::masterVolume_l() const return mMasterVolume; } +float AudioFlinger::getMasterBalance_l() const +{ + return mMasterBalance; +} + bool AudioFlinger::masterMute_l() const { return mMasterMute; diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index c1169d2806..7e3a77547e 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -137,6 +137,10 @@ public: virtual float masterVolume() const; virtual bool masterMute() const; + // Balance value must be within -1.f (left only) to 1.f (right only) inclusive. + status_t setMasterBalance(float balance) override; + status_t getMasterBalance(float *balance) const override; + virtual status_t setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output); virtual status_t setStreamMute(audio_stream_type_t stream, bool muted); @@ -776,6 +780,7 @@ using effect_buffer_t = int16_t; // member variables below are protected by mLock float mMasterVolume; bool mMasterMute; + float mMasterBalance = 0.f; // end of variables protected by mLock DefaultKeyedVector< audio_io_handle_t, sp > mRecordThreads; @@ -793,6 +798,7 @@ using effect_buffer_t = int16_t; Vector mAudioSessionRefs; float masterVolume_l() const; + float getMasterBalance_l() const; bool masterMute_l() const; audio_module_handle_t loadHwModule_l(const char *name); diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index f328577456..7c9e15a0eb 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -60,7 +60,6 @@ FastMixer::FastMixer(audio_io_handle_t parentIoHandle) mSinkChannelCount(FCC_2), mMixerBuffer(NULL), mMixerBufferSize(0), - mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), mMixerBufferState(UNDEFINED), mFormat(Format_Invalid), mSampleRate(0), @@ -161,6 +160,7 @@ void FastMixer::onStateChange() mOutputSink = current->mOutputSink; mOutputSinkGen = current->mOutputSinkGen; mSinkChannelMask = current->mSinkChannelMask; + mBalance.setChannelMask(mSinkChannelMask); if (mOutputSink == NULL) { mFormat = Format_Invalid; mSampleRate = 0; @@ -191,10 +191,6 @@ void FastMixer::onStateChange() free(mSinkBuffer); mSinkBuffer = NULL; if (frameCount > 0 && mSampleRate > 0) { - // The mixer produces either 16 bit PCM or float output, select - // float output if the HAL supports higher than 16 bit precision. - mMixerBufferFormat = mFormat.mFormat == AUDIO_FORMAT_PCM_16_BIT ? - AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT; // FIXME new may block for unbounded time at internal mutex of the heap // implementation; it would be better to have normal mixer allocate for us // to avoid blocking here and to prevent possible priority inversion @@ -471,6 +467,12 @@ void FastMixer::onWork() mono_blend(mMixerBuffer, mMixerBufferFormat, Format_channelCount(mFormat), frameCount, true /*limit*/); } + + // Balance must take effect after mono conversion. + // mBalance detects zero balance within the class for speed (not needed here). + mBalance.setBalance(mMasterBalance.load()); + mBalance.process((float *)mMixerBuffer, frameCount); + // prepare the buffer used to write to sink void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer; if (mFormat.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 1d332e01af..c31d476c48 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIO_FAST_MIXER_H #include +#include #include "FastThread.h" #include "StateQueue.h" #include "FastMixerState.h" @@ -41,6 +42,8 @@ public: FastMixerStateQueue* sq(); virtual void setMasterMono(bool mono) { mMasterMono.store(mono); /* memory_order_seq_cst */ } + virtual void setMasterBalance(float balance) { mMasterBalance.store(balance); } + virtual float getMasterBalance() const { return mMasterBalance.load(); } virtual void setBoottimeOffset(int64_t boottimeOffset) { mBoottimeOffset.store(boottimeOffset); /* memory_order_seq_cst */ } @@ -74,7 +77,7 @@ private: audio_channel_mask_t mSinkChannelMask; void* mMixerBuffer; // mixer output buffer. size_t mMixerBufferSize; - audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + static constexpr audio_format_t mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; uint32_t mAudioChannelCount; // audio channel count, excludes haptic channels. @@ -89,8 +92,11 @@ private: ExtendedTimestamp mTimestamp; int64_t mNativeFramesWrittenButNotPresented; + audio_utils::Balance mBalance; + // accessed without lock between multiple threads. std::atomic_bool mMasterMono; + std::atomic mMasterBalance{}; std::atomic_int_fast64_t mBoottimeOffset; const audio_io_handle_t mThreadIoHandle; // parent thread id for debugging purposes diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 9f838a3db8..1790f112cf 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -2271,6 +2272,11 @@ void AudioFlinger::PlaybackThread::setMasterVolume(float value) } } +void AudioFlinger::PlaybackThread::setMasterBalance(float balance) +{ + mMasterBalance.store(balance); +} + void AudioFlinger::PlaybackThread::setMasterMute(bool muted) { if (isDuplicating()) { @@ -2523,6 +2529,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() mChannelMask); } mChannelCount = audio_channel_count_from_out_mask(mChannelMask); + mBalance.setChannelMask(mChannelMask); // Get actual HAL format. status_t result = mOutput->stream->getFormat(&mHALFormat); @@ -2642,7 +2649,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() free(mMixerBuffer); mMixerBuffer = NULL; if (mMixerBufferEnabled) { - mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // also valid: AUDIO_FORMAT_PCM_16_BIT. + mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // no longer valid: AUDIO_FORMAT_PCM_16_BIT. mMixerBufferSize = mNormalFrameCount * mChannelCount * audio_bytes_per_sample(mMixerBufferFormat); (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); @@ -3531,6 +3538,14 @@ bool AudioFlinger::PlaybackThread::threadLoop() true /*limit*/); } + if (!hasFastMixer()) { + // Balance must take effect after mono conversion. + // We do it here if there is no FastMixer. + // mBalance detects zero balance within the class for speed (not needed here). + mBalance.setBalance(mMasterBalance.load()); + mBalance.process((float *)mMixerBuffer, mNormalFrameCount); + } + memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat, mNormalFrameCount * (mChannelCount + mHapticChannelCount)); @@ -3585,6 +3600,14 @@ bool AudioFlinger::PlaybackThread::threadLoop() true /*limit*/); } + if (!hasFastMixer()) { + // Balance must take effect after mono conversion. + // We do it here if there is no FastMixer. + // mBalance detects zero balance within the class for speed (not needed here). + mBalance.setBalance(mMasterBalance.load()); + mBalance.process((float *)mEffectBuffer, mNormalFrameCount); + } + memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat, mNormalFrameCount * (mChannelCount + mHapticChannelCount)); // The sample data is partially interleaved when haptic channels exist, @@ -3985,6 +4008,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // mPipeSink below // mNormalSink below { + setMasterBalance(audioFlinger->getMasterBalance_l()); ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type); ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%#x, mFrameSize=%zu, " "mFrameCount=%zu, mNormalFrameCount=%zu", @@ -5266,6 +5290,9 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector& ar dprintf(fd, " Thread throttle time (msecs): %u\n", mThreadThrottleTimeMs); dprintf(fd, " AudioMixer tracks: %s\n", mAudioMixer->trackNames().c_str()); dprintf(fd, " Master mono: %s\n", mMasterMono ? "on" : "off"); + dprintf(fd, " Master balance: %f (%s)\n", mMasterBalance.load(), + (hasFastMixer() ? std::to_string(mFastMixer->getMasterBalance()) + : mBalance.toString()).c_str()); const double latencyMs = mTimestamp.getOutputServerLatencyMs(mSampleRate); if (latencyMs != 0.) { dprintf(fd, " NormalMixer latency ms: %.2lf\n", latencyMs); @@ -5333,12 +5360,30 @@ AudioFlinger::DirectOutputThread::DirectOutputThread(const sp& aud ThreadBase::type_t type, bool systemReady) : PlaybackThread(audioFlinger, output, id, device, type, systemReady) { + setMasterBalance(audioFlinger->getMasterBalance_l()); } AudioFlinger::DirectOutputThread::~DirectOutputThread() { } +void AudioFlinger::DirectOutputThread::dumpInternals(int fd, const Vector& args) +{ + PlaybackThread::dumpInternals(fd, args); + dprintf(fd, " Master balance: %f Left: %f Right: %f\n", + mMasterBalance.load(), mMasterBalanceLeft, mMasterBalanceRight); +} + +void AudioFlinger::DirectOutputThread::setMasterBalance(float balance) +{ + Mutex::Autolock _l(mLock); + if (mMasterBalance != balance) { + mMasterBalance.store(balance); + mBalance.computeStereoBalance(balance, &mMasterBalanceLeft, &mMasterBalanceRight); + broadcast_l(); + } +} + void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack) { float left, right; @@ -5362,12 +5407,12 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr if (left > GAIN_FLOAT_UNITY) { left = GAIN_FLOAT_UNITY; } - left *= v; + left *= v * mMasterBalanceLeft; // DirectOutputThread balance applied as track volume right = float_from_gain(gain_minifloat_unpack_right(vlr)); if (right > GAIN_FLOAT_UNITY) { right = GAIN_FLOAT_UNITY; } - right *= v; + right *= v * mMasterBalanceRight; } if (lastTrack) { diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 8b8222c0cd..1131b266bc 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -733,6 +733,7 @@ public: // VolumeInterface virtual void setMasterVolume(float value); + virtual void setMasterBalance(float balance); virtual void setMasterMute(bool muted); virtual void setStreamVolume(audio_stream_type_t stream, float value); virtual void setStreamMute(audio_stream_type_t stream, bool muted); @@ -1027,6 +1028,8 @@ private: AudioStreamOut *mOutput; float mMasterVolume; + std::atomic mMasterBalance{}; + audio_utils::Balance mBalance; nsecs_t mLastWriteTime; int mNumWrites; int mNumDelayedWrites; @@ -1199,6 +1202,13 @@ protected: // Blending with limiter is not idempotent, // and blending without limiter is idempotent but inefficient to do twice. virtual bool requireMonoBlend() { return mMasterMono.load() && !hasFastMixer(); } + + void setMasterBalance(float balance) override { + mMasterBalance.store(balance); + if (hasFastMixer()) { + mFastMixer->setMasterBalance(balance); + } + } }; class DirectOutputThread : public PlaybackThread { @@ -1216,8 +1226,13 @@ public: virtual bool checkForNewParameter_l(const String8& keyValuePair, status_t& status); + + void dumpInternals(int fd, const Vector& args) override; + virtual void flushHw_l(); + void setMasterBalance(float balance) override; + protected: virtual uint32_t activeSleepTimeUs() const; virtual uint32_t idleSleepTimeUs() const; @@ -1245,6 +1260,10 @@ protected: wp mPreviousTrack; // used to detect track switch + // This must be initialized for initial condition of mMasterBalance = 0 (disabled). + float mMasterBalanceLeft = 1.f; + float mMasterBalanceRight = 1.f; + public: virtual bool hasFastMixer() const { return false; }