fifo.cpp revision 7cc8f545385d09aaa0c6e081856d7e3c8e500133
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
32audio_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
46audio_utils_fifo_base::~audio_utils_fifo_base()
47{
48}
49
50uint32_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
68int32_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
111void audio_utils_fifo_base::shutdown() const
112{
113    ALOGE("%s", __func__);
114    mIsShutdown = true;
115}
116
117////////////////////////////////////////////////////////////////////////////////
118
119audio_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
131audio_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
138audio_utils_fifo::~audio_utils_fifo()
139{
140}
141
142////////////////////////////////////////////////////////////////////////////////
143
144audio_utils_fifo_provider::audio_utils_fifo_provider(audio_utils_fifo& fifo) :
145    mFifo(fifo), mObtained(0), mTotalReleased(0)
146{
147}
148
149audio_utils_fifo_provider::~audio_utils_fifo_provider()
150{
151}
152
153////////////////////////////////////////////////////////////////////////////////
154
155audio_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
163audio_utils_fifo_writer::~audio_utils_fifo_writer()
164{
165}
166
167ssize_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
187ssize_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
292void 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
345ssize_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
351void 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
369uint32_t audio_utils_fifo_writer::size() const
370{
371    return mEffectiveFrames;
372}
373
374void 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
391void audio_utils_fifo_writer::getHysteresis(uint32_t *armLevel, uint32_t *triggerLevel) const
392{
393    *armLevel = mArmLevel;
394    *triggerLevel = mTriggerLevel;
395}
396
397////////////////////////////////////////////////////////////////////////////////
398
399audio_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
408audio_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
413ssize_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
432ssize_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
439void 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
492ssize_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
591ssize_t audio_utils_fifo_reader::available()
592{
593    return available(NULL /*lost*/);
594}
595
596ssize_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
602ssize_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
615void 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
634void audio_utils_fifo_reader::getHysteresis(int32_t *armLevel, uint32_t *triggerLevel) const
635{
636    *armLevel = mArmLevel;
637    *triggerLevel = mTriggerLevel;
638}
639