OSDN Git Service

fifo: isolate atomic and futex operations
[android-x86/system-media.git] / audio_utils / fifo.cpp
1 /*
2  * Copyright (C) 2015 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_fifo"
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <audio_utils/clock_nanosleep.h>
26 #include <audio_utils/fifo.h>
27 #include <audio_utils/futex.h>
28 #include <audio_utils/roundup.h>
29 #include <cutils/log.h>
30 #include <utils/Errors.h>
31
32 audio_utils_fifo_base::audio_utils_fifo_base(uint32_t frameCount,
33         audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
34         __attribute__((no_sanitize("integer"))) :
35     mFrameCount(frameCount), mFrameCountP2(roundup(frameCount)),
36     mFudgeFactor(mFrameCountP2 - mFrameCount),
37     // FIXME need an API to configure the sync types
38     mWriterRear(writerRear), mWriterRearSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
39     mThrottleFront(throttleFront), mThrottleFrontSync(AUDIO_UTILS_FIFO_SYNC_SHARED),
40     mIsShutdown(false)
41 {
42     // actual upper bound on frameCount will depend on the frame size
43     LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameCount > ((uint32_t) INT32_MAX));
44 }
45
46 audio_utils_fifo_base::~audio_utils_fifo_base()
47 {
48 }
49
50 uint32_t audio_utils_fifo_base::sum(uint32_t index, uint32_t increment) const
51         __attribute__((no_sanitize("integer")))
52 {
53     if (mFudgeFactor) {
54         uint32_t mask = mFrameCountP2 - 1;
55         ALOG_ASSERT((index & mask) < mFrameCount);
56         ALOG_ASSERT(increment <= mFrameCountP2);
57         if ((index & mask) + increment >= mFrameCount) {
58             increment += mFudgeFactor;
59         }
60         index += increment;
61         ALOG_ASSERT((index & mask) < mFrameCount);
62         return index;
63     } else {
64         return index + increment;
65     }
66 }
67
68 int32_t audio_utils_fifo_base::diff(uint32_t rear, uint32_t front, size_t *lost) const
69         __attribute__((no_sanitize("integer")))
70 {
71     // TODO replace multiple returns by a single return point so this isn't needed
72     if (lost != NULL) {
73         *lost = 0;
74     }
75     if (mIsShutdown) {
76         return -EIO;
77     }
78     uint32_t diff = rear - front;
79     if (mFudgeFactor) {
80         uint32_t mask = mFrameCountP2 - 1;
81         uint32_t rearOffset = rear & mask;
82         uint32_t frontOffset = front & mask;
83         if (rearOffset >= mFrameCount || frontOffset >= mFrameCount) {
84             ALOGE("%s frontOffset=%u rearOffset=%u mFrameCount=%u",
85                     __func__, frontOffset, rearOffset, mFrameCount);
86             shutdown();
87             return -EIO;
88         }
89         uint32_t genDiff = (rear & ~mask) - (front & ~mask);
90         if (genDiff != 0) {
91             if (genDiff > mFrameCountP2) {
92                 if (lost != NULL) {
93                     // TODO provide a more accurate estimate
94                     *lost = (genDiff / mFrameCountP2) * mFrameCount;
95                 }
96                 return -EOVERFLOW;
97             }
98             diff -= mFudgeFactor;
99         }
100     }
101     // FIFO should not be overfull
102     if (diff > mFrameCount) {
103         if (lost != NULL) {
104             *lost = diff;
105         }
106         return -EOVERFLOW;
107     }
108     return (int32_t) diff;
109 }
110
111 void audio_utils_fifo_base::shutdown() const
112 {
113     ALOGE("%s", __func__);
114     mIsShutdown = true;
115 }
116
117 ////////////////////////////////////////////////////////////////////////////////
118
119 audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
120         audio_utils_fifo_index& writerRear, audio_utils_fifo_index *throttleFront)
121         __attribute__((no_sanitize("integer"))) :
122     audio_utils_fifo_base(frameCount, writerRear, throttleFront),
123     mFrameSize(frameSize), mBuffer(buffer)
124 {
125     // maximum value of frameCount * frameSize is INT32_MAX (2^31 - 1), not 2^31, because we need to
126     // be able to distinguish successful and error return values from read and write.
127     LOG_ALWAYS_FATAL_IF(frameCount == 0 || frameSize == 0 || buffer == NULL ||
128             frameCount > ((uint32_t) INT32_MAX) / frameSize);
129 }
130
131 audio_utils_fifo::audio_utils_fifo(uint32_t frameCount, uint32_t frameSize, void *buffer,
132         bool throttlesWriter) :
133     audio_utils_fifo(frameCount, frameSize, buffer, mSingleProcessSharedRear,
134         throttlesWriter ?  &mSingleProcessSharedFront : NULL)
135 {
136 }
137
138 audio_utils_fifo::~audio_utils_fifo()
139 {
140 }
141
142 ////////////////////////////////////////////////////////////////////////////////
143
144 audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
145     mFifo(fifo), mObtained(0), mTotalReleased(0)
146 {
147 }
148
149 audio_utils_fifo_provider::~audio_utils_fifo_provider()
150 {
151 }
152
153 ////////////////////////////////////////////////////////////////////////////////
154
155 audio_utils_fifo_writer::audio_utils_fifo_writer(audio_utils_fifo& fifo) :
156     audio_utils_fifo_provider(fifo), mLocalRear(0),
157     mArmLevel(fifo.mFrameCount), mTriggerLevel(0),
158     mIsArmed(true), // because initial fill level of zero is < mArmLevel
159     mEffectiveFrames(fifo.mFrameCount)
160 {
161 }
162
163 audio_utils_fifo_writer::~audio_utils_fifo_writer()
164 {
165 }
166
167 ssize_t audio_utils_fifo_writer::write(const void *buffer, size_t count,
168         const struct timespec *timeout)
169         __attribute__((no_sanitize("integer")))
170 {
171     audio_utils_iovec iovec[2];
172     ssize_t availToWrite = obtain(iovec, count, timeout);
173     if (availToWrite > 0) {
174         memcpy((char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize, buffer,
175                 iovec[0].mLength * mFifo.mFrameSize);
176         if (iovec[1].mLength > 0) {
177             memcpy((char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
178                     (char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
179                     iovec[1].mLength * mFifo.mFrameSize);
180         }
181         release(availToWrite);
182     }
183     return availToWrite;
184 }
185
186 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
187 ssize_t audio_utils_fifo_writer::obtain(audio_utils_iovec iovec[2], size_t count,
188         const struct timespec *timeout)
189         __attribute__((no_sanitize("integer")))
190 {
191     int err = 0;
192     size_t availToWrite;
193     if (mFifo.mThrottleFront != NULL) {
194         int retries = kRetries;
195         uint32_t front;
196         for (;;) {
197             front = mFifo.mThrottleFront->loadAcquire();
198             // returns -EIO if mIsShutdown
199             int32_t filled = mFifo.diff(mLocalRear, front);
200             if (filled < 0) {
201                 // on error, return an empty slice
202                 err = filled;
203                 availToWrite = 0;
204                 break;
205             }
206             availToWrite = mEffectiveFrames > (uint32_t) filled ?
207                     mEffectiveFrames - (uint32_t) filled : 0;
208             // TODO pull out "count == 0"
209             if (count == 0 || availToWrite > 0 || timeout == NULL ||
210                     (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
211                 break;
212             }
213             // TODO add comments
214             // TODO abstract out switch and replace by general sync object
215             //      the high level code (synchronization, sleep, futex, iovec) should be completely
216             //      separate from the low level code (indexes, available, masking).
217             int op = FUTEX_WAIT;
218             switch (mFifo.mThrottleFrontSync) {
219             case AUDIO_UTILS_FIFO_SYNC_SLEEP:
220                 err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
221                         NULL /*remain*/);
222                 if (err < 0) {
223                     LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
224                     err = -errno;
225                 } else {
226                     err = -ETIMEDOUT;
227                 }
228                 break;
229             case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
230                 op = FUTEX_WAIT_PRIVATE;
231                 // fall through
232             case AUDIO_UTILS_FIFO_SYNC_SHARED:
233                 if (timeout->tv_sec == LONG_MAX) {
234                     timeout = NULL;
235                 }
236                 err = mFifo.mThrottleFront->wait(op, front, timeout);
237                 if (err < 0) {
238                     switch (errno) {
239                     case EWOULDBLOCK:
240                         // Benign race condition with partner: mFifo.mThrottleFront->mIndex
241                         // changed value between the earlier atomic_load_explicit() and sys_futex().
242                         // Try to load index again, but give up if we are unable to converge.
243                         if (retries-- > 0) {
244                             // bypass the "timeout = NULL;" below
245                             continue;
246                         }
247                         // fall through
248                     case EINTR:
249                     case ETIMEDOUT:
250                         err = -errno;
251                         break;
252                     default:
253                         LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
254                         break;
255                     }
256                 }
257                 break;
258             default:
259                 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
260                 break;
261             }
262             timeout = NULL;
263         }
264     } else {
265         if (mFifo.mIsShutdown) {
266             err = -EIO;
267             availToWrite = 0;
268         } else {
269             availToWrite = mEffectiveFrames;
270         }
271     }
272     if (availToWrite > count) {
273         availToWrite = count;
274     }
275     uint32_t rearOffset = mLocalRear & (mFifo.mFrameCountP2 - 1);
276     size_t part1 = mFifo.mFrameCount - rearOffset;
277     if (part1 > availToWrite) {
278         part1 = availToWrite;
279     }
280     size_t part2 = part1 > 0 ? availToWrite - part1 : 0;
281     // return slice
282     if (iovec != NULL) {
283         iovec[0].mOffset = rearOffset;
284         iovec[0].mLength = part1;
285         iovec[1].mOffset = 0;
286         iovec[1].mLength = part2;
287         mObtained = availToWrite;
288     }
289     return availToWrite > 0 ? availToWrite : err;
290 }
291
292 void audio_utils_fifo_writer::release(size_t count)
293         __attribute__((no_sanitize("integer")))
294 {
295     // no need to do an early check for mIsShutdown, because the extra code executed is harmless
296     if (count > 0) {
297         if (count > mObtained) {
298             ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
299             mFifo.shutdown();
300             return;
301         }
302         if (mFifo.mThrottleFront != NULL) {
303             uint32_t front = mFifo.mThrottleFront->loadAcquire();
304             // returns -EIO if mIsShutdown
305             int32_t filled = mFifo.diff(mLocalRear, front);
306             mLocalRear = mFifo.sum(mLocalRear, count);
307             mFifo.mWriterRear.storeRelease(mLocalRear);
308             // TODO add comments
309             int op = FUTEX_WAKE;
310             switch (mFifo.mWriterRearSync) {
311             case AUDIO_UTILS_FIFO_SYNC_SLEEP:
312                 break;
313             case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
314                 op = FUTEX_WAKE_PRIVATE;
315                 // fall through
316             case AUDIO_UTILS_FIFO_SYNC_SHARED:
317                 if (filled >= 0) {
318                     if ((uint32_t) filled < mArmLevel) {
319                         mIsArmed = true;
320                     }
321                     if (mIsArmed && filled + count > mTriggerLevel) {
322                         int err = mFifo.mWriterRear.wake(op, INT32_MAX /*waiters*/);
323                         // err is number of processes woken up
324                         if (err < 0) {
325                             LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
326                                     __func__, err, errno);
327                         }
328                         mIsArmed = false;
329                     }
330                 }
331                 break;
332             default:
333                 LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
334                 break;
335             }
336         } else {
337             mLocalRear = mFifo.sum(mLocalRear, count);
338             mFifo.mWriterRear.storeRelease(mLocalRear);
339         }
340         mObtained -= count;
341         mTotalReleased += count;
342     }
343 }
344
345 ssize_t audio_utils_fifo_writer::available()
346 {
347     // iovec == NULL is not part of the public API, but internally it means don't set mObtained
348     return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/);
349 }
350
351 void audio_utils_fifo_writer::resize(uint32_t frameCount)
352 {
353     // cap to range [0, mFifo.mFrameCount]
354     if (frameCount > mFifo.mFrameCount) {
355         frameCount = mFifo.mFrameCount;
356     }
357     // if we reduce the effective frame count, update hysteresis points to be within the new range
358     if (frameCount < mEffectiveFrames) {
359         if (mArmLevel > frameCount) {
360             mArmLevel = frameCount;
361         }
362         if (mTriggerLevel > frameCount) {
363             mTriggerLevel = frameCount;
364         }
365     }
366     mEffectiveFrames = frameCount;
367 }
368
369 uint32_t audio_utils_fifo_writer::size() const
370 {
371     return mEffectiveFrames;
372 }
373
374 void audio_utils_fifo_writer::setHysteresis(uint32_t lowLevelArm, uint32_t highLevelTrigger)
375 {
376     // cap to range [0, mEffectiveFrames]
377     if (lowLevelArm > mEffectiveFrames) {
378         lowLevelArm = mEffectiveFrames;
379     }
380     if (highLevelTrigger > mEffectiveFrames) {
381         highLevelTrigger = mEffectiveFrames;
382     }
383     // TODO this is overly conservative; it would be better to arm based on actual fill level
384     if (lowLevelArm > mArmLevel) {
385         mIsArmed = true;
386     }
387     mArmLevel = lowLevelArm;
388     mTriggerLevel = highLevelTrigger;
389 }
390
391 void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
392 {
393     *armLevel = mArmLevel;
394     *triggerLevel = mTriggerLevel;
395 }
396
397 ////////////////////////////////////////////////////////////////////////////////
398
399 audio_utils_fifo_reader::audio_utils_fifo_reader(audio_utils_fifo& fifo, bool throttlesWriter) :
400     audio_utils_fifo_provider(fifo), mLocalFront(0),
401     mThrottleFront(throttlesWriter ? mFifo.mThrottleFront : NULL),
402     mArmLevel(-1), mTriggerLevel(mFifo.mFrameCount),
403     mIsArmed(true), // because initial fill level of zero is > mArmLevel
404     mTotalLost(0), mTotalFlushed(0)
405 {
406 }
407
408 audio_utils_fifo_reader::~audio_utils_fifo_reader()
409 {
410     // TODO Need a way to pass throttle capability to the another reader, should one reader exit.
411 }
412
413 ssize_t audio_utils_fifo_reader::read(void *buffer, size_t count, const struct timespec *timeout,
414         size_t *lost)
415         __attribute__((no_sanitize("integer")))
416 {
417     audio_utils_iovec iovec[2];
418     ssize_t availToRead = obtain(iovec, count, timeout, lost);
419     if (availToRead > 0) {
420         memcpy(buffer, (char *) mFifo.mBuffer + iovec[0].mOffset * mFifo.mFrameSize,
421                 iovec[0].mLength * mFifo.mFrameSize);
422         if (iovec[1].mLength > 0) {
423             memcpy((char *) buffer + (iovec[0].mLength * mFifo.mFrameSize),
424                     (char *) mFifo.mBuffer + iovec[1].mOffset * mFifo.mFrameSize,
425                     iovec[1].mLength * mFifo.mFrameSize);
426         }
427         release(availToRead);
428     }
429     return availToRead;
430 }
431
432 ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
433         const struct timespec *timeout)
434         __attribute__((no_sanitize("integer")))
435 {
436     return obtain(iovec, count, timeout, NULL /*lost*/);
437 }
438
439 void audio_utils_fifo_reader::release(size_t count)
440         __attribute__((no_sanitize("integer")))
441 {
442     // no need to do an early check for mIsShutdown, because the extra code executed is harmless
443     if (count > 0) {
444         if (count > mObtained) {
445             ALOGE("%s(count=%zu) > mObtained=%u", __func__, count, mObtained);
446             mFifo.shutdown();
447             return;
448         }
449         if (mThrottleFront != NULL) {
450             uint32_t rear = mFifo.mWriterRear.loadAcquire();
451             // returns -EIO if mIsShutdown
452             int32_t filled = mFifo.diff(rear, mLocalFront);
453             mLocalFront = mFifo.sum(mLocalFront, count);
454             mThrottleFront->storeRelease(mLocalFront);
455             // TODO add comments
456             int op = FUTEX_WAKE;
457             switch (mFifo.mThrottleFrontSync) {
458             case AUDIO_UTILS_FIFO_SYNC_SLEEP:
459                 break;
460             case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
461                 op = FUTEX_WAKE_PRIVATE;
462                 // fall through
463             case AUDIO_UTILS_FIFO_SYNC_SHARED:
464                 if (filled >= 0) {
465                     if (filled > mArmLevel) {
466                         mIsArmed = true;
467                     }
468                     if (mIsArmed && filled - count < mTriggerLevel) {
469                         int err = mThrottleFront->wake(op, 1 /*waiters*/);
470                         // err is number of processes woken up
471                         if (err < 0 || err > 1) {
472                             LOG_ALWAYS_FATAL("%s: unexpected err=%d errno=%d",
473                                     __func__, err, errno);
474                         }
475                         mIsArmed = false;
476                     }
477                 }
478                 break;
479             default:
480                 LOG_ALWAYS_FATAL("mFifo.mThrottleFrontSync=%d", mFifo.mThrottleFrontSync);
481                 break;
482             }
483         } else {
484             mLocalFront = mFifo.sum(mLocalFront, count);
485         }
486         mObtained -= count;
487         mTotalReleased += count;
488     }
489 }
490
491 // iovec == NULL is not part of the public API, but internally it means don't set mObtained
492 ssize_t audio_utils_fifo_reader::obtain(audio_utils_iovec iovec[2], size_t count,
493         const struct timespec *timeout, size_t *lost)
494         __attribute__((no_sanitize("integer")))
495 {
496     int err = 0;
497     int retries = kRetries;
498     uint32_t rear;
499     for (;;) {
500         rear = mFifo.mWriterRear.loadAcquire();
501         // TODO pull out "count == 0"
502         if (count == 0 || rear != mLocalFront || timeout == NULL ||
503                 (timeout->tv_sec == 0 && timeout->tv_nsec == 0)) {
504             break;
505         }
506         // TODO add comments
507         int op = FUTEX_WAIT;
508         switch (mFifo.mWriterRearSync) {
509         case AUDIO_UTILS_FIFO_SYNC_SLEEP:
510             err = audio_utils_clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, timeout,
511                     NULL /*remain*/);
512             if (err < 0) {
513                 LOG_ALWAYS_FATAL_IF(errno != EINTR, "unexpected err=%d errno=%d", err, errno);
514                 err = -errno;
515             } else {
516                 err = -ETIMEDOUT;
517             }
518             break;
519         case AUDIO_UTILS_FIFO_SYNC_PRIVATE:
520             op = FUTEX_WAIT_PRIVATE;
521             // fall through
522         case AUDIO_UTILS_FIFO_SYNC_SHARED:
523             if (timeout->tv_sec == LONG_MAX) {
524                 timeout = NULL;
525             }
526             err = mFifo.mWriterRear.wait(op, rear, timeout);
527             if (err < 0) {
528                 switch (errno) {
529                 case EWOULDBLOCK:
530                     // Benign race condition with partner: mFifo.mWriterRear->mIndex
531                     // changed value between the earlier atomic_load_explicit() and sys_futex().
532                     // Try to load index again, but give up if we are unable to converge.
533                     if (retries-- > 0) {
534                         // bypass the "timeout = NULL;" below
535                         continue;
536                     }
537                     // fall through
538                 case EINTR:
539                 case ETIMEDOUT:
540                     err = -errno;
541                     break;
542                 default:
543                     LOG_ALWAYS_FATAL("unexpected err=%d errno=%d", err, errno);
544                     break;
545                 }
546             }
547             break;
548         default:
549             LOG_ALWAYS_FATAL("mFifo.mWriterRearSync=%d", mFifo.mWriterRearSync);
550             break;
551         }
552         timeout = NULL;
553     }
554     size_t ourLost;
555     if (lost == NULL) {
556         lost = &ourLost;
557     }
558     // returns -EIO if mIsShutdown
559     int32_t filled = mFifo.diff(rear, mLocalFront, lost);
560     mTotalLost += *lost;
561     mTotalReleased += *lost;
562     if (filled < 0) {
563         if (filled == -EOVERFLOW) {
564             mLocalFront = rear;
565         }
566         // on error, return an empty slice
567         err = filled;
568         filled = 0;
569     }
570     size_t availToRead = (size_t) filled;
571     if (availToRead > count) {
572         availToRead = count;
573     }
574     uint32_t frontOffset = mLocalFront & (mFifo.mFrameCountP2 - 1);
575     size_t part1 = mFifo.mFrameCount - frontOffset;
576     if (part1 > availToRead) {
577         part1 = availToRead;
578     }
579     size_t part2 = part1 > 0 ? availToRead - part1 : 0;
580     // return slice
581     if (iovec != NULL) {
582         iovec[0].mOffset = frontOffset;
583         iovec[0].mLength = part1;
584         iovec[1].mOffset = 0;
585         iovec[1].mLength = part2;
586         mObtained = availToRead;
587     }
588     return availToRead > 0 ? availToRead : err;
589 }
590
591 ssize_t audio_utils_fifo_reader::available()
592 {
593     return available(NULL /*lost*/);
594 }
595
596 ssize_t audio_utils_fifo_reader::available(size_t *lost)
597 {
598     // iovec == NULL is not part of the public API, but internally it means don't set mObtained
599     return obtain(NULL /*iovec*/, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
600 }
601
602 ssize_t audio_utils_fifo_reader::flush(size_t *lost)
603 {
604     audio_utils_iovec iovec[2];
605     ssize_t ret = obtain(iovec, SIZE_MAX /*count*/, NULL /*timeout*/, lost);
606     if (ret > 0) {
607         size_t flushed = (size_t) ret;
608         release(flushed);
609         mTotalFlushed += flushed;
610         ret = flushed;
611     }
612     return ret;
613 }
614
615 void audio_utils_fifo_reader::setHysteresis(int32_t armLevel, uint32_t triggerLevel)
616 {
617     // cap to range [0, mFifo.mFrameCount]
618     if (armLevel < 0) {
619         armLevel = -1;
620     } else if ((uint32_t) armLevel > mFifo.mFrameCount) {
621         armLevel = mFifo.mFrameCount;
622     }
623     if (triggerLevel > mFifo.mFrameCount) {
624         triggerLevel = mFifo.mFrameCount;
625     }
626     // TODO this is overly conservative; it would be better to arm based on actual fill level
627     if (armLevel < mArmLevel) {
628         mIsArmed = true;
629     }
630     mArmLevel = armLevel;
631     mTriggerLevel = triggerLevel;
632 }
633
634 void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
635 {
636     *armLevel = mArmLevel;
637     *triggerLevel = mTriggerLevel;
638 }