1 /* Copyright (c) 2012 The Chromium OS 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 
6 #include <errno.h>
7 #include <stddef.h>
8 #include <stdlib.h>
9 #include <syslog.h>
10 
11 #include "cras_audio_format.h"
12 
13 /* Table for allowed alternatives when doing channel re-mapping.
14  * When channel_alt[X][Y] is non-zero, it's allowed to map channel
15  * from X to Y. */
16 static const int channel_alt[CRAS_CH_MAX][CRAS_CH_MAX] = {
17 	/*FL FR RL RR FC LFE SL SR RC FLC FRC */
18 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FL */
19 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FR */
20 	{ 0, 0, 0, 0, 0, 0, +1, 0, 0, 0, +0 }, /* RL */
21 	{ 0, 0, 0, 0, 0, 0, +0, 1, 0, 0, +0 }, /* RR */
22 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FC */
23 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* LFE */
24 	{ 0, 0, 1, 0, 0, 0, +0, 0, 0, 0, +0 }, /* SL */
25 	{ 0, 0, 0, 1, 0, 0, +0, 0, 0, 0, +0 }, /* SR */
26 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* RC */
27 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FLC */
28 	{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FRC */
29 };
30 
31 /* Create an audio format structure. */
cras_audio_format_create(snd_pcm_format_t format,size_t frame_rate,size_t num_channels)32 struct cras_audio_format *cras_audio_format_create(snd_pcm_format_t format,
33 						   size_t frame_rate,
34 						   size_t num_channels)
35 {
36 	struct cras_audio_format *fmt;
37 	unsigned i;
38 
39 	fmt = (struct cras_audio_format *)calloc(1, sizeof(*fmt));
40 	if (fmt == NULL)
41 		return fmt;
42 
43 	fmt->format = format;
44 	fmt->frame_rate = frame_rate;
45 	fmt->num_channels = num_channels;
46 
47 	/* Set a default working channel layout according to num_channels.
48 	 * Initialize all other channel position to -1(not set)
49 	 */
50 	for (i = 0; i < CRAS_CH_MAX; i++)
51 		fmt->channel_layout[i] = (i < num_channels) ? i : -1;
52 
53 	return fmt;
54 }
55 
cras_audio_format_set_channel_layout(struct cras_audio_format * format,const int8_t layout[CRAS_CH_MAX])56 int cras_audio_format_set_channel_layout(struct cras_audio_format *format,
57 					 const int8_t layout[CRAS_CH_MAX])
58 {
59 	int i;
60 
61 	/* Check that the max channel index should not exceed the
62 	 * channel count set in format.
63 	 */
64 	for (i = 0; i < CRAS_CH_MAX; i++)
65 		if (layout[i] < -1 || layout[i] >= (int)format->num_channels)
66 			return -EINVAL;
67 
68 	for (i = 0; i < CRAS_CH_MAX; i++)
69 		format->channel_layout[i] = layout[i];
70 
71 	return 0;
72 }
73 
74 /* Verifies if all channel_layout[i] are in [-1, fmt->num_channels). */
cras_audio_format_valid(const struct cras_audio_format * fmt)75 bool cras_audio_format_valid(const struct cras_audio_format *fmt)
76 {
77 	int i;
78 	for (i = 0; i < CRAS_CH_MAX; i++) {
79 		if (fmt->channel_layout[i] < -1 ||
80 		    fmt->channel_layout[i] >= (int)fmt->num_channels) {
81 			return false;
82 		}
83 	}
84 	return true;
85 }
86 
87 /* Destroy an audio format struct created with cras_audio_format_crate. */
cras_audio_format_destroy(struct cras_audio_format * fmt)88 void cras_audio_format_destroy(struct cras_audio_format *fmt)
89 {
90 	free(fmt);
91 }
92 
cras_channel_conv_matrix_alloc(size_t in_ch,size_t out_ch)93 float **cras_channel_conv_matrix_alloc(size_t in_ch, size_t out_ch)
94 {
95 	size_t i;
96 	float **p;
97 	p = (float **)calloc(out_ch, sizeof(*p));
98 	if (p == NULL)
99 		return NULL;
100 	for (i = 0; i < out_ch; i++) {
101 		p[i] = (float *)calloc(in_ch, sizeof(*p[i]));
102 		if (p[i] == NULL)
103 			goto alloc_err;
104 	}
105 	return p;
106 
107 alloc_err:
108 	if (p)
109 		cras_channel_conv_matrix_destroy(p, out_ch);
110 	return NULL;
111 }
112 
cras_channel_conv_matrix_destroy(float ** p,size_t out_ch)113 void cras_channel_conv_matrix_destroy(float **p, size_t out_ch)
114 {
115 	size_t i;
116 	for (i = 0; i < out_ch; i++)
117 		free(p[i]);
118 	free(p);
119 }
120 
cras_channel_conv_matrix_create(const struct cras_audio_format * in,const struct cras_audio_format * out)121 float **cras_channel_conv_matrix_create(const struct cras_audio_format *in,
122 					const struct cras_audio_format *out)
123 {
124 	int i;
125 	float **mtx;
126 
127 	for (i = 0; i < CRAS_CH_MAX; i++) {
128 		if (in->channel_layout[i] >= (int)in->num_channels ||
129 		    out->channel_layout[i] >= (int)out->num_channels) {
130 			syslog(LOG_ERR, "Fail to create conversion matrix "
131 					"due to invalid channel layout");
132 			return NULL;
133 		}
134 	}
135 
136 	mtx = cras_channel_conv_matrix_alloc(in->num_channels,
137 					     out->num_channels);
138 
139 	/* For the in/out format pair which has the same set of channels
140 	 * in use, create a permutation matrix for them.
141 	 */
142 	for (i = 0; i < CRAS_CH_MAX; i++) {
143 		if (in->channel_layout[i] == -1 && out->channel_layout[i] == -1)
144 			continue;
145 		if (in->channel_layout[i] != -1 && out->channel_layout[i] != -1)
146 			mtx[out->channel_layout[i]][in->channel_layout[i]] = 1;
147 		else if (in->channel_layout[i] != -1) {
148 			/* When the same channel does not appear at output
149 			 * channel layout. Look up for allowed channel
150 			 * alternatives.
151 			 */
152 			int alt;
153 			for (alt = 0; alt <= CRAS_CH_MAX; alt++) {
154 				if (alt == CRAS_CH_MAX)
155 					goto fail;
156 				if (channel_alt[i][alt] &&
157 				    in->channel_layout[alt] == -1 &&
158 				    out->channel_layout[alt] != -1) {
159 					mtx[out->channel_layout[alt]]
160 					   [in->channel_layout[i]] = 1;
161 					break;
162 				}
163 			}
164 		}
165 	}
166 
167 	return mtx;
168 fail:
169 	cras_channel_conv_matrix_destroy(mtx, out->num_channels);
170 	return NULL;
171 }
172