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