HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
audio_sample_format.hpp
1// Copyright Take Vos 2021.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../utility/utility.hpp"
8#include "../macros.hpp"
9#include <hikocpu/hikocpu.hpp>
10#include <bit>
11
12hi_export_module(hikogui.audio.audio_sample_format);
13
14hi_export namespace hi { inline namespace v1 {
15
30hi_export struct audio_sample_format {
34 uint8_t num_bytes;
35
40
51 uint8_t num_bits;
52
57
60 std::endian endian;
61
62 constexpr audio_sample_format() noexcept :
63 num_bytes(0), num_guard_bits(0), num_bits(0), is_float(false), endian(std::endian::native)
64 {
65 }
66
67 constexpr audio_sample_format(audio_sample_format const&) noexcept = default;
68 constexpr audio_sample_format(audio_sample_format&&) noexcept = default;
69 constexpr audio_sample_format& operator=(audio_sample_format const&) noexcept = default;
70 constexpr audio_sample_format& operator=(audio_sample_format&&) noexcept = default;
71
80 [[nodiscard]] constexpr audio_sample_format(
81 uint8_t num_bytes,
82 uint8_t num_guard_bits,
83 uint8_t num_bits,
84 bool is_float,
85 std::endian endian) noexcept :
87 {
88 hi_axiom(holds_invariant());
89 }
90
91 [[nodiscard]] constexpr static audio_sample_format float32_le() noexcept
92 {
93 return {4, 8, 23, true, std::endian::little};
94 }
95
96 [[nodiscard]] constexpr static audio_sample_format float32_be() noexcept
97 {
98 return {4, 8, 23, true, std::endian::big};
99 }
100
101 [[nodiscard]] constexpr static audio_sample_format float32() noexcept
102 {
103 return {4, 8, 23, true, std::endian::native};
104 }
105
106 [[nodiscard]] constexpr static audio_sample_format int16_le() noexcept
107 {
108 return {2, 0, 15, false, std::endian::little};
109 }
110
111 [[nodiscard]] constexpr static audio_sample_format int16_be() noexcept
112 {
113 return {2, 0, 15, false, std::endian::big};
114 }
115
116 [[nodiscard]] constexpr static audio_sample_format int16() noexcept
117 {
118 return {2, 0, 15, false, std::endian::native};
119 }
120
121 [[nodiscard]] constexpr static audio_sample_format int20_le() noexcept
122 {
123 return {3, 0, 19, false, std::endian::little};
124 }
125
126 [[nodiscard]] constexpr static audio_sample_format int20_be() noexcept
127 {
128 return {3, 0, 19, false, std::endian::big};
129 }
130
131 [[nodiscard]] constexpr static audio_sample_format int20() noexcept
132 {
133 return {3, 0, 19, false, std::endian::native};
134 }
135
136 [[nodiscard]] constexpr static audio_sample_format int24_le() noexcept
137 {
138 return {3, 0, 23, false, std::endian::little};
139 }
140
141 [[nodiscard]] constexpr static audio_sample_format int24_be() noexcept
142 {
143 return {3, 0, 23, false, std::endian::big};
144 }
145
146 [[nodiscard]] constexpr static audio_sample_format int24() noexcept
147 {
148 return {3, 0, 23, false, std::endian::native};
149 }
150
151 [[nodiscard]] constexpr static audio_sample_format int32_le() noexcept
152 {
153 return {4, 0, 31, false, std::endian::little};
154 }
155
156 [[nodiscard]] constexpr static audio_sample_format int32_be() noexcept
157 {
158 return {4, 0, 31, false, std::endian::big};
159 }
160
161 [[nodiscard]] constexpr static audio_sample_format int32() noexcept
162 {
163 return {4, 0, 31, false, std::endian::native};
164 }
165
166 [[nodiscard]] constexpr static audio_sample_format fix8_23_le() noexcept
167 {
168 return {4, 8, 23, false, std::endian::little};
169 }
170
171 [[nodiscard]] constexpr static audio_sample_format fix8_23_be() noexcept
172 {
173 return {4, 8, 23, false, std::endian::big};
174 }
175
176 [[nodiscard]] constexpr static audio_sample_format fix8_23() noexcept
177 {
178 return {4, 8, 23, false, std::endian::native};
179 }
180
181 constexpr explicit operator bool() const noexcept
182 {
183 return num_bytes != 0;
184 }
185
188 [[nodiscard]] float pack_multiplier() const noexcept
189 {
190 if (is_float) {
191 return 1.0f;
192
193 } else {
194 // Find the maximum value of the fraction bits as a signed number.
195 auto max_value = (1_uz << num_bits) - 1;
196
197 // Align left inside an int32_t.
198 hi_assert(num_bits + num_guard_bits <= 31);
199 max_value <<= narrow_cast<size_t>(31 - num_bits - num_guard_bits);
200
201 return static_cast<float>(max_value);
202 }
203 }
204
207 [[nodiscard]] float unpack_multiplier() const noexcept
208 {
209 return 1.0f / pack_multiplier();
210 }
211
215 [[nodiscard]] std::size_t num_samples_per_chunk(std::size_t stride) const noexcept
216 {
217 auto r = narrow_cast<int>(std::bit_floor((((16u - num_bytes) / stride) & 3) + 1));
218 hi_assert(r == 1 or r == 2 or r == 4);
219 return r;
220 }
221
224 [[nodiscard]] std::size_t chunk_stride(std::size_t stride) const noexcept
225 {
226 return stride * num_samples_per_chunk(stride);
227 }
228
231 [[nodiscard]] std::size_t num_chunks_per_quad(std::size_t stride) const noexcept
232 {
233 return 4 / num_samples_per_chunk(stride);
234 }
235
238 [[nodiscard]] std::size_t num_fast_quads(std::size_t stride, std::size_t num_samples) const noexcept
239 {
240 auto const src_buffer_size = (num_samples - 1) * stride + num_bytes;
241 if (src_buffer_size < 16) {
242 return 0;
243 }
244
245 auto const num_chunks = (src_buffer_size - 16) / chunk_stride(stride) + 1;
246 return num_chunks / num_chunks_per_quad(stride);
247 }
248
251 [[nodiscard]] i8x16 load_shuffle_indices(std::size_t stride) const noexcept
252 {
253 auto const num_samples = num_samples_per_chunk(stride);
254
255 // Indices set to -1 result in a zero after a byte shuffle.
256 auto r = i8x16::broadcast(-1);
257 for (int sample_nr = 0; sample_nr != num_samples; ++sample_nr) {
258 auto const sample_src_offset = sample_nr * stride;
259
260 // Offset the samples to the highest elements in the i32x4 vector.
261 // By shifting the samples from high to low together with 'OR' we can
262 // concatenate 1, 2, or 4 loads into a single 4 samples vector.
263 // Where the sample in the lowest index is the first sample in memory.
264 auto const sample_dst_offset = (sample_nr + (4 - num_samples)) * 4;
265
266 // Bytes are ordered least to most significant.
267 for (int byte_nr = 0; byte_nr != num_bytes; ++byte_nr) {
268 auto const src_offset = sample_src_offset + (endian == std::endian::little ? byte_nr : num_bytes - byte_nr - 1);
269
270 // Offset the bytes so they become aligned to the left.
271 auto const dst_offset = sample_dst_offset + byte_nr + (4 - num_bytes);
272
273 r[dst_offset] = narrow_cast<int8_t>(src_offset);
274 }
275 }
276
277 return r;
278 }
279
282 [[nodiscard]] i8x16 store_shuffle_indices(std::size_t stride) const noexcept
283 {
284 auto const num_samples = num_samples_per_chunk(stride);
285
286 // Indices set to -1 result in a zero after a byte shuffle.
287 auto r = i8x16::broadcast(-1);
288 for (int sample_nr = 0; sample_nr != num_samples; ++sample_nr) {
289 auto const sample_dst_offset = sample_nr * stride;
290
291 // Offset the samples to the lowest elements in the i32x4 vector.
292 // By shifting the samples from high to low we can extract 1, 2, or 4 stores
293 // from a single 4 samples vector.
294 // Where the sample at the lowest index becomes the first sample in memory.
295 auto const sample_src_offset = sample_nr * 4;
296
297 // Bytes are ordered least to most significant.
298 for (int byte_nr = 0; byte_nr != num_bytes; ++byte_nr) {
299 auto const dst_offset = sample_dst_offset + (endian == std::endian::little ? byte_nr : num_bytes - byte_nr - 1);
300
301 // Offset the bytes so they become aligned to the left.
302 auto const src_offset = sample_src_offset + byte_nr + (4 - num_bytes);
303
304 r[dst_offset] = narrow_cast<int8_t>(src_offset);
305 }
306 }
307
308 return r;
309 }
310
313 [[nodiscard]] i8x16 concat_shuffle_indices(std::size_t stride) const noexcept
314 {
315 auto const num_samples = num_samples_per_chunk(stride);
316
317 // The bytes are shifted right.
318 auto const byte_shift = (4 - num_samples) * 4;
319
320 return i8x16::byte_srl_shuffle_indices(narrow_cast<unsigned int>(byte_shift));
321 }
322
325 [[nodiscard]] constexpr bool holds_invariant() const noexcept
326 {
327 return (num_bytes >= 1 && num_bytes <= 4) && (num_bits + num_guard_bits <= num_bytes * 8) &&
328 (endian == std::endian::little || endian == std::endian::big);
329 }
330};
331
332}} // namespace hi::inline v1
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:20
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Audio sample format.
Definition audio_sample_format.hpp:30
uint8_t num_bytes
The number of bytes of the container.
Definition audio_sample_format.hpp:34
constexpr bool holds_invariant() const noexcept
Is the audio sample format valid.
Definition audio_sample_format.hpp:325
i8x16 load_shuffle_indices(std::size_t stride) const noexcept
Return a shuffle indices for loading samples into 32 bit integers.
Definition audio_sample_format.hpp:251
std::size_t num_chunks_per_quad(std::size_t stride) const noexcept
The number of chunks to load or store to handle 4 samples.
Definition audio_sample_format.hpp:231
bool is_float
The numeric type is floating point.
Definition audio_sample_format.hpp:56
std::size_t chunk_stride(std::size_t stride) const noexcept
The number of bytes to advance to the next chunk to be loaded or stored.
Definition audio_sample_format.hpp:224
std::size_t num_samples_per_chunk(std::size_t stride) const noexcept
The number of packed samples that are handled in a single 128 bit load or store.
Definition audio_sample_format.hpp:215
i8x16 store_shuffle_indices(std::size_t stride) const noexcept
Return a shuffle indices for storing 32 bit samples into packed samples.
Definition audio_sample_format.hpp:282
uint8_t num_bits
The number of significant bits of the sample format.
Definition audio_sample_format.hpp:51
std::endian endian
The endian order of the bytes in the container.
Definition audio_sample_format.hpp:60
uint8_t num_guard_bits
The number of bits used for the integer part of a fixed point number.
Definition audio_sample_format.hpp:39
i8x16 concat_shuffle_indices(std::size_t stride) const noexcept
Return a shuffle indices to shift previous loaded samples for concatenation.
Definition audio_sample_format.hpp:313
float pack_multiplier() const noexcept
How much to multiply float samples to create integer samples.
Definition audio_sample_format.hpp:188
float unpack_multiplier() const noexcept
How much to multiply integer samples to create float samples.
Definition audio_sample_format.hpp:207
std::size_t num_fast_quads(std::size_t stride, std::size_t num_samples) const noexcept
Calculate the number of 4 sample-quads can be handled as chunked loads and stores.
Definition audio_sample_format.hpp:238