1 /* 2 * Copyright (C) 2023 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 <android-base/stringprintf.h> 20 #include <android-base/thread_annotations.h> 21 #include <audio_utils/SimpleLog.h> 22 #include <chrono> 23 #include <math.h> 24 #include <mutex> 25 #include <vector> 26 27 namespace android::media { 28 29 /** 30 * VectorRecorder records a vector of floats computing the average, max, and min 31 * over given time periods. 32 * 33 * The class is thread-safe. 34 */ 35 class VectorRecorder { 36 public: 37 /** 38 * @param vectorSize is the size of the vector input. 39 * If the input does not match this size, it is ignored. 40 * @param threshold is the time interval we bucket for averaging. 41 * @param maxLogLine is the number of lines we log. At this 42 * threshold, the oldest line will expire when the new line comes in. 43 * @param delimiterIdx is an optional array of delimiter indices that 44 * replace the ',' with a ':'. For example if delimiterIdx = { 3 } then 45 * the above example would format as [0.00, 0.00, 0.00 : -1.29, -0.50, 15.27]. 46 * @param formatString is the sprintf format string for the double converted data 47 * to use. 48 */ 49 VectorRecorder( 50 size_t vectorSize, std::chrono::duration<double> threshold, int maxLogLine, 51 std::vector<size_t> delimiterIdx = {}, 52 const std::string_view formatString = {}) mVectorSize(vectorSize)53 : mVectorSize(vectorSize) 54 , mDelimiterIdx(std::move(delimiterIdx)) 55 , mFormatString(formatString) 56 , mRecordLog(maxLogLine) 57 , mRecordThreshold(threshold) 58 { 59 resetRecord_l(); // OK to call - we're in the constructor. 60 } 61 62 /** Convert recorded vector data to string with level indentation */ 63 std::string toString(size_t indent) const; 64 65 /** 66 * @brief Record a vector of floats. 67 * 68 * @param record a vector of floats. 69 */ 70 void record(const std::vector<float>& record); 71 72 /** 73 * Format vector to a string, [0.00, 0.00, 0.00, -1.29, -0.50, 15.27]. 74 * 75 * @param delimiterIdx is an optional array of delimiter indices that 76 * replace the ',' with a ':'. For example if delimiterIdx = { 3 } then 77 * the above example would format as [0.00, 0.00, 0.00 : -1.29, -0.50, 15.27]. 78 * @param formatString is the sprintf format string for the double converted data 79 * to use. 80 */ 81 template <typename T> 82 static std::string toString(const std::vector<T>& record, 83 const std::vector<size_t>& delimiterIdx = {}, 84 const char * const formatString = nullptr) { 85 if (record.size() == 0) { 86 return "[]"; 87 } 88 89 std::string ss = "["; 90 auto nextDelimiter = delimiterIdx.begin(); 91 for (size_t i = 0; i < record.size(); ++i) { 92 if (i > 0) { 93 if (nextDelimiter != delimiterIdx.end() 94 && *nextDelimiter <= i) { 95 ss.append(" : "); 96 ++nextDelimiter; 97 } else { 98 ss.append(", "); 99 } 100 } 101 if (formatString != nullptr && *formatString) { 102 base::StringAppendF(&ss, formatString, static_cast<double>(record[i])); 103 } else { 104 base::StringAppendF(&ss, "%5.2lf", static_cast<double>(record[i])); 105 } 106 } 107 ss.append("]"); 108 return ss; 109 } 110 111 private: 112 static constexpr int mMaxLocalLogLine = 10; 113 114 const size_t mVectorSize; 115 const std::vector<size_t> mDelimiterIdx; 116 const std::string mFormatString; 117 118 // Local log for historical vector data. 119 // Locked internally, so does not need mutex below. 120 SimpleLog mRecordLog{mMaxLocalLogLine}; 121 122 std::mutex mLock; 123 124 // Time threshold to record vectors in the local log. 125 // Vector data will be recorded into log at least every mRecordThreshold. 126 std::chrono::duration<double> mRecordThreshold GUARDED_BY(mLock); 127 128 // Number of seconds since first sample in mSum. 129 std::chrono::duration<double> mNumberOfSecondsSinceFirstSample GUARDED_BY(mLock); 130 131 // Timestamp of first sample recorded in mSum. 132 std::chrono::time_point<std::chrono::steady_clock> mFirstSampleTimestamp GUARDED_BY(mLock); 133 134 // Number of samples in mSum. 135 size_t mNumberOfSamples GUARDED_BY(mLock) = 0; 136 137 std::vector<double> mSum GUARDED_BY(mLock); 138 std::vector<float> mMax GUARDED_BY(mLock); 139 std::vector<float> mMin GUARDED_BY(mLock); 140 141 // Computes mNumberOfSecondsSinceFirstSample, returns true if time to record. 142 bool shouldRecordLog_l() REQUIRES(mLock); 143 144 // Resets the running mNumberOfSamples, mSum, mMax, mMin. 145 void resetRecord_l() REQUIRES(mLock); 146 147 // Convert mSum to an average. 148 void sumToAverage_l() REQUIRES(mLock); 149 }; // VectorRecorder 150 151 } // namespace android::media 152