xref: /netbsd-src/sys/dev/audio/linear.c (revision f3cfa6f6ce31685c6c4a758bc430e69eb99f50a4)
1 /*	$NetBSD: linear.c,v 1.2 2019/05/08 13:40:17 isaki 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 #if defined(_KERNEL)
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: linear.c,v 1.2 2019/05/08 13:40:17 isaki Exp $");
32 
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 #else
39 #include <stdint.h>
40 #include <stdbool.h>
41 #include "compat.h"
42 #include "audiovar.h"
43 #endif /* _KERNEL */
44 
45 /*
46  * audio_linear8_to_internal:
47  *	This filter performs conversion from [US]LINEAR8 to internal format.
48  */
49 void
50 audio_linear8_to_internal(audio_filter_arg_t *arg)
51 {
52 	const uint8_t *s;
53 	aint_t *d;
54 	uint8_t xor;
55 	u_int sample_count;
56 	u_int i;
57 
58 	DIAGNOSTIC_filter_arg(arg);
59 	KASSERT(audio_format2_is_linear(arg->srcfmt));
60 	KASSERT(arg->srcfmt->precision == 8);
61 	KASSERT(arg->srcfmt->stride == 8);
62 	KASSERT(audio_format2_is_internal(arg->dstfmt));
63 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
64 
65 	s = arg->src;
66 	d = arg->dst;
67 	sample_count = arg->count * arg->srcfmt->channels;
68 	xor = audio_format2_is_signed(arg->srcfmt) ? 0 : 0x80;
69 
70 	for (i = 0; i < sample_count; i++) {
71 		uint8_t val;
72 		val = *s++;
73 		val ^= xor;
74 		*d++ = (auint_t)val << (AUDIO_INTERNAL_BITS - 8);
75 	}
76 }
77 
78 /*
79  * audio_internal_to_linear8:
80  *	This filter performs conversion from internal format to [US]LINEAR8.
81  */
82 void
83 audio_internal_to_linear8(audio_filter_arg_t *arg)
84 {
85 	const aint_t *s;
86 	uint8_t *d;
87 	uint8_t xor;
88 	u_int sample_count;
89 	u_int i;
90 
91 	DIAGNOSTIC_filter_arg(arg);
92 	KASSERT(audio_format2_is_linear(arg->dstfmt));
93 	KASSERT(arg->dstfmt->precision == 8);
94 	KASSERT(arg->dstfmt->stride == 8);
95 	KASSERT(audio_format2_is_internal(arg->srcfmt));
96 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
97 
98 	s = arg->src;
99 	d = arg->dst;
100 	sample_count = arg->count * arg->srcfmt->channels;
101 	xor = audio_format2_is_signed(arg->dstfmt) ? 0 : 0x80;
102 
103 	for (i = 0; i < sample_count; i++) {
104 		uint8_t val;
105 		val = (*s++) >> (AUDIO_INTERNAL_BITS - 8);
106 		val ^= xor;
107 		*d++ = val;
108 	}
109 }
110 
111 /*
112  * audio_linear16_to_internal:
113  *	This filter performs conversion from [US]LINEAR16{LE,BE} to internal
114  *	format.
115  */
116 void
117 audio_linear16_to_internal(audio_filter_arg_t *arg)
118 {
119 	const uint16_t *s;
120 	aint_t *d;
121 	uint16_t xor;
122 	u_int sample_count;
123 	u_int shift;
124 	u_int i;
125 	bool is_src_NE;
126 
127 	DIAGNOSTIC_filter_arg(arg);
128 	KASSERT(audio_format2_is_linear(arg->srcfmt));
129 	KASSERT(arg->srcfmt->precision == 16);
130 	KASSERT(arg->srcfmt->stride == 16);
131 	KASSERT(audio_format2_is_internal(arg->dstfmt));
132 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
133 
134 	s = arg->src;
135 	d = arg->dst;
136 	sample_count = arg->count * arg->srcfmt->channels;
137 
138 	shift = AUDIO_INTERNAL_BITS - 16;
139 	xor = audio_format2_is_signed(arg->srcfmt) ? 0 : 0x8000;
140 	is_src_NE = (audio_format2_endian(arg->srcfmt) == BYTE_ORDER);
141 
142 	/*
143 	 * Since slinear16_OppositeEndian to slinear_NativeEndian is used
144 	 * so much especially on big endian machines, so it's expanded.
145 	 * Other conversions are rarely used, so they are compressed.
146 	 */
147 	if (__predict_true(xor == 0) && is_src_NE == false) {
148 		/* slinear16_OE to slinear<AI>_NE */
149 		for (i = 0; i < sample_count; i++) {
150 			uint16_t val;
151 			val = *s++;
152 			val = bswap16(val);
153 			*d++ = (auint_t)val << shift;
154 		}
155 	} else {
156 		/* slinear16_NE      to slinear<AI>_NE */
157 		/* ulinear16_{NE,OE} to slinear<AI>_NE */
158 		for (i = 0; i < sample_count; i++) {
159 			uint16_t val;
160 			val = *s++;
161 			if (!is_src_NE)
162 				val = bswap16(val);
163 			val ^= xor;
164 			*d++ = (auint_t)val << shift;
165 		}
166 	}
167 }
168 
169 /*
170  * audio_internal_to_linear16:
171  *	This filter performs conversion from internal format to
172  *	[US]LINEAR16{LE,BE}.
173  */
174 void
175 audio_internal_to_linear16(audio_filter_arg_t *arg)
176 {
177 	const aint_t *s;
178 	uint16_t *d;
179 	uint16_t xor;
180 	u_int sample_count;
181 	u_int shift;
182 	u_int i;
183 	bool is_dst_NE;
184 
185 	DIAGNOSTIC_filter_arg(arg);
186 	KASSERT(audio_format2_is_linear(arg->dstfmt));
187 	KASSERT(arg->dstfmt->precision == 16);
188 	KASSERT(arg->dstfmt->stride == 16);
189 	KASSERT(audio_format2_is_internal(arg->srcfmt));
190 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
191 
192 	s = arg->src;
193 	d = arg->dst;
194 	sample_count = arg->count * arg->srcfmt->channels;
195 
196 	shift = AUDIO_INTERNAL_BITS - 16;
197 	xor = audio_format2_is_signed(arg->dstfmt) ? 0 : 0x8000;
198 	is_dst_NE = (audio_format2_endian(arg->dstfmt) == BYTE_ORDER);
199 
200 	/*
201 	 * Since slinear_NativeEndian to slinear16_OppositeEndian is used
202 	 * so much especially on big endian machines, so it's expanded.
203 	 * Other conversions are rarely used, so they are compressed.
204 	 */
205 	if (__predict_true(xor == 0) && is_dst_NE == false) {
206 		/* slinear<AI>_NE -> slinear16_OE */
207 		for (i = 0; i < sample_count; i++) {
208 			uint16_t val;
209 			val = (*s++) >> shift;
210 			val = bswap16(val);
211 			*d++ = val;
212 		}
213 	} else {
214 		/* slinear<AI>_NE -> slinear16_NE */
215 		/* slinear<AI>_NE -> ulinear16_{NE,OE} */
216 		for (i = 0; i < sample_count; i++) {
217 			uint16_t val;
218 			val = (*s++) >> shift;
219 			val ^= xor;
220 			if (!is_dst_NE)
221 				val = bswap16(val);
222 			*d++ = val;
223 		}
224 	}
225 }
226 
227 #if defined(AUDIO_SUPPORT_LINEAR24)
228 /*
229  * audio_linear24_to_internal:
230  *	This filter performs conversion from [US]LINEAR24/24{LE,BE} to
231  *	internal format.  Since it's rerely used, it's size optimized.
232  */
233 void
234 audio_linear24_to_internal(audio_filter_arg_t *arg)
235 {
236 	const uint8_t *s;
237 	aint_t *d;
238 	auint_t xor;
239 	u_int sample_count;
240 	u_int i;
241 	bool is_src_LE;
242 
243 	DIAGNOSTIC_filter_arg(arg);
244 	KASSERT(audio_format2_is_linear(arg->srcfmt));
245 	KASSERT(arg->srcfmt->precision == 24);
246 	KASSERT(arg->srcfmt->stride == 24);
247 	KASSERT(audio_format2_is_internal(arg->dstfmt));
248 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
249 
250 	s = arg->src;
251 	d = arg->dst;
252 	sample_count = arg->count * arg->srcfmt->channels;
253 	xor = audio_format2_is_signed(arg->srcfmt)
254 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
255 	is_src_LE = (audio_format2_endian(arg->srcfmt) == LITTLE_ENDIAN);
256 
257 	for (i = 0; i < sample_count; i++) {
258 		uint32_t val;
259 		if (is_src_LE) {
260 			val = s[0] | (s[1] << 8) | (s[2] << 16);
261 		} else {
262 			val = (s[0] << 16) | (s[1] << 8) | s[2];
263 		}
264 		s += 3;
265 
266 #if AUDIO_INTERNAL_BITS < 24
267 		val >>= 24 - AUDIO_INTERNAL_BITS;
268 #else
269 		val <<= AUDIO_INTERNAL_BITS - 24;
270 #endif
271 		val ^= xor;
272 		*d++ = val;
273 	}
274 }
275 
276 /*
277  * audio_internal_to_linear24:
278  *	This filter performs conversion from internal format to
279  *	[US]LINEAR24/24{LE,BE}.  Since it's rarely used, it's size optimized.
280  */
281 void
282 audio_internal_to_linear24(audio_filter_arg_t *arg)
283 {
284 	const aint_t *s;
285 	uint8_t *d;
286 	auint_t xor;
287 	u_int sample_count;
288 	u_int i;
289 	bool is_dst_LE;
290 
291 	DIAGNOSTIC_filter_arg(arg);
292 	KASSERT(audio_format2_is_linear(arg->dstfmt));
293 	KASSERT(arg->dstfmt->precision == 24);
294 	KASSERT(arg->dstfmt->stride == 24);
295 	KASSERT(audio_format2_is_internal(arg->srcfmt));
296 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
297 
298 	s = arg->src;
299 	d = arg->dst;
300 	sample_count = arg->count * arg->srcfmt->channels;
301 	xor = audio_format2_is_signed(arg->dstfmt)
302 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
303 	is_dst_LE = (audio_format2_endian(arg->dstfmt) == LITTLE_ENDIAN);
304 
305 	for (i = 0; i < sample_count; i++) {
306 		uint32_t val;
307 		val = *s++;
308 		val ^= xor;
309 #if AUDIO_INTERNAL_BITS < 24
310 		val <<= 24 - AUDIO_INTERNAL_BITS;
311 #else
312 		val >>= AUDIO_INTERNAL_BITS - 24;
313 #endif
314 		if (is_dst_LE) {
315 			d[0] = val & 0xff;
316 			d[1] = (val >> 8) & 0xff;
317 			d[2] = (val >> 16) & 0xff;
318 		} else {
319 			d[0] = (val >> 16) & 0xff;
320 			d[1] = (val >> 8) & 0xff;
321 			d[2] = val & 0xff;
322 		}
323 		d += 3;
324 	}
325 }
326 #endif /* AUDIO_SUPPORT_LINEAR24 */
327 
328 /*
329  * audio_linear32_to_internal:
330  *	This filter performs conversion from [US]LINEAR32{LE,BE} to internal
331  *	format.  Since it's rarely used, it's size optimized.
332  */
333 void
334 audio_linear32_to_internal(audio_filter_arg_t *arg)
335 {
336 	const uint32_t *s;
337 	aint_t *d;
338 	auint_t xor;
339 	u_int sample_count;
340 	u_int i;
341 	bool is_src_NE;
342 
343 	DIAGNOSTIC_filter_arg(arg);
344 	KASSERT(audio_format2_is_linear(arg->srcfmt));
345 	KASSERT(arg->srcfmt->precision == 32);
346 	KASSERT(arg->srcfmt->stride == 32);
347 	KASSERT(audio_format2_is_internal(arg->dstfmt));
348 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
349 
350 	s = arg->src;
351 	d = arg->dst;
352 	sample_count = arg->count * arg->srcfmt->channels;
353 	xor = audio_format2_is_signed(arg->srcfmt)
354 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
355 	is_src_NE = (audio_format2_endian(arg->srcfmt) == BYTE_ORDER);
356 
357 	for (i = 0; i < sample_count; i++) {
358 		uint32_t val;
359 		val = *s++;
360 		if (!is_src_NE)
361 			val = bswap32(val);
362 		val >>= 32 - AUDIO_INTERNAL_BITS;
363 		val ^= xor;
364 		*d++ = val;
365 	}
366 }
367 
368 /*
369  * audio_internal_to_linear32:
370  *	This filter performs conversion from internal format to
371  *	[US]LINEAR32{LE,BE}.  Since it's rarely used, it's size optimized.
372  */
373 void
374 audio_internal_to_linear32(audio_filter_arg_t *arg)
375 {
376 	const aint_t *s;
377 	uint32_t *d;
378 	auint_t xor;
379 	u_int sample_count;
380 	u_int i;
381 	bool is_dst_NE;
382 
383 	DIAGNOSTIC_filter_arg(arg);
384 	KASSERT(audio_format2_is_linear(arg->dstfmt));
385 	KASSERT(arg->dstfmt->precision == 32);
386 	KASSERT(arg->dstfmt->stride == 32);
387 	KASSERT(audio_format2_is_internal(arg->srcfmt));
388 	KASSERT(arg->srcfmt->channels == arg->dstfmt->channels);
389 
390 	s = arg->src;
391 	d = arg->dst;
392 	sample_count = arg->count * arg->srcfmt->channels;
393 	xor = audio_format2_is_signed(arg->dstfmt)
394 	    ? 0 : (1 << (AUDIO_INTERNAL_BITS - 1));
395 	is_dst_NE = (audio_format2_endian(arg->dstfmt) == BYTE_ORDER);
396 
397 	for (i = 0; i < sample_count; i++) {
398 		uint32_t val;
399 		val = *s++;
400 		val ^= xor;
401 		val <<= 32 - AUDIO_INTERNAL_BITS;
402 		if (!is_dst_NE)
403 			val = bswap32(val);
404 		*d++ = val;
405 	}
406 }
407