You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
9.4 KiB
248 lines
9.4 KiB
/*
|
|
* Copyright 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
#include <android-base/thread_annotations.h>
|
|
#include <audio_utils/BiquadFilter.h>
|
|
#include <system/audio.h>
|
|
#include <utils/Errors.h>
|
|
#include <utils/RefBase.h>
|
|
|
|
namespace android::audio_utils {
|
|
|
|
/**
|
|
* Class used to capture the MEL (momentary exposure levels) values as defined
|
|
* by IEC62368-1 3rd edition. MELs are computed for each second.
|
|
*/
|
|
class MelProcessor : public RefBase {
|
|
public:
|
|
|
|
static constexpr int kCascadeBiquadNumber = 3;
|
|
/** Should represent the minimal value after which a 1% CSD change can occur. */
|
|
static constexpr int32_t kMaxMelValues = 3;
|
|
|
|
/**
|
|
* An interface through which the MelProcessor client will be notified about
|
|
* important events.
|
|
*/
|
|
class MelCallback : public virtual RefBase {
|
|
public:
|
|
~MelCallback() override = default;
|
|
/**
|
|
* Called with a time-continuous vector of computed MEL values
|
|
*
|
|
* \param mels contains MELs (one per second) with values above RS1.
|
|
* \param offset the offset in mels for new MEL data.
|
|
* \param length the number of valid MEL values in the vector starting at offset. The
|
|
* maximum number of elements in mels is defined in the MelProcessor
|
|
* constructor.
|
|
* \param deviceId id of device where the samples were processed
|
|
*/
|
|
virtual void onNewMelValues(const std::vector<float>& mels,
|
|
size_t offset,
|
|
size_t length,
|
|
audio_port_handle_t deviceId) const = 0;
|
|
|
|
/**
|
|
* Called when the momentary exposure exceeds the RS2 upper bound.
|
|
*
|
|
* Note: RS2 is configurable vie MelProcessor#setOutputRs2UpperBound.
|
|
*/
|
|
virtual void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const = 0;
|
|
};
|
|
|
|
/**
|
|
* \brief Creates a MelProcessor object.
|
|
*
|
|
* \param sampleRate sample rate of the audio data.
|
|
* \param channelCount channel count of the audio data.
|
|
* \param format format of the audio data. It must be allowed by
|
|
* audio_utils_is_compute_mel_format_supported()
|
|
* else the constructor will abort.
|
|
* \param callback reports back the new mel values.
|
|
* \param deviceId the device ID for the MEL callbacks
|
|
* \param rs2Value initial RS2 upper bound to use
|
|
* \param maxMelsCallback the number of max elements a callback can have.
|
|
*/
|
|
MelProcessor(uint32_t sampleRate,
|
|
uint32_t channelCount,
|
|
audio_format_t format,
|
|
const sp<MelCallback>& callback,
|
|
audio_port_handle_t deviceId,
|
|
float rs2Value,
|
|
size_t maxMelsCallback = kMaxMelValues);
|
|
|
|
/**
|
|
* Sets the output RS2 upper bound for momentary exposure warnings. Default value
|
|
* is 100dBA as specified in IEC62368-1 3rd edition. Must not be higher than
|
|
* 100dBA and not lower than 80dBA.
|
|
*
|
|
* \param rs2Value to use for momentary exposure
|
|
* \return NO_ERROR if rs2Value is between 80dBA amd 100dBA or BAD_VALUE
|
|
* otherwise
|
|
*/
|
|
status_t setOutputRs2UpperBound(float rs2Value);
|
|
|
|
/** Returns the RS2 upper bound used for momentary exposures. */
|
|
float getOutputRs2UpperBound() const;
|
|
|
|
/** Updates the device id. */
|
|
void setDeviceId(audio_port_handle_t deviceId);
|
|
|
|
/** Returns the device id. */
|
|
audio_port_handle_t getDeviceId();
|
|
|
|
/** Update the format to use for the input frames to process. */
|
|
void updateAudioFormat(uint32_t sampleRate, uint32_t channelCount, audio_format_t newFormat);
|
|
|
|
/**
|
|
* \brief Computes the MEL values for the given buffer and triggers a
|
|
* callback with time-continuous MEL values when: MEL buffer is full or if
|
|
* there is a discontinue in MEL calculation (e.g.: MEL is under RS1)
|
|
*
|
|
* \param buffer pointer to the audio data buffer.
|
|
* \param bytes buffer size in bytes.
|
|
*
|
|
* \return the number of bytes that were processed. Note: the method will
|
|
* output 0 if the processor is paused or the sample rate is not supported.
|
|
*/
|
|
int32_t process(const void* buffer, size_t bytes);
|
|
|
|
/**
|
|
* Pauses the processing of MEL values. Process calls after this will be
|
|
* ignored until resume.
|
|
*/
|
|
void pause();
|
|
|
|
/** Resumes the processing of MEL values. */
|
|
void resume();
|
|
|
|
/**
|
|
* Sets the given attenuation for the MEL calculation. This can be used when
|
|
* the audio framework is operating in absolute volume mode.
|
|
*
|
|
* @param attenuationDB attenuation to use on computed MEL values
|
|
*/
|
|
void setAttenuation(float attenuationDB);
|
|
|
|
void onLastStrongRef(const void* id) override;
|
|
|
|
private:
|
|
/** Struct to store the possible callback data. */
|
|
struct MelCallbackData {
|
|
// used for momentaryExposure callback
|
|
float mMel = 0.f;
|
|
// used for newMelValues callback
|
|
std::vector<float> mMels = std::vector<float>(kMaxMelValues);
|
|
// represents the number of valid MEL values in mMels
|
|
size_t mMelsSize = 0;
|
|
// port of deviceId for this callback
|
|
audio_port_handle_t mPort = AUDIO_PORT_HANDLE_NONE;
|
|
};
|
|
|
|
// class used to asynchronously execute all MelProcessor callbacks
|
|
class MelWorker {
|
|
public:
|
|
static constexpr int kRingBufferSize = 32;
|
|
|
|
MelWorker(std::string threadName, const wp<MelCallback>& callback)
|
|
: mCallback(callback),
|
|
mThreadName(std::move(threadName)),
|
|
mCallbackRingBuffer(kRingBufferSize) {};
|
|
|
|
void run();
|
|
|
|
// blocks until the MelWorker thread is stopped
|
|
void stop();
|
|
|
|
// callback methods for new MEL values
|
|
void momentaryExposure(float mel, audio_port_handle_t port);
|
|
void newMelValues(const std::vector<float>& mels,
|
|
size_t melsSize,
|
|
audio_port_handle_t port);
|
|
|
|
static void incRingBufferIndex(std::atomic_size_t& idx);
|
|
bool ringBufferIsFull() const;
|
|
|
|
const wp<MelCallback> mCallback;
|
|
const std::string mThreadName;
|
|
std::vector<MelCallbackData> mCallbackRingBuffer GUARDED_BY(mCondVarMutex);
|
|
|
|
std::atomic_size_t mRbReadPtr = 0;
|
|
std::atomic_size_t mRbWritePtr = 0;
|
|
|
|
std::thread mThread;
|
|
std::condition_variable mCondVar;
|
|
std::mutex mCondVarMutex;
|
|
bool mStopRequested GUARDED_BY(mCondVarMutex) = false;
|
|
};
|
|
|
|
std::string pointerString() const;
|
|
void createBiquads_l() REQUIRES(mLock);
|
|
bool isSampleRateSupported_l() const REQUIRES(mLock);
|
|
void applyAWeight_l(const void* buffer, size_t frames) REQUIRES(mLock);
|
|
float getCombinedChannelEnergy_l() REQUIRES(mLock);
|
|
void addMelValue_l(float mel) REQUIRES(mLock);
|
|
|
|
const wp<MelCallback> mCallback; // callback to notify about new MEL values
|
|
// and momentary exposure warning
|
|
// does not own the callback, must outlive
|
|
|
|
MelWorker mMelWorker; // spawns thread for asynchronous callbacks,
|
|
// worker is thread-safe
|
|
|
|
mutable std::mutex mLock; // monitor mutex
|
|
// audio data sample rate
|
|
uint32_t mSampleRate GUARDED_BY(mLock);
|
|
// number of audio frames per MEL value
|
|
size_t mFramesPerMelValue GUARDED_BY(mLock);
|
|
// audio data channel count
|
|
uint32_t mChannelCount GUARDED_BY(mLock);
|
|
// audio data format
|
|
audio_format_t mFormat GUARDED_BY(mLock);
|
|
// contains the A-weighted input samples to be processed
|
|
std::vector<float> mAWeightSamples GUARDED_BY(mLock);
|
|
// contains the input samples converted to float
|
|
std::vector<float> mFloatSamples GUARDED_BY(mLock);
|
|
// local energy accumulation
|
|
std::vector<float> mCurrentChannelEnergy GUARDED_BY(mLock);
|
|
// accumulated MEL values
|
|
std::vector<float> mMelValues GUARDED_BY(mLock);
|
|
// current index to store the MEL values
|
|
uint32_t mCurrentIndex GUARDED_BY(mLock);
|
|
using DefaultBiquadFilter = BiquadFilter<float, true, details::DefaultBiquadConstOptions>;
|
|
// Biquads used for the A-weighting
|
|
std::array<std::unique_ptr<DefaultBiquadFilter>, kCascadeBiquadNumber>
|
|
mCascadedBiquads GUARDED_BY(mLock);
|
|
|
|
std::atomic<float> mAttenuationDB = 0.f;
|
|
// device id used for the callbacks
|
|
std::atomic<audio_port_handle_t> mDeviceId;
|
|
// Value used for momentary exposure
|
|
std::atomic<float> mRs2UpperBound;
|
|
// number of samples in the energy
|
|
std::atomic_size_t mCurrentSamples;
|
|
std::atomic_bool mPaused;
|
|
};
|
|
|
|
} // namespace android::audio_utils
|