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
9 namespace audio {
10 namespace utils {
11
12 // Note: these sets must be kept in monotonically increasing order.
13 static const uint32_t RATES_48000_FAMILY[] = { 8000,16000,32000,48000,96000,192000,384000,768000 };
14 static const uint32_t RATES_44100_FAMILY[] = { 11025,22050,44100,88200,176400 };
15 static const uint32_t* RATES_48000_FAMILY_LAST = RATES_48000_FAMILY + fbl::count_of(RATES_48000_FAMILY);
16 static const uint32_t* RATES_44100_FAMILY_LAST = RATES_44100_FAMILY + fbl::count_of(RATES_44100_FAMILY);
17 static constexpr auto DISCRETE_FLAGS = ASF_RANGE_FLAG_FPS_48000_FAMILY
18 | ASF_RANGE_FLAG_FPS_44100_FAMILY;
19
FrameRateIn48kFamily(uint32_t rate)20 bool 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
FrameRateIn441kFamily(uint32_t rate)25 bool 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)
ComputeFrameSize(uint16_t channels,audio_sample_format_t sample_format)32 uint32_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
FormatIsCompatible(uint32_t frame_rate,uint16_t channels,audio_sample_format_t sample_format,const audio_stream_format_range_t & format_range)53 bool 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
iterator(const FrameRateEnumerator * enumerator)114 FrameRateEnumerator::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
Advance()139 void 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