xref: /netbsd/sys/dev/audio/linear.c (revision 007c1f91)
1 /*	$NetBSD: linear.c,v 1.4 2021/07/21 06:35:44 skrll Exp $	*/
2 
3 /*
4  * Copyright (C) 2017 Tetsuya Isaki. All rights reserved.
5  * Copyright (C) 2017 Y.Sugahara (moveccr). All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: linear.c,v 1.4 2021/07/21 06:35:44 skrll Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <dev/audio/audiovar.h>
37 #include <dev/audio/linear.h>
38 
39 /*
40  * audio_linear8_to_internal:
41  *	This filter performs conversion from [US]LINEAR8 to internal format.
42  */
43 void
audio_linear8_to_internal(audio_filter_arg_t * arg)44 audio_linear8_to_internal(audio_filter_arg_t *arg)
45 {
46 	const uint8_t *s;
47 	aint_t *d;
48 	uint8_t xor;
49 	u_int sample_count;
50 	u_int i;
51 
52 	DIAGNOSTIC_filter_arg(arg);
53 	KASSERT(audio_format2_is_linear(arg->srcfmt));
54 	KASSERT(arg->srcfmt->precision == 8);
55 	KASSERT(arg->srcfmt->stride == 8);
56 	KASSERT(audio_format2_is_internal(arg->dstfmt));
57 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
58 
59 	s = arg->src;
60 	d = arg->dst;
61 	sample_count = arg->count * arg->srcfmt->channels;
62 	xor = audio_format2_is_signed(arg->srcfmt) ? 0 : 0x80;
63 
64 	for (i = 0; i < sample_count; i++) {
65 		uint8_t val;
66 		val = *s++;
67 		val ^= xor;
68 		*d++ = (auint_t)val << (AUDIO_INTERNAL_BITS - 8);
69 	}
70 }
71 
72 /*
73  * audio_internal_to_linear8:
74  *	This filter performs conversion from internal format to [US]LINEAR8.
75  */
76 void
audio_internal_to_linear8(audio_filter_arg_t * arg)77 audio_internal_to_linear8(audio_filter_arg_t *arg)
78 {
79 	const aint_t *s;
80 	uint8_t *d;
81 	uint8_t xor;
82 	u_int sample_count;
83 	u_int i;
84 
85 	DIAGNOSTIC_filter_arg(arg);
86 	KASSERT(audio_format2_is_linear(arg->dstfmt));
87 	KASSERT(arg->dstfmt->precision == 8);
88 	KASSERT(arg->dstfmt->stride == 8);
89 	KASSERT(audio_format2_is_internal(arg->srcfmt));
90 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
91 
92 	s = arg->src;
93 	d = arg->dst;
94 	sample_count = arg->count * arg->srcfmt->channels;
95 	xor = audio_format2_is_signed(arg->dstfmt) ? 0 : 0x80;
96 
97 	for (i = 0; i < sample_count; i++) {
98 		uint8_t val;
99 		val = (*s++) >> (AUDIO_INTERNAL_BITS - 8);
100 		val ^= xor;
101 		*d++ = val;
102 	}
103 }
104 
105 /*
106  * audio_linear16_to_internal:
107  *	This filter performs conversion from [US]LINEAR16{LE,BE} to internal
108  *	format.
109  */
110 void
audio_linear16_to_internal(audio_filter_arg_t * arg)111 audio_linear16_to_internal(audio_filter_arg_t *arg)
112 {
113 	const uint16_t *s;
114 	aint_t *d;
115 	uint16_t xor;
116 	u_int sample_count;
117 	u_int shift;
118 	u_int i;
119 	bool is_src_NE;
120 
121 	DIAGNOSTIC_filter_arg(arg);
122 	KASSERT(audio_format2_is_linear(arg->srcfmt));
123 	KASSERT(arg->srcfmt->precision == 16);
124 	KASSERT(arg->srcfmt->stride == 16);
125 	KASSERT(audio_format2_is_internal(arg->dstfmt));
126 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
127 
128 	s = arg->src;
129 	d = arg->dst;
130 	sample_count = arg->count * arg->srcfmt->channels;
131 
132 	shift = AUDIO_INTERNAL_BITS - 16;
133 	xor = audio_format2_is_signed(arg->srcfmt) ? 0 : 0x8000;
134 	is_src_NE = (audio_format2_endian(arg->srcfmt) == BYTE_ORDER);
135 
136 	/*
137 	 * Since slinear16_OppositeEndian to slinear_NativeEndian is used
138 	 * so much especially on big endian machines, so it's expanded.
139 	 * Other conversions are rarely used, so they are compressed.
140 	 */
141 	if (__predict_true(xor == 0) && is_src_NE == false) {
142 		/* slinear16_OE to slinear<AI>_NE */
143 		for (i = 0; i < sample_count; i++) {
144 			uint16_t val;
145 			val = *s++;
146 			val = bswap16(val);
147 			*d++ = (auint_t)val << shift;
148 		}
149 	} else {
150 		/* slinear16_NE      to slinear<AI>_NE */
151 		/* ulinear16_{NE,OE} to slinear<AI>_NE */
152 		for (i = 0; i < sample_count; i++) {
153 			uint16_t val;
154 			val = *s++;
155 			if (!is_src_NE)
156 				val = bswap16(val);
157 			val ^= xor;
158 			*d++ = (auint_t)val << shift;
159 		}
160 	}
161 }
162 
163 /*
164  * audio_internal_to_linear16:
165  *	This filter performs conversion from internal format to
166  *	[US]LINEAR16{LE,BE}.
167  */
168 void
audio_internal_to_linear16(audio_filter_arg_t * arg)169 audio_internal_to_linear16(audio_filter_arg_t *arg)
170 {
171 	const aint_t *s;
172 	uint16_t *d;
173 	uint16_t xor;
174 	u_int sample_count;
175 	u_int shift;
176 	u_int i;
177 	bool is_dst_NE;
178 
179 	DIAGNOSTIC_filter_arg(arg);
180 	KASSERT(audio_format2_is_linear(arg->dstfmt));
181 	KASSERT(arg->dstfmt->precision == 16);
182 	KASSERT(arg->dstfmt->stride == 16);
183 	KASSERT(audio_format2_is_internal(arg->srcfmt));
184 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
185 
186 	s = arg->src;
187 	d = arg->dst;
188 	sample_count = arg->count * arg->srcfmt->channels;
189 
190 	shift = AUDIO_INTERNAL_BITS - 16;
191 	xor = audio_format2_is_signed(arg->dstfmt) ? 0 : 0x8000;
192 	is_dst_NE = (audio_format2_endian(arg->dstfmt) == BYTE_ORDER);
193 
194 	/*
195 	 * Since slinear_NativeEndian to slinear16_OppositeEndian is used
196 	 * so much especially on big endian machines, so it's expanded.
197 	 * Other conversions are rarely used, so they are compressed.
198 	 */
199 	if (__predict_true(xor == 0) && is_dst_NE == false) {
200 		/* slinear<AI>_NE -> slinear16_OE */
201 		for (i = 0; i < sample_count; i++) {
202 			uint16_t val;
203 			val = (*s++) >> shift;
204 			val = bswap16(val);
205 			*d++ = val;
206 		}
207 	} else {
208 		/* slinear<AI>_NE -> slinear16_NE */
209 		/* slinear<AI>_NE -> ulinear16_{NE,OE} */
210 		for (i = 0; i < sample_count; i++) {
211 			uint16_t val;
212 			val = (*s++) >> shift;
213 			val ^= xor;
214 			if (!is_dst_NE)
215 				val = bswap16(val);
216 			*d++ = val;
217 		}
218 	}
219 }
220 
221 #if defined(AUDIO_SUPPORT_LINEAR24)
222 /*
223  * audio_linear24_to_internal:
224  *	This filter performs conversion from [US]LINEAR24/24{LE,BE} to
225  *	internal format.  Since it's rerely used, it's size optimized.
226  */
227 void
audio_linear24_to_internal(audio_filter_arg_t * arg)228 audio_linear24_to_internal(audio_filter_arg_t *arg)
229 {
230 	const uint8_t *s;
231 	aint_t *d;
232 	auint_t xor;
233 	u_int sample_count;
234 	u_int i;
235 	bool is_src_LE;
236 
237 	DIAGNOSTIC_filter_arg(arg);
238 	KASSERT(audio_format2_is_linear(arg->srcfmt));
239 	KASSERT(arg->srcfmt->precision == 24);
240 	KASSERT(arg->srcfmt->stride == 24);
241 	KASSERT(audio_format2_is_internal(arg->dstfmt));
242 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
243 
244 	s = arg->src;
245 	d = arg->dst;
246 	sample_count = arg->count * arg->srcfmt->channels;
247 	xor = audio_format2_is_signed(arg->srcfmt)
248 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
249 	is_src_LE = (audio_format2_endian(arg->srcfmt) == LITTLE_ENDIAN);
250 
251 	for (i = 0; i < sample_count; i++) {
252 		uint32_t val;
253 		if (is_src_LE) {
254 			val = s[0] | (s[1] << 8) | (s[2] << 16);
255 		} else {
256 			val = (s[0] << 16) | (s[1] << 8) | s[2];
257 		}
258 		s += 3;
259 
260 #if AUDIO_INTERNAL_BITS < 24
261 		val >>= 24 - AUDIO_INTERNAL_BITS;
262 #else
263 		val <<= AUDIO_INTERNAL_BITS - 24;
264 #endif
265 		val ^= xor;
266 		*d++ = val;
267 	}
268 }
269 
270 /*
271  * audio_internal_to_linear24:
272  *	This filter performs conversion from internal format to
273  *	[US]LINEAR24/24{LE,BE}.  Since it's rarely used, it's size optimized.
274  */
275 void
audio_internal_to_linear24(audio_filter_arg_t * arg)276 audio_internal_to_linear24(audio_filter_arg_t *arg)
277 {
278 	const aint_t *s;
279 	uint8_t *d;
280 	auint_t xor;
281 	u_int sample_count;
282 	u_int i;
283 	bool is_dst_LE;
284 
285 	DIAGNOSTIC_filter_arg(arg);
286 	KASSERT(audio_format2_is_linear(arg->dstfmt));
287 	KASSERT(arg->dstfmt->precision == 24);
288 	KASSERT(arg->dstfmt->stride == 24);
289 	KASSERT(audio_format2_is_internal(arg->srcfmt));
290 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
291 
292 	s = arg->src;
293 	d = arg->dst;
294 	sample_count = arg->count * arg->srcfmt->channels;
295 	xor = audio_format2_is_signed(arg->dstfmt)
296 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
297 	is_dst_LE = (audio_format2_endian(arg->dstfmt) == LITTLE_ENDIAN);
298 
299 	for (i = 0; i < sample_count; i++) {
300 		uint32_t val;
301 		val = *s++;
302 		val ^= xor;
303 #if AUDIO_INTERNAL_BITS < 24
304 		val <<= 24 - AUDIO_INTERNAL_BITS;
305 #else
306 		val >>= AUDIO_INTERNAL_BITS - 24;
307 #endif
308 		if (is_dst_LE) {
309 			d[0] = val & 0xff;
310 			d[1] = (val >> 8) & 0xff;
311 			d[2] = (val >> 16) & 0xff;
312 		} else {
313 			d[0] = (val >> 16) & 0xff;
314 			d[1] = (val >> 8) & 0xff;
315 			d[2] = val & 0xff;
316 		}
317 		d += 3;
318 	}
319 }
320 #endif /* AUDIO_SUPPORT_LINEAR24 */
321 
322 /*
323  * audio_linear32_to_internal:
324  *	This filter performs conversion from [US]LINEAR32{LE,BE} to internal
325  *	format.  Since it's rarely used, it's size optimized.
326  */
327 void
audio_linear32_to_internal(audio_filter_arg_t * arg)328 audio_linear32_to_internal(audio_filter_arg_t *arg)
329 {
330 	const uint32_t *s;
331 	aint_t *d;
332 	auint_t xor;
333 	u_int sample_count;
334 	u_int i;
335 	bool is_src_NE;
336 
337 	DIAGNOSTIC_filter_arg(arg);
338 	KASSERT(audio_format2_is_linear(arg->srcfmt));
339 	KASSERT(arg->srcfmt->precision == 32);
340 	KASSERT(arg->srcfmt->stride == 32);
341 	KASSERT(audio_format2_is_internal(arg->dstfmt));
342 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
343 
344 	s = arg->src;
345 	d = arg->dst;
346 	sample_count = arg->count * arg->srcfmt->channels;
347 	xor = audio_format2_is_signed(arg->srcfmt)
348 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
349 	is_src_NE = (audio_format2_endian(arg->srcfmt) == BYTE_ORDER);
350 
351 	for (i = 0; i < sample_count; i++) {
352 		uint32_t val;
353 		val = *s++;
354 		if (!is_src_NE)
355 			val = bswap32(val);
356 		val >>= 32 - AUDIO_INTERNAL_BITS;
357 		val ^= xor;
358 		*d++ = val;
359 	}
360 }
361 
362 /*
363  * audio_internal_to_linear32:
364  *	This filter performs conversion from internal format to
365  *	[US]LINEAR32{LE,BE}.  Since it's rarely used, it's size optimized.
366  */
367 void
audio_internal_to_linear32(audio_filter_arg_t * arg)368 audio_internal_to_linear32(audio_filter_arg_t *arg)
369 {
370 	const aint_t *s;
371 	uint32_t *d;
372 	auint_t xor;
373 	u_int sample_count;
374 	u_int i;
375 	bool is_dst_NE;
376 
377 	DIAGNOSTIC_filter_arg(arg);
378 	KASSERT(audio_format2_is_linear(arg->dstfmt));
379 	KASSERT(arg->dstfmt->precision == 32);
380 	KASSERT(arg->dstfmt->stride == 32);
381 	KASSERT(audio_format2_is_internal(arg->srcfmt));
382 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
383 
384 	s = arg->src;
385 	d = arg->dst;
386 	sample_count = arg->count * arg->srcfmt->channels;
387 	xor = audio_format2_is_signed(arg->dstfmt)
388 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
389 	is_dst_NE = (audio_format2_endian(arg->dstfmt) == BYTE_ORDER);
390 
391 	for (i = 0; i < sample_count; i++) {
392 		uint32_t val;
393 		val = *s++;
394 		val ^= xor;
395 		val <<= 32 - AUDIO_INTERNAL_BITS;
396 		if (!is_dst_NE)
397 			val = bswap32(val);
398 		*d++ = val;
399 	}
400 }
401