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