/*
* Copyright (C) 2013 - 2016 Sony 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.
*/
#include "ldacBT_internal.h"
enum {
/* 2-DH5 */
LDACBT_2DH5_02, LDACBT_2DH5_03, LDACBT_2DH5_04, LDACBT_2DH5_05,
LDACBT_2DH5_06, LDACBT_2DH5_07, LDACBT_2DH5_08, LDACBT_2DH5_09, LDACBT_2DH5_10,
LDACBT_2DH5_11, LDACBT_2DH5_12, LDACBT_2DH5_13, LDACBT_2DH5_14,
};
#define LDACBT_NO_DEF_ -1
DECLFUNC const LDACBT_EQMID_PROPERTY tbl_ldacbt_eqmid_property[] = {
/* kbps, ID , label, ID for 2DH5 */
/* 990 */ { LDACBT_EQMID_HQ, "HQ" , LDACBT_2DH5_02 },
/* 660 */ { LDACBT_EQMID_SQ, "SQ" , LDACBT_2DH5_03 },
/* 492 */ { LDACBT_EQMID_Q0, "Q0" , LDACBT_2DH5_04 },
/* 396 */ { LDACBT_EQMID_Q1, "Q1" , LDACBT_2DH5_05 },
/* 330 */ { LDACBT_EQMID_MQ, "MQ" , LDACBT_2DH5_06 },
/* 282 */ { LDACBT_EQMID_Q2, "Q2" , LDACBT_2DH5_07 },
/* 246 */ { LDACBT_EQMID_Q3, "Q3" , LDACBT_2DH5_08 },
/* 216 */ { LDACBT_EQMID_Q4, "Q4" , LDACBT_2DH5_09 },
/* 198 */ { LDACBT_EQMID_Q5, "Q5" , LDACBT_2DH5_10 },
/* 180 */ { LDACBT_EQMID_Q6, "Q6" , LDACBT_2DH5_11 },
/* 162 */ { LDACBT_EQMID_Q7, "Q7" , LDACBT_2DH5_12 },
/* 150 */ { LDACBT_EQMID_Q8, "Q8" , LDACBT_2DH5_13 },
/* 138 */ { LDACBT_EQMID_END, "Q9" , LDACBT_2DH5_14 },
};
/* LDAC config table
* - NFRM/PCKT must be less than 16.
*/
DECLFUNC const LDACBT_CONFIG tbl_ldacbt_config[] = {
/*
* index , NFRM , LDAC , FRM
* , ---- , FRM , LEN
* , PCKT , LEN , /CH
* , , [byte], [byte]
*/
{ LDACBT_2DH5_02, 2, 330, 165},
{ LDACBT_2DH5_03, 3, 220, 110},
{ LDACBT_2DH5_04, 4, 164, 82},
{ LDACBT_2DH5_05, 5, 132, 66},
{ LDACBT_2DH5_06, 6, 110, 55},
{ LDACBT_2DH5_07, 7, 94, 47},
{ LDACBT_2DH5_08, 8, 82, 41},
{ LDACBT_2DH5_09, 9, 72, 36},
{ LDACBT_2DH5_10, 10, 66, 33},
{ LDACBT_2DH5_11, 11, 60, 30},
{ LDACBT_2DH5_12, 12, 54, 27},
{ LDACBT_2DH5_13, 13, 50, 25},
{ LDACBT_2DH5_14, 14, 46, 23},
};
/* Clear LDAC handle parameters */
DECLFUNC void ldacBT_param_clear(HANDLE_LDAC_BT hLdacBT)
{
int ich;
if( hLdacBT == NULL ) { return ; }
hLdacBT->proc_mode = LDACBT_PROCMODE_UNSET;
hLdacBT->error_code = LDACBT_ERR_NONE;
hLdacBT->error_code_api = LDACBT_ERR_NONE;
hLdacBT->frm_samples = 0;
hLdacBT->sfid = UNSET;
hLdacBT->pcm.sf = UNSET;
hLdacBT->tx.mtu = UNSET;
hLdacBT->tx.tx_size = UNSET;
hLdacBT->tx.pkt_hdr_sz = UNSET;
hLdacBT->frmlen_tx = UNSET;
hLdacBT->tx.nfrm_in_pkt = UNSET;
hLdacBT->pcm.ch = 0;
hLdacBT->pcm.fmt = LDACBT_SMPL_FMT_S24;
hLdacBT->nshift = 0;
hLdacBT->frmlen = UNSET;
hLdacBT->frm_status = 0;
hLdacBT->bitrate = 0;
/* for alter frame length */
hLdacBT->tgt_nfrm_in_pkt = UNSET;
hLdacBT->tgt_frmlen = UNSET;
hLdacBT->tgt_eqmid = UNSET;
hLdacBT->stat_alter_op = LDACBT_ALTER_OP__NON;
hLdacBT->cm = UNSET;
hLdacBT->cci = UNSET;
hLdacBT->eqmid = UNSET;
hLdacBT->transport = UNSET;
clear_data_ldac( hLdacBT->ldac_trns_frm_buf.buf, sizeof(hLdacBT->ldac_trns_frm_buf.buf));
hLdacBT->ldac_trns_frm_buf.used = 0;
hLdacBT->ldac_trns_frm_buf.nfrm_in = 0;
clear_data_ldac( hLdacBT->pcmring.buf, sizeof(hLdacBT->pcmring.buf));
hLdacBT->pcmring.wp = 0;
hLdacBT->pcmring.rp = 0;
hLdacBT->pcmring.nsmpl = 0;
/* work buffer for I/O */
for( ich = 0; ich < LDAC_PRCNCH; ich++ ){
hLdacBT->ap_pcm[ich] = &hLdacBT->a_pcm[ ich * LDACBT_MAX_LSU * LDACBT_PCM_WLEN_MAX ];
}
hLdacBT->pp_pcm = hLdacBT->ap_pcm;
clear_data_ldac( hLdacBT->a_pcm, LDAC_PRCNCH * LDACBT_MAX_LSU * LDACBT_PCM_WLEN_MAX );
}
/* get ldaclib error code */
DECLFUNC int ldacBT_check_ldaclib_error_code(HANDLE_LDAC_BT hLdacBT)
{
HANDLE_LDAC hData;
int error_code, internal_error_code;
if( hLdacBT == NULL ){ return LDACBT_E_FAIL; }
if( (hData = hLdacBT->hLDAC) == NULL ){ return LDACBT_E_FAIL; }
ldaclib_get_error_code(hData, &error_code);
ldaclib_get_internal_error_code(hData, &internal_error_code);
hLdacBT->error_code = error_code << 10 | internal_error_code;
return LDACBT_S_OK;
}
/* Assertions. */
DECLFUNC int ldacBT_assert_cm( int cm )
{
if( (cm != LDACBT_CHANNEL_MODE_STEREO )
&& (cm != LDACBT_CHANNEL_MODE_DUAL_CHANNEL )
&& (cm != LDACBT_CHANNEL_MODE_MONO )
){
return LDACBT_ERR_ASSERT_CHANNEL_MODE;
}
return LDACBT_ERR_NONE;
}
UNUSED_ATTR DECLFUNC int ldacBT_assert_cci( int cci )
{
if( (cci != LDAC_CCI_STEREO )
&& (cci != LDAC_CCI_DUAL_CHANNEL )
&& (cci != LDAC_CCI_MONO )
){
return LDACBT_ERR_ASSERT_CHANNEL_CONFIG;
}
return LDACBT_ERR_NONE;
}
DECLFUNC int ldacBT_assert_sample_format( LDACBT_SMPL_FMT_T fmt )
{
#ifndef _32BIT_FIXED_POINT
if( (fmt != LDACBT_SMPL_FMT_S16)
&& (fmt != LDACBT_SMPL_FMT_S24)
&& (fmt != LDACBT_SMPL_FMT_S32)
&& (fmt != LDACBT_SMPL_FMT_F32)
){
#else /* _32BIT_FIXED_POINT */
if( (fmt != LDACBT_SMPL_FMT_S16)
&& (fmt != LDACBT_SMPL_FMT_S24)
&& (fmt != LDACBT_SMPL_FMT_S32)
){
#endif /* _32BIT_FIXED_POINT */
return LDACBT_ERR_ILL_SMPL_FORMAT;
}
return LDACBT_ERR_NONE;
}
DECLFUNC int ldacBT_assert_pcm_sampling_freq( int sampling_freq )
{
if( (sampling_freq != 1*44100) && (sampling_freq != 1*48000)
&& (sampling_freq != 2*44100) && (sampling_freq != 2*48000)
){
return LDACBT_ERR_ILL_SAMPLING_FREQ;
}
return LDACBT_ERR_NONE;
}
DECLFUNC int ldacBT_assert_mtu( int mtu )
{
if( mtu < LDACBT_MTU_REQUIRED ){
return LDACBT_ERR_ILL_MTU_SIZE;
}
return LDACBT_ERR_NONE;
}
DECLFUNC int ldacBT_assert_eqmid( int eqmid )
{
if( (eqmid == LDACBT_EQMID_HQ) || (eqmid == LDACBT_EQMID_SQ) || (eqmid == LDACBT_EQMID_MQ)){
return LDACBT_ERR_NONE;
}
return LDACBT_ERR_ILL_EQMID;
}
/* LDAC set Encode Quality Mode index core */
DECLFUNC void ldacBT_set_eqmid_core( HANDLE_LDAC_BT hLdacBT, int eqmid )
{
/* "eqmid" must be checked before calling this function. */
/* just update tgt_eqmid */
P_LDACBT_CONFIG pCfg;
pCfg = ldacBT_get_config( eqmid, hLdacBT->tx.pkt_type );
hLdacBT->tgt_eqmid = eqmid;
hLdacBT->tgt_frmlen = hLdacBT->pcm.ch * pCfg->frmlen_1ch;
hLdacBT->tgt_frmlen -= LDACBT_FRMHDRBYTES;
hLdacBT->tgt_nfrm_in_pkt = pCfg->nfrm_in_pkt;
}
/* Split LR interleaved PCM into buffer that for LDAC encode. */
DECLFUNC void ldacBT_prepare_pcm_encode( void *pbuff, char **ap_pcm, int nsmpl, int nch,
LDACBT_SMPL_FMT_T fmt )
{
int i;
if( nch == 2 ){
if( fmt == LDACBT_SMPL_FMT_S16 ){
short *p_pcm_16 = (short *)pbuff;
short *p_lch_16 = (short *)ap_pcm[0];
short *p_rch_16 = (short *)ap_pcm[1];
for (i = 0; i < nsmpl; i++) {
*p_lch_16++ = p_pcm_16[0];
*p_rch_16++ = p_pcm_16[1];
p_pcm_16+=2;
}
}
else if( fmt == LDACBT_SMPL_FMT_S24 ){
char *p_pcm_8 = (char *)pbuff;
char *p_lch_8 = (char *)ap_pcm[0];
char *p_rch_8 = (char *)ap_pcm[1];
#if __BYTE_ORDER == __LITTLE_ENDIAN
for (i = 0; i < nsmpl; i++) {
*p_lch_8++ = p_pcm_8[0];
*p_lch_8++ = p_pcm_8[1];
*p_lch_8++ = p_pcm_8[2];
p_pcm_8+=3;
*p_rch_8++ = p_pcm_8[0];
*p_rch_8++ = p_pcm_8[1];
*p_rch_8++ = p_pcm_8[2];
p_pcm_8+=3;
}
#else /* __BYTE_ORDER */
#error unsupported byte order
#endif /* #if __BYTE_ORDER == __LITTLE_ENDIAN */
}
else if ( fmt == LDACBT_SMPL_FMT_S32 ){
char *p_pcm_8 = (char *)pbuff;
char *p_lch_8 = (char *)ap_pcm[0];
char *p_rch_8 = (char *)ap_pcm[1];
#if __BYTE_ORDER == __LITTLE_ENDIAN
for (i = 0; i < nsmpl; i++) {
*p_lch_8++ = p_pcm_8[0]; *p_lch_8++ = p_pcm_8[1]; *p_lch_8++ = p_pcm_8[2]; *p_lch_8++ = p_pcm_8[3];
p_pcm_8+=4;
*p_rch_8++ = p_pcm_8[0]; *p_rch_8++ = p_pcm_8[1]; *p_rch_8++ = p_pcm_8[2]; *p_rch_8++ = p_pcm_8[3];
p_pcm_8+=4;
}
#else /* __BYTE_ORDER */
#error unsupported byte order
#endif /* #if __BYTE_ORDER == __LITTLE_ENDIAN */
}
else if ( fmt == LDACBT_SMPL_FMT_F32 ){
float *p_pcm = (float *)pbuff;
float *p_lch = (float *)ap_pcm[0];
float *p_rch = (float *)ap_pcm[1];
for (i = 0; i < nsmpl; i++) {
*p_lch++ = p_pcm[0];
p_pcm++;
*p_rch++ = p_pcm[0];
p_pcm++;
}
}
else{;} /* never be happend */
}
else if( nch == 1 ){
switch(fmt){
case LDACBT_SMPL_FMT_S16:
copy_data_ldac( pbuff, ap_pcm[0], 2*nsmpl );
break;
case LDACBT_SMPL_FMT_S24:
copy_data_ldac( pbuff, ap_pcm[0], 3*nsmpl );
break;
case LDACBT_SMPL_FMT_S32:
case LDACBT_SMPL_FMT_F32:
copy_data_ldac( pbuff, ap_pcm[0], 4*nsmpl );
break;
default:
break;
}
}
}
/* update framelength */
DECLFUNC int ldacBT_update_frmlen(HANDLE_LDAC_BT hLdacBT, int frmlen)
{
int status, sf, ch, fl, fl_per_ch;
int nbasebands, grad_mode, grad_qu_l, grad_qu_h, grad_ofst_l, grad_ofst_h, abc_flag;
LDACBT_TX_INFO *ptx;
LDAC_RESULT result;
status = LDACBT_E_FAIL;
if( hLdacBT == NULL ){
return LDACBT_E_FAIL;
}
sf = hLdacBT->pcm.sf; /* sampling frequency */
ch = hLdacBT->pcm.ch; /* number of channels */
ptx = &hLdacBT->tx;
ldac_setup_AGAIN:
/* update LDAC parameters. */
if( frmlen == UNSET ){
goto ldac_setup_END;
}
/* check & update frameLength */
ldaclib_get_encode_frame_length( hLdacBT->hLDAC, &fl );
if( fl == 0 ){ // This meens that the handle was not initialized yet. Shall not happen.
goto ldac_setup_END;
}
else if( frmlen == fl ){
/* No need to update frame length. Just update bitrate information. */
status = LDACBT_S_OK;
hLdacBT->bitrate = ldacBT_frmlen_to_bitrate( fl, 1, sf, hLdacBT->frm_samples );
goto ldac_setup_END;
}
/* Time to update the frame_length. */
/* Get ldac encoding information for frame_length. */
fl_per_ch = (frmlen+LDACBT_FRMHDRBYTES) / ch;
result = ldaclib_get_encode_setting( fl_per_ch, hLdacBT->sfid, &nbasebands,
&grad_mode, &grad_qu_l, &grad_qu_h, &grad_ofst_l, &grad_ofst_h, &abc_flag);
if (LDAC_FAILED(result)) {
goto ldac_setup_END;
}
/* Set Encoding Information */
result = ldaclib_set_encode_info( hLdacBT->hLDAC, nbasebands, grad_mode,
grad_qu_l, grad_qu_h, grad_ofst_l, grad_ofst_h, abc_flag);
if (LDAC_FAILED(result)) {
ldacBT_check_ldaclib_error_code(hLdacBT);
goto ldac_setup_END;
}
if( !LDAC_SUCCEEDED(ldaclib_set_encode_frame_length( hLdacBT->hLDAC, frmlen ))){
goto ldac_setup_END;
}
/* Update parameters in handle. */
hLdacBT->frmlen = frmlen;
hLdacBT->frmlen_tx = LDACBT_FRMHDRBYTES + frmlen;
ptx->nfrm_in_pkt = ptx->tx_size / hLdacBT->frmlen_tx;
if( ptx->nfrm_in_pkt > LDACBT_NFRM_TX_MAX ){
ptx->nfrm_in_pkt = LDACBT_NFRM_TX_MAX;
}
else if( ptx->nfrm_in_pkt < 2 ){
/* Not allowed 1frame/packet transportation for current version of LDAC A2DP */
if( frmlen <= (ptx->tx_size / 2 - LDACBT_FRMHDRBYTES)){
goto ldac_setup_END;
}
frmlen = ptx->tx_size / 2 - LDACBT_FRMHDRBYTES;
goto ldac_setup_AGAIN;
}
/* Update bitrate and EQMID. */
hLdacBT->bitrate = ldacBT_frmlen_to_bitrate( frmlen, 1, sf, hLdacBT->frm_samples );
hLdacBT->eqmid = ldacBT_get_eqmid_from_frmlen( frmlen, ch, hLdacBT->transport, ptx->pkt_type );
if( hLdacBT->tgt_eqmid == UNSET){
hLdacBT->eqmid = UNSET;
}
status = LDACBT_S_OK;
ldac_setup_END:
return status;
}
/* Get channel_config_index from channel_mode.
* The argument cm, channel_mode, must be checked by function ldacBT_assert_cm() before calling this
* function.
*/
DECLFUNC int ldacBT_cm_to_cci( int cm )
{
if( cm == LDACBT_CHANNEL_MODE_STEREO ){
return LDAC_CCI_STEREO;
}
else if( cm == LDACBT_CHANNEL_MODE_DUAL_CHANNEL ){
return LDAC_CCI_DUAL_CHANNEL;
}
else/* if( cm == LDACBT_CHANNEL_MODE_MONO )*/{
return LDAC_CCI_MONO;
}
}
/* Get channel_mode from channel_config_index.
* The argument cci, channel_config_index, must be checked by the function ldacBT_assert_cci() before
* calling this function.
*/
UNUSED_ATTR DECLFUNC int ldacBT_cci_to_cm( int cci )
{
if( cci == LDAC_CCI_STEREO ){
return LDACBT_CHANNEL_MODE_STEREO;
}
else if( cci == LDAC_CCI_DUAL_CHANNEL ){
return LDACBT_CHANNEL_MODE_DUAL_CHANNEL;
}
else/* if( cci == LDAC_CCI_MONO )*/{
return LDACBT_CHANNEL_MODE_MONO;
}
}
/* Get bitrate from frame length */
DECLFUNC int ldacBT_frmlen_to_bitrate( int frmlen, int flgFrmHdr, int sf, int frame_samples )
{
int bitrate;
if( (frmlen == UNSET) || (flgFrmHdr == UNSET) || (sf == UNSET) || (frame_samples == UNSET) ){
return LDACBT_E_FAIL;
}
if( flgFrmHdr ){
frmlen += LDACBT_FRMHDRBYTES;
}
bitrate = frmlen * sf / frame_samples / (1000 / 8);
return bitrate;
}
/* Get Encode Quality Mode index property */
DECLFUNC P_LDACBT_EQMID_PROPERTY ldacBT_get_eqmid_conv_tbl ( int eqmid )
{
int i, tbl_size;
P_LDACBT_EQMID_PROPERTY pEqmIdProp;
pEqmIdProp = (P_LDACBT_EQMID_PROPERTY)tbl_ldacbt_eqmid_property;
tbl_size = (int)(sizeof(tbl_ldacbt_eqmid_property)/sizeof(tbl_ldacbt_eqmid_property[0]));
/* Search current eqmid */
for( i = 0; i < tbl_size; ++i, ++pEqmIdProp ){
if( pEqmIdProp->eqmid == eqmid ){
return pEqmIdProp;
}
}
return NULL;
}
/* Get altered Encode Quality Mode index */
DECLFUNC int ldacBT_get_altered_eqmid ( HANDLE_LDAC_BT hLdacBT, int priority )
{
int i, eqmid_0, eqmid_1, eqmid_new, tbl_size;
if( priority == 0 ){ return LDACBT_E_FAIL; }
switch( hLdacBT->tx.pkt_type ){
case _2_DH5:
break;
default:
return LDACBT_E_FAIL;
}
tbl_size = (int)(sizeof(tbl_ldacbt_eqmid_property)/sizeof(tbl_ldacbt_eqmid_property[0]));
/* Search target eqmid */
for( i = 0; i < tbl_size; ++i ){
if( tbl_ldacbt_eqmid_property[i].eqmid == hLdacBT->tgt_eqmid ){
break;
}
}
eqmid_0 = i;
eqmid_1 = eqmid_0 - priority;
if( eqmid_1 < 0 ){ return LDACBT_E_FAIL; }
if( eqmid_1 >= tbl_size ){ return LDACBT_E_FAIL; }
eqmid_new = tbl_ldacbt_eqmid_property[eqmid_1].eqmid;
for( i = 0; i < tbl_size; ++i ){
if( tbl_ldacbt_eqmid_property[i].eqmid == LDACBT_LIMIT_ALTER_EQMID_PRIORITY ){
break;
}
}
if( eqmid_1 > i ){ return LDACBT_E_FAIL; }
return eqmid_new;
}
/* Get LDAC bitrate info from Encode Quality Mode Index */
DECLFUNC P_LDACBT_CONFIG ldacBT_get_config( int ldac_bt_mode, int pkt_type )
{
int i, tbl_size, ldac_mode_id;
P_LDACBT_EQMID_PROPERTY pEqmIdProp;
P_LDACBT_CONFIG pCfg;
if( (pEqmIdProp = ldacBT_get_eqmid_conv_tbl( ldac_bt_mode )) == NULL ){
return NULL;
}
if( pkt_type == _2_DH5 ){ ldac_mode_id = pEqmIdProp->id_for_2DH5;}
else{
return NULL;
}
pCfg = (P_LDACBT_CONFIG)tbl_ldacbt_config;
tbl_size = (int)(sizeof(tbl_ldacbt_config)/sizeof(tbl_ldacbt_config[0]));
for( i = 0; i < tbl_size; ++i, ++pCfg ){
if( ldac_mode_id == pCfg->id ){
return pCfg;
}
}
return NULL; /* not found */
}
/* Get Encode Quality Mode Index from framelength */
DECLFUNC int ldacBT_get_eqmid_from_frmlen( int frmlen, int nch, int flgFrmHdr, int pktType )
{
int i, n, eqmid;
P_LDACBT_CONFIG pCfg;
if( flgFrmHdr ){
frmlen += LDACBT_FRMHDRBYTES;
}
if( nch > 0 ){
frmlen /= nch;
}
eqmid = LDACBT_EQMID_END;
n = (int)(sizeof(tbl_ldacbt_eqmid_property)/sizeof(tbl_ldacbt_eqmid_property[0]));
for( i = 0; i < n; ++i ){
if( (pCfg = ldacBT_get_config( tbl_ldacbt_eqmid_property[i].eqmid, pktType )) != NULL ){
if( frmlen >= pCfg->frmlen_1ch){
eqmid = tbl_ldacbt_eqmid_property[i].eqmid;
break;
}
}
}
return eqmid;
}