xref: /aosp_15_r20/system/media/audio_utils/include/audio_utils/MelProcessor.h (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
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