1456 lines
51 KiB
C
1456 lines
51 KiB
C
/*
|
|
* Copyright (c) 2020 Amlogic, Inc. All rights reserved.
|
|
* *
|
|
This source code is subject to the terms and conditions defined in the
|
|
* file 'LICENSE' which is part of this source code package.
|
|
* *
|
|
Description:
|
|
*/
|
|
|
|
|
|
#define LOG_TAG "audio_data_process"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include "aml_audio_log.h"
|
|
#include <system/audio.h>
|
|
|
|
#include "audio_data_process.h"
|
|
#include "aml_malloc_debug.h"
|
|
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
|
|
#define MINUS_3_DB_IN_FLOAT M_SQRT1_2 /*-3dB = 0.70710678 */
|
|
|
|
extern void memcpy_by_audio_format(void *dst, audio_format_t dst_format,
|
|
const void *src, audio_format_t src_format, size_t count);
|
|
void memcpy_by_channel_mask(void *dst, uint32_t dst_mask,
|
|
const void *src, uint32_t src_mask, size_t sample_size, size_t count);
|
|
|
|
static int ch2_ch8_n_b16_b32(void *data_mixed, void *data_sys, size_t frames)
|
|
{
|
|
uint32_t *out_buf = data_mixed;
|
|
uint16_t *in_buf = data_sys;
|
|
uint32_t i = 0;
|
|
for (i = 0; i < frames; i++) {
|
|
out_buf[8 * i] = in_buf[2 * i] << 16;
|
|
out_buf[8 * i + 1] = in_buf[2 * i + 1] << 16;
|
|
out_buf[8 * i + 2] = in_buf[2 * i] << 16;
|
|
out_buf[8 * i + 3] = in_buf[2 * i + 1] << 16;
|
|
out_buf[8 * i + 4] = in_buf[2 * i] << 16;
|
|
out_buf[8 * i + 5] = in_buf[2 * i + 1] << 16;
|
|
out_buf[8 * i + 6] = in_buf[2 * i] << 16;
|
|
out_buf[8 * i + 7] = in_buf[2 * i + 1] << 16;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline short CLIPSHORT(int32_t r)
|
|
{
|
|
if (r > 32767)
|
|
r = 32767;
|
|
else if (r < -32768)
|
|
r = -32768;
|
|
return r;
|
|
}
|
|
|
|
static inline int CLIPINT(int64_t r)
|
|
{
|
|
if (r > 2147483647)
|
|
r = 2147483647;
|
|
else if (r < -2147483648)
|
|
r = -2147483648;
|
|
return r;
|
|
}
|
|
|
|
//should be same channelCnt 2
|
|
int do_mixing_2ch(void *data_mixed,
|
|
void *data_in, size_t frames,
|
|
audio_format_t in_format, audio_format_t out_format)
|
|
{
|
|
int i = 0;
|
|
if (out_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
if (in_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
int16_t *in = data_in;
|
|
int32_t *out = data_mixed;
|
|
int64_t tmp = 0;
|
|
for (i = 0; i < frames * 2; i++) {
|
|
tmp = (int64_t)*out + (int64_t)((*in++) << 16);
|
|
*out++ = CLIPINT(tmp);
|
|
}
|
|
} else if (in_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
int32_t *in = data_in;
|
|
int32_t *out = data_mixed;
|
|
int64_t tmp = 0;
|
|
for (i = 0; i < frames * 2; i++) {
|
|
tmp = (int64_t)*out + (int64_t)*in++;
|
|
*out++ = CLIPINT(tmp);
|
|
}
|
|
}
|
|
} else if (out_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
if (in_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
int16_t *in = data_in;
|
|
int16_t *out = data_mixed;
|
|
int32_t tmp = 0;
|
|
for (i = 0; i < frames * 2; i++) {
|
|
tmp = (int32_t)*out + (int32_t)*in++;
|
|
*out++ = CLIPSHORT(tmp);
|
|
}
|
|
} else if (in_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
int32_t *in = data_in;
|
|
int16_t *out = data_mixed;
|
|
int32_t tmp = 0;
|
|
for (i = 0; i < frames * 2; i++) {
|
|
tmp = (int32_t)*out + ((*in++) >> 16);
|
|
*out++ = CLIPSHORT(tmp);
|
|
}
|
|
}
|
|
} else {
|
|
AM_LOGE("do_mixing_2ch invalid in_format:%#x out_format:%#x invalid", in_format, out_format);
|
|
return 0;
|
|
}
|
|
return frames;
|
|
}
|
|
|
|
/* do mixing with specified channel count and should be same channel count*/
|
|
int do_mixing_specified_channel_cnt(void *data_mixed,
|
|
void *data_in, size_t frames,
|
|
audio_format_t in_format, audio_format_t out_format,
|
|
unsigned int channel_cnt)
|
|
{
|
|
if (0 == channel_cnt || 0 == frames) {
|
|
return 0;
|
|
}
|
|
int i = 0;
|
|
if (out_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
if (in_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
int16_t *in = data_in;
|
|
int32_t *out = data_mixed;
|
|
int64_t tmp = 0;
|
|
for (i = 0; i < frames * channel_cnt; i++) {
|
|
tmp = (int64_t)*out + (int64_t)((*in++) << 16);
|
|
*out++ = CLIPINT(tmp);
|
|
}
|
|
} else if (in_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
int32_t *in = data_in;
|
|
int32_t *out = data_mixed;
|
|
int64_t tmp = 0;
|
|
for (i = 0; i < frames * channel_cnt; i++) {
|
|
tmp = (int64_t)*out + (int64_t)*in++;
|
|
*out++ = CLIPINT(tmp);
|
|
}
|
|
}
|
|
} else if (out_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
if (in_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
int16_t *in = data_in;
|
|
int16_t *out = data_mixed;
|
|
int32_t tmp = 0;
|
|
for (i = 0; i < frames * channel_cnt; i++) {
|
|
tmp = (int32_t)*out + (int32_t)*in++;
|
|
*out++ = CLIPSHORT(tmp);
|
|
}
|
|
} else if (in_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
int32_t *in = data_in;
|
|
int16_t *out = data_mixed;
|
|
int32_t tmp = 0;
|
|
for (i = 0; i < frames * channel_cnt; i++) {
|
|
tmp = (int32_t)*out + ((*in++) >> 16);
|
|
*out++ = CLIPSHORT(tmp);
|
|
}
|
|
}
|
|
} else {
|
|
AM_LOGE("do_mixing_specified_channel_cnt invalid in_format:%#x out_format:%#x invalid", in_format, out_format);
|
|
return 0;
|
|
}
|
|
return frames;
|
|
}
|
|
|
|
// 2->8ch, 32bit, in out no overlap
|
|
int extend_channel_2_8(void *data_out, void *data_in,
|
|
size_t frames, int ch_cnt_out, int ch_cnt_in)
|
|
{
|
|
(void) ch_cnt_out;
|
|
(void) ch_cnt_in;
|
|
int32_t *in = (int32_t *)data_in;
|
|
int32_t *out = (int32_t *)data_out;
|
|
int32_t Lval = 0, Rval = 0;
|
|
uint32_t i = 0 , j = 0;
|
|
|
|
for (i = 0; i < frames; i++) {
|
|
Lval = *in++;
|
|
Rval = *in++;
|
|
for (j = 0; j < 4; j++) {
|
|
*out++ = Lval;
|
|
*out++ = Rval;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int extend_channel_5_8(void *data_out, void *data_in,
|
|
size_t frames, int ch_cnt_out, int ch_cnt_in)
|
|
{
|
|
(void) ch_cnt_out;
|
|
(void) ch_cnt_in;
|
|
int32_t *out = data_out;
|
|
int32_t *in = data_in;
|
|
uint32_t i = 0;
|
|
for (i = 0; i < frames; i++) {
|
|
out[8 * i] = in[5 * i + 2];
|
|
out[8 * i + 1] = in[5 * i + 3];
|
|
out[8 * i + 2] = in[5 * i];
|
|
out[8 * i + 3] = in[5 * i + 1];
|
|
out[8 * i + 4] = in[5 * i + 4];
|
|
out[8 * i + 5] = 0;
|
|
out[8 * i + 6] = 0;
|
|
out[8 * i + 7] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int extend_channel_6_8(void *data_out, void *data_in,
|
|
size_t frames, int ch_cnt_out, int ch_cnt_in)
|
|
{
|
|
(void) ch_cnt_out;
|
|
(void) ch_cnt_in;
|
|
int16_t *out = data_out;
|
|
int16_t *in = data_in;
|
|
uint32_t i = 0;
|
|
for (i = 0; i < frames; i++) {
|
|
out[8 * i] = in[6 * i];
|
|
out[8 * i + 1] = in[6 * i + 1];
|
|
out[8 * i + 2] = in[6 * i + 2];
|
|
out[8 * i + 3] = in[6 * i + 3];
|
|
out[8 * i + 4] = in[6 * i + 4];
|
|
out[8 * i + 5] = in[6 * i + 5];
|
|
out[8 * i + 6] = 0;
|
|
out[8 * i + 7] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int extend_channel_6_2(void *data_out, void *data_in,
|
|
size_t frames, int ch_cnt_out, int ch_cnt_in)
|
|
{
|
|
if (!data_out || !data_in || frames == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (ch_cnt_out != 2 || ch_cnt_in != 6) {
|
|
return -1;
|
|
}
|
|
|
|
int16_t *in = (int16_t *)data_in;
|
|
int16_t *out = (int16_t *)data_out;
|
|
|
|
for (size_t i = 0; i < frames; i++) {
|
|
*out++ = in[6 * i];
|
|
*out++ = in[6 * i + 1];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int processing_and_convert(void *data_mixed,
|
|
void *data_sys, size_t frames,
|
|
struct audioCfg inCfg, struct audioCfg mixerCfg)
|
|
{
|
|
if (data_mixed == NULL || data_sys == NULL) {
|
|
AM_LOGE("NULL pointer");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (inCfg.format == AUDIO_FORMAT_PCM_16_BIT && mixerCfg.format == AUDIO_FORMAT_PCM_32_BIT
|
|
&& inCfg.channelCnt == 2 && mixerCfg.channelCnt == 8) {
|
|
ch2_ch8_n_b16_b32(data_mixed, data_sys, frames);
|
|
} else {
|
|
AM_LOGE("not support");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* convert L R C LFE *** -> L R LFE C ** or convert L R LFE C *** -> L R C LFE **
|
|
*
|
|
*/
|
|
void channel_layout_swap_center_lfe(void * data, int size, int channels, audio_format_t audio_format) {
|
|
int i = 0;
|
|
int frames = 0;
|
|
|
|
if (data == NULL) {
|
|
AM_LOGE("NULL pointer");
|
|
return;
|
|
}
|
|
if (channels == 0 || size <= 0) {
|
|
AM_LOGE("channels=%d size=%d", channels, size);
|
|
return;
|
|
}
|
|
|
|
if (audio_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
int16_t *in_buf = data;
|
|
int16_t temp = 0;
|
|
frames = size / (channels * 2);
|
|
for (i = 0; i < frames; i++) {
|
|
temp = in_buf[channels * i + 2];
|
|
in_buf[channels * i + 2] = in_buf[channels * i + 3];
|
|
in_buf[channels * i + 3] = temp;
|
|
}
|
|
} else if (audio_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
int32_t *in_buf = data;
|
|
int32_t temp = 0;
|
|
frames = size / (channels * 4);
|
|
for (i = 0; i < frames; i++) {
|
|
temp = in_buf[channels * i + 2];
|
|
in_buf[channels * i + 2] = in_buf[channels * i + 3];
|
|
in_buf[channels * i + 3] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
int init_aml_pcm_mixer(aml_pcm_mixing_st *p_mixer, const struct audioCfg *p_mixer_cfg, int mixed_frames)
|
|
{
|
|
memset(p_mixer, 0, sizeof(*p_mixer));
|
|
p_mixer->mixed_frame_size = p_mixer_cfg->channelCnt * audio_bytes_per_sample(p_mixer_cfg->format);
|
|
p_mixer->mixed_buf_size = mixed_frames * p_mixer->mixed_frame_size;
|
|
p_mixer->mixed_frames = mixed_frames;
|
|
p_mixer->mixed_buf = aml_audio_realloc(p_mixer->mixed_buf, p_mixer->mixed_buf_size);
|
|
if (p_mixer->mixed_buf == NULL) {
|
|
AM_LOGE("allocate mixed_buf no memory");
|
|
return -1;
|
|
}
|
|
memcpy(&p_mixer->cfg, p_mixer_cfg, sizeof(p_mixer->cfg));
|
|
return 0;
|
|
}
|
|
|
|
void deinit_aml_pcm_mixer(aml_pcm_mixing_st *p_mixer)
|
|
{
|
|
if (p_mixer->mixed_buf && !p_mixer->mixed_buf_is_static) {
|
|
aml_audio_free(p_mixer->mixed_buf);
|
|
}
|
|
if (p_mixer->format_buf) {
|
|
aml_audio_free(p_mixer->format_buf);
|
|
}
|
|
if (p_mixer->channel_buf) {
|
|
aml_audio_free(p_mixer->channel_buf);
|
|
}
|
|
if (!p_mixer->mixed_buf_is_static) {
|
|
aml_planar_free_buf(&p_mixer->mixed_planar_buf);
|
|
}
|
|
memset(p_mixer, 0, sizeof(*p_mixer));
|
|
}
|
|
|
|
int do_mixing_multi_ch(aml_pcm_mixing_st *p_mixer, void *p_in_data, size_t in_frames, struct audioCfg *p_in_cfg)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
size_t req_buf_size = 0;
|
|
void *data_src = p_in_data;
|
|
void *mixer_src = p_in_data;
|
|
|
|
audio_format_t in_format = p_in_cfg->format;
|
|
uint32_t in_channelCnt = p_in_cfg->channelCnt;
|
|
audio_channel_mask_t in_channelMask = p_in_cfg->channelMask;
|
|
audio_format_t out_format = p_mixer->cfg.format;
|
|
uint32_t out_channelCnt = p_mixer->cfg.channelCnt;
|
|
audio_channel_mask_t out_channelMask = p_mixer->cfg.channelMask;
|
|
|
|
if (in_frames > p_mixer->mixed_frames) {
|
|
AM_LOGE("in_frames(%zu > %d) is too large !", in_frames, p_mixer->mixed_frames);
|
|
return -1;
|
|
}
|
|
|
|
if (out_format != in_format) {
|
|
req_buf_size = in_frames * audio_bytes_per_frame(in_channelCnt, out_format);
|
|
ret = aml_audio_check_and_realloc(&p_mixer->format_buf, &p_mixer->format_buf_size, req_buf_size);
|
|
if ((ret != 0) || (p_mixer->format_buf == NULL)) {
|
|
AM_LOGE("allocate format_buf(%zu bytes) failed", req_buf_size);
|
|
return -1;
|
|
}
|
|
data_src = p_mixer->format_buf;
|
|
memcpy_by_audio_format(data_src, out_format, p_in_data, in_format, in_channelCnt*in_frames);
|
|
}
|
|
|
|
if (in_channelMask == out_channelMask) {
|
|
mixer_src = data_src;
|
|
} else {
|
|
int out_sample_size = audio_bytes_per_sample(out_format);
|
|
req_buf_size = out_sample_size * out_channelCnt * in_frames;
|
|
ret = aml_audio_check_and_realloc(&p_mixer->channel_buf, &p_mixer->channel_buf_size, req_buf_size);
|
|
if ((ret != 0) || (p_mixer->channel_buf == NULL)) {
|
|
AM_LOGE("allocate channel_buf(%zu bytes) failed", req_buf_size);
|
|
return -1;
|
|
}
|
|
mixer_src = p_mixer->channel_buf;
|
|
memcpy_by_channel_mask(mixer_src, out_channelMask, data_src, in_channelMask, out_sample_size, in_frames);
|
|
}
|
|
|
|
if (out_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
int16_t *in = mixer_src;
|
|
int16_t *out = p_mixer->mixed_buf;
|
|
int64_t tmp = 0;
|
|
for (i = 0; i < in_frames * out_channelCnt; i++) {
|
|
tmp = (int64_t)*out + (int64_t)*in++;
|
|
*out++ = CLIPSHORT(tmp);
|
|
}
|
|
} else if (out_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
int32_t *in = mixer_src;
|
|
int32_t *out = p_mixer->mixed_buf;
|
|
int64_t tmp = 0;
|
|
for (i = 0; i < in_frames * out_channelCnt; i++) {
|
|
tmp = (int64_t)*out + (int64_t)*in++;
|
|
*out++ = CLIPINT(tmp);
|
|
}
|
|
} else {
|
|
AM_LOGE("not support p_mixer format:0x%x", out_format);
|
|
return -1;
|
|
}
|
|
return in_frames;
|
|
}
|
|
|
|
static bool setInputChannelMask(aml_pcm_downmix_st *p_downmix, audio_channel_mask_t inputChannelMask)
|
|
{
|
|
float (*mMatrix)[2] = p_downmix->mMatrix;
|
|
|
|
if (p_downmix->mInputChannelMask != inputChannelMask) {
|
|
if (inputChannelMask & ~((1 << MAX_INPUT_CHANNELS_SUPPORTED) - 1)) {
|
|
return false; // not channel position mask, or has unknown channels.
|
|
}
|
|
|
|
// Compute at what index each channel is: samples will be in the following order:
|
|
// FL FR FC LFE BL BR BC SL SR
|
|
//
|
|
// Prior to API 32, use of downmix resulted in channels being scaled in half amplitude.
|
|
// We now use a compliant downmix matrix for 5.1 with the following standards:
|
|
// ITU-R 775-2, ATSC A/52, ETSI TS 101 154, IEC 14496-3, which is unity gain for the
|
|
// front left and front right channel contribution.
|
|
//
|
|
// For 7.1 to 5.1 we set equal contributions for the side and back channels
|
|
// which follow Dolby downmix recommendations.
|
|
//
|
|
// We add contributions from the LFE into the L and R channels
|
|
// at a weight of 0.5 (rather than the power preserving 0.707)
|
|
// which is to ensure that headphones can still experience LFE
|
|
// with lesser risk of speaker overload.
|
|
//
|
|
// Note: geometrically left and right channels contribute only to the corresponding
|
|
// left and right outputs respectively. Geometrically center channels contribute
|
|
// to both left and right outputs, so they are scaled by 0.707 to preserve power.
|
|
//
|
|
// (transfer matrix)
|
|
// FL FR FC LFE BL BR BC SL SR
|
|
// 1.0 0.707 0.5 0.707 0.5 0.707
|
|
// 1.0 0.707 0.5 0.707 0.5 0.707
|
|
int index = 0;
|
|
const float COEF_25 = 0.2508909536f;
|
|
const float COEF_35 = 0.3543928915f;
|
|
const float COEF_36 = 0.3552343859f;
|
|
const float COEF_61 = 0.6057043428f;
|
|
for (unsigned tmp = inputChannelMask; tmp != 0; ++index) {
|
|
const unsigned lowestBit = tmp & -(signed)tmp;
|
|
switch (lowestBit) {
|
|
case AUDIO_CHANNEL_OUT_FRONT_LEFT:
|
|
case AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT:
|
|
//case AUDIO_CHANNEL_OUT_BOTTOM_FRONT_LEFT:
|
|
mMatrix[index][0] = 1.f;
|
|
mMatrix[index][1] = 0.f;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_SIDE_LEFT:
|
|
case AUDIO_CHANNEL_OUT_BACK_LEFT:
|
|
case AUDIO_CHANNEL_OUT_TOP_BACK_LEFT:
|
|
//case AUDIO_CHANNEL_OUT_FRONT_WIDE_LEFT: // FRONT_WIDE closer to SIDE.
|
|
mMatrix[index][0] = MINUS_3_DB_IN_FLOAT;
|
|
mMatrix[index][1] = 0.f;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_FRONT_RIGHT:
|
|
case AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT:
|
|
//case AUDIO_CHANNEL_OUT_BOTTOM_FRONT_RIGHT:
|
|
mMatrix[index][0] = 0.f;
|
|
mMatrix[index][1] = 1.f;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_SIDE_RIGHT:
|
|
case AUDIO_CHANNEL_OUT_BACK_RIGHT:
|
|
case AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT:
|
|
//case AUDIO_CHANNEL_OUT_FRONT_WIDE_RIGHT: // FRONT_WIDE closer to SIDE.
|
|
mMatrix[index][0] = 0.f;
|
|
mMatrix[index][1] = MINUS_3_DB_IN_FLOAT;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_FRONT_CENTER:
|
|
case AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER:
|
|
//case AUDIO_CHANNEL_OUT_BOTTOM_FRONT_CENTER:
|
|
mMatrix[index][0] = mMatrix[index][1] = MINUS_3_DB_IN_FLOAT;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT:
|
|
mMatrix[index][0] = COEF_61;
|
|
mMatrix[index][1] = 0.f;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT:
|
|
mMatrix[index][0] = 0.f;
|
|
mMatrix[index][1] = COEF_61;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER:
|
|
mMatrix[index][0] = COEF_61;
|
|
mMatrix[index][1] = COEF_25;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER:
|
|
mMatrix[index][0] = COEF_25;
|
|
mMatrix[index][1] = COEF_61;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_TOP_CENTER:
|
|
mMatrix[index][0] = mMatrix[index][1] = COEF_36;
|
|
break;
|
|
case AUDIO_CHANNEL_OUT_TOP_BACK_CENTER:
|
|
mMatrix[index][0] = mMatrix[index][1] = COEF_35;
|
|
break;
|
|
/*case AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2:
|
|
mMatrix[index][0] = 0.f;
|
|
mMatrix[index][1] = MINUS_3_DB_IN_FLOAT;
|
|
break;*/
|
|
/*case AUDIO_CHANNEL_OUT_LOW_FREQUENCY:
|
|
if (inputChannelMask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2) {
|
|
mMatrix[index][0] = MINUS_3_DB_IN_FLOAT;
|
|
mMatrix[index][1] = 0.f;
|
|
break;
|
|
}
|
|
FALLTHROUGH_INTENDED;*/
|
|
case AUDIO_CHANNEL_OUT_BACK_CENTER:
|
|
mMatrix[index][0] = mMatrix[index][1] = 0.5f;
|
|
break;
|
|
}
|
|
tmp ^= lowestBit;
|
|
}
|
|
p_downmix->mInputChannelMask = inputChannelMask;
|
|
// Note: mLastValidChannelIndexPlusOne is the same as mInputChannelCount for
|
|
// this particular matrix, as it has a nonzero column for every channel position.
|
|
p_downmix->mInputChannelCount = index;
|
|
p_downmix->mLastValidChannelIndexPlusOne = index;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* reference : system/media/audio_utils/include/audio_utils/ChannelMix.h
|
|
*/
|
|
static bool specificProcess_16bit(const int16_t *src, int16_t *dst, size_t frameCount, int channelCount)
|
|
{
|
|
while (frameCount > 0) {
|
|
int64_t ch[2]; // left, right
|
|
if (channelCount == 4) { // QUAD
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is RL (or SL)
|
|
// sample at index 3 is RR (or SR)
|
|
// FL + RL
|
|
ch[0] = src[0] + src[2] * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR
|
|
ch[1] = src[1] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
} else if (channelCount == 6) { // 5.1
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is FC
|
|
// sample at index 3 is LFE
|
|
// sample at index 4 is RL (or SL)
|
|
// sample at index 5 is RR (or SR)
|
|
const int64_t centerPlusLfeContrib = src[2] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
// FL + RL + centerPlusLfeContrib
|
|
ch[0] = src[0] + (src[4] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR + centerPlusLfeContrib
|
|
ch[1] = src[1] + (src[5] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
} else if (channelCount == 8) { // 7.1
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is FC
|
|
// sample at index 3 is LFE
|
|
// sample at index 4 is RL
|
|
// sample at index 5 is RR
|
|
// sample at index 6 is SL
|
|
// sample at index 7 is SR
|
|
const int64_t centerPlusLfeContrib = src[2] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
// FL + RL + SL + centerPlusLfeContrib
|
|
ch[0] = src[0] + (src[4] + src[6] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR + SR + centerPlusLfeContrib
|
|
ch[1] = src[1] + (src[5] + src[7] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
} else if (channelCount == 10) { // 7.1.2
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is FC
|
|
// sample at index 3 is LFE
|
|
// sample at index 4 is RL
|
|
// sample at index 5 is RR
|
|
// sample at index 6 is SL
|
|
// sample at index 7 is SR
|
|
// sample at index 8 is TL
|
|
// sample at index 9 is TR
|
|
const int64_t centerPlusLfeContrib = src[2] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
// FL + RL + SL + TL + centerPlusLfeContrib
|
|
ch[0] = src[0] + (src[4] + src[6] + src[8] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR + SR + TR + centerPlusLfeContrib
|
|
ch[1] = src[1] + (src[5] + src[7] + src[9] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
} else {
|
|
return false;
|
|
}
|
|
dst[0] = CLIPSHORT(ch[0]);
|
|
dst[1] = CLIPSHORT(ch[1]);
|
|
|
|
src += channelCount;
|
|
dst += 2;
|
|
--frameCount;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool specificProcess_32bit(const int32_t *src, int32_t *dst, size_t frameCount, int channelCount)
|
|
{
|
|
while (frameCount > 0) {
|
|
int64_t ch[2]; // left, right
|
|
if (channelCount == 4) { // QUAD
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is RL (or SL)
|
|
// sample at index 3 is RR (or SR)
|
|
// FL + RL
|
|
ch[0] = src[0] + src[2] * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR
|
|
ch[1] = src[1] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
} else if (channelCount == 6) { // 5.1
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is FC
|
|
// sample at index 3 is LFE
|
|
// sample at index 4 is RL (or SL)
|
|
// sample at index 5 is RR (or SR)
|
|
const int64_t centerPlusLfeContrib = src[2] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
// FL + RL + centerPlusLfeContrib
|
|
ch[0] = src[0] + (src[4] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR + centerPlusLfeContrib
|
|
ch[1] = src[1] + (src[5] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
} else if (channelCount == 8) { // 7.1
|
|
// sample at index 0 is FL
|
|
// sample at index 1 is FR
|
|
// sample at index 2 is FC
|
|
// sample at index 3 is LFE
|
|
// sample at index 4 is RL
|
|
// sample at index 5 is RR
|
|
// sample at index 6 is SL
|
|
// sample at index 7 is SR
|
|
const int64_t centerPlusLfeContrib = src[2] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
// FL + RL + SL + centerPlusLfeContrib
|
|
ch[0] = src[0] + (src[4] + src[6] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
// FR + RR + SR + centerPlusLfeContrib
|
|
ch[1] = src[1] + (src[5] + src[7] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
} else if (channelCount == 10) { // 7.1.2
|
|
const int64_t centerPlusLfeContrib = src[2] + src[3] * MINUS_3_DB_IN_FLOAT;
|
|
ch[0] = src[0] + (src[4] + src[6] + src[8] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
ch[1] = src[1] + (src[5] + src[7] + src[9] + centerPlusLfeContrib) * MINUS_3_DB_IN_FLOAT;
|
|
} else {
|
|
return false;
|
|
}
|
|
dst[0] = CLIPINT(ch[0]);
|
|
dst[1] = CLIPINT(ch[1]);
|
|
|
|
src += channelCount;
|
|
dst += 2;
|
|
--frameCount;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool matrixProcess_16bit(aml_pcm_downmix_st *p_downmix, const int16_t *src, int16_t *dst, size_t frameCount)
|
|
{
|
|
// matrix multiply
|
|
if (p_downmix->mInputChannelMask == AUDIO_CHANNEL_NONE) return false;
|
|
while (frameCount) {
|
|
int64_t ch[2] = {0, 0}; // left, right
|
|
for (size_t i = 0; i < p_downmix->mLastValidChannelIndexPlusOne; ++i) {
|
|
ch[0] += p_downmix->mMatrix[i][0] * src[i];
|
|
ch[1] += p_downmix->mMatrix[i][1] * src[i];
|
|
}
|
|
|
|
dst[0] = CLIPSHORT(ch[0]);
|
|
dst[1] = CLIPSHORT(ch[1]);
|
|
src += p_downmix->mInputChannelCount;
|
|
dst += 2;
|
|
--frameCount;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool matrixProcess_32bit(aml_pcm_downmix_st *p_downmix, const int32_t *src, int32_t *dst, size_t frameCount)
|
|
{
|
|
// matrix multiply
|
|
if (p_downmix->mInputChannelMask == AUDIO_CHANNEL_NONE) return false;
|
|
while (frameCount) {
|
|
int64_t ch[2] = {0, 0}; // left, right
|
|
for (size_t i = 0; i < p_downmix->mLastValidChannelIndexPlusOne; ++i) {
|
|
ch[0] += p_downmix->mMatrix[i][0] * src[i];
|
|
ch[1] += p_downmix->mMatrix[i][1] * src[i];
|
|
}
|
|
|
|
dst[0] = CLIPINT(ch[0]);
|
|
dst[1] = CLIPINT(ch[1]);
|
|
src += p_downmix->mInputChannelCount;
|
|
dst += 2;
|
|
--frameCount;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool processSwitch_16bit(aml_pcm_downmix_st *p_downmix, const int16_t *src, int16_t *dst, size_t frameCount)
|
|
{
|
|
switch (p_downmix->mInputChannelMask) {
|
|
case AUDIO_CHANNEL_OUT_QUAD_BACK:
|
|
case AUDIO_CHANNEL_OUT_QUAD_SIDE:
|
|
return specificProcess_16bit(src, dst, frameCount, 4);
|
|
case AUDIO_CHANNEL_OUT_5POINT1_BACK:
|
|
case AUDIO_CHANNEL_OUT_5POINT1_SIDE:
|
|
return specificProcess_16bit(src, dst, frameCount, 6);
|
|
case AUDIO_CHANNEL_OUT_7POINT1:
|
|
return specificProcess_16bit(src, dst, frameCount, 8);
|
|
case AUDIO_CHANNEL_OUT_7POINT1POINT2:
|
|
return specificProcess_16bit(src, dst, frameCount, 10);
|
|
default:
|
|
break; // handled below.
|
|
}
|
|
|
|
return matrixProcess_16bit(p_downmix, src, dst, frameCount);
|
|
}
|
|
|
|
static bool processSwitch_32bit(aml_pcm_downmix_st *p_downmix, const int32_t *src, int32_t *dst, size_t frameCount)
|
|
{
|
|
switch (p_downmix->mInputChannelMask) {
|
|
case AUDIO_CHANNEL_OUT_QUAD_BACK:
|
|
case AUDIO_CHANNEL_OUT_QUAD_SIDE:
|
|
return specificProcess_32bit(src, dst, frameCount, 4);
|
|
case AUDIO_CHANNEL_OUT_5POINT1_BACK:
|
|
case AUDIO_CHANNEL_OUT_5POINT1_SIDE:
|
|
return specificProcess_32bit(src, dst, frameCount, 6);
|
|
case AUDIO_CHANNEL_OUT_7POINT1:
|
|
return specificProcess_32bit(src, dst, frameCount, 8);
|
|
case AUDIO_CHANNEL_OUT_7POINT1POINT2:
|
|
return specificProcess_32bit(src, dst, frameCount, 10);
|
|
default:
|
|
break; // handled below.
|
|
}
|
|
|
|
return matrixProcess_32bit(p_downmix, src, dst, frameCount);
|
|
}
|
|
|
|
int do_downmix_to_2ch(aml_pcm_downmix_st *p_downmix, void *data_in, size_t in_frames, struct audioCfg *p_in_cfg)
|
|
{
|
|
int ret = 0;
|
|
audio_format_t out_format = p_in_cfg->format;
|
|
uint32_t in_channelCnt = p_in_cfg->channelCnt;
|
|
int req_buf_size = in_frames * 2 * audio_bytes_per_sample(out_format);
|
|
|
|
ret = aml_audio_check_and_realloc(&p_downmix->output_buf, &p_downmix->output_buf_size, req_buf_size);
|
|
if ((ret != 0) || (p_downmix->output_buf == NULL)) {
|
|
AM_LOGE("allocate output_buf(%d bytes) failed", req_buf_size);
|
|
return -1;
|
|
}
|
|
p_downmix->output_buf_size = req_buf_size;
|
|
if (in_channelCnt == 2) {
|
|
memcpy(p_downmix->output_buf, data_in, req_buf_size);
|
|
return 0;
|
|
}
|
|
|
|
if ((out_format != AUDIO_FORMAT_PCM_16_BIT) && (out_format != AUDIO_FORMAT_PCM_32_BIT)) {
|
|
AM_LOGE("not support output format 0x%x", out_format);
|
|
return -1;
|
|
}
|
|
if (setInputChannelMask(p_downmix, p_in_cfg->channelMask) != true) {
|
|
AM_LOGE("setInputChannelMask(0x%x) failed !", p_in_cfg->channelMask);
|
|
return -1;
|
|
}
|
|
|
|
if (out_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
ret = processSwitch_16bit(p_downmix, data_in, p_downmix->output_buf, in_frames);
|
|
} else if (out_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
ret = processSwitch_32bit(p_downmix, data_in, p_downmix->output_buf, in_frames);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* 7.1.2 interleaved: FL FR FC LFE BL BR SL SR TSL TSR
|
|
* Mix TSL/TSR into SL/SR with COEF_61 (~0.606), do not drop height. */
|
|
#define DOWNMIX_10_TO_8_HEIGHT_COEF (0.6057043428f)
|
|
|
|
int do_downmix_to_8ch(aml_pcm_downmix_st *p_downmix, void *data_in, size_t in_frames, struct audioCfg *p_in_cfg)
|
|
{
|
|
int ret = 0;
|
|
audio_format_t format;
|
|
uint32_t in_ch;
|
|
int sample_size;
|
|
int req_buf_size;
|
|
size_t i;
|
|
|
|
if (!p_downmix || !data_in || !p_in_cfg) {
|
|
AM_LOGE("invalid param");
|
|
return -1;
|
|
}
|
|
if (in_frames == 0) {
|
|
return -1;
|
|
}
|
|
|
|
format = p_in_cfg->format;
|
|
in_ch = p_in_cfg->channelCnt;
|
|
sample_size = audio_bytes_per_sample(format);
|
|
req_buf_size = in_frames * 8 * sample_size;
|
|
|
|
ret = aml_audio_check_and_realloc(&p_downmix->output_buf, &p_downmix->output_buf_size, req_buf_size);
|
|
if ((ret != 0) || (p_downmix->output_buf == NULL)) {
|
|
AM_LOGE("allocate output_buf(%d bytes) failed", req_buf_size);
|
|
return -1;
|
|
}
|
|
p_downmix->output_buf_size = req_buf_size;
|
|
|
|
if (in_ch == 8) {
|
|
memcpy(p_downmix->output_buf, data_in, req_buf_size);
|
|
return 0;
|
|
}
|
|
if (in_ch != 10) {
|
|
AM_LOGE("not support in_ch %u, expect 10ch 7.1.2 or 8ch", in_ch);
|
|
return -1;
|
|
}
|
|
if ((format != AUDIO_FORMAT_PCM_16_BIT) && (format != AUDIO_FORMAT_PCM_32_BIT)) {
|
|
AM_LOGE("not support format 0x%x", format);
|
|
return -1;
|
|
}
|
|
|
|
if (format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
const int16_t *in = (const int16_t *)data_in;
|
|
int16_t *out = (int16_t *)p_downmix->output_buf;
|
|
for (i = 0; i < in_frames; i++) {
|
|
const int16_t *src = in + i * 10;
|
|
int16_t *dst = out + i * 8;
|
|
dst[0] = src[0];
|
|
dst[1] = src[1];
|
|
dst[2] = src[2];
|
|
dst[3] = src[3];
|
|
dst[4] = src[4];
|
|
dst[5] = src[5];
|
|
dst[6] = CLIPSHORT((int32_t)src[6] + (int32_t)(DOWNMIX_10_TO_8_HEIGHT_COEF * (double)src[8]));
|
|
dst[7] = CLIPSHORT((int32_t)src[7] + (int32_t)(DOWNMIX_10_TO_8_HEIGHT_COEF * (double)src[9]));
|
|
}
|
|
} else {
|
|
const int32_t *in = (const int32_t *)data_in;
|
|
int32_t *out = (int32_t *)p_downmix->output_buf;
|
|
for (i = 0; i < in_frames; i++) {
|
|
const int32_t *src = in + i * 10;
|
|
int32_t *dst = out + i * 8;
|
|
dst[0] = src[0];
|
|
dst[1] = src[1];
|
|
dst[2] = src[2];
|
|
dst[3] = src[3];
|
|
dst[4] = src[4];
|
|
dst[5] = src[5];
|
|
dst[6] = CLIPINT((int64_t)src[6] + (int64_t)(DOWNMIX_10_TO_8_HEIGHT_COEF * (double)src[8]));
|
|
dst[7] = CLIPINT((int64_t)src[7] + (int64_t)(DOWNMIX_10_TO_8_HEIGHT_COEF * (double)src[9]));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int pcm_extract_by_channel_mask(void **dst_buf, size_t *dst_buf_size,
|
|
const void *src, audio_channel_mask_t src_mask,
|
|
audio_channel_mask_t dst_mask, audio_format_t format, size_t frames)
|
|
{
|
|
uint32_t dst_ch;
|
|
int sample_size;
|
|
size_t req_buf_size;
|
|
int ret;
|
|
|
|
if (!dst_buf || !dst_buf_size || !src) {
|
|
AM_LOGE("invalid param");
|
|
return -1;
|
|
}
|
|
if (frames == 0 || dst_mask == AUDIO_CHANNEL_NONE || dst_mask == AUDIO_CHANNEL_INVALID) {
|
|
AM_LOGE("invalid extract param frames:%zu dst_mask:0x%x", frames, dst_mask);
|
|
return -1;
|
|
}
|
|
|
|
dst_ch = audio_channel_count_from_out_mask(dst_mask);
|
|
sample_size = audio_bytes_per_sample(format);
|
|
if (dst_ch == 0 || sample_size <= 0) {
|
|
AM_LOGE("invalid dst_ch:%u sample_size:%d mask:0x%x", dst_ch, sample_size, dst_mask);
|
|
return -1;
|
|
}
|
|
|
|
req_buf_size = frames * dst_ch * sample_size;
|
|
ret = aml_audio_check_and_realloc(dst_buf, dst_buf_size, req_buf_size);
|
|
if ((ret != 0) || (*dst_buf == NULL)) {
|
|
AM_LOGE("allocate extract buf(%zu bytes) failed", req_buf_size);
|
|
return -1;
|
|
}
|
|
*dst_buf_size = req_buf_size;
|
|
memcpy_by_channel_mask(*dst_buf, dst_mask, src, src_mask, sample_size, frames);
|
|
return 0;
|
|
}
|
|
|
|
void init_aml_pcm_downmix(aml_pcm_downmix_st *p_downmix)
|
|
{
|
|
int i = 0;
|
|
|
|
memset(p_downmix, 0, sizeof(*p_downmix));
|
|
for (i = 0; i < MAX_INPUT_CHANNELS_SUPPORTED; i++) {
|
|
p_downmix->mMatrix[i][0] = 0;
|
|
p_downmix->mMatrix[i][1] = 0;
|
|
}
|
|
p_downmix->mInputChannelMask = AUDIO_CHANNEL_NONE;
|
|
p_downmix->mLastValidChannelIndexPlusOne = 0;
|
|
p_downmix->mInputChannelCount = 0;
|
|
}
|
|
|
|
void deinit_aml_pcm_downmix(aml_pcm_downmix_st *p_downmix)
|
|
{
|
|
if (p_downmix->output_buf) {
|
|
aml_audio_free(p_downmix->output_buf);
|
|
}
|
|
memset(p_downmix, 0, sizeof(*p_downmix));
|
|
}
|
|
|
|
/* audio format convert */
|
|
int aml_convert_audio_format(unsigned int samples, void *in_buffer, void *out_buffer,
|
|
audio_format_t in_format, audio_format_t out_format)
|
|
{
|
|
if (0 == samples || !in_buffer || !out_buffer ) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (out_format) {
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
switch (in_format) {
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
break;
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
{
|
|
int32_t *input32 = (int32_t *)in_buffer;
|
|
int16_t *output16 = (int16_t *)out_buffer;
|
|
for (int i = samples - 1; i >= 0; i--) {
|
|
int32_t samp = ((int32_t)input32[i]);
|
|
output16[i] = CLIPSHORT((int32_t)(samp));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
goto err;
|
|
}
|
|
break;
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
switch (in_format) {
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
{
|
|
int16_t *input16 = (int16_t *)in_buffer;
|
|
int32_t *output32 = (int32_t *)out_buffer;
|
|
for (int i = samples - 1; i >= 0; i--) {
|
|
int32_t samp = ((int32_t)input16[i]) << 16;
|
|
output32[i] = CLIPINT((int64_t)(samp));
|
|
}
|
|
break;
|
|
}
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
return 0;
|
|
err:
|
|
AM_LOGE("out_format:%#x in_format:%#x not supported", out_format, in_format);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static inline float clamp_float_pcm(float value)
|
|
{
|
|
if (value > 1.0f)
|
|
return 1.0f;
|
|
else if (value < -1.0f)
|
|
return -1.0f;
|
|
return value;
|
|
}
|
|
|
|
int planar_pcm_mixer_add_data(aml_pcm_mixing_st *p_mixer, const aml_planar_buf_st *p_input_buf, bool unlimited_float)
|
|
{
|
|
int i = 0;
|
|
int j = 0;
|
|
int ret = 0;
|
|
int in_frames = 0;
|
|
int mix_frames = 0;
|
|
size_t req_buf_size = 0;
|
|
const aml_planar_buf_st *p_planar_src = NULL;
|
|
aml_planar_buf_st *p_planar_dst = NULL;
|
|
void *data_dst = NULL;
|
|
const void *data_src = NULL;
|
|
|
|
if (p_mixer == NULL || p_input_buf == NULL) {
|
|
AM_LOGE("invalid p_mixer %p, p_input_buf %p", p_mixer, p_input_buf);
|
|
return -1;
|
|
}
|
|
p_planar_src = p_input_buf;
|
|
p_planar_dst = &p_mixer->mixed_planar_buf;
|
|
in_frames = p_planar_src->per_ch_frames;
|
|
mix_frames = p_planar_dst->per_ch_frames;
|
|
|
|
audio_format_t in_format = p_planar_src->format;
|
|
uint32_t in_channelCnt = p_planar_src->ch_num;
|
|
audio_format_t out_format = p_planar_dst->format;
|
|
uint32_t out_channelCnt = p_planar_dst->ch_num;
|
|
|
|
if (in_frames > mix_frames) {
|
|
AM_LOGE("in_frames(%d > %d) is too large !", in_frames, mix_frames);
|
|
return -1;
|
|
}
|
|
if (out_channelCnt == 0 || in_channelCnt == 0 || in_channelCnt > out_channelCnt) {
|
|
AM_LOGE("invalid : out_channelCnt(%d) in_channelCnt(%d) !", out_channelCnt, in_channelCnt);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < (int)in_channelCnt; i++) {
|
|
data_src = p_planar_src->ch_data[i];
|
|
data_dst = p_planar_dst->ch_data[i];
|
|
if (out_format != in_format) {
|
|
req_buf_size = in_frames * audio_bytes_per_sample(out_format);
|
|
ret = aml_audio_check_and_realloc(&p_mixer->format_buf, &p_mixer->format_buf_size, req_buf_size);
|
|
if ((ret != 0) || (p_mixer->format_buf == NULL)) {
|
|
AM_LOGE("allocate format_buf(%zu bytes) failed", req_buf_size);
|
|
return -1;
|
|
}
|
|
memcpy_by_audio_format(p_mixer->format_buf, out_format, data_src, in_format, in_frames);
|
|
data_src = p_mixer->format_buf;
|
|
}
|
|
|
|
if (out_format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
const int16_t *in = data_src;
|
|
int16_t *out = data_dst;
|
|
int32_t tmp = 0;
|
|
for (j = 0; j < in_frames; j++) {
|
|
tmp = (int32_t)*out + (int32_t)*in++;
|
|
*out++ = CLIPSHORT(tmp);
|
|
}
|
|
} else if (out_format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
const int32_t *in = data_src;
|
|
int32_t *out = data_dst;
|
|
int64_t tmp = 0;
|
|
for (j = 0; j < in_frames; j++) {
|
|
tmp = (int64_t)*out + (int64_t)*in++;
|
|
*out++ = CLIPINT(tmp);
|
|
}
|
|
} else if (out_format == AUDIO_FORMAT_PCM_FLOAT) {
|
|
const float *in = data_src;
|
|
float *out = data_dst;
|
|
if (unlimited_float) {
|
|
for (j = 0; j < in_frames; j++) {
|
|
*out = *out + *in;
|
|
out++;
|
|
in++;
|
|
}
|
|
} else {
|
|
for (j = 0; j < in_frames; j++) {
|
|
*out = clamp_float_pcm(*out + *in);
|
|
out++;
|
|
in++;
|
|
}
|
|
}
|
|
} else {
|
|
AM_LOGE("not support p_mixer format:0x%x", out_format);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return in_frames;
|
|
}
|
|
|
|
void aml_static_planar_pcm_mixer_start(aml_pcm_mixing_st *p_mixer, const aml_planar_buf_st *p_planar_buf)
|
|
{
|
|
if (p_mixer == NULL || p_planar_buf == NULL) {
|
|
return;
|
|
}
|
|
memcpy(&p_mixer->mixed_planar_buf, p_planar_buf, sizeof(*p_planar_buf));
|
|
p_mixer->mixed_buf_is_static = true;
|
|
p_mixer->first_mix = false;
|
|
}
|
|
|
|
void aml_static_planar_pcm_mixer_end(aml_pcm_mixing_st *p_mixer)
|
|
{
|
|
if (p_mixer && p_mixer->mixed_buf_is_static) {
|
|
memset(&p_mixer->mixed_planar_buf, 0, sizeof(p_mixer->mixed_planar_buf));
|
|
p_mixer->mixed_buf_is_static = false;
|
|
p_mixer->first_mix = false;
|
|
}
|
|
}
|
|
|
|
int aml_planar_to_interleaved_pcm(const aml_planar_buf_st *p_src_buf, void *p_dst_buf, int dst_buf_size)
|
|
{
|
|
if (p_src_buf == NULL || p_src_buf->per_ch_frames <= 0 || p_src_buf->ch_num <= 0
|
|
|| p_dst_buf == NULL || dst_buf_size <= 0) {
|
|
AM_LOGE("invalid parameters");
|
|
return 0;
|
|
}
|
|
|
|
int i, j;
|
|
int convert_ch_frames = 0;
|
|
const int ch_num = p_src_buf->ch_num;
|
|
const int frame_bytes = audio_bytes_per_frame(ch_num, p_src_buf->format);
|
|
const int src_ch_frames = p_src_buf->per_ch_frames;
|
|
|
|
if (frame_bytes <= 0) {
|
|
AM_LOGE("not support format 0x%x or ch_num %d", p_src_buf->format, ch_num);
|
|
return 0;
|
|
}
|
|
convert_ch_frames = dst_buf_size / frame_bytes;
|
|
if (convert_ch_frames >= src_ch_frames) {
|
|
convert_ch_frames = src_ch_frames;
|
|
} else {
|
|
AM_LOGW("dst_buf_size %d is too small, convert_ch_frames %d, src_ch_frames %d",
|
|
dst_buf_size, convert_ch_frames, src_ch_frames);
|
|
}
|
|
|
|
switch (p_src_buf->format) {
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
for (i = 0; i < ch_num; i++) {
|
|
for (j = 0; j < convert_ch_frames; j++) {
|
|
((short *)p_dst_buf)[j * ch_num + i] = ((short *)p_src_buf->ch_data[i])[j];
|
|
}
|
|
}
|
|
break;
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
for (i = 0; i < ch_num; i++) {
|
|
for (j = 0; j < convert_ch_frames; j++) {
|
|
((int32_t *)p_dst_buf)[j * ch_num + i] = ((int32_t *)p_src_buf->ch_data[i])[j];
|
|
}
|
|
}
|
|
break;
|
|
case AUDIO_FORMAT_PCM_FLOAT:
|
|
for (i = 0; i < ch_num; i++) {
|
|
for (j = 0; j < convert_ch_frames; j++) {
|
|
((float *)p_dst_buf)[j * ch_num + i] = ((float *)p_src_buf->ch_data[i])[j];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
AM_LOGE("not support format 0x%x", p_src_buf->format);
|
|
convert_ch_frames = 0;
|
|
break;
|
|
}
|
|
return convert_ch_frames;
|
|
}
|
|
|
|
int aml_interleaved_to_planar_pcm(const void *p_src_buf, int src_ch_num, int src_frames,
|
|
audio_format_t src_format, aml_planar_buf_st *p_dst_buf)
|
|
{
|
|
int i = 0, j = 0, k = 0;
|
|
size_t req_buf_size = 0;
|
|
|
|
if (p_src_buf == NULL || src_frames <= 0 || p_dst_buf == NULL) {
|
|
AM_LOGE("invalid parameters");
|
|
return 0;
|
|
}
|
|
if (src_ch_num <= 0 || src_ch_num > AML_PLANAR_MAX_CH_NUM) {
|
|
AM_LOGE("not support src_ch_num %d", src_ch_num);
|
|
return 0;
|
|
}
|
|
req_buf_size = audio_bytes_per_sample(src_format) * src_frames;
|
|
|
|
for (i = 0; i < src_ch_num; i++) {
|
|
if (p_dst_buf->ch_buf_size[i] < req_buf_size) {
|
|
AM_LOGE("ch_buf_size[%d] %zu < req_buf_size %zu, too small !", i, p_dst_buf->ch_buf_size[i], req_buf_size);
|
|
return 0;
|
|
}
|
|
}
|
|
p_dst_buf->ch_num = src_ch_num;
|
|
p_dst_buf->per_ch_frames = src_frames;
|
|
p_dst_buf->format = src_format;
|
|
p_dst_buf->data_valid = true;
|
|
|
|
switch (src_format) {
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
for (j = 0; j < src_frames; j++) {
|
|
for (i = 0; i < src_ch_num; i++) {
|
|
((short *)p_dst_buf->ch_data[i])[j] = ((short *)p_src_buf)[k++];
|
|
}
|
|
}
|
|
break;
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
for (j = 0; j < src_frames; j++) {
|
|
for (i = 0; i < src_ch_num; i++) {
|
|
((int32_t *)p_dst_buf->ch_data[i])[j] = ((int32_t *)p_src_buf)[k++];
|
|
}
|
|
}
|
|
break;
|
|
case AUDIO_FORMAT_PCM_FLOAT:
|
|
for (j = 0; j < src_frames; j++) {
|
|
for (i = 0; i < src_ch_num; i++) {
|
|
((float *)p_dst_buf->ch_data[i])[j] = ((float *)p_src_buf)[k++];
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
AM_LOGE("not support format 0x%x", src_format);
|
|
src_frames = 0;
|
|
p_dst_buf->data_valid = false;
|
|
break;
|
|
}
|
|
return src_frames;
|
|
}
|
|
|
|
void aml_planar_fill_buf(aml_planar_buf_st *p_buffer,
|
|
void **ppdata, int frames, int ch_num, audio_format_t format)
|
|
{
|
|
int i = 0;
|
|
int ch_buf_size = 0;
|
|
if (p_buffer == NULL || ppdata == NULL || ch_num <= 0 || ch_num > AML_PLANAR_MAX_CH_NUM) {
|
|
return;
|
|
}
|
|
ch_buf_size = audio_bytes_per_sample(format) * frames;
|
|
|
|
for (i = 0; i < ch_num; i++) {
|
|
p_buffer->ch_data[i] = ppdata[i];
|
|
p_buffer->ch_buf_size[i] = ch_buf_size;
|
|
}
|
|
for (; i < AML_PLANAR_MAX_CH_NUM; i++) {
|
|
p_buffer->ch_data[i] = NULL;
|
|
p_buffer->ch_buf_size[i] = 0;
|
|
}
|
|
|
|
p_buffer->ch_num = ch_num;
|
|
p_buffer->per_ch_frames = frames;
|
|
p_buffer->format = format;
|
|
p_buffer->data_valid = true;
|
|
}
|
|
|
|
int aml_planar_malloc_buf(aml_planar_buf_st *p_buffer,
|
|
int frame_count, int ch_num, audio_format_t format)
|
|
{
|
|
int i = 0;
|
|
size_t req_buf_size = 0;
|
|
if (p_buffer == NULL || frame_count <= 0 || ch_num <= 0 || ch_num > AML_PLANAR_MAX_CH_NUM) {
|
|
AM_LOGE("invalid param, p_buffer %p, frame_count %d, ch_num %d !", p_buffer, frame_count, ch_num);
|
|
return -1;
|
|
}
|
|
|
|
req_buf_size = audio_bytes_per_sample(format) * frame_count;
|
|
memset(p_buffer, 0, sizeof(*p_buffer));
|
|
|
|
for (i = 0; i < ch_num; i++) {
|
|
p_buffer->ch_data[i] = aml_audio_malloc(req_buf_size);
|
|
if (p_buffer->ch_data[i] == NULL) {
|
|
AM_LOGE("malloc %zu bytes fail !", req_buf_size);
|
|
return -1;
|
|
}
|
|
p_buffer->ch_buf_size[i] = req_buf_size;
|
|
}
|
|
p_buffer->ch_num = ch_num;
|
|
p_buffer->per_ch_frames = frame_count;
|
|
p_buffer->format = format;
|
|
p_buffer->data_valid = false;
|
|
return 0;
|
|
}
|
|
|
|
int aml_planar_check_and_realloc_buf(aml_planar_buf_st *p_buffer,
|
|
int frame_count, int ch_num, audio_format_t format)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
size_t req_buf_size = 0;
|
|
if (p_buffer == NULL || frame_count <= 0 || ch_num <= 0 || ch_num > AML_PLANAR_MAX_CH_NUM) {
|
|
AM_LOGE("invalid param, p_buffer %p, frame_count %d, ch_num %d !", p_buffer, frame_count, ch_num);
|
|
return -1;
|
|
}
|
|
|
|
if (p_buffer->ch_num == ch_num
|
|
&& p_buffer->per_ch_frames == frame_count
|
|
&& p_buffer->format == format) {
|
|
AM_LOGV("not need to check");
|
|
return 0;
|
|
}
|
|
req_buf_size = audio_bytes_per_sample(format) * frame_count;
|
|
|
|
for (i = 0; i < ch_num; i++) {
|
|
ret = aml_audio_check_and_realloc(&p_buffer->ch_data[i], &p_buffer->ch_buf_size[i], req_buf_size);
|
|
if (ret != 0 || p_buffer->ch_data[i] == NULL) {
|
|
AM_LOGE("ch %d, realloc %zu bytes fail !", i, req_buf_size);
|
|
return -1;
|
|
}
|
|
}
|
|
p_buffer->ch_num = ch_num;
|
|
p_buffer->per_ch_frames = frame_count;
|
|
p_buffer->format = format;
|
|
p_buffer->data_valid = false;
|
|
return 0;
|
|
}
|
|
|
|
int aml_planar_memcpy_buf(
|
|
aml_planar_buf_st *p_dst_buf, int dst_frame_offset, audio_format_t dst_format,
|
|
const aml_planar_buf_st *p_src_buf, int src_frame_offset, int copy_frames)
|
|
{
|
|
int i = 0;
|
|
int src_ch_num = 0;
|
|
int src_sample_size = 0;
|
|
int src_frame_num = 0;
|
|
int dst_sample_size = 0;
|
|
int dst_ch_buf_size = 0;
|
|
void *p_dst_ch = NULL;
|
|
const void *p_src_ch = NULL;
|
|
|
|
if (p_dst_buf == NULL || p_src_buf == NULL) {
|
|
AM_LOGE("invalid p_dst_buf %p, p_src_buf %p", p_dst_buf, p_src_buf);
|
|
return -1;
|
|
}
|
|
if (dst_frame_offset < 0 || src_frame_offset < 0 || copy_frames <= 0) {
|
|
AM_LOGE("invalid dst_frame_offset %d, src_frame_offset %d, copy_frames %d", dst_frame_offset, src_frame_offset, copy_frames);
|
|
return -1;
|
|
}
|
|
src_frame_num = p_src_buf->per_ch_frames;
|
|
|
|
if (src_frame_offset + copy_frames > src_frame_num) {
|
|
AM_LOGW("offset %d + copy %d > buf_frames %d", src_frame_offset, copy_frames, src_frame_num);
|
|
copy_frames = src_frame_num - src_frame_offset;
|
|
AM_LOGW("copy_frames truncate to %d", copy_frames);
|
|
if (copy_frames <= 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
src_ch_num = p_src_buf->ch_num;
|
|
src_sample_size = audio_bytes_per_sample(p_src_buf->format);
|
|
dst_sample_size = audio_bytes_per_sample(dst_format);
|
|
|
|
for (i = 0; i < src_ch_num; i++) {
|
|
if (p_src_buf->ch_data[i] == NULL || p_dst_buf->ch_data[i] == NULL) {
|
|
AM_LOGE("ch_data[%d] src %p, dst %p, skip ...", i,
|
|
p_src_buf->ch_data[i], p_dst_buf->ch_data[i]);
|
|
continue;
|
|
}
|
|
dst_ch_buf_size = (dst_frame_offset + copy_frames) * dst_sample_size;
|
|
if (p_dst_buf->ch_buf_size[i] < (size_t)dst_ch_buf_size) {
|
|
AM_LOGE("dst ch_buf_size[%d] %zu < %d, skip ...", i,
|
|
p_dst_buf->ch_buf_size[i], dst_ch_buf_size);
|
|
continue;
|
|
}
|
|
|
|
p_dst_ch = (uint8_t *)p_dst_buf->ch_data[i] + dst_sample_size * dst_frame_offset;
|
|
p_src_ch = (uint8_t *)p_src_buf->ch_data[i] + src_sample_size * src_frame_offset;
|
|
|
|
if (dst_format != p_src_buf->format) {
|
|
memcpy_by_audio_format(p_dst_ch, dst_format, p_src_ch, p_src_buf->format, copy_frames);
|
|
} else {
|
|
memcpy(p_dst_ch, p_src_ch, copy_frames * src_sample_size);
|
|
}
|
|
}
|
|
|
|
p_dst_buf->ch_num = src_ch_num;
|
|
if (p_dst_buf != p_src_buf) {
|
|
p_dst_buf->per_ch_frames = dst_frame_offset + copy_frames;
|
|
}
|
|
p_dst_buf->format = dst_format;
|
|
p_dst_buf->data_valid = true;
|
|
return 0;
|
|
}
|
|
|
|
int aml_planar_clear_buf(aml_planar_buf_st *p_planar_buf)
|
|
{
|
|
int i = 0;
|
|
if (p_planar_buf == NULL) {
|
|
AM_LOGE("p_planar_buf is NULL");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < AML_PLANAR_MAX_CH_NUM; i++) {
|
|
if (p_planar_buf->ch_data[i] == NULL || p_planar_buf->ch_buf_size[i] == 0) {
|
|
continue;
|
|
}
|
|
memset(p_planar_buf->ch_data[i], 0, p_planar_buf->ch_buf_size[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void aml_planar_free_buf(aml_planar_buf_st *p_buffer)
|
|
{
|
|
int i = 0;
|
|
if (p_buffer == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < AML_PLANAR_MAX_CH_NUM; i++) {
|
|
if (p_buffer->ch_data[i] != NULL) {
|
|
aml_audio_free(p_buffer->ch_data[i]);
|
|
p_buffer->ch_data[i] = NULL;
|
|
}
|
|
p_buffer->ch_buf_size[i] = 0;
|
|
}
|
|
p_buffer->ch_num = 0;
|
|
p_buffer->per_ch_frames = 0;
|
|
p_buffer->format = AUDIO_FORMAT_INVALID;
|
|
p_buffer->data_valid = false;
|
|
}
|
|
|
|
int pcm_mixer_enable_limiter(aml_pcm_mixing_st *p_mixer, bool enable)
|
|
{
|
|
if (p_mixer == NULL) {
|
|
return -1;
|
|
}
|
|
p_mixer->limiter_enable = enable;
|
|
return 0;
|
|
}
|
|
|
|
int pcm_mixer_set_limiter(aml_pcm_mixing_st *p_mixer, float peak_level, int proc_frames, int release_time_ms, audio_format_t format)
|
|
{
|
|
(void)peak_level;
|
|
(void)proc_frames;
|
|
(void)release_time_ms;
|
|
(void)format;
|
|
if (p_mixer == NULL) {
|
|
return -1;
|
|
}
|
|
/* Interleaved limiter is not required by MS12 expand planar mix. */
|
|
p_mixer->limiter_enable = true;
|
|
return 0;
|
|
}
|
|
|
|
bool pcm_mixer_get_limiter_status(aml_pcm_mixing_st *p_mixer)
|
|
{
|
|
if (p_mixer == NULL) {
|
|
return false;
|
|
}
|
|
return p_mixer->limiter_enable;
|
|
}
|
|
|
|
void static_pcm_mixer_start(aml_pcm_mixing_st *p_mixer, const struct audioCfg *p_data_cfg, void *p_in_data, size_t data_bytes)
|
|
{
|
|
if (p_mixer == NULL || p_data_cfg == NULL || p_in_data == NULL) {
|
|
return;
|
|
}
|
|
p_mixer->mixed_frame_size = p_data_cfg->channelCnt * audio_bytes_per_sample(p_data_cfg->format);
|
|
p_mixer->mixed_buf_size = data_bytes;
|
|
p_mixer->mixed_frames = p_mixer->mixed_frame_size ? (data_bytes / p_mixer->mixed_frame_size) : 0;
|
|
p_mixer->mixed_buf = p_in_data;
|
|
p_mixer->mixed_buf_is_static = true;
|
|
memcpy(&p_mixer->cfg, p_data_cfg, sizeof(p_mixer->cfg));
|
|
p_mixer->first_mix = false;
|
|
}
|
|
|
|
void static_pcm_mixer_end(aml_pcm_mixing_st *p_mixer)
|
|
{
|
|
if (p_mixer && p_mixer->mixed_buf_is_static) {
|
|
p_mixer->mixed_buf = NULL;
|
|
p_mixer->mixed_buf_is_static = false;
|
|
p_mixer->first_mix = false;
|
|
}
|
|
}
|
|
|
|
int pcm_mixer_add_data(aml_pcm_mixing_st *p_mixer, void *data_in, size_t in_frames, const struct audioCfg *p_in_cfg)
|
|
{
|
|
if (p_mixer == NULL || data_in == NULL || p_in_cfg == NULL) {
|
|
return -1;
|
|
}
|
|
if (p_mixer->mixed_buf == NULL) {
|
|
return -1;
|
|
}
|
|
return do_mixing_multi_ch(p_mixer, data_in, in_frames, (struct audioCfg *)p_in_cfg);
|
|
}
|