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 EXCLUDES(mLock); 118 119 /** 120 * \brief Computes the MEL values for the given buffer and triggers a 121 * callback with time-continuous MEL values when: MEL buffer is full or if 122 * there is a discontinue in MEL calculation (e.g.: MEL is under RS1) 123 * 124 * \param buffer pointer to the audio data buffer. 125 * \param bytes buffer size in bytes. 126 * 127 * \return the number of bytes that were processed. Note: the method will 128 * output 0 if the processor is paused or the sample rate is not supported. 129 */ 130 int32_t process(const void* buffer, size_t bytes); 131 132 /** 133 * Pauses the processing of MEL values. Process calls after this will be 134 * ignored until resume. 135 */ 136 void pause(); 137 138 /** Resumes the processing of MEL values. */ 139 void resume(); 140 141 /** Signals to drain the remaining mel data. Does not wait. */ 142 void drain(); 143 144 /** Signals to drain the remaining mel data and waits for completion. 145 * If more data is being delivered, wait time may be long. */ 146 void drainAndWait(); 147 148 /** 149 * Sets the given attenuation for the MEL calculation. This can be used when 150 * the audio framework is operating in absolute volume mode. 151 * 152 * @param attenuationDB attenuation to use on computed MEL values 153 */ 154 void setAttenuation(float attenuationDB); 155 156 void onLastStrongRef(const void* id) override; 157 158 private: 159 /** Struct to store the possible callback data. */ 160 struct MelCallbackData { 161 // used for momentaryExposure callback 162 float mMel = 0.f; 163 // used for newMelValues callback 164 std::vector<float> mMels = std::vector<float>(kMaxMelValues); 165 // represents the number of valid MEL values in mMels 166 size_t mMelsSize = 0; 167 // port of deviceId for this callback 168 audio_port_handle_t mPort = AUDIO_PORT_HANDLE_NONE; 169 }; 170 171 // class used to asynchronously execute all MelProcessor callbacks 172 class MelWorker { 173 public: 174 static constexpr int kRingBufferSize = 32; 175 MelWorker(std::string threadName,const wp<MelCallback> & callback)176 MelWorker(std::string threadName, const wp<MelCallback>& callback) 177 : mCallback(callback), 178 mThreadName(std::move(threadName)), 179 mCallbackRingBuffer(kRingBufferSize) {}; 180 181 void run() EXCLUDES(mCondVarMutex); 182 183 // blocks until the MelWorker thread is stopped 184 void stop() EXCLUDES(mCondVarMutex); 185 186 // Signals the MelWorker to wake up to process 187 // any remaining queued data. Like notify() but with lock held. 188 void drain() EXCLUDES(mCondVarMutex); 189 190 // callback methods for new MEL values 191 void momentaryExposure(float mel, audio_port_handle_t port); 192 void newMelValues(const std::vector<float>& mels, 193 size_t melsSize, 194 audio_port_handle_t port); 195 getThreadName()196 std::string getThreadName() const { return mThreadName; } 197 notify()198 void notify() { mCondVar.notify_one(); } 199 ringBufferIsEmpty()200 bool ringBufferIsEmpty() const { return mRbReadPtr == mRbWritePtr; } 201 202 private: nextRingBufferIndex(size_t idx)203 static size_t nextRingBufferIndex(size_t idx) { 204 return idx >= kRingBufferSize - 1 ? 0 : idx + 1; 205 } 206 bool ringBufferIsFull() const; 207 208 const wp<MelCallback> mCallback; 209 const std::string mThreadName; 210 std::mutex mCondVarMutex; 211 std::condition_variable mCondVar; 212 213 // mRbReadPtr, mRbWritePtr, mCallbackRingBuffer form a lock free queue. 214 std::vector<MelCallbackData> mCallbackRingBuffer; // reader / writer on different indices 215 216 // reader updated only, aligned to cache line. 217 alignas(64 /* std::hardware_destructive_interference_size */) 218 std::atomic_size_t mRbReadPtr = 0; 219 220 // writer updated only, aligned to cache line. 221 alignas(64 /* std::hardware_destructive_interference_size */) 222 std::atomic_size_t mRbWritePtr = 0; 223 224 bool mStopRequested GUARDED_BY(mCondVarMutex) = false; 225 std::thread mThread; 226 }; 227 228 std::string pointerString() const; 229 void createBiquads_l() REQUIRES(mLock); 230 bool isSampleRateSupported_l() const REQUIRES(mLock); 231 void applyAWeight_l(const void* buffer, size_t frames) REQUIRES(mLock); 232 float getCombinedChannelEnergy_l() REQUIRES(mLock); 233 void addMelValue_l(float mel) REQUIRES(mLock); 234 235 const wp<MelCallback> mCallback; // callback to notify about new MEL values 236 // and momentary exposure warning 237 // does not own the callback, must outlive 238 239 MelWorker mMelWorker; // spawns thread for asynchronous callbacks, 240 // worker is thread-safe 241 242 mutable std::mutex mLock; // monitor mutex 243 // audio data sample rate 244 uint32_t mSampleRate GUARDED_BY(mLock); 245 // number of audio frames per MEL value 246 size_t mFramesPerMelValue GUARDED_BY(mLock); 247 // audio data channel count 248 uint32_t mChannelCount GUARDED_BY(mLock); 249 // audio data format 250 audio_format_t mFormat GUARDED_BY(mLock); 251 // number of samples in the energy 252 size_t mCurrentSamples GUARDED_BY(mLock) = 0; 253 // contains the A-weighted input samples to be processed 254 std::vector<float> mAWeightSamples GUARDED_BY(mLock); 255 // contains the input samples converted to float 256 std::vector<float> mFloatSamples GUARDED_BY(mLock); 257 // local energy accumulation 258 std::vector<float> mCurrentChannelEnergy GUARDED_BY(mLock); 259 // accumulated MEL values 260 std::vector<float> mMelValues GUARDED_BY(mLock); 261 // current index to store the MEL values 262 uint32_t mCurrentIndex GUARDED_BY(mLock) = 0; 263 using DefaultBiquadFilter = BiquadFilter<float, true, details::DefaultBiquadConstOptions>; 264 // Biquads used for the A-weighting 265 std::array<std::unique_ptr<DefaultBiquadFilter>, kCascadeBiquadNumber> 266 mCascadedBiquads GUARDED_BY(mLock); 267 268 std::atomic<float> mAttenuationDB = 0.f; 269 // device id used for the callbacks 270 std::atomic<audio_port_handle_t> mDeviceId; 271 // Value used for momentary exposure 272 std::atomic<float> mRs2UpperBound; 273 // Skip processing data. 274 std::atomic_bool mPaused = false; 275 }; 276 277 } // namespace android::audio_utils 278