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