1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <array> 20 #include <condition_variable> 21 #include <mutex> 22 #include <thread> 23 24 #include <android-base/thread_annotations.h> 25 #include <audio_utils/BiquadFilter.h> 26 #include <system/audio.h> 27 #include <utils/Errors.h> 28 #include <utils/RefBase.h> 29 30 namespace android::audio_utils { 31 32 /** 33 * Class used to capture the MEL (momentary exposure levels) values as defined 34 * by IEC62368-1 3rd edition. MELs are computed for each second. 35 */ 36 class MelProcessor : public RefBase { 37 public: 38 39 static constexpr int kCascadeBiquadNumber = 3; 40 /** Should represent the minimal value after which a 1% CSD change can occur. */ 41 static constexpr int32_t kMaxMelValues = 3; 42 43 /** 44 * An interface through which the MelProcessor client will be notified about 45 * important events. 46 */ 47 class MelCallback : public virtual RefBase { 48 public: 49 ~MelCallback() override = default; 50 /** 51 * Called with a time-continuous vector of computed MEL values 52 * 53 * \param mels contains MELs (one per second) with values above RS1. 54 * \param offset the offset in mels for new MEL data. 55 * \param length the number of valid MEL values in the vector starting at offset. The 56 * maximum number of elements in mels is defined in the MelProcessor 57 * constructor. 58 * \param deviceId id of device where the samples were processed 59 */ 60 virtual void onNewMelValues(const std::vector<float>& mels, 61 size_t offset, 62 size_t length, 63 audio_port_handle_t deviceId, 64 bool attenuated) const = 0; 65 66 /** 67 * Called when the momentary exposure exceeds the RS2 upper bound. 68 * 69 * Note: RS2 is configurable vie MelProcessor#setOutputRs2UpperBound. 70 */ 71 virtual void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const = 0; 72 }; 73 74 /** 75 * \brief Creates a MelProcessor object. 76 * 77 * \param sampleRate sample rate of the audio data. 78 * \param channelCount channel count of the audio data. 79 * \param format format of the audio data. It must be allowed by 80 * audio_utils_is_compute_mel_format_supported() 81 * else the constructor will abort. 82 * \param callback reports back the new mel values. 83 * \param deviceId the device ID for the MEL callbacks 84 * \param rs2Value initial RS2 upper bound to use 85 * \param maxMelsCallback the number of max elements a callback can have. 86 */ 87 MelProcessor(uint32_t sampleRate, 88 uint32_t channelCount, 89 audio_format_t format, 90 const sp<MelCallback>& callback, 91 audio_port_handle_t deviceId, 92 float rs2Value, 93 size_t maxMelsCallback = kMaxMelValues); 94 95 /** 96 * Sets the output RS2 upper bound for momentary exposure warnings. Default value 97 * is 100dBA as specified in IEC62368-1 3rd edition. Must not be higher than 98 * 100dBA and not lower than 80dBA. 99 * 100 * \param rs2Value to use for momentary exposure 101 * \return NO_ERROR if rs2Value is between 80dBA amd 100dBA or BAD_VALUE 102 * otherwise 103 */ 104 status_t setOutputRs2UpperBound(float rs2Value); 105 106 /** Returns the RS2 upper bound used for momentary exposures. */ 107 float getOutputRs2UpperBound() const; 108 109 /** Updates the device id. */ 110 void setDeviceId(audio_port_handle_t deviceId); 111 112 /** Returns the device id. */ 113 audio_port_handle_t getDeviceId(); 114 115 /** Update the format to use for the input frames to process. */ 116 void updateAudioFormat(uint32_t sampleRate, uint32_t channelCount, audio_format_t newFormat); 117 118 /** 119 * \brief Computes the MEL values for the given buffer and triggers a 120 * callback with time-continuous MEL values when: MEL buffer is full or if 121 * there is a discontinue in MEL calculation (e.g.: MEL is under RS1) 122 * 123 * \param buffer pointer to the audio data buffer. 124 * \param bytes buffer size in bytes. 125 * 126 * \return the number of bytes that were processed. Note: the method will 127 * output 0 if the processor is paused or the sample rate is not supported. 128 */ 129 int32_t process(const void* buffer, size_t bytes); 130 131 /** 132 * Pauses the processing of MEL values. Process calls after this will be 133 * ignored until resume. 134 */ 135 void pause(); 136 137 /** Resumes the processing of MEL values. */ 138 void resume(); 139 140 /** 141 * Sets the given attenuation for the MEL calculation. This can be used when 142 * the audio framework is operating in absolute volume mode. 143 * 144 * @param attenuationDB attenuation to use on computed MEL values 145 */ 146 void setAttenuation(float attenuationDB); 147 148 void onLastStrongRef(const void* id) override; 149 150 private: 151 /** Struct to store the possible callback data. */ 152 struct MelCallbackData { 153 // used for momentaryExposure callback 154 float mMel = 0.f; 155 // used for newMelValues callback 156 std::vector<float> mMels = std::vector<float>(kMaxMelValues); 157 // represents the number of valid MEL values in mMels 158 size_t mMelsSize = 0; 159 // port of deviceId for this callback 160 audio_port_handle_t mPort = AUDIO_PORT_HANDLE_NONE; 161 }; 162 163 // class used to asynchronously execute all MelProcessor callbacks 164 class MelWorker { 165 public: 166 static constexpr int kRingBufferSize = 32; 167 MelWorker(std::string threadName,const wp<MelCallback> & callback)168 MelWorker(std::string threadName, const wp<MelCallback>& callback) 169 : mCallback(callback), 170 mThreadName(std::move(threadName)), 171 mCallbackRingBuffer(kRingBufferSize) {}; 172 173 void run(); 174 175 // blocks until the MelWorker thread is stopped 176 void stop(); 177 178 // callback methods for new MEL values 179 void momentaryExposure(float mel, audio_port_handle_t port); 180 void newMelValues(const std::vector<float>& mels, 181 size_t melsSize, 182 audio_port_handle_t port); 183 184 static void incRingBufferIndex(std::atomic_size_t& idx); 185 bool ringBufferIsFull() const; 186 187 const wp<MelCallback> mCallback; 188 const std::string mThreadName; 189 std::vector<MelCallbackData> mCallbackRingBuffer GUARDED_BY(mCondVarMutex); 190 191 std::atomic_size_t mRbReadPtr = 0; 192 std::atomic_size_t mRbWritePtr = 0; 193 194 std::thread mThread; 195 std::condition_variable mCondVar; 196 std::mutex mCondVarMutex; 197 bool mStopRequested GUARDED_BY(mCondVarMutex) = false; 198 }; 199 200 std::string pointerString() const; 201 void createBiquads_l() REQUIRES(mLock); 202 bool isSampleRateSupported_l() const REQUIRES(mLock); 203 void applyAWeight_l(const void* buffer, size_t frames) REQUIRES(mLock); 204 float getCombinedChannelEnergy_l() REQUIRES(mLock); 205 void addMelValue_l(float mel) REQUIRES(mLock); 206 207 const wp<MelCallback> mCallback; // callback to notify about new MEL values 208 // and momentary exposure warning 209 // does not own the callback, must outlive 210 211 MelWorker mMelWorker; // spawns thread for asynchronous callbacks, 212 // worker is thread-safe 213 214 mutable std::mutex mLock; // monitor mutex 215 // audio data sample rate 216 uint32_t mSampleRate GUARDED_BY(mLock); 217 // number of audio frames per MEL value 218 size_t mFramesPerMelValue GUARDED_BY(mLock); 219 // audio data channel count 220 uint32_t mChannelCount GUARDED_BY(mLock); 221 // audio data format 222 audio_format_t mFormat GUARDED_BY(mLock); 223 // contains the A-weighted input samples to be processed 224 std::vector<float> mAWeightSamples GUARDED_BY(mLock); 225 // contains the input samples converted to float 226 std::vector<float> mFloatSamples GUARDED_BY(mLock); 227 // local energy accumulation 228 std::vector<float> mCurrentChannelEnergy GUARDED_BY(mLock); 229 // accumulated MEL values 230 std::vector<float> mMelValues GUARDED_BY(mLock); 231 // current index to store the MEL values 232 uint32_t mCurrentIndex GUARDED_BY(mLock); 233 using DefaultBiquadFilter = BiquadFilter<float, true, details::DefaultBiquadConstOptions>; 234 // Biquads used for the A-weighting 235 std::array<std::unique_ptr<DefaultBiquadFilter>, kCascadeBiquadNumber> 236 mCascadedBiquads GUARDED_BY(mLock); 237 238 std::atomic<float> mAttenuationDB = 0.f; 239 // device id used for the callbacks 240 std::atomic<audio_port_handle_t> mDeviceId; 241 // Value used for momentary exposure 242 std::atomic<float> mRs2UpperBound; 243 // number of samples in the energy 244 std::atomic_size_t mCurrentSamples; 245 std::atomic_bool mPaused; 246 }; 247 248 } // namespace android::audio_utils 249