OSDN Git Service

Make libaudioeffects vendor available.
[android-x86/system-media.git] / audio_utils / PowerLog.cpp
1 /*
2  * Copyright 2017 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 // #define LOG_NDEBUG 0
18 #define LOG_TAG "audio_utils_PowerLog"
19 #include <log/log.h>
20
21 #include <algorithm>
22 #include <iomanip>
23 #include <math.h>
24 #include <sstream>
25 #include <stdint.h>
26 #include <unistd.h>
27
28 #include <audio_utils/clock.h>
29 #include <audio_utils/power.h>
30 #include <audio_utils/PowerLog.h>
31
32 namespace android {
33
34 // TODO move to separate file
35 template <typename T, size_t N>
36 constexpr size_t array_size(const T(&)[N])
37 {
38     return N;
39 }
40
41 PowerLog::PowerLog(uint32_t sampleRate,
42         uint32_t channelCount,
43         audio_format_t format,
44         size_t entries,
45         size_t framesPerEntry)
46     : mCurrentTime(0)
47     , mCurrentEnergy(0)
48     , mCurrentFrames(0)
49     , mIdx(0)
50     , mConsecutiveZeroes(0)
51     , mSampleRate(sampleRate)
52     , mChannelCount(channelCount)
53     , mFormat(format)
54     , mFramesPerEntry(framesPerEntry)
55     , mEntries(entries)
56 {
57     (void)mSampleRate; // currently unused, for future use
58     LOG_ALWAYS_FATAL_IF(!audio_utils_is_compute_power_format_supported(format),
59             "unsupported format: %#x", format);
60 }
61
62 void PowerLog::log(const void *buffer, size_t frames, int64_t nowNs)
63 {
64     std::lock_guard<std::mutex> guard(mLock);
65
66     const size_t bytes_per_sample = audio_bytes_per_sample(mFormat);
67     while (frames > 0) {
68         // check partial computation
69         size_t required = mFramesPerEntry - mCurrentFrames;
70         size_t process = std::min(required, frames);
71
72         if (mCurrentTime == 0) {
73             mCurrentTime = nowNs;
74         }
75         mCurrentEnergy +=
76                 audio_utils_compute_energy_mono(buffer, mFormat, process * mChannelCount);
77         mCurrentFrames += process;
78
79         ALOGV("nowNs:%lld, required:%zu, process:%zu, mCurrentEnergy:%f, mCurrentFrames:%zu",
80                 (long long)nowNs, required, process, mCurrentEnergy, mCurrentFrames);
81         if (process < required) {
82             return;
83         }
84
85         // We store the data as normalized energy per sample. The energy sequence is
86         // zero terminated. Consecutive zeroes are ignored.
87         if (mCurrentEnergy == 0.f) {
88             if (mConsecutiveZeroes++ == 0) {
89                 mEntries[mIdx++] = std::make_pair(nowNs, 0.f);
90                 // zero terminate the signal sequence.
91             }
92         } else {
93             mConsecutiveZeroes = 0;
94             mEntries[mIdx++] = std::make_pair(mCurrentTime, mCurrentEnergy);
95             ALOGV("writing %lld %f", (long long)mCurrentTime, mCurrentEnergy);
96         }
97         if (mIdx >= mEntries.size()) {
98             mIdx -= mEntries.size();
99         }
100         mCurrentTime = 0;
101         mCurrentEnergy = 0;
102         mCurrentFrames = 0;
103         frames -= process;
104         buffer = (const uint8_t *)buffer + mCurrentFrames * mChannelCount * bytes_per_sample;
105     }
106 }
107
108 std::string PowerLog::dumpToString(const char *prefix, size_t lines, int64_t limitNs) const
109 {
110     std::lock_guard<std::mutex> guard(mLock);
111
112     const size_t maxColumns = 10;
113     const size_t numberOfEntries = mEntries.size();
114     if (lines == 0) lines = SIZE_MAX;
115
116     // compute where to start logging
117     enum {
118         AT_END,
119         IN_SIGNAL,
120     } state = IN_SIGNAL;
121     size_t count = 1;
122     size_t column = 0;
123     size_t nonzeros = 0;
124     ssize_t offset; // TODO doesn't dump if # entries exceeds SSIZE_MAX
125     for (offset = 0; offset < (ssize_t)numberOfEntries && count < lines; ++offset) {
126         const size_t idx = (mIdx + numberOfEntries - offset - 1) % numberOfEntries; // reverse direction
127         const int64_t time = mEntries[idx].first;
128         const float energy = mEntries[idx].second;
129
130         if (state == AT_END) {
131             if (energy == 0.f) {
132                 ALOGV("two zeroes detected");
133                 break; // normally single zero terminated - two zeroes means no more data.
134             }
135             state = IN_SIGNAL;
136         } else { // IN_SIGNAL
137             if (energy == 0.f) {
138                 if (column != 0) {
139                     column = 0;
140                     ++count;
141                 }
142                 state = AT_END;
143                 continue;
144             }
145         }
146         if (column == 0 && time < limitNs) {
147             break;
148         }
149         ++nonzeros;
150         if (++column == maxColumns) {
151             column = 0;
152             // TODO ideally we would peek the previous entry to see if it is 0
153             // to ensure we properly put in a starting signal bracket.
154             // We don't do that because it would complicate the logic here.
155             ++count;
156         }
157     }
158     if (offset > 0) {
159         --offset;
160     }
161     // We accumulate the log info into a string, and write to the fd once.
162     std::stringstream ss;
163     ss << std::fixed << std::setprecision(1);
164     // ss << std::scientific;
165     if (nonzeros == 0) {
166         ss << prefix << "Signal power history: (none)\n";
167     } else {
168         ss << prefix << "Signal power history:\n";
169
170         size_t column = 0;
171         bool first = true;
172         bool start = false;
173         float cumulative = 0.f;
174         for (; offset >= 0; --offset) {
175             const size_t idx = (mIdx + numberOfEntries - offset - 1) % numberOfEntries;
176             const int64_t time = mEntries[idx].first;
177             const float energy = mEntries[idx].second;
178
179             if (energy == 0.f) {
180                 if (!first) {
181                     ss << " ] sum(" << audio_utils_power_from_energy(cumulative) << ")";
182                 }
183                 cumulative = 0.f;
184                 column = 0;
185                 start = true;
186                 continue;
187             }
188             if (column == 0) {
189                 // print time if at start of column
190                 if (!first) {
191                     ss << "\n";
192                 }
193                 ss << prefix << " " << audio_utils_time_string_from_ns(time).time
194                         << (start ? ": [ ": ":   ");
195                 first = false;
196                 start = false;
197             }  else {
198                 ss << " ";
199             }
200             if (++column >= maxColumns) {
201                 column = 0;
202             }
203
204             cumulative += energy;
205             // convert energy to power and print
206             const float power =
207                     audio_utils_power_from_energy(energy / (mChannelCount * mFramesPerEntry));
208             ss << std::setw(6) << power;
209             ALOGV("state: %d %lld %f", state, (long long)time, power);
210         }
211         ss << "\n";
212     }
213     return ss.str();
214 }
215
216 status_t PowerLog::dump(int fd, const char *prefix, size_t lines, int64_t limitNs) const
217 {
218     // Since dumpToString and write are thread safe, this function
219     // is conceptually thread-safe but simultaneous calls to dump
220     // by different threads to the same file descriptor may not write
221     // the two logs in time order.
222     const std::string s = dumpToString(prefix, lines, limitNs);
223     if (s.size() > 0 && write(fd, s.c_str(), s.size()) < 0) {
224         return -errno;
225     }
226     return NO_ERROR;
227 }
228
229 } // namespace android
230
231 using namespace android;
232
233 power_log_t *power_log_create(uint32_t sample_rate,
234         uint32_t channel_count, audio_format_t format, size_t entries, size_t frames_per_entry)
235 {
236     if (!audio_utils_is_compute_power_format_supported(format)) {
237         return nullptr;
238     }
239     return reinterpret_cast<power_log_t *>
240             (new(std::nothrow)
241                     PowerLog(sample_rate, channel_count, format, entries, frames_per_entry));
242 }
243
244 void power_log_log(power_log_t *power_log,
245         const void *buffer, size_t frames, int64_t now_ns)
246 {
247     if (power_log == nullptr) {
248         return;
249     }
250     reinterpret_cast<PowerLog *>(power_log)->log(buffer, frames, now_ns);
251 }
252
253 int power_log_dump(
254         power_log_t *power_log, int fd, const char *prefix, size_t lines, int64_t limit_ns)
255 {
256     if (power_log == nullptr) {
257         return BAD_VALUE;
258     }
259     return reinterpret_cast<PowerLog *>(power_log)->dump(fd, prefix, lines, limit_ns);
260 }
261
262 void power_log_destroy(power_log_t *power_log)
263 {
264     delete reinterpret_cast<PowerLog *>(power_log);
265 }