1 /* Spa
2  *
3  * Copyright © 2019 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <string.h>
26 #include <stdio.h>
27 #include <math.h>
28 
29 #include <spa/support/cpu.h>
30 #include <spa/utils/defs.h>
31 #include <spa/param/audio/format-utils.h>
32 
33 #include "mix-ops.h"
34 
35 typedef void (*mix_func_t) (struct mix_ops *ops, void * SPA_RESTRICT dst,
36 		const void * SPA_RESTRICT src[], uint32_t n_src, uint32_t n_samples);
37 
38 struct mix_info {
39 	uint32_t fmt;
40 	uint32_t n_channels;
41 	uint32_t cpu_flags;
42 	uint32_t stride;
43 	mix_func_t process;
44 };
45 
46 static struct mix_info mix_table[] =
47 {
48 	/* f32 */
49 #if defined(HAVE_AVX)
50 	{ SPA_AUDIO_FORMAT_F32, 0, SPA_CPU_FLAG_AVX, 4, mix_f32_avx },
51 	{ SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_AVX, 4, mix_f32_avx },
52 #endif
53 #if defined (HAVE_SSE)
54 	{ SPA_AUDIO_FORMAT_F32, 0, SPA_CPU_FLAG_SSE, 4, mix_f32_sse },
55 	{ SPA_AUDIO_FORMAT_F32P, 0, SPA_CPU_FLAG_SSE, 4, mix_f32_sse },
56 #endif
57 	{ SPA_AUDIO_FORMAT_F32, 0, 0, 4, mix_f32_c },
58 	{ SPA_AUDIO_FORMAT_F32P, 0, 0, 4, mix_f32_c },
59 
60 	/* f64 */
61 #if defined (HAVE_SSE2)
62 	{ SPA_AUDIO_FORMAT_F64, 0, SPA_CPU_FLAG_SSE2, 8, mix_f64_sse2 },
63 	{ SPA_AUDIO_FORMAT_F64P, 0, SPA_CPU_FLAG_SSE2, 8, mix_f64_sse2 },
64 #endif
65 	{ SPA_AUDIO_FORMAT_F64, 0, 0, 8, mix_f64_c },
66 	{ SPA_AUDIO_FORMAT_F64P, 0, 0, 8, mix_f64_c },
67 
68 	/* s8 */
69 	{ SPA_AUDIO_FORMAT_S8, 0, 0, 1, mix_s8_c },
70 	{ SPA_AUDIO_FORMAT_S8P, 0, 0, 1, mix_s8_c },
71 	{ SPA_AUDIO_FORMAT_U8, 0, 0, 1, mix_u8_c },
72 	{ SPA_AUDIO_FORMAT_U8P, 0, 0, 1, mix_u8_c },
73 
74 	/* s16 */
75 	{ SPA_AUDIO_FORMAT_S16, 0, 0, 2, mix_s16_c },
76 	{ SPA_AUDIO_FORMAT_S16P, 0, 0, 2, mix_s16_c },
77 	{ SPA_AUDIO_FORMAT_U16, 0, 0, 2, mix_u16_c },
78 
79 	/* s24 */
80 	{ SPA_AUDIO_FORMAT_S24, 0, 0, 3, mix_s24_c },
81 	{ SPA_AUDIO_FORMAT_S24P, 0, 0, 3, mix_s24_c },
82 	{ SPA_AUDIO_FORMAT_U24, 0, 0, 3, mix_u24_c },
83 
84 	/* s32 */
85 	{ SPA_AUDIO_FORMAT_S32, 0, 0, 4, mix_s32_c },
86 	{ SPA_AUDIO_FORMAT_S32P, 0, 0, 4, mix_s32_c },
87 	{ SPA_AUDIO_FORMAT_U32, 0, 0, 4, mix_u32_c },
88 
89 	/* s24_32 */
90 	{ SPA_AUDIO_FORMAT_S24_32, 0, 0, 4, mix_s24_32_c },
91 	{ SPA_AUDIO_FORMAT_S24_32P, 0, 0, 4, mix_s24_32_c },
92 	{ SPA_AUDIO_FORMAT_U24_32, 0, 0, 4, mix_u24_32_c },
93 };
94 
95 #define MATCH_CHAN(a,b)		((a) == 0 || (a) == (b))
96 #define MATCH_CPU_FLAGS(a,b)	((a) == 0 || ((a) & (b)) == a)
97 
find_mix_info(uint32_t fmt,uint32_t n_channels,uint32_t cpu_flags)98 static const struct mix_info *find_mix_info(uint32_t fmt,
99 		uint32_t n_channels, uint32_t cpu_flags)
100 {
101 	size_t i;
102 
103 	for (i = 0; i < SPA_N_ELEMENTS(mix_table); i++) {
104 		if (mix_table[i].fmt == fmt &&
105 		    MATCH_CHAN(mix_table[i].n_channels, n_channels) &&
106 		    MATCH_CPU_FLAGS(mix_table[i].cpu_flags, cpu_flags))
107 			return &mix_table[i];
108 	}
109 	return NULL;
110 }
111 
impl_mix_ops_clear(struct mix_ops * ops,void * SPA_RESTRICT dst,uint32_t n_samples)112 static void impl_mix_ops_clear(struct mix_ops *ops, void * SPA_RESTRICT dst, uint32_t n_samples)
113 {
114 	const struct mix_info *info = ops->priv;
115 	memset(dst, 0, n_samples * info->stride);
116 }
117 
impl_mix_ops_free(struct mix_ops * ops)118 static void impl_mix_ops_free(struct mix_ops *ops)
119 {
120 	spa_zero(*ops);
121 }
122 
mix_ops_init(struct mix_ops * ops)123 int mix_ops_init(struct mix_ops *ops)
124 {
125 	const struct mix_info *info;
126 
127 	info = find_mix_info(ops->fmt, ops->n_channels, ops->cpu_flags);
128 	if (info == NULL)
129 		return -ENOTSUP;
130 
131 	ops->priv = info;
132 	ops->cpu_flags = info->cpu_flags;
133 	ops->clear = impl_mix_ops_clear;
134 	ops->process = info->process;
135 	ops->free = impl_mix_ops_free;
136 
137 	return 0;
138 }
139