C++程序  |  163行  |  4.19 KB

/* 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,  0, 0, 0, 0,  0 }, /* RL */
	{  0, 0, 0, 0, 0, 0,  0, 0, 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] >= (int)format->num_channels)
			return -EINVAL;

	for (i = 0; i < CRAS_CH_MAX; i++)
		format->channel_layout[i] = layout[i];

	return 0;
}

/* 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;
}