diff options
Diffstat (limited to 'bta/av/bta_av_sbc.c')
-rw-r--r-- | bta/av/bta_av_sbc.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/bta/av/bta_av_sbc.c b/bta/av/bta_av_sbc.c new file mode 100644 index 0000000..1517270 --- /dev/null +++ b/bta/av/bta_av_sbc.c @@ -0,0 +1,590 @@ +/****************************************************************************** + * + * Copyright (C) 2004-2012 Broadcom 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. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This module contains utility functions for dealing with SBC data frames + * and codec capabilities. + * + ******************************************************************************/ + +#include "a2d_api.h" +#include "a2d_sbc.h" +#include "bta_av_sbc.h" + +typedef int (tBTA_AV_SBC_ACT)(void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret); + +typedef struct +{ + INT32 cur_pos; /* current position */ + UINT32 src_sps; /* samples per second (source audio data) */ + UINT32 dst_sps; /* samples per second (converted audio data) */ + tBTA_AV_SBC_ACT *p_act; /* the action function to do the conversion */ + UINT16 bits; /* number of bits per pcm sample */ + UINT16 n_channels; /* number of channels (i.e. mono(1), stereo(2)...) */ + INT16 worker1; + INT16 worker2; + UINT8 div; +} tBTA_AV_SBC_UPS_CB; + +tBTA_AV_SBC_UPS_CB bta_av_sbc_ups_cb; + +/******************************************************************************* +** +** Function bta_av_sbc_init_up_sample +** +** Description initialize the up sample +** +** src_sps: samples per second (source audio data) +** dst_sps: samples per second (converted audio data) +** bits: number of bits per pcm sample +** n_channels: number of channels (i.e. mono(1), stereo(2)...) +** +** Returns none +** +*******************************************************************************/ +void bta_av_sbc_init_up_sample (UINT32 src_sps, UINT32 dst_sps, UINT16 bits, UINT16 n_channels) +{ + bta_av_sbc_ups_cb.cur_pos = -1; + bta_av_sbc_ups_cb.src_sps = src_sps; + bta_av_sbc_ups_cb.dst_sps = dst_sps; + bta_av_sbc_ups_cb.bits = bits; + bta_av_sbc_ups_cb.n_channels= n_channels; + + if(n_channels == 1) + { + /* mono */ + if(bits == 8) + { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_8m; + bta_av_sbc_ups_cb.div = 1; + } + else + { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_16m; + bta_av_sbc_ups_cb.div = 2; + } + } + else + { + /* stereo */ + if(bits == 8) + { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_8s; + bta_av_sbc_ups_cb.div = 2; + } + else + { + bta_av_sbc_ups_cb.p_act = bta_av_sbc_up_sample_16s; + bta_av_sbc_ups_cb.div = 4; + } + } +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (number of bytes) +** dst_samples: The size of p_dst (number of bytes) +** +** Note: An AE reported an issue with this function. +** When called with bta_av_sbc_up_sample(src, uint8_array_dst..) +** the byte before uint8_array_dst may get overwritten. +** Using uint16_array_dst avoids the problem. +** This issue is related to endian-ness and is hard to resolve +** in a generic manner. +** **************** Please use uint16 array as dst. +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + UINT32 src; + UINT32 dst; + + if(bta_av_sbc_ups_cb.p_act) + { + src = src_samples/bta_av_sbc_ups_cb.div; + dst = dst_samples/bta_av_sbc_ups_cb.div; + return (*bta_av_sbc_ups_cb.p_act)(p_src, p_dst, src, dst, p_ret); + } + else + { + *p_ret = 0; + return 0; + } +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_16s (16bits-stereo) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 4 bytes) +** dst_samples: The size of p_dst (in uint of 4 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_16s (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + INT16 *p_src_tmp = (INT16 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker1 = &bta_av_sbc_ups_cb.worker1; + INT16 *p_worker2 = &bta_av_sbc_ups_cb.worker2; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) + { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + } + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples-- && dst_samples) + { + *p_worker1 = *p_src_tmp++; + *p_worker2 = *p_src_tmp++; + + do + { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) + bta_av_sbc_ups_cb.cur_pos = 0; + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_16m (16bits-mono) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 2 bytes) +** dst_samples: The size of p_dst (in uint of 2 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_16m (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + INT16 *p_src_tmp = (INT16 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker = &bta_av_sbc_ups_cb.worker1; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) + { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + } + + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples-- && dst_samples) + { + *p_worker = *p_src_tmp++; + + do + { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) + bta_av_sbc_ups_cb.cur_pos = 0; + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_8s (8bits-stereo) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (in uint of 2 bytes) +** dst_samples: The size of p_dst (in uint of 2 bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_8s (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + UINT8 *p_src_tmp = (UINT8 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker1 = &bta_av_sbc_ups_cb.worker1; + INT16 *p_worker2 = &bta_av_sbc_ups_cb.worker2; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) + { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + } + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples -- && dst_samples) + { + *p_worker1 = *(UINT8 *)p_src_tmp++; + *p_worker1 -= 0x80; + *p_worker1 <<= 8; + *p_worker2 = *(UINT8 *)p_src_tmp++; + *p_worker2 -= 0x80; + *p_worker2 <<= 8; + + do + { + *p_dst_tmp++ = *p_worker1; + *p_dst_tmp++ = *p_worker2; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples--; + dst_samples--; + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) + bta_av_sbc_ups_cb.cur_pos = 0; + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_up_sample_8m (8bits-mono) +** +** Description Given the source (p_src) audio data and +** source speed (src_sps, samples per second), +** This function converts it to audio data in the desired format +** +** p_src: the data buffer that holds the source audio data +** p_dst: the data buffer to hold the converted audio data +** src_samples: The number of source samples (number of bytes) +** dst_samples: The size of p_dst (number of bytes) +** +** Returns The number of bytes used in p_dst +** The number of bytes used in p_src (in *p_ret) +** +*******************************************************************************/ +int bta_av_sbc_up_sample_8m (void *p_src, void *p_dst, + UINT32 src_samples, UINT32 dst_samples, + UINT32 *p_ret) +{ + UINT8 *p_src_tmp = (UINT8 *)p_src; + INT16 *p_dst_tmp = (INT16 *)p_dst; + INT16 *p_worker = &bta_av_sbc_ups_cb.worker1; + UINT32 src_sps = bta_av_sbc_ups_cb.src_sps; + UINT32 dst_sps = bta_av_sbc_ups_cb.dst_sps; + + while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples) + { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples -= 4; + } + + + bta_av_sbc_ups_cb.cur_pos = dst_sps; + + while (src_samples-- && dst_samples) + { + *p_worker = *(UINT8 *)p_src_tmp++; + *p_worker -= 0x80; + *p_worker <<= 8; + + do + { + *p_dst_tmp++ = *p_worker; + *p_dst_tmp++ = *p_worker; + + bta_av_sbc_ups_cb.cur_pos -= src_sps; + dst_samples -= 4; + + } while (bta_av_sbc_ups_cb.cur_pos > 0 && dst_samples); + + bta_av_sbc_ups_cb.cur_pos += dst_sps; + } + + if (bta_av_sbc_ups_cb.cur_pos == (INT32)dst_sps) + bta_av_sbc_ups_cb.cur_pos = 0; + + *p_ret = ((char *)p_src_tmp - (char *)p_src); + return ((char *)p_dst_tmp - (char *)p_dst); +} + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_for_cap +** +** Description Determine the preferred SBC codec configuration for the +** given codec capabilities. The function is passed the +** preferred codec configuration and the peer codec +** capabilities for the stream. The function attempts to +** match the preferred capabilities with the configuration +** as best it can. The resulting codec configuration is +** returned in the same memory used for the capabilities. +** +** Returns 0 if ok, nonzero if error. +** Codec configuration in p_cap. +** +*******************************************************************************/ +UINT8 bta_av_sbc_cfg_for_cap(UINT8 *p_peer, tA2D_SBC_CIE *p_cap, tA2D_SBC_CIE *p_pref) +{ + UINT8 status = A2D_SUCCESS; + tA2D_SBC_CIE peer_cie; + + /* parse peer capabilities */ + if ((status = A2D_ParsSbcInfo(&peer_cie, p_peer, TRUE)) != 0) + { + return status; + } + + /* Check if the peer supports our channel mode */ + if (peer_cie.ch_mode & p_pref->ch_mode) + { + peer_cie.ch_mode = p_pref->ch_mode; + } + else + { + APPL_TRACE_ERROR1("bta_av_sbc_cfg_for_cap: ch_mode(0x%02X) not supported", p_pref->ch_mode); + return A2D_FAIL; + } + + /* Check if the peer supports our sampling freq */ + if (peer_cie.samp_freq & p_pref->samp_freq) + { + peer_cie.samp_freq = p_pref->samp_freq; + } + else + { + APPL_TRACE_ERROR1("bta_av_sbc_cfg_for_cap: samp_freq(0x%02X) not supported", p_pref->samp_freq); + return A2D_FAIL; + } + + /* Check if the peer supports our block len */ + if (peer_cie.block_len & p_pref->block_len) + { + peer_cie.block_len = p_pref->block_len; + } + else + { + APPL_TRACE_ERROR1("bta_av_sbc_cfg_for_cap: block_len(0x%02X) not supported", p_pref->block_len); + return A2D_FAIL; + } + + /* Check if the peer supports our num subbands */ + if (peer_cie.num_subbands & p_pref->num_subbands) + { + peer_cie.num_subbands = p_pref->num_subbands; + } + else + { + APPL_TRACE_ERROR1("bta_av_sbc_cfg_for_cap: num_subbands(0x%02X) not supported", p_pref->num_subbands); + return A2D_FAIL; + } + + /* Check if the peer supports our alloc method */ + if (peer_cie.alloc_mthd & p_pref->alloc_mthd) + { + peer_cie.alloc_mthd = p_pref->alloc_mthd; + } + else + { + APPL_TRACE_ERROR1("bta_av_sbc_cfg_for_cap: alloc_mthd(0x%02X) not supported", p_pref->alloc_mthd); + return A2D_FAIL; + } + + /* max bitpool */ + if (p_pref->max_bitpool != 0 && p_pref->max_bitpool < peer_cie.max_bitpool) + { + peer_cie.max_bitpool = p_pref->max_bitpool; + } + + /* min bitpool */ + if (p_pref->min_bitpool != 0 && p_pref->min_bitpool > peer_cie.min_bitpool) + { + peer_cie.min_bitpool = p_pref->min_bitpool; + } + + if (status == A2D_SUCCESS) + { + /* build configuration */ + A2D_BldSbcInfo(A2D_MEDIA_TYPE_AUDIO, &peer_cie, p_peer); + } + return status; +} + +/******************************************************************************* +** +** Function bta_av_sbc_cfg_in_cap +** +** Description This function checks whether an SBC codec configuration +** is allowable for the given codec capabilities. +** +** Returns 0 if ok, nonzero if error. +** +*******************************************************************************/ +UINT8 bta_av_sbc_cfg_in_cap(UINT8 *p_cfg, tA2D_SBC_CIE *p_cap) +{ + UINT8 status = 0; + tA2D_SBC_CIE cfg_cie; + + /* parse configuration */ + if ((status = A2D_ParsSbcInfo(&cfg_cie, p_cfg, FALSE)) != 0) + { + return status; + } + + /* verify that each parameter is in range */ + + /* sampling frequency */ + if ((cfg_cie.samp_freq & p_cap->samp_freq) == 0) + { + status = A2D_NS_SAMP_FREQ; + } + /* channel mode */ + else if ((cfg_cie.ch_mode & p_cap->ch_mode) == 0) + { + status = A2D_NS_CH_MODE; + } + /* block length */ + else if ((cfg_cie.block_len & p_cap->block_len) == 0) + { + status = A2D_BAD_BLOCK_LEN; + } + /* subbands */ + else if ((cfg_cie.num_subbands & p_cap->num_subbands) == 0) + { + status = A2D_NS_SUBBANDS; + } + /* allocation method */ + else if ((cfg_cie.alloc_mthd & p_cap->alloc_mthd) == 0) + { + status = A2D_NS_ALLOC_MTHD; + } + /* max bitpool */ + else if (cfg_cie.max_bitpool > p_cap->max_bitpool) + { + status = A2D_NS_MAX_BITPOOL; + } + /* min bitpool */ + else if (cfg_cie.min_bitpool < p_cap->min_bitpool) + { + status = A2D_NS_MIN_BITPOOL; + } + + return status; +} + +/******************************************************************************* +** +** Function bta_av_sbc_bld_hdr +** +** Description This function builds the packet header for MPF1. +** +** Returns void +** +*******************************************************************************/ +void bta_av_sbc_bld_hdr(BT_HDR *p_buf, UINT16 fr_per_pkt) +{ + UINT8 *p; + + p_buf->offset -= BTA_AV_SBC_HDR_SIZE; + p = (UINT8 *) (p_buf + 1) + p_buf->offset; + p_buf->len += BTA_AV_SBC_HDR_SIZE; + A2D_BldSbcMplHdr(p, FALSE, FALSE, FALSE, (UINT8) fr_per_pkt); +} + |