blob: 8bd486562450abda0ec7e403cc05c0810ce95c8b [file] [log] [blame]
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <syslog.h>
#include "cras_audio_format.h"
/* Table for allowed alternatives when doing channel re-mapping.
* When channel_alt[X][Y] is non-zero, it's allowed to map channel
* from X to Y. */
static const int channel_alt[CRAS_CH_MAX][CRAS_CH_MAX] = {
/*FL FR RL RR FC LFE SL SR RC FLC FRC */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FL */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FR */
{ 0, 0, 0, 0, 0, 0, +1, 0, 0, 0, +0 }, /* RL */
{ 0, 0, 0, 0, 0, 0, +0, 1, 0, 0, +0 }, /* RR */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FC */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* LFE */
{ 0, 0, 1, 0, 0, 0, +0, 0, 0, 0, +0 }, /* SL */
{ 0, 0, 0, 1, 0, 0, +0, 0, 0, 0, +0 }, /* SR */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* RC */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FLC */
{ 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, +0 }, /* FRC */
};
/* Create an audio format structure. */
struct cras_audio_format *cras_audio_format_create(snd_pcm_format_t format,
size_t frame_rate,
size_t num_channels)
{
struct cras_audio_format *fmt;
unsigned i;
fmt = (struct cras_audio_format *)calloc(1, sizeof(*fmt));
if (fmt == NULL)
return fmt;
fmt->format = format;
fmt->frame_rate = frame_rate;
fmt->num_channels = num_channels;
/* Set a default working channel layout according to num_channels.
* Initialize all other channel position to -1(not set)
*/
for (i = 0; i < CRAS_CH_MAX; i++)
fmt->channel_layout[i] = (i < num_channels) ? i : -1;
return fmt;
}
int cras_audio_format_set_channel_layout(struct cras_audio_format *format,
const int8_t layout[CRAS_CH_MAX])
{
int i;
/* Check that the max channel index should not exceed the
* channel count set in format.
*/
for (i = 0; i < CRAS_CH_MAX; i++)
if (layout[i] < -1 || layout[i] >= (int)format->num_channels)
return -EINVAL;
for (i = 0; i < CRAS_CH_MAX; i++)
format->channel_layout[i] = layout[i];
return 0;
}
/* Verifies if all channel_layout[i] are in [-1, fmt->num_channels). */
bool cras_audio_format_valid(const struct cras_audio_format *fmt)
{
int i;
for (i = 0; i < CRAS_CH_MAX; i++) {
if (fmt->channel_layout[i] < -1 ||
fmt->channel_layout[i] >= (int)fmt->num_channels) {
return false;
}
}
return true;
}
/* Destroy an audio format struct created with cras_audio_format_crate. */
void cras_audio_format_destroy(struct cras_audio_format *fmt)
{
free(fmt);
}
float **cras_channel_conv_matrix_alloc(size_t in_ch, size_t out_ch)
{
size_t i;
float **p;
p = (float **)calloc(out_ch, sizeof(*p));
if (p == NULL)
return NULL;
for (i = 0; i < out_ch; i++) {
p[i] = (float *)calloc(in_ch, sizeof(*p[i]));
if (p[i] == NULL)
goto alloc_err;
}
return p;
alloc_err:
if (p)
cras_channel_conv_matrix_destroy(p, out_ch);
return NULL;
}
void cras_channel_conv_matrix_destroy(float **p, size_t out_ch)
{
size_t i;
for (i = 0; i < out_ch; i++)
free(p[i]);
free(p);
}
float **cras_channel_conv_matrix_create(const struct cras_audio_format *in,
const struct cras_audio_format *out)
{
int i;
float **mtx;
for (i = 0; i < CRAS_CH_MAX; i++) {
if (in->channel_layout[i] >= (int)in->num_channels ||
out->channel_layout[i] >= (int)out->num_channels) {
syslog(LOG_ERR, "Fail to create conversion matrix "
"due to invalid channel layout");
return NULL;
}
}
mtx = cras_channel_conv_matrix_alloc(in->num_channels,
out->num_channels);
/* For the in/out format pair which has the same set of channels
* in use, create a permutation matrix for them.
*/
for (i = 0; i < CRAS_CH_MAX; i++) {
if (in->channel_layout[i] == -1 && out->channel_layout[i] == -1)
continue;
if (in->channel_layout[i] != -1 && out->channel_layout[i] != -1)
mtx[out->channel_layout[i]][in->channel_layout[i]] = 1;
else if (in->channel_layout[i] != -1) {
/* When the same channel does not appear at output
* channel layout. Look up for allowed channel
* alternatives.
*/
int alt;
for (alt = 0; alt <= CRAS_CH_MAX; alt++) {
if (alt == CRAS_CH_MAX)
goto fail;
if (channel_alt[i][alt] &&
in->channel_layout[alt] == -1 &&
out->channel_layout[alt] != -1) {
mtx[out->channel_layout[alt]]
[in->channel_layout[i]] = 1;
break;
}
}
}
}
return mtx;
fail:
cras_channel_conv_matrix_destroy(mtx, out->num_channels);
return NULL;
}