1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <audio-proto-utils/format-utils.h>
6#include <fbl/algorithm.h>
7#include <string.h>
8
9namespace audio {
10namespace utils {
11
12// Note: these sets must be kept in monotonically increasing order.
13static const uint32_t RATES_48000_FAMILY[] = { 8000,16000,32000,48000,96000,192000,384000,768000 };
14static const uint32_t RATES_44100_FAMILY[] = { 11025,22050,44100,88200,176400 };
15static const uint32_t* RATES_48000_FAMILY_LAST = RATES_48000_FAMILY + fbl::count_of(RATES_48000_FAMILY);
16static const uint32_t* RATES_44100_FAMILY_LAST = RATES_44100_FAMILY + fbl::count_of(RATES_44100_FAMILY);
17static constexpr auto DISCRETE_FLAGS = ASF_RANGE_FLAG_FPS_48000_FAMILY
18                                     | ASF_RANGE_FLAG_FPS_44100_FAMILY;
19
20bool FrameRateIn48kFamily(uint32_t rate) {
21    const uint32_t* found = fbl::lower_bound(RATES_48000_FAMILY, RATES_48000_FAMILY_LAST, rate);
22    return ((found < RATES_48000_FAMILY_LAST) && (*found == rate));
23}
24
25bool FrameRateIn441kFamily(uint32_t rate) {
26    const uint32_t* found = fbl::lower_bound(RATES_44100_FAMILY, RATES_44100_FAMILY_LAST, rate);
27    return ((found < RATES_44100_FAMILY_LAST) && (*found == rate));
28}
29
30// Figure out the size of an audio frame based on the sample format.  Returns 0
31// in the case of an error (bad channel count, bad sample format)
32uint32_t ComputeFrameSize(uint16_t channels, audio_sample_format_t sample_format) {
33    uint32_t fmt_noflags = sample_format & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK;
34
35    switch (fmt_noflags) {
36    case AUDIO_SAMPLE_FORMAT_8BIT:         return 1u * channels;
37    case AUDIO_SAMPLE_FORMAT_16BIT:        return 2u * channels;
38    case AUDIO_SAMPLE_FORMAT_24BIT_PACKED: return 3u * channels;
39    case AUDIO_SAMPLE_FORMAT_20BIT_IN32:
40    case AUDIO_SAMPLE_FORMAT_24BIT_IN32:
41    case AUDIO_SAMPLE_FORMAT_32BIT:
42    case AUDIO_SAMPLE_FORMAT_32BIT_FLOAT:  return 4u * channels;
43
44    // See ZX-1003
45    // We currently don't really know how 20 bit audio should be packed.  For
46    // now, treat it as an error.
47    case AUDIO_SAMPLE_FORMAT_20BIT_PACKED:
48    default:
49        return 0;
50    }
51}
52
53bool FormatIsCompatible(uint32_t frame_rate,
54                        uint16_t channels,
55                        audio_sample_format_t sample_format,
56                        const audio_stream_format_range_t& format_range) {
57    // Are the requested number of channels in range?
58    if ((channels < format_range.min_channels) || (channels > format_range.max_channels))
59        return false;
60
61    // Is the requested sample format compatible with the range's supported
62    // formats?  If so...
63    //
64    // 1) The flags for each (requested and supported) must match exactly.
65    // 2) The requested format must be unique, and a PCM format (we don't know
66    //    how to test compatibility for compressed bitstream formats right now)
67    // 3) The requested format must intersect the set of supported formats.
68    //
69    // Start by testing requirement #1.
70    uint32_t requested_flags = sample_format & AUDIO_SAMPLE_FORMAT_FLAG_MASK;
71    uint32_t supported_flags = format_range.sample_formats & AUDIO_SAMPLE_FORMAT_FLAG_MASK;
72    if (requested_flags != supported_flags)
73        return false;
74
75    // Requirement #2.  If this format is unique and PCM, then there is exactly
76    // 1 bit set in it and that bit is not AUDIO_SAMPLE_FORMAT_BITSTREAM.  We
77    // can use fbl::is_pow2 to check if there is exactly 1 bit set.  (note,
78    // fbl::is_pow2 does not consider 0 to be a power of 2, so it's perfect for
79    // this)
80    uint32_t requested_noflags = sample_format & ~AUDIO_SAMPLE_FORMAT_FLAG_MASK;
81    if ((requested_noflags == AUDIO_SAMPLE_FORMAT_BITSTREAM) ||
82        (!fbl::is_pow2(requested_noflags)))
83        return false;
84
85    // Requirement #3.  Testing intersection is easy, just and the two.  No need
86    // to strip the flags from the supported format bitmask, we have already
87    // stripped them from the request when checking requirement #2.
88    if (!(format_range.sample_formats & requested_noflags))
89        return false;
90
91    // Check the requested frame rate.  If it is not in the range expressed by
92    // the format_range, then we know this is not a match.
93    if ((frame_rate < format_range.min_frames_per_second) ||
94        (frame_rate > format_range.max_frames_per_second))
95        return false;
96
97    // The frame rate is in range, if this format_range supports continuous
98    // frame rates, then this is a match.
99    if (format_range.flags & ASF_RANGE_FLAG_FPS_CONTINUOUS)
100        return true;
101
102    // Check the 48k family.
103    if ((format_range.flags & ASF_RANGE_FLAG_FPS_48000_FAMILY) && FrameRateIn48kFamily(frame_rate))
104        return true;
105
106    // Check the 44.1k family.
107    if ((format_range.flags & ASF_RANGE_FLAG_FPS_44100_FAMILY) && FrameRateIn441kFamily(frame_rate))
108        return true;
109
110    // No supported frame rates found.  Declare no-match.
111    return false;
112}
113
114FrameRateEnumerator::iterator::iterator(const FrameRateEnumerator* enumerator)
115    : enumerator_(enumerator) {
116    // If we have no enumerator, then we cannot advance to the first valid frame
117    // rate.  Just get out.
118    if (!enumerator_)
119        return;
120
121    // Sanity check our range first.  If it is continuous, or invalid in any
122    // way, then we are not going to enumerate any valid frame rates.  Just set
123    // our enumerator to nullptr and get out.
124    const auto& range = enumerator_->range();
125    if ((range.flags & ASF_RANGE_FLAG_FPS_CONTINUOUS) ||
126       !(range.flags & DISCRETE_FLAGS) ||
127        (range.min_frames_per_second > range.max_frames_per_second)) {
128        enumerator_ = nullptr;
129        return;
130    }
131
132    // Reset our current iterator state, then advance to the first valid
133    // frame rate (if any)
134    cur_flag_ = ASF_RANGE_FLAG_FPS_48000_FAMILY;
135    fmt_ndx_  = static_cast<uint16_t>(-1);
136    Advance();
137}
138
139void FrameRateEnumerator::iterator::Advance() {
140    if (enumerator_ == nullptr) {
141        ZX_DEBUG_ASSERT(!cur_rate_ && !cur_flag_ && !fmt_ndx_);
142        return;
143    }
144
145    const auto& range = enumerator_->range();
146
147    while (cur_flag_ & DISCRETE_FLAGS) {
148        const uint32_t* rates;
149        uint16_t rates_count;
150
151        if (cur_flag_ == ASF_RANGE_FLAG_FPS_48000_FAMILY) {
152            rates = RATES_48000_FAMILY;
153            rates_count = sizeof(RATES_48000_FAMILY);
154        } else {
155            ZX_DEBUG_ASSERT(cur_flag_ == ASF_RANGE_FLAG_FPS_44100_FAMILY);
156            rates = RATES_44100_FAMILY;
157            rates_count = sizeof(RATES_44100_FAMILY);
158        }
159
160        if (range.flags & cur_flag_) {
161            for (++fmt_ndx_; fmt_ndx_ < rates_count; ++fmt_ndx_) {
162                uint32_t rate = rates[fmt_ndx_];
163
164                // If the rate in the table is less than the minimum
165                // frames_per_second, keep advancing the index.
166                if (rate < range.min_frames_per_second)
167                    continue;
168
169                // If the rate in the table is greater than the maximum
170                // frames_per_second, then we are done with this table.  There are
171                // no more matches to be found in it.
172                if (rate > range.max_frames_per_second)
173                    break;
174
175                // The rate in this table is between the min and the max rates
176                // supported by this range.  Record it and get out.
177                cur_rate_ = rate;
178                return;
179            }
180        }
181
182        // We are done with this table.  If we were searching the 48KHz family,
183        // move on to the 44.1KHz family.  Otherwise, we are finished.
184        if (cur_flag_ == ASF_RANGE_FLAG_FPS_48000_FAMILY) {
185            cur_flag_ = ASF_RANGE_FLAG_FPS_44100_FAMILY;
186            fmt_ndx_  = static_cast<uint16_t>(-1);
187        } else {
188            break;
189        }
190    }
191
192    memset(this, 0, sizeof(*this));
193}
194
195}  // namespace utils
196}  // namespace audio
197