diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index bb8d76f8fc..af12e75e8c 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -46,11 +46,20 @@ cc_library { "SoundDose.cpp", ], shared_libs: [ + "libaudio_aidl_conversion_common_ndk", + "libaudioutils", "libbase", "libbinder_ndk", "libcutils", "libutils", ], + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wthread-safety", + "-DBACKEND_NDK", + ], visibility: [ "//hardware/interfaces/audio/aidl/sounddose/default", ], @@ -111,8 +120,12 @@ cc_library { shared_libs: [ "android.hardware.bluetooth.audio-V3-ndk", "libaudio_aidl_conversion_common_ndk", + "libaudioutils", + "libaudioutils_nonvndk", "libbluetooth_audio_session_aidl", + "liblog", "libmedia_helper", + "libmediautils_vendor", "libstagefright_foundation", ], export_shared_lib_headers: [ @@ -143,8 +156,10 @@ cc_binary { ], shared_libs: [ "android.hardware.bluetooth.audio-V3-ndk", + "libaudioutils_nonvndk", "libaudio_aidl_conversion_common_ndk", "libbluetooth_audio_session_aidl", + "liblog", "libmedia_helper", "libstagefright_foundation", ], diff --git a/audio/aidl/default/Module.cpp b/audio/aidl/default/Module.cpp index d721b32d83..9fc99d4f8c 100644 --- a/audio/aidl/default/Module.cpp +++ b/audio/aidl/default/Module.cpp @@ -213,6 +213,11 @@ ndk::ScopedAStatus Module::createStreamContext( StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs, mVendorDebug.forceTransientBurst, mVendorDebug.forceSynchronousDrain}; + std::shared_ptr soundDose; + if (!getSoundDose(&soundDose).isOk()) { + LOG(ERROR) << __func__ << ": could not create sound dose instance"; + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); + } StreamContext temp( std::make_unique(1, true /*configureEventFlagWord*/), std::make_unique(1, true /*configureEventFlagWord*/), @@ -220,7 +225,7 @@ ndk::ScopedAStatus Module::createStreamContext( portConfigIt->channelMask.value(), portConfigIt->sampleRate.value().value, flags, portConfigIt->ext.get().handle, std::make_unique(frameSize * in_bufferSizeFrames), - asyncCallback, outEventCallback, params); + asyncCallback, outEventCallback, mSoundDose.getInstance(), params); if (temp.isValid()) { *out_context = std::move(temp); } else { diff --git a/audio/aidl/default/SoundDose.cpp b/audio/aidl/default/SoundDose.cpp index f12ce5d806..1c9e081353 100644 --- a/audio/aidl/default/SoundDose.cpp +++ b/audio/aidl/default/SoundDose.cpp @@ -18,7 +18,15 @@ #include "core-impl/SoundDose.h" +#include #include +#include +#include + +using aidl::android::hardware::audio::core::sounddose::ISoundDose; +using aidl::android::media::audio::common::AudioDevice; +using aidl::android::media::audio::common::AudioDeviceDescription; +using aidl::android::media::audio::common::AudioFormatDescription; namespace aidl::android::hardware::audio::core::sounddose { @@ -28,11 +36,16 @@ ndk::ScopedAStatus SoundDose::setOutputRs2UpperBound(float in_rs2ValueDbA) { return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + ::android::audio_utils::lock_guard l(mMutex); mRs2Value = in_rs2ValueDbA; + if (mMelProcessor != nullptr) { + mMelProcessor->setOutputRs2UpperBound(in_rs2ValueDbA); + } return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus SoundDose::getOutputRs2UpperBound(float* _aidl_return) { + ::android::audio_utils::lock_guard l(mMutex); *_aidl_return = mRs2Value; LOG(DEBUG) << __func__ << ": returning " << *_aidl_return; return ndk::ScopedAStatus::ok(); @@ -44,6 +57,8 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback( LOG(ERROR) << __func__ << ": Callback is nullptr"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); } + + ::android::audio_utils::lock_guard l(mCbMutex); if (mCallback != nullptr) { LOG(ERROR) << __func__ << ": Sound dose callback was already registered"; return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE); @@ -51,7 +66,81 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback( mCallback = in_callback; LOG(DEBUG) << __func__ << ": Registered sound dose callback "; + return ndk::ScopedAStatus::ok(); } +void SoundDose::setAudioDevice(const AudioDevice& audioDevice) { + ::android::audio_utils::lock_guard l(mCbMutex); + mAudioDevice = audioDevice; +} + +void SoundDose::startDataProcessor(uint32_t sampleRate, uint32_t channelCount, + const AudioFormatDescription& aidlFormat) { + ::android::audio_utils::lock_guard l(mMutex); + const auto result = aidl2legacy_AudioFormatDescription_audio_format_t(aidlFormat); + const audio_format_t format = result.value_or(AUDIO_FORMAT_INVALID); + + if (mMelProcessor == nullptr) { + // we don't have the deviceId concept on the vendor side so just pass 0 + mMelProcessor = ::android::sp<::android::audio_utils::MelProcessor>::make( + sampleRate, channelCount, format, mMelCallback, /*deviceId=*/0, mRs2Value); + } else { + mMelProcessor->updateAudioFormat(sampleRate, channelCount, format); + } +} + +void SoundDose::process(const void* buffer, size_t bytes) { + ::android::audio_utils::lock_guard l(mMutex); + if (mMelProcessor != nullptr) { + mMelProcessor->process(buffer, bytes); + } +} + +void SoundDose::onNewMelValues(const std::vector& mels, size_t offset, size_t length, + audio_port_handle_t deviceId __attribute__((__unused__))) const { + ::android::audio_utils::lock_guard l(mCbMutex); + if (!mAudioDevice.has_value()) { + LOG(WARNING) << __func__ << ": New mel values without a registered device"; + return; + } + if (mCallback == nullptr) { + LOG(ERROR) << __func__ << ": New mel values without a registered callback"; + return; + } + + ISoundDose::IHalSoundDoseCallback::MelRecord melRecord; + melRecord.timestamp = nanoseconds_to_seconds(systemTime()); + melRecord.melValues = std::vector(mels.begin() + offset, mels.begin() + offset + length); + + mCallback->onNewMelValues(melRecord, mAudioDevice.value()); +} + +void SoundDose::MelCallback::onNewMelValues(const std::vector& mels, size_t offset, + size_t length, + audio_port_handle_t deviceId + __attribute__((__unused__))) const { + mSoundDose.onNewMelValues(mels, offset, length, deviceId); +} + +void SoundDose::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId + __attribute__((__unused__))) const { + ::android::audio_utils::lock_guard l(mCbMutex); + if (!mAudioDevice.has_value()) { + LOG(WARNING) << __func__ << ": Momentary exposure without a registered device"; + return; + } + if (mCallback == nullptr) { + LOG(ERROR) << __func__ << ": Momentary exposure without a registered callback"; + return; + } + + mCallback->onMomentaryExposureWarning(currentMel, mAudioDevice.value()); +} + +void SoundDose::MelCallback::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId + __attribute__((__unused__))) const { + mSoundDose.onMomentaryExposure(currentMel, deviceId); +} + } // namespace aidl::android::hardware::audio::core::sounddose diff --git a/audio/aidl/default/Stream.cpp b/audio/aidl/default/Stream.cpp index f7298c0286..f00e35833a 100644 --- a/audio/aidl/default/Stream.cpp +++ b/audio/aidl/default/Stream.cpp @@ -90,6 +90,14 @@ bool StreamContext::isValid() const { return true; } +void StreamContext::startStreamDataProcessor() { + auto streamDataProcessor = mStreamDataProcessor.lock(); + if (streamDataProcessor != nullptr) { + streamDataProcessor->startDataProcessor(mSampleRate, getChannelCount(mChannelLayout), + mFormat); + } +} + void StreamContext::reset() { mCommandMQ.reset(); mReplyMQ.reset(); @@ -593,6 +601,10 @@ bool StreamOutWorkerLogic::write(size_t clientSize, StreamDescriptor::Reply* rep fatal = true; LOG(ERROR) << __func__ << ": write failed: " << status; } + auto streamDataProcessor = mContext->getStreamDataProcessor().lock(); + if (streamDataProcessor != nullptr) { + streamDataProcessor->process(mDataBuffer.get(), actualFrameCount * frameSize); + } } else { if (mContext->getAsyncCallback() == nullptr) { usleep(3000); // Simulate blocking transfer delay. diff --git a/audio/aidl/default/include/core-impl/Module.h b/audio/aidl/default/include/core-impl/Module.h index da94815722..f407e25bf7 100644 --- a/audio/aidl/default/include/core-impl/Module.h +++ b/audio/aidl/default/include/core-impl/Module.h @@ -157,7 +157,7 @@ class Module : public BnModule { bool mMicMute = false; bool mMasterMute = false; float mMasterVolume = 1.0f; - ChildInterface mSoundDose; + ChildInterface mSoundDose; std::optional mIsMmapSupported; protected: diff --git a/audio/aidl/default/include/core-impl/SoundDose.h b/audio/aidl/default/include/core-impl/SoundDose.h index 2a069d9bac..82c1077f1b 100644 --- a/audio/aidl/default/include/core-impl/SoundDose.h +++ b/audio/aidl/default/include/core-impl/SoundDose.h @@ -20,23 +20,68 @@ #include #include - -using aidl::android::media::audio::common::AudioDevice; +#include +#include +#include namespace aidl::android::hardware::audio::core::sounddose { -class SoundDose : public BnSoundDose { +// Interface used for processing the data received by a stream. +class StreamDataProcessorInterface { public: - SoundDose() : mRs2Value(DEFAULT_MAX_RS2){}; + virtual ~StreamDataProcessorInterface() = default; + virtual void startDataProcessor( + uint32_t samplerate, uint32_t channelCount, + const ::aidl::android::media::audio::common::AudioFormatDescription& format) = 0; + virtual void setAudioDevice( + const ::aidl::android::media::audio::common::AudioDevice& audioDevice) = 0; + virtual void process(const void* buffer, size_t size) = 0; +}; + +class SoundDose final : public BnSoundDose, public StreamDataProcessorInterface { + public: + SoundDose() : mMelCallback(::android::sp::make(this)){}; + + // -------------------------------------- BnSoundDose ------------------------------------------ ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override; ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override; ndk::ScopedAStatus registerSoundDoseCallback( const std::shared_ptr& in_callback) override; + // ----------------------------- StreamDataProcessorInterface ---------------------------------- + void setAudioDevice( + const ::aidl::android::media::audio::common::AudioDevice& audioDevice) override; + void startDataProcessor( + uint32_t samplerate, uint32_t channelCount, + const ::aidl::android::media::audio::common::AudioFormatDescription& format) override; + void process(const void* buffer, size_t size) override; + private: - std::shared_ptr mCallback; - float mRs2Value; + class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback { + public: + explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {} + + // ------------------------------------ MelCallback ---------------------------------------- + void onNewMelValues(const std::vector& mels, size_t offset, size_t length, + audio_port_handle_t deviceId) const override; + void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override; + + SoundDose& mSoundDose; // must outlive MelCallback, not owning + }; + + void onNewMelValues(const std::vector& mels, size_t offset, size_t length, + audio_port_handle_t deviceId) const; + void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const; + + mutable ::android::audio_utils::mutex mCbMutex; + std::shared_ptr mCallback GUARDED_BY(mCbMutex); + std::optional<::aidl::android::media::audio::common::AudioDevice> mAudioDevice + GUARDED_BY(mCbMutex); + mutable ::android::audio_utils::mutex mMutex; + float mRs2Value GUARDED_BY(mMutex) = DEFAULT_MAX_RS2; + ::android::sp<::android::audio_utils::MelProcessor> mMelProcessor GUARDED_BY(mMutex); + ::android::sp mMelCallback GUARDED_BY(mMutex); }; } // namespace aidl::android::hardware::audio::core::sounddose diff --git a/audio/aidl/default/include/core-impl/Stream.h b/audio/aidl/default/include/core-impl/Stream.h index 88fddec233..daa920d9b5 100644 --- a/audio/aidl/default/include/core-impl/Stream.h +++ b/audio/aidl/default/include/core-impl/Stream.h @@ -44,6 +44,7 @@ #include #include "core-impl/ChildInterface.h" +#include "core-impl/SoundDose.h" #include "core-impl/utils.h" namespace aidl::android::hardware::audio::core { @@ -87,6 +88,7 @@ class StreamContext { int32_t mixPortHandle, std::unique_ptr dataMQ, std::shared_ptr asyncCallback, std::shared_ptr outEventCallback, + std::weak_ptr streamDataProcessor, DebugParameters debugParameters) : mCommandMQ(std::move(commandMQ)), mInternalCommandCookie(std::rand()), @@ -100,6 +102,7 @@ class StreamContext { mDataMQ(std::move(dataMQ)), mAsyncCallback(asyncCallback), mOutEventCallback(outEventCallback), + mStreamDataProcessor(streamDataProcessor), mDebugParameters(debugParameters) {} StreamContext(StreamContext&& other) : mCommandMQ(std::move(other.mCommandMQ)), @@ -114,6 +117,7 @@ class StreamContext { mDataMQ(std::move(other.mDataMQ)), mAsyncCallback(std::move(other.mAsyncCallback)), mOutEventCallback(std::move(other.mOutEventCallback)), + mStreamDataProcessor(std::move(other.mStreamDataProcessor)), mDebugParameters(std::move(other.mDebugParameters)), mFrameCount(other.mFrameCount) {} StreamContext& operator=(StreamContext&& other) { @@ -129,6 +133,7 @@ class StreamContext { mDataMQ = std::move(other.mDataMQ); mAsyncCallback = std::move(other.mAsyncCallback); mOutEventCallback = std::move(other.mOutEventCallback); + mStreamDataProcessor = std::move(other.mStreamDataProcessor); mDebugParameters = std::move(other.mDebugParameters); mFrameCount = other.mFrameCount; return *this; @@ -154,6 +159,10 @@ class StreamContext { std::shared_ptr getOutEventCallback() const { return mOutEventCallback; } + std::weak_ptr getStreamDataProcessor() const { + return mStreamDataProcessor; + } + void startStreamDataProcessor(); int getPortId() const { return mPortId; } ReplyMQ* getReplyMQ() const { return mReplyMQ.get(); } int getTransientStateDelayMs() const { return mDebugParameters.transientStateDelayMs; } @@ -179,6 +188,7 @@ class StreamContext { std::unique_ptr mDataMQ; std::shared_ptr mAsyncCallback; std::shared_ptr mOutEventCallback; // Only used by output streams + std::weak_ptr mStreamDataProcessor; DebugParameters mDebugParameters; long mFrameCount = 0; }; diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h index b3ddd0bd53..b64b749ec5 100644 --- a/audio/aidl/default/include/core-impl/StreamPrimary.h +++ b/audio/aidl/default/include/core-impl/StreamPrimary.h @@ -79,6 +79,10 @@ class StreamOutPrimary final : public StreamOut, ndk::ScopedAStatus getHwVolume(std::vector* _aidl_return) override; ndk::ScopedAStatus setHwVolume(const std::vector& in_channelVolumes) override; + + ndk::ScopedAStatus setConnectedDevices( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) + override; }; } // namespace aidl::android::hardware::audio::core diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp index e01be8a3c6..17de2baf7a 100644 --- a/audio/aidl/default/primary/StreamPrimary.cpp +++ b/audio/aidl/default/primary/StreamPrimary.cpp @@ -37,7 +37,9 @@ using android::base::GetBoolProperty; namespace aidl::android::hardware::audio::core { StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata) - : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) {} + : StreamAlsa(context, metadata, 3 /*readWriteRetries*/), mIsInput(isInput(metadata)) { + context->startStreamDataProcessor(); +} std::vector StreamPrimary::getDeviceProfiles() { static const std::vector kBuiltInSource{ @@ -183,4 +185,15 @@ ndk::ScopedAStatus StreamOutPrimary::setHwVolume(const std::vector& in_ch return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus StreamOutPrimary::setConnectedDevices( + const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) { + if (!devices.empty()) { + auto streamDataProcessor = mContextInstance.getStreamDataProcessor().lock(); + if (streamDataProcessor != nullptr) { + streamDataProcessor->setAudioDevice(devices[0]); + } + } + return StreamSwitcher::setConnectedDevices(devices); +} + } // namespace aidl::android::hardware::audio::core