/*************************************************************************** * * Copyright 2015-2019 BES. * All rights reserved. All unpublished rights reserved. * * No part of this work may be used or reproduced in any form or by any * means, or stored in a database or retrieval system, without prior written * permission of BES. * * Use of this work is governed by a license granted by BES. * This work contains confidential and proprietary information of * BES. which is protected by copyright, trade secret, * trademark and other intellectual property rights. * ****************************************************************************/ #if defined(APP_LINEIN_A2DP_SOURCE) || defined(APP_I2S_A2DP_SOURCE) #include "app_a2dp_source.h" #include "a2dp_api.h" #include "app_overlay.h" uint8_t *a2dp_linein_buff; static char a2dp_transmit_buffer[A2DP_TRANS_SIZE]; sbcbank_t sbcbank; A2DP_SOURCE_STRUCT a2dp_source; static sbcpack_t *get_sbcPacket(void) { int index = sbcbank.free; sbcbank.free += 1; if (sbcbank.free == 1) { sbcbank.free = 0; } return &(sbcbank.sbcpacks[index]); } #if 0 typedef struct { AvrcpAdvancedPdu pdu; uint8_t para_buf[10]; }APP_A2DP_AVRCPADVANCEDPDU; extern osPoolId app_a2dp_avrcpadvancedpdu_mempool; #define app_a2dp_avrcpadvancedpdu_mempool_init() \ do { \ if (app_a2dp_avrcpadvancedpdu_mempool == NULL) \ app_a2dp_avrcpadvancedpdu_mempool = \ osPoolCreate(osPool(app_a2dp_avrcpadvancedpdu_mempool)); \ } while (0); #define app_a2dp_avrcpadvancedpdu_mempool_calloc(buf) \ do { \ APP_A2DP_AVRCPADVANCEDPDU *avrcpadvancedpdu; \ avrcpadvancedpdu = (APP_A2DP_AVRCPADVANCEDPDU *)osPoolCAlloc( \ app_a2dp_avrcpadvancedpdu_mempool); \ buf = &(avrcpadvancedpdu->pdu); \ buf->parms = avrcpadvancedpdu->para_buf; \ } while (0); #define app_a2dp_avrcpadvancedpdu_mempool_free(buf) \ do { \ osPoolFree(app_a2dp_avrcpadvancedpdu_mempool, buf); \ } while (0); void a2dp_source_volume_local_set(int8_t vol) { app_bt_stream_volume_get_ptr()->a2dp_vol = vol; nv_record_touch_cause_flush(); } static int a2dp_volume_get(void) { int vol = app_bt_stream_volume_get_ptr()->a2dp_vol; vol = 8*vol-1; if (vol > (0x7f-1)) vol = 0x7f; return (vol); } static int a2dp_volume_set(U8 vol) { int dest_vol; dest_vol = (((int)vol&0x7f)<<4)/0x7f + 1; if (dest_vol > TGT_VOLUME_LEVEL_15) dest_vol = TGT_VOLUME_LEVEL_15; if (dest_vol < TGT_VOLUME_LEVEL_0) dest_vol = TGT_VOLUME_LEVEL_0; a2dp_source_volume_local_set(dest_vol); app_bt_stream_volumeset(dest_vol); return (vol); } void avrcp_source_callback_TG(AvrcpChannel *chnl, const AvrcpCallbackParms *Parms) { TRACE(3,"%s : chnl %p, Parms %p,Parms->event\n", __FUNCTION__,chnl, Parms,Parms->event); enum BT_DEVICE_ID_T device_id = BT_DEVICE_ID_1; switch(Parms->event) { case AVRCP_EVENT_CONNECT: if(0)//(chnl->avrcpVersion >=0x103) { TRACE(1,"::%s AVRCP_GET_CAPABILITY\n",__FUNCTION__); if (app_bt_device.avrcp_cmd1[device_id] == NULL) app_a2dp_avrcpadvancedpdu_mempool_calloc(app_bt_device.avrcp_cmd1[device_id]); AVRCP_CtGetCapabilities(chnl,app_bt_device.avrcp_cmd1[device_id],AVRCP_CAPABILITY_EVENTS_SUPPORTED); } app_bt_device.avrcp_channel[device_id].avrcpState = AVRCP_STATE_CONNECTED; TRACE(3,"::%s AVRCP_EVENT_CONNECT %x,device_id=%d\n",__FUNCTION__, chnl->avrcpVersion,device_id); break; case AVRCP_EVENT_DISCONNECT: TRACE(1,"::%s AVRCP_EVENT_DISCONNECT",__FUNCTION__); app_bt_device.avrcp_channel[device_id].avrcpState = AVRCP_STATE_DISCONNECTED; if (app_bt_device.avrcp_get_capabilities_rsp[device_id]){ app_a2dp_avrcpadvancedpdu_mempool_free(app_bt_device.avrcp_get_capabilities_rsp[device_id]); app_bt_device.avrcp_get_capabilities_rsp[device_id] = NULL; } if (app_bt_device.avrcp_control_rsp[device_id]){ app_a2dp_avrcpadvancedpdu_mempool_free(app_bt_device.avrcp_control_rsp[device_id]); app_bt_device.avrcp_control_rsp[device_id] = NULL; } if (app_bt_device.avrcp_notify_rsp[device_id]){ app_a2dp_avrcpadvancedpdu_mempool_free(app_bt_device.avrcp_notify_rsp[device_id]); app_bt_device.avrcp_notify_rsp[device_id] = NULL; } if (app_bt_device.avrcp_cmd1[device_id]){ app_a2dp_avrcpadvancedpdu_mempool_free(app_bt_device.avrcp_cmd1[device_id]); app_bt_device.avrcp_cmd1[device_id] = NULL; } if (app_bt_device.avrcp_cmd2[device_id]){ app_a2dp_avrcpadvancedpdu_mempool_free(app_bt_device.avrcp_cmd2[device_id]); app_bt_device.avrcp_cmd2[device_id] = NULL; } app_bt_device.volume_report[device_id] = 0; break; case AVRCP_EVENT_RESPONSE: TRACE(3,"::%s AVRCP_EVENT_RESPONSE op=%x,status=%x\n",__FUNCTION__, Parms->advOp,Parms->status); break; case AVRCP_EVENT_PANEL_CNF: TRACE(4,"::%s AVRCP_EVENT_PANEL_CNF %x,%x,%x",__FUNCTION__, Parms->p.panelCnf.response,Parms->p.panelCnf.operation,Parms->p.panelCnf.press); break; case AVRCP_EVENT_ADV_TX_DONE: TRACE(4,"::%s AVRCP_EVENT_ADV_TX_DONE device_id=%d,status=%x,errorcode=%x\n",__FUNCTION__,device_id,Parms->status,Parms->errorCode); TRACE(3,"::%s AVRCP_EVENT_ADV_TX_DONE op:%d, transid:%x\n", __FUNCTION__,Parms->p.adv.txPdu->op,Parms->p.adv.txPdu->transId); if (Parms->p.adv.txPdu->op == AVRCP_OP_GET_CAPABILITIES){ if (app_bt_device.avrcp_get_capabilities_rsp[device_id] == Parms->p.adv.txPdu){ app_bt_device.avrcp_get_capabilities_rsp[device_id] = NULL; app_a2dp_avrcpadvancedpdu_mempool_free(Parms->p.adv.txPdu); } } break; case AVRCP_EVENT_COMMAND: #ifndef __AVRCP_EVENT_COMMAND_VOLUME_SKIP__ TRACE(3,"::%s AVRCP_EVENT_COMMAND device_id=%d,role=%x\n",__FUNCTION__,device_id,chnl->role); TRACE(3,"::%s AVRCP_EVENT_COMMAND ctype=%x,subunitype=%x\n",__FUNCTION__, Parms->p.cmdFrame->ctype,Parms->p.cmdFrame->subunitType); TRACE(3,"::%s AVRCP_EVENT_COMMAND subunitId=%x,opcode=%x\n",__FUNCTION__, Parms->p.cmdFrame->subunitId,Parms->p.cmdFrame->opcode); TRACE(3,"::%s AVRCP_EVENT_COMMAND operands=%x,operandLen=%x\n",__FUNCTION__, Parms->p.cmdFrame->operands,Parms->p.cmdFrame->operandLen); TRACE(2,"::%s AVRCP_EVENT_COMMAND more=%x\n", Parms->p.cmdFrame->more); if(Parms->p.cmdFrame->ctype == AVRCP_CTYPE_STATUS) { uint32_t company_id = *(Parms->p.cmdFrame->operands+2) + ((uint32_t)(*(Parms->p.cmdFrame->operands+1))<<8) + ((uint32_t)(*(Parms->p.cmdFrame->operands))<<16); TRACE(2,"::%s AVRCP_EVENT_COMMAND company_id=%x\n",__FUNCTION__, company_id); if(company_id == 0x001958) //bt sig { AvrcpOperation op = *(Parms->p.cmdFrame->operands+3); uint8_t oplen = *(Parms->p.cmdFrame->operands+6)+ ((uint32_t)(*(Parms->p.cmdFrame->operands+5))<<8); TRACE(3,"::%s AVRCP_EVENT_COMMAND op=%x,oplen=%x\n",__FUNCTION__, op,oplen); switch(op) { case AVRCP_OP_GET_CAPABILITIES: { uint8_t event = *(Parms->p.cmdFrame->operands+7); if(event==AVRCP_CAPABILITY_COMPANY_ID) { TRACE(1,"::%s AVRCP_EVENT_COMMAND send support compay id",__FUNCTION__); } else if(event == AVRCP_CAPABILITY_EVENTS_SUPPORTED) { TRACE(2,"::%s AVRCP_EVENT_COMMAND send support event transId:%d", __FUNCTION__,Parms->p.cmdFrame->transId); chnl->adv.eventMask = AVRCP_ENABLE_VOLUME_CHANGED; ///volume control if (app_bt_device.avrcp_get_capabilities_rsp[device_id] == NULL) app_a2dp_avrcpadvancedpdu_mempool_calloc(app_bt_device.avrcp_get_capabilities_rsp[device_id]); app_bt_device.avrcp_get_capabilities_rsp[device_id]->transId = Parms->p.cmdFrame->transId; app_bt_device.avrcp_get_capabilities_rsp[device_id]->ctype = AVCTP_RESPONSE_IMPLEMENTED_STABLE; TRACE(2,"::%s AVRCP_EVENT_COMMAND send support event transId:%d", __FUNCTION__,app_bt_device.avrcp_get_capabilities_rsp[device_id]->transId); AVRCP_CtGetCapabilities_Rsp(chnl,app_bt_device.avrcp_get_capabilities_rsp[device_id],AVRCP_CAPABILITY_EVENTS_SUPPORTED,chnl->adv.eventMask); } else { TRACE(1,"::%s AVRCP_EVENT_COMMAND send error event value",__FUNCTION__); } } break; } } }else if(Parms->p.cmdFrame->ctype == AVCTP_CTYPE_CONTROL){ TRACE(1,"::%s AVRCP_EVENT_COMMAND AVCTP_CTYPE_CONTROL\n",__FUNCTION__); DUMP8("%02x ", Parms->p.cmdFrame->operands, Parms->p.cmdFrame->operandLen); if (Parms->p.cmdFrame->operands[3] == AVRCP_OP_SET_ABSOLUTE_VOLUME){ TRACE(2,"::%s AVRCP_EID_VOLUME_CHANGED transId:%d\n", __FUNCTION__,Parms->p.cmdFrame->transId); a2dp_volume_set(Parms->p.cmdFrame->operands[7]); if (app_bt_device.avrcp_control_rsp[device_id] == NULL) app_a2dp_avrcpadvancedpdu_mempool_calloc(app_bt_device.avrcp_control_rsp[device_id]); app_bt_device.avrcp_control_rsp[device_id]->transId = Parms->p.cmdFrame->transId; app_bt_device.avrcp_control_rsp[device_id]->ctype = AVCTP_RESPONSE_ACCEPTED; DUMP8("%02x ", Parms->p.cmdFrame->operands, Parms->p.cmdFrame->operandLen); AVRCP_CtAcceptAbsoluteVolume_Rsp(chnl, app_bt_device.avrcp_control_rsp[device_id], Parms->p.cmdFrame->operands[7]); } }else if (Parms->p.cmdFrame->ctype == AVCTP_CTYPE_NOTIFY){ BtStatus status; TRACE(1,"::%s AVRCP_EVENT_COMMAND AVCTP_CTYPE_NOTIFY\n",__FUNCTION__); DUMP8("%02x ", Parms->p.cmdFrame->operands, Parms->p.cmdFrame->operandLen); if (Parms->p.cmdFrame->operands[7] == AVRCP_EID_VOLUME_CHANGED){ TRACE(2,"::%s AVRCP_EID_VOLUME_CHANGED transId:%d\n", __FUNCTION__,Parms->p.cmdFrame->transId); if (app_bt_device.avrcp_notify_rsp[device_id] == NULL) app_a2dp_avrcpadvancedpdu_mempool_calloc(app_bt_device.avrcp_notify_rsp[device_id]); app_bt_device.avrcp_notify_rsp[device_id]->transId = Parms->p.cmdFrame->transId; app_bt_device.avrcp_notify_rsp[device_id]->ctype = AVCTP_RESPONSE_INTERIM; app_bt_device.volume_report[device_id] = AVCTP_RESPONSE_INTERIM; status = AVRCP_CtGetAbsoluteVolume_Rsp(chnl, app_bt_device.avrcp_notify_rsp[device_id], a2dp_volume_get()); TRACE(2,"::%s AVRCP_EVENT_COMMAND AVRCP_EID_VOLUME_CHANGED nRet:%x\n",__FUNCTION__,status); } } #endif break; case AVRCP_EVENT_ADV_CMD_TIMEOUT: TRACE(3,"::%s AVRCP_EVENT_ADV_CMD_TIMEOUT device_id=%d,role=%x\n",__FUNCTION__,device_id,chnl->role); break; } } #endif osMutexId a2dp_source_mutex_id = NULL; osMutexDef(a2dp_source_mutex); static void a2dp_source_mutex_lock(void) { osMutexWait(a2dp_source_mutex_id, osWaitForever); } static void a2dp_source_mutex_unlock(void) { osMutexRelease(a2dp_source_mutex_id); } static void a2dp_source_sem_lock(a2dp_source_lock_t *lock) { osSemaphoreWait(lock->_osSemaphoreId, osWaitForever); } static void a2dp_source_sem_unlock(a2dp_source_lock_t *lock) { osSemaphoreRelease(lock->_osSemaphoreId); } static void a2dp_source_reset_send_lock(void) { PSCB p_scb = (PSCB)(a2dp_source.sbc_send_lock._osSemaphoreDef.semaphore); uint32_t lock = int_lock(); p_scb->tokens = 0; int_unlock(lock); } static bool a2dp_source_is_send_wait(void) { bool ret = false; uint32_t lock = int_lock(); PSCB p_scb = (PSCB)(a2dp_source.sbc_send_lock._osSemaphoreDef.semaphore); if (p_scb->p_lnk) { ret = true; } int_unlock(lock); return ret; } static void a2dp_source_wait_pcm_data(void) { a2dp_source_lock_t *lock = &(a2dp_source.data_lock); PSCB p_scb = (PSCB)(lock->_osSemaphoreDef.semaphore); uint32_t iflag = int_lock(); p_scb->tokens = 0; int_unlock(iflag); a2dp_source_sem_lock(lock); } static void a2dp_source_put_data(void) { a2dp_source_lock_t *lock = &(a2dp_source.data_lock); a2dp_source_sem_unlock(lock); } #if 0 static void a2dp_source_lock_sbcsending(void * channel) { tws_lock_t *lock = &(a2dp_source.sbc_send_lock); sem_lock(lock); } static void a2dp_source_unlock_sbcsending(void * channel) { tws_lock_t *lock = &(a2dp_source.sbc_send_lock); sem_unlock(lock); } #endif static int32_t a2dp_source_wait_sent(uint32_t timeout) { int32_t ret = 0; a2dp_source_lock_t *lock = &(a2dp_source.sbc_send_lock); a2dp_source_reset_send_lock(); ret = osSemaphoreWait(lock->_osSemaphoreId, timeout); return ret; } void a2dp_source_notify_send(void) { if (a2dp_source_is_send_wait()) { // task wait lock // TWS_DBLOG("\nNOTIFY SEND\n"); a2dp_source_sem_unlock(&(a2dp_source.sbc_send_lock)); } } // loop write static int A2dpSourceEnCQueue(CQueue *Q, CQItemType *e, unsigned int len) { int status = CQ_OK; if (AvailableOfCQueue(Q) < (int)len) { Q->write = 0; Q->len = 0; Q->read = 0; status = CQ_ERR; } else { status = CQ_OK; } Q->len += len; while (len > 0) { Q->base[Q->write] = *e; ++Q->write; ++e; --len; if (Q->write >= Q->size) Q->write = 0; } // TRACE(1,"Q->len:%d ", Q->len); return status; } static int a2dp_source_pcm_buffer_write(uint8_t *pcm_buf, uint16_t len) { int status; // TWS_DBLOG("\nenter: %s %d\n",__FUNCTION__,__LINE__); a2dp_source_mutex_lock(); status = A2dpSourceEnCQueue(&(a2dp_source.pcm_queue), pcm_buf, len); a2dp_source_mutex_unlock(); // TWS_DBLOG("\nexit: %s %d\n",__FUNCTION__,__LINE__); return status; } static int a2dp_source_pcm_buffer_read(uint8_t *buff, uint16_t len) { uint8_t *e1 = NULL, *e2 = NULL; unsigned int len1 = 0, len2 = 0; int status; a2dp_source_mutex_lock(); status = PeekCQueue(&(a2dp_source.pcm_queue), len, &e1, &len1, &e2, &len2); if (len == (len1 + len2)) { memcpy(buff, e1, len1); memcpy(buff + len1, e2, len2); DeCQueue(&(a2dp_source.pcm_queue), 0, len); } else { // SOURCE_DBLOG("memset buffer"); memset(buff, 0x00, len); status = -1; } a2dp_source_mutex_unlock(); return status; } uint32_t a2dp_source_linein_more_pcm_data(uint8_t *pcm_buf, uint32_t len) { #if 1 int status; status = a2dp_source_pcm_buffer_write(pcm_buf, len); // pcm data from adc // DUMP8("%02x ",pcm_buf, 10); if (status != CQ_OK) { SOURCE_DBLOG("linin buff overflow!"); } a2dp_source_put_data(); return len; #else int status; status = a2dp_source_pcm_buffer_write(pcm_buf, len); // pcm data from adc // DUMP8("%02x ",pcm_buf, 10); if (status != CQ_OK) { SOURCE_DBLOG("linin buff overflow!"); } if (((app_bt_device.input_onoff == 1) && (a2dp_source.pcm_queue.len > 10 * 1024)) || (app_bt_device.input_onoff == 2)) { a2dp_source_put_data(); app_bt_device.input_onoff = 2; } return len; #endif } static btif_handler a2dp_source_handler; static uint8_t app_a2dp_source_find_process = 0; void app_a2dp_source_stop_find(void) { app_a2dp_source_find_process = 0; // ME_UnregisterGlobalHandler(&a2dp_source_handler); } #if 0 static void bt_a2dp_source_call_back(const BtEvent* event) { switch (event->eType) { case BTEVENT_HCI_COMMAND_SENT: case BTEVENT_ACL_DATA_NOT_ACTIVE: return; case BTEVENT_ACL_DATA_ACTIVE: CmgrHandler *cmgrHandler; /* Start the sniff timer */ cmgrHandler = CMGR_GetAclHandler(event->p.remDev); if (cmgrHandler) app_bt_CMGR_SetSniffTimer(cmgrHandler, NULL, CMGR_SNIFF_TIMER); return; } TRACE(1,"SRC app_bt_golbal_handle evt = %d",event->eType); switch(event->eType){ case BTEVENT_NAME_RESULT: SOURCE_DBLOG("\n%s %d BTEVENT_NAME_RESULT\n",__FUNCTION__,__LINE__); break; case BTEVENT_INQUIRY_RESULT: SOURCE_DBLOG("\n%s %d BTEVENT_INQUIRY_RESULT\n",__FUNCTION__,__LINE__); DUMP8("%02x ", event->p.inqResult.bdAddr.addr, 6); SOURCE_DBLOG("inqmode = %x",event->p.inqResult.inqMode); DUMP8("%02x ", event->p.inqResult.extInqResp, 20); SOURCE_DBLOG("classdevice=%x",event->p.inqResult.classOfDevice); ///check the class of device to find the handfree device if(event->p.inqResult.classOfDevice & COD_MAJOR_AUDIO) //if((event->p.inqResult.classOfDevice & COD_MAJOR_AUDIO)&&(!memcmp(event->p.inqResult.bdAddr.addr,"\x36\x35\x34",3))) { memcpy(app_bt_device.inquried_snk_bdAddr.addr,event->p.inqResult.bdAddr.addr,sizeof(event->p.inqResult.bdAddr.addr)); DUMP8("%02x ",app_bt_device.inquried_snk_bdAddr.addr, 6); ME_CancelInquiry(); app_a2dp_source_stop_find(); } break; case BTEVENT_INQUIRY_COMPLETE: SOURCE_DBLOG("\n%s %d BTEVENT_INQUIRY_COMPLETE\n",__FUNCTION__,__LINE__); app_a2dp_source_stop_find(); break; /** The Inquiry process is canceled. */ case BTEVENT_INQUIRY_CANCELED: SOURCE_DBLOG("\n%s %d BTEVENT_INQUIRY_CANCELED\n",__FUNCTION__,__LINE__); SOURCE_DBLOG("start to connect peer device"); A2DP_OpenStream(&app_bt_device.a2dp_stream[BT_DEVICE_ID_1], (BT_BD_ADDR *)&app_bt_device.inquried_snk_bdAddr); break; case BTEVENT_LINK_CONNECT_CNF: case BTEVENT_LINK_CONNECT_IND: SOURCE_DBLOG("CONNECT_IND/CNF evt:%d errCode:0x%0x newRole:%d activeCons:%d",event->eType, event->errCode, event->p.remDev->role, MEC(activeCons)); #if defined(__BTIF_EARPHONE__) && defined(__BTIF_AUTOPOWEROFF__) && \ !defined(FPGA) if (MEC(activeCons) == 0){ app_start_10_second_timer(APP_POWEROFF_TIMER_ID); }else{ app_stop_10_second_timer(APP_POWEROFF_TIMER_ID); } #endif if (MEC(activeCons) > 1){ app_bt_MeDisconnectLink(event->p.remDev); } default: //SOURCE_DBLOG("\n%s %d etype:%d\n",__FUNCTION__,__LINE__,event->eType); break; } app_bt_role_manager_process(event); app_bt_accessible_manager_process(event); app_bt_sniff_manager_process(event); app_bt_golbal_handle_hook(event); //SOURCE_DBLOG("\nexit: %s %d\n",__FUNCTION__,__LINE__); } #else uint8_t source_bt_addr[6] = {0x88, 0xaa, 0x33, 0x22, 0x11, 0x11}; // uint8_t source_bt_addr[6]={0x85,0x7e,0xaa,0x3c,0xd0,0xe8}; static void bt_a2dp_source_call_back(const btif_event_t *Event) { uint8_t etype = btif_me_get_callback_event_type(Event); btif_remote_device_t *remDev; switch (etype) { case BTIF_BTEVENT_HCI_COMMAND_SENT: case BTIF_BTEVENT_ACL_DATA_NOT_ACTIVE: return; case BTIF_BTEVENT_ACL_DATA_ACTIVE: btif_cmgr_handler_t *cmgrHandler; /* Start the sniff timer */ cmgrHandler = btif_cmgr_get_acl_handler(btif_me_get_callback_event_rem_dev(Event)); if (cmgrHandler) app_bt_CMGR_SetSniffTimer(cmgrHandler, NULL, BTIF_CMGR_SNIFF_TIMER); return; } TRACE(1, "SRC app_bt_golbal_handle evt = %d", etype); switch (etype) { case BTIF_BTEVENT_NAME_RESULT: SOURCE_DBLOG("\n%s %d BTEVENT_NAME_RESULT\n", __FUNCTION__, __LINE__); break; case BTIF_BTEVENT_INQUIRY_RESULT: remDev = btif_me_get_callback_event_rem_dev(Event); SOURCE_DBLOG("\n%s %d BTEVENT_INQUIRY_RESULT\n", __FUNCTION__, __LINE__); DUMP8("%02x ", btif_me_get_callback_event_inq_result_bd_addr_addr(Event), 6); SOURCE_DBLOG("inqmode = %x", btif_me_get_callback_event_inq_result_inq_mode(Event)); DUMP8("%02x ", btif_me_get_callback_event_inq_result_ext_inq_resp(Event), 20); SOURCE_DBLOG("classdevice=%x", btif_me_get_callback_event_inq_result_classofdevice(Event)); /// check the class of device to find the handfree device // if(btif_me_get_callback_event_inq_result_classofdevice(Event) & // BTIF_COD_MAJOR_AUDIO) if ((btif_me_get_callback_event_inq_result_classofdevice(Event) & BTIF_COD_MAJOR_AUDIO) && (!memcmp(btif_me_get_callback_event_inq_result_bd_addr_addr(Event), source_bt_addr, 6))) { memcpy(app_bt_device.inquried_snk_bdAddr.addr, btif_me_get_callback_event_inq_result_bd_addr_addr(Event), 6); DUMP8("%02x ", app_bt_device.inquried_snk_bdAddr.addr, 6); btif_me_cancel_inquiry(); app_a2dp_source_stop_find(); } break; case BTIF_BTEVENT_INQUIRY_COMPLETE: SOURCE_DBLOG("\n%s %d BTEVENT_INQUIRY_COMPLETE\n", __FUNCTION__, __LINE__); app_a2dp_source_stop_find(); break; /** The Inquiry process is canceled. */ case BTIF_BTEVENT_INQUIRY_CANCELED: SOURCE_DBLOG("\n%s %d BTEVENT_INQUIRY_CANCELED\n", __FUNCTION__, __LINE__); SOURCE_DBLOG("start to connect peer device"); btif_a2dp_open_stream( app_bt_device.a2dp_stream[BT_DEVICE_ID_1]->a2dp_stream, (bt_bdaddr_t *)&app_bt_device.inquried_snk_bdAddr); break; case BTIF_BTEVENT_LINK_CONNECT_CNF: case BTIF_BTEVENT_LINK_CONNECT_IND: SOURCE_DBLOG( "CONNECT_IND/CNF evt:%d errCode:0x%0x newRole:%d activeCons:%d", etype, btif_me_get_callback_event_err_code(Event), btif_me_get_remote_device_role( btif_me_get_callback_event_rem_dev(Event)), btif_me_get_activeCons()); #if defined(__BTIF_EARPHONE__) && defined(__BTIF_AUTOPOWEROFF__) && \ !defined(FPGA) if (btif_me_get_activeCons() == 0) { app_start_10_second_timer(APP_POWEROFF_TIMER_ID); } else { app_stop_10_second_timer(APP_POWEROFF_TIMER_ID); } #endif if (btif_me_get_activeCons() > 1) { remDev = btif_me_get_callback_event_rem_dev(Event); app_bt_MeDisconnectLink(remDev); } default: // SOURCE_DBLOG("\n%s %d etype:%d\n",__FUNCTION__,__LINE__,event->eType); break; } app_bt_role_manager_process(Event); app_bt_accessible_manager_process(Event); app_bt_sniff_manager_process(Event); // SOURCE_DBLOG("\nexit: %s %d\n",__FUNCTION__,__LINE__); } #endif void app_a2dp_start_stream(void) { btif_a2dp_start_stream(&app_bt_device.a2dp_stream[BT_DEVICE_ID_1]); } void app_a2dp_suspend_stream(void) { btif_a2dp_suspend_stream(&app_bt_device.a2dp_stream[BT_DEVICE_ID_1]); } static void _find_a2dp_sink_peer_device_start(void) { SOURCE_DBLOG("\nenter: %s %d\n", __FUNCTION__, __LINE__); bt_status_t state; if (app_a2dp_source_find_process == 0 && app_bt_device.a2dp_state[0] == 0) { app_a2dp_source_find_process = 1; again: state = btif_me_inquiry(BTIF_BT_IAC_GIAC, 30, 0); SOURCE_DBLOG("\n%s %d\n", __FUNCTION__, __LINE__); if (state != BT_STS_PENDING) { osDelay(500); goto again; } SOURCE_DBLOG("\n%s %d\n", __FUNCTION__, __LINE__); } } void app_a2dp_source_find_sink(void) { _find_a2dp_sink_peer_device_start(); } static bool need_init_encoder = true; //__PSRAMDATA static SbcStreamInfo StreamInfo = {0}; static btif_sbc_stream_info_short_t StreamInfo = {0}; static btif_sbc_encoder_t sbc_encoder; static uint8_t app_a2dp_source_samplerate_2_sbcenc_type(enum AUD_SAMPRATE_T sample_rate) { uint8_t rate = BTIF_SBC_CHNL_SAMPLE_FREQ_16; switch (sample_rate) { case AUD_SAMPRATE_16000: rate = BTIF_SBC_CHNL_SAMPLE_FREQ_16; break; case AUD_SAMPRATE_32000: rate = BTIF_SBC_CHNL_SAMPLE_FREQ_32; break; case AUD_SAMPRATE_44100: rate = BTIF_SBC_CHNL_SAMPLE_FREQ_44_1; break; case AUD_SAMPRATE_48000: rate = BTIF_SBC_CHNL_SAMPLE_FREQ_48; break; default: TRACE(0, "error! sbc enc don't support other samplerate"); break; } SOURCE_DBLOG("\n%s %d rate = %x\n", __FUNCTION__, __LINE__, rate); return rate; } #if 1 static void a2dp_source_send_sbc_packet(void) { uint32_t frame_size = 512; uint32_t frame_num = A2DP_TRANS_SIZE / frame_size; // a2dp_source.lock_stream(&(tws.tws_source)); uint16_t byte_encoded = 0; // uint16_t pcm_frame_size = 512/2; unsigned short enc_len = 0; bt_status_t status = BT_STS_FAILED; int lock = int_lock(); sbcpack_t *sbcpack = get_sbcPacket(); btif_a2dp_sbc_packet_t *sbcPacket = &(sbcpack->sbcPacket); sbcPacket->data = (U8 *)sbcpack->buffer; memcpy(sbcpack->buffer, a2dp_transmit_buffer, A2DP_TRANS_SIZE); // sbcPacket->dataLen = len; // sbcPacket->frameSize = len/frame_num; btif_sbc_pcm_data_t PcmEncData; if (need_init_encoder) { btif_sbc_init_encoder(&sbc_encoder); sbc_encoder.streamInfo.numChannels = 2; sbc_encoder.streamInfo.channelMode = BTIF_SBC_CHNL_MODE_JOINT_STEREO; sbc_encoder.streamInfo.bitPool = A2DP_SBC_BITPOOL; sbc_encoder.streamInfo.sampleFreq = app_a2dp_source_samplerate_2_sbcenc_type(a2dp_source.sample_rate); sbc_encoder.streamInfo.allocMethod = BTIF_SBC_ALLOC_METHOD_SNR; sbc_encoder.streamInfo.numBlocks = 16; sbc_encoder.streamInfo.numSubBands = 8; sbc_encoder.streamInfo.mSbcFlag = 0; need_init_encoder = 0; } PcmEncData.data = (uint8_t *)a2dp_transmit_buffer; PcmEncData.dataLen = A2DP_TRANS_SIZE; PcmEncData.numChannels = 2; PcmEncData.sampleFreq = sbc_encoder.streamInfo.sampleFreq; btif_sbc_encode_frames(&sbc_encoder, &PcmEncData, &byte_encoded, (unsigned char *)sbcpack->buffer, &enc_len, A2DP_TRANS_SIZE); sbcPacket->dataLen = enc_len; sbcPacket->frameSize = enc_len / frame_num; int_unlock(lock); status = btif_a2dp_stream_send_sbc_packet( app_bt_device.a2dp_stream[BT_DEVICE_ID_1]->a2dp_stream, sbcPacket, &StreamInfo); if (status == BT_STS_PENDING) a2dp_source_wait_sent(osWaitForever); } #endif //#define BT_A2DP_SOURCE_LINEIN_BUFF_SIZE (512*5*2*2) #define BT_A2DP_SOURCE_LINEIN_BUFF_SIZE (A2DP_TRANS_SIZE * 2) #if defined(APP_LINEIN_A2DP_SOURCE) //////////start the audio linein stream for capure the pcm data int app_a2dp_source_linein_on(bool on) { uint8_t *buff_play = NULL; struct AF_STREAM_CONFIG_T stream_cfg; static bool isRun = false; SOURCE_DBLOG("app_a2dp_source_linein_on work:%d op:%d", isRun, on); if (isRun == on) return 0; if (on) { #if defined(SLAVE_USE_OPUS) || defined(MASTER_USE_OPUS) || defined(ALL_USE_OPUS) app_audio_mempool_init(app_audio_get_basebuf_ptr(APP_MEM_LINEIN_AUDIO), app_audio_get_basebuf_size(APP_MEM_LINEIN_AUDIO)); #else app_audio_mempool_init(); #endif app_sysfreq_req(APP_SYSFREQ_USER_APP_0, APP_SYSFREQ_104M); app_audio_mempool_get_buff(&buff_play, BT_A2DP_SOURCE_LINEIN_BUFF_SIZE); memset(&stream_cfg, 0, sizeof(stream_cfg)); stream_cfg.bits = AUD_BITS_16; stream_cfg.channel_num = AUD_CHANNEL_NUM_2; stream_cfg.sample_rate = a2dp_source.sample_rate; #if FPGA == 0 stream_cfg.device = AUD_STREAM_USE_INT_CODEC; #else stream_cfg.device = AUD_STREAM_USE_EXT_CODEC; #endif stream_cfg.vol = 10; // stream_cfg.io_path = AUD_INPUT_PATH_HP_MIC; // stream_cfg.io_path =AUD_INPUT_PATH_MAINMIC; stream_cfg.io_path = AUD_INPUT_PATH_LINEIN; stream_cfg.handler = a2dp_source_linein_more_pcm_data; stream_cfg.data_ptr = buff_play; stream_cfg.data_size = BT_A2DP_SOURCE_LINEIN_BUFF_SIZE; af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE, &stream_cfg); af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE); SOURCE_DBLOG("app_source_linein_on on"); } else { af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE); af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE); SOURCE_DBLOG("app_source_linein_on off"); // clear buffer data a2dp_source.pcm_queue.write = 0; a2dp_source.pcm_queue.len = 0; a2dp_source.pcm_queue.read = 0; app_sysfreq_req(APP_SYSFREQ_USER_APP_0, APP_SYSFREQ_32K); } isRun = on; return 0; } #endif ////////////////////////////creat the thread for send sbc data to a2dp sink /// device /////////////////// static void send_thread(const void *arg); osThreadDef(send_thread, osPriorityHigh, 1, 1024 * 2, "a2dp_send"); #if 1 static void send_thread(const void *arg) { while (1) { a2dp_source_wait_pcm_data(); while (a2dp_source_pcm_buffer_read((uint8_t *)a2dp_transmit_buffer, A2DP_TRANS_SIZE) == 0) { a2dp_source_send_sbc_packet(); } } } #endif void app_source_init(void) { // register the bt global handler a2dp_source_handler.callback = bt_a2dp_source_call_back; btif_me_register_global_handler(&a2dp_source_handler); btif_me_set_event_mask( &a2dp_source_handler, BTIF_BEM_LINK_DISCONNECT | BTIF_BEM_ROLE_CHANGE | BTIF_BEM_INQUIRY_RESULT | BTIF_BEM_INQUIRY_COMPLETE | BTIF_BEM_INQUIRY_CANCELED | BTIF_BEM_LINK_CONNECT_CNF | BTIF_BEM_LINK_CONNECT_IND); } ///////init the a2dp source feature void app_a2dp_source_init(void) { a2dp_source_lock_t *lock; // get heap from app_audio_buffer app_audio_mempool_get_buff(&a2dp_linein_buff, A2DP_LINEIN_SIZE); InitCQueue(&a2dp_source.pcm_queue, A2DP_LINEIN_SIZE, (CQItemType *)a2dp_linein_buff); if (a2dp_source_mutex_id == NULL) { a2dp_source_mutex_id = osMutexCreate((osMutex(a2dp_source_mutex))); } lock = &(a2dp_source.data_lock); memset(lock, 0, sizeof(a2dp_source_lock_t)); lock->_osSemaphoreDef.semaphore = lock->_semaphore_data; lock->_osSemaphoreId = osSemaphoreCreate(&(lock->_osSemaphoreDef), 0); lock = &(a2dp_source.sbc_send_lock); memset(lock, 0, sizeof(a2dp_source_lock_t)); lock->_osSemaphoreDef.semaphore = lock->_semaphore_data; lock->_osSemaphoreId = osSemaphoreCreate(&(lock->_osSemaphoreDef), 0); a2dp_source.sbc_send_id = osThreadCreate(osThread(send_thread), NULL); a2dp_source.sample_rate = AUD_SAMPRATE_44100; } #endif