/* * Copyright (C) 2018 Amlogic Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * DESCRIPTION: * This program is the factory of PCM data. * */ #define LOG_TAG "audio_data_utils" //#define LOG_NDEBUG 0 #include #include #include #include #include #include "aml_audio_log.h" #include #include #include #include #include #include #include "aml_malloc_debug.h" #if defined (BUILDHOSTEXE) //#define DEBUG #ifdef DEBUG #define AMLOGE printf #define AMLOGD printf #else #define AMLOGE #define AMLOGD #endif #else #define AMLOGE ALOGE #define AMLOGD ALOGD #endif static struct aml_audio_channel_name gAudioChName[] = { {AML_CH_IDX_L, "left"}, {AML_CH_IDX_R, "right"}, {AML_CH_IDX_C, "center"}, {AML_CH_IDX_LFE, "lfe"}, {AML_CH_IDX_LS, "left_surround"}, {AML_CH_IDX_RS, "right_urround"}, {AML_CH_IDX_LT, "left_top"}, {AML_CH_IDX_RT, "right_top"}, }; static int _name_trans_to_i2s_chidx(const char *name) { if (!strcmp(name, gAudioChName[0].ch_name)) { return gAudioChName[0].ch_idx; } else if (!strcmp(name, gAudioChName[1].ch_name)) { return gAudioChName[1].ch_idx; } else if (!strcmp(name, gAudioChName[2].ch_name)) { return gAudioChName[2].ch_idx; } else if (!strcmp(name, gAudioChName[3].ch_name)) { return gAudioChName[3].ch_idx; } else if (!strcmp(name, gAudioChName[4].ch_name)) { return gAudioChName[4].ch_idx; } else if (!strcmp(name, gAudioChName[5].ch_name)) { return gAudioChName[5].ch_idx; } else if (!strcmp(name, gAudioChName[6].ch_name)) { return gAudioChName[6].ch_idx; } else if (!strcmp(name, gAudioChName[7].ch_name)) { return gAudioChName[7].ch_idx; } return AML_CH_IDX_NULL; } static inline int16_t _clamp16(int32_t sample) { if ((sample>>15) ^ (sample>>31)) sample = 0x7FFF ^ (sample>>31); return sample; } /* static inline int32_t _clamp32(int64_t sample) { //TODO: return sample; } */ static char *_get_ch_name(eChannelContentIdx idx) { int i = 0; int cnt = 0; cnt = sizeof(gAudioChName)/sizeof(struct aml_audio_channel_name); for (i=AML_CH_IDX_L; i i2s-port: %d\n", __func__, _get_ch_name(channelName), map[i].i2s_idx); return map[i].i2s_idx; } } return AML_I2S_PORT_IDX_NULL; } int data_get_channel_bit_mask( struct aml_channel_map *map, eChannelContentIdx channelName) { int i = 0; int bit_mask = AML_I2S_CHANNEL_NULL; if (map == NULL) { return bit_mask; } for (i=AML_CH_IDX_L; i i2s-bit-mask: 0x%08x\n", __func__, _get_ch_name(channelName), bit_mask); return bit_mask; } } switch (channelName) { case AML_CH_IDX_5_1_ALL: case AML_CH_IDX_7_1_ALL: case AML_CH_IDX_5_1_2_ALL: for (i=0; i i2s-bit-mask: 0x%08x\n", __func__, _get_ch_name(channelName), bit_mask); return bit_mask; default: break; } return bit_mask; } eChannelContentIdx data_get_channel_content_idx( struct aml_channel_map *map, int bitmask) { int idx = AML_CH_IDX_NULL; int i = 0; // int bit_mask = AML_I2S_CHANNEL_NULL; if (map == NULL) { return idx; } for (i=AML_CH_IDX_L; i %s\n", __func__, bitmask, _get_ch_name(idx)); break; } } return idx; } static int _data_remix_center_to_lr(void *buf, size_t frames, size_t framesz, int channels, int bitmask) { int16_t center16; // int32_t center32; int16_t *buf16 = (int16_t *)buf; // int32_t *buf32 = (int32_t *)buf; int i, tmp; ///< TODO: should use bitmask to find the center channel if (bitmask == AML_I2S_CHANNEL_NULL) return 0; ///< TODO: buf is 6ch pcm(L,R,C,lfe,Lr,Rs) if (channels != 6) { AMLOGD("%s: only support 6 ch now!\n", __func__); return -1; } ///< TODO: if (framesz != e16BitPerSample) { AMLOGD("%s: only support 16bit now!\n", __func__); return -1; } switch (framesz) { case e16BitPerSample: ///< 3/0 input L_out/R_out = = 0.707*(L/R + 0.707*C); for (i=0; i<(int)frames; i++) { /* save data of center */ center16 = buf16[channels*i + 2]; /* calculate L */ tmp = buf16[channels*i + 0] << 12; buf16[channels*i + 0] = _clamp16((MINUS_3_DB_IN_Q19_12 * ((tmp + MINUS_3_DB_IN_Q19_12*center16) >>12))>>12); /* calculate R */ tmp = buf16[channels*i + 1] << 12; buf16[channels*i + 1] = _clamp16((MINUS_3_DB_IN_Q19_12 * ((tmp + MINUS_3_DB_IN_Q19_12*center16) >>12))>>12); } break; /* coverity[deadcode]:Reserved for use */ case e32BitPerSample: //TODO: break; /* coverity[deadcode]:Reserved for use */ default: break; } return 0; } static int _data_remix_all_to_lr(void *buf, size_t frames, size_t framesz, int channels, int bitmask) { /* * -------------------------------------- * 3/2 input module: * L_out = 0.707*(L + 0.707*C + 0.707*Ls) * R_out = 0.707*(R + 0.707*C + 0.707*Rs) * -------------------------------------- * our channel sequeces: * 0->L * 1->R * 2->C * 3->lfe * 4->Ls * 5->Rs * -------------------------------------- */ int16_t l_16, r_16, c_16, ls_16, rs_16; // int16_t l_32, r_32, c_32, ls_32, rs_32; int16_t *buf16 = (int16_t *)buf; // int32_t *buf32 = (int32_t *)buf; int i, tmp; if (!bitmask) return 0; ///< buf is 6ch pcm(L,R,C,lfe,Lr,Rs) if (channels != 6) { AMLOGD("%s: only support 6 ch now!\n", __func__); return -1; } ///< TODO: if (framesz != e16BitPerSample) { AMLOGD("%s: only support 16bit now!\n", __func__); return -1; } switch (framesz) { case e16BitPerSample: for (i = 0; i < (int)frames; i++) { /* save l/r/c/ls/rs */ l_16 = buf16[channels*i + 0]; r_16 = buf16[channels*i + 1]; c_16 = buf16[channels*i + 2]; ls_16 = buf16[channels*i + 4]; rs_16 = buf16[channels*i + 5]; /* handle L channel */ tmp = l_16 << 12; buf16[channels*i] = _clamp16((MINUS_3_DB_IN_Q19_12 * ((tmp + MINUS_3_DB_IN_Q19_12 * c_16 + MINUS_3_DB_IN_Q19_12 * ls_16) >> 12)) >> 12); /* handle R channel */ tmp = r_16 << 12; buf16[channels*i + 1] = _clamp16((MINUS_3_DB_IN_Q19_12 * ((tmp + MINUS_3_DB_IN_Q19_12 * c_16 + MINUS_3_DB_IN_Q19_12 * rs_16) >> 12)) >> 12); } break; /* coverity[deadcode]:Reserved for use */ case e32BitPerSample: //TODO: break; /* coverity[deadcode]:Reserved for use */ default: break; } return 0; } int data_remix_to_lr_channel( struct aml_channel_map *map, void *buf, size_t frames, size_t framesz, int channels, eChannelContentIdx chIdx) { int bit_mask = data_get_channel_bit_mask(map, chIdx); switch (chIdx) { case AML_CH_IDX_C: return _data_remix_center_to_lr(buf, frames, framesz, channels, bit_mask); case AML_CH_IDX_5_1_ALL: case AML_CH_IDX_7_1_ALL: case AML_CH_IDX_5_1_2_ALL: return _data_remix_all_to_lr(buf, frames, framesz, channels, bit_mask); default: break; } return 0; } int data_empty_channels( struct aml_channel_map *map, void *buf, size_t frames, size_t framesz, int channels, int channel_empty_bit_mask) { int i, j; int16_t *buf16 = (int16_t *)buf; int32_t *buf32 = (int32_t *)buf; if (!map) return 0; switch (framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j %d\n", i2s_idx1, i2s_idx2); switch (framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j 0x%08x 0x%08x\n\n", buf16[i*channels + i2s_idx1*AML_CH_CNT_PER_PORT + 0], buf16[i*channels + i2s_idx1*AML_CH_CNT_PER_PORT + 1], buf16[i*channels + i2s_idx2*AML_CH_CNT_PER_PORT + 0], buf16[i*channels + i2s_idx2*AML_CH_CNT_PER_PORT + 1]); #endif } break; case e32BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j> 16); } } break; default: break; } break; case e32BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j out_channels) { AMLOGE("%s: out_channels %zu < %zu inclannels\n", __func__, out_channels, in_channels); return -EINVAL; } switch (out_framesz) { case e16BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { if (in_channels >= 2) { buf_out16[out_channels*i + 0] = buf_in1_16[2*i + 0]; buf_out16[out_channels*i + 1] = buf_in1_16[2*i + 1]; } if (in_channels >= 4) { buf_out16[out_channels*i + 2] = buf_in2_16[2*i + 0]; buf_out16[out_channels*i + 3] = buf_in2_16[2*i + 1]; } if (in_channels >= 6) { buf_out16[out_channels*i + 4] = buf_in3_16[2*i + 0]; buf_out16[out_channels*i + 5] = buf_in3_16[2*i + 1]; } if (in_channels >= 8) { buf_out16[out_channels*i + 6] = buf_in4_16[2*i + 0]; buf_out16[out_channels*i + 7] = buf_in4_16[2*i + 1]; } } break; case e32BitPerSample: for (i=0; i<(int)frames; i++) { if (in_channels >= 2) { buf_out16[out_channels*i + 0] = (int16_t)(buf_in1_32[2*i + 0]>>16); buf_out16[out_channels*i + 1] = (int16_t)(buf_in1_32[2*i + 1]>>16); } if (in_channels >= 4) { buf_out16[out_channels*i + 2] = (int16_t)(buf_in2_32[2*i + 0]>>16); buf_out16[out_channels*i + 3] = (int16_t)(buf_in2_32[2*i + 1]>>16); } if (in_channels >= 6) { buf_out16[out_channels*i + 4] = (int16_t)(buf_in3_32[2*i + 0]>>16); buf_out16[out_channels*i + 5] = (int16_t)(buf_in3_32[2*i + 1]>>16); } if (in_channels >= 8) { buf_out16[out_channels*i + 6] = (int16_t)(buf_in4_32[2*i + 0]>>16); buf_out16[out_channels*i + 7] = (int16_t)(buf_in4_32[2*i + 1]>>16); } } break; default: break; } break; case e32BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { if (in_channels >= 2) { buf_out32[out_channels*i + 0] = ((int32_t)(buf_in1_16[2*i + 0]))<<16; buf_out32[out_channels*i + 1] = ((int32_t)(buf_in1_16[2*i + 1]))<<16; } if (in_channels >= 4) { buf_out32[out_channels*i + 2] = ((int32_t)(buf_in2_16[2*i + 0]))<<16; buf_out32[out_channels*i + 3] = ((int32_t)(buf_in2_16[2*i + 1]))<<16; } if (in_channels >= 6) { buf_out32[out_channels*i + 4] = ((int32_t)(buf_in3_16[2*i + 0]))<<16; buf_out32[out_channels*i + 5] = ((int32_t)(buf_in3_16[2*i + 1]))<<16; } if (in_channels >= 8) { buf_out32[out_channels*i + 6] = ((int32_t)(buf_in4_16[2*i + 0]))<<16; buf_out32[out_channels*i + 7] = ((int32_t)(buf_in4_16[2*i + 1]))<<16; } } break; case e32BitPerSample: for (i=0; i<(int)frames; i++) { if (in_channels >= 2) { buf_out32[out_channels*i + 0] = buf_in1_32[2*i + 0]; buf_out32[out_channels*i + 1] = buf_in1_32[2*i + 1]; } if (in_channels >= 4) { buf_out32[out_channels*i + 2] = buf_in2_32[2*i + 0]; buf_out32[out_channels*i + 3] = buf_in2_32[2*i + 1]; } if (in_channels >= 6) { buf_out32[out_channels*i + 4] = buf_in3_32[2*i + 0]; buf_out32[out_channels*i + 5] = buf_in3_32[2*i + 1]; } if (in_channels >= 8) { buf_out32[out_channels*i + 6] = buf_in4_32[2*i + 0]; buf_out32[out_channels*i + 7] = buf_in4_32[2*i + 1]; } } break; default: break; } break; default: break; } return 0; } #define COUNT_DITTER_FRAMES (4) #define COUNT_DITTER_CHANNELS (8) int16_t ditter_8ch_16[COUNT_DITTER_FRAMES*COUNT_DITTER_CHANNELS] = { 0xffff, 0x0001, 0xffff, 0x0000, 0xffff, 0x0000, 0x0001, 0xffff, 0x0001, 0xffff, 0x0001, 0xffff, 0x0001, 0xffff, 0xffff, 0x0001, 0xffff, 0x0001, 0xffff, 0x0000, 0xffff, 0x0001, 0x0000, 0x0000, 0x0001, 0xffff, 0x0001, 0x0001, 0x0000, 0x0001, 0xffff, 0xffff }; int32_t ditter_8ch_32[COUNT_DITTER_FRAMES*COUNT_DITTER_CHANNELS] = { 0xffffffff, 0x00000001, 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, 0x00000001, 0xffffffff, 0x00000001, 0xffffffff, 0x00000001, 0xffffffff, 0x00000001, 0xffffffff, 0xffffffff, 0x00000001, 0xffffffff, 0x00000001, 0xffffffff, 0x00000000, 0xffffffff, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0xffffffff, 0x00000001, 0x00000001, 0x00000000, 0x00000001, 0xffffffff, 0xffffffff }; int data_add_ditter_to_channels( void *buffer, size_t frames, size_t framesz, int channels, int channel_ditter_bit_mask) { int i, j; int16_t *buf16 = (int16_t *)buffer; int32_t *buf32 = (int32_t *)buffer; int16_t *ditter16; int32_t *ditter32; int32_t tmpbuf32; int64_t tmpbuf64; if (channels > 8) { AMLOGE("%s: only support 5.1.2 ch(8channels) now\n", __func__); return -1; } //TODO: need remove this switch (channel_ditter_bit_mask) { case AML_CH_IDX_5_1_ALL: channel_ditter_bit_mask = AML_I2S_CHANNEL_0|AML_I2S_CHANNEL_1 |AML_I2S_CHANNEL_2|AML_I2S_CHANNEL_3|AML_I2S_CHANNEL_4|AML_I2S_CHANNEL_5; break; case AML_CH_IDX_7_1_ALL: case AML_CH_IDX_5_1_2_ALL: channel_ditter_bit_mask = AML_I2S_CHANNEL_0|AML_I2S_CHANNEL_1 |AML_I2S_CHANNEL_2|AML_I2S_CHANNEL_3|AML_I2S_CHANNEL_4|AML_I2S_CHANNEL_5 |AML_I2S_CHANNEL_6|AML_I2S_CHANNEL_7; break; default: break; } switch (framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { ditter16 = &ditter_8ch_16[i%(COUNT_DITTER_FRAMES+1)]; for (j=0; j 32767 || tmpbuf32 < -32768) { continue; } else { buf16[channels*i + j] = (int16_t)tmpbuf32; } } } } break; case e32BitPerSample: for (i=0; i<(int)frames; i++) { ditter32 = &ditter_8ch_32[i%(COUNT_DITTER_FRAMES+1)]; for (j=0; j 2147483647 || tmpbuf64 < -2147483647) { continue; } else { buf32[channels*i + j] = (int32_t)tmpbuf64; } } } } break; default: break; } return 0; } int data_extend_channels( void *out_buf, size_t out_channels, size_t out_framesz, void *in_buf, size_t in_channels, size_t in_framesz, size_t frames) { int i, j; int16_t *buf_in16 = (int16_t *)in_buf; int32_t *buf_in32 = (int32_t *)in_buf; int16_t *buf_out16 = (int16_t *)out_buf; int32_t *buf_out32 = (int32_t *)out_buf; //TODO: use one interfcae for data_extend_channels/data_extract_channels if (out_channels < in_channels) { AMLOGE("%s: only support extend channels\n", __func__); return -1; } // initial out buffer first memset((char *)out_buf, 0x00, frames*out_channels*out_framesz); switch (out_framesz) { case e16BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j<(int)out_channels; j++) { if (j < (int)in_channels) { buf_out16[out_channels*i + j] = buf_in16[in_channels*i + j]; } else { buf_out16[out_channels*i + j] = 0x00; } } } break; case e32BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j<(int)out_channels; j++) { if (j < (int)in_channels) { buf_out16[out_channels*i + j] = (int16_t)(buf_in32[in_channels*i + j]>>16); } else { buf_out16[out_channels*i + j] = 0x00; } } } break; default: break; } break; case e32BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j<(int)out_channels; j++) { if (j < (int)in_channels) { buf_out32[out_channels*i + j] = ((int32_t)buf_in16[in_channels*i + j])<<16; } else { buf_out32[out_channels*i + j] = 0x00; } } } break; case e32BitPerSample: for (i=0; i<(int)frames; i++) { for (j=0; j<(int)out_channels; j++) { if (j < (int)in_channels) { buf_out32[out_channels*i + j] = buf_in32[in_channels*i + j]; } else { buf_out32[out_channels*i + j] = 0x00; } } } break; default: break; } break; default: break; } return 0; } int data_extract_channels( struct aml_channel_map *map, void *out_buf, size_t out_channels, size_t out_framesz, void *in_buf, size_t in_channels, size_t in_framesz, size_t frames, int channel_extract_bit_mask) { int i, k; int16_t *buf_in16 = (int16_t *)in_buf; int32_t *buf_in32 = (int32_t *)in_buf; int16_t *buf_out16 = (int16_t *)out_buf; int32_t *buf_out32 = (int32_t *)out_buf; int cnt; if (!map) return 0; //TODO: use one interfcae for data_extend_channels/data_extract_channels if (out_channels > in_channels) { AMLOGE("%s: only support extract channels\n", __func__); return -1; } //check param cnt = 0; for (i=0; i<8; i++) if (channel_extract_bit_mask & (AML_I2S_CHANNEL_0< (int)out_channels) { AMLOGE("%s: need extract %d channels, but buf only have %zu chanels\n", __func__, cnt, out_channels); return -1; } switch (out_framesz) { case e16BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { cnt = 0; for (k=0; k<(int)in_channels; k++) { if (channel_extract_bit_mask & (AML_I2S_CHANNEL_0<>16); cnt++; } } } break; default: break; } break; case e32BitPerSample: switch (in_framesz) { case e16BitPerSample: for (i=0; i<(int)frames; i++) { cnt = 0; for (k=0; k<(int)in_channels; k++) { if (channel_extract_bit_mask & (AML_I2S_CHANNEL_0<