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