CSR867x學習筆記:低音炮音頻傳輸協議(SWAT)

爲了讓CSR867x的開發更容易,現與思度科技聯合推出CSR867x學習板【淘寶鏈接:思度科技CSR開發板】

技術交流QQ羣號:743434463
開發板會員QQ羣號:725398389(憑訂單號入羣,贈PPT、項目源碼、視頻教程)

——————————正文分割線———————————–

1. 引言

最近公司準備向客戶推薦我們的soundbar+subwoofer整體解決方案。方案中bar和sub之間通過TWS無線藍牙連接,soundbar作爲master,subwoofer作爲slave。

Demo試聽過程中發現如下問題:

  • 藍牙源的系統音頻延遲接近700ms,AUX源的系統音頻延遲接近400ms,而競品的系統音頻延遲不到200ms。
  • TWS不支持SPDIF輸入源

諮詢了原廠FAE,答覆是TWS模式不適用於soundbar類型的產品,建議我們改用SWAT協議。

今天用運行ADK4.1的soundbar和ADK2.5.1的subwoofer體驗了一把SWAT:

  • 在AUX等有線源的情況下,系統音頻延遲有顯著改善,基本上感覺不到。
  • 在藍牙源SBC解碼的情況下,系統音頻延遲大約有300ms,優於TWS模式。

我們知道TWS模式下,master和slave之間的音頻數據走的是A2DP協議,控制命令走的是AVRCP協議。同樣是音頻傳輸協議,爲何SWAT能夠顯著縮短音頻延遲呢?相信答案就在swat庫的源碼中,讓我們一探究竟吧。

2. 代碼整體框架

swat庫源碼在..\src\lib\swat路徑下,乍一看文件還挺多:

  • swap_api.c/.h
  • swap_audio_manager.c/.h
  • swat_command_handler.c/.h
  • swat_device_manager.c/.h
  • swat_init.c/.h
  • swat_l2cap_handler.c/.h
  • swat_packet_handler.c/.h
  • swat_profile_handler.c/.h
  • swat_state_manager.c/.h
  • swat_service_record.h
  • swat.h

這裏寫圖片描述

2.1. swat.h

swat.h文件包含了所有swat庫中允許應用層訪問的api函數聲明、變量聲明、消息ID聲明、結構體聲明。swat.h也給出了SWAT協議用到的L2CAP通道編號。

#define SWAT_SIGNALLING_PSM     0x8001   /* L2CAP PSM for the signalling channel */
#define SWAT_MEDIA_PSM          0x8003   /* L2CAP PSM for the media channel */

參考藍牙官網的Logic Link Control頁面,可見SWAT的兩個PSM不在predefined L2CAP channel ID列表中。在谷歌搜索SWAT,找不到相關信息,基本上可以坐實SWAT是CSR的自家協議。

2.2. swat_service_record.h

此頭文件給出了swat協議的服務描述,截取最重要的信息:

    /* Protocol Descriptor List */
    0x09, 0x00, 0x04,   /* AttrID ProtocolDescriptorList */
    0x35, 0x08,         /* "Data element sequence" (Sequence length 0x08) */
    0x35, 0x06,         /* "Data element sequence" (Sequence length = 0x06) */
    0x19, 0x01, 0x00,   /* "UUID" (UUID 0x0100 = L2CAP)*/
    0x09, ((SWAT_SIGNALLING_PSM >> 8) & 0xff), (SWAT_SIGNALLING_PSM & 0xff),   /* "Unsigned Integer" (Describes the L2CAP SIGNALLING PSM) */

    /* Additional Protocol Descriptor List */
    0x09, 0x00, 0x0d,   /* AttrID Additional Protocol Descriptor List */
    0x35, 0x08,         /* "Data element sequence" (Sequence length = 0x08) */
    0x35, 0x06,         /* "Data element sequence" (Sequence length = 0x06) */
    0x19, 0x01, 0x00,   /* "UUID" (UUID 0x0100 = L2CAP)*/
    0x09, ((SWAT_MEDIA_PSM >> 8) & 0xff), (SWAT_MEDIA_PSM & 0xff),   /* "Unsigned Integer" Describes the L2CAP MEDIA PSM) */

可見SWAT協議需要用到2個L2CAP連接,一個用來傳輸控制信號,另一個用來傳輸媒體信號,這裏即是低音通道的音頻信號。

2.3. swat_state_manager.c/.h

swat_state_manager.c/.h給出兩個函數,用以設置signalling和media的狀態。

signalling的狀態:

/* Enum defining the SWAT signalling channel states */
typedef enum
{
/*00*/  swat_signalling_idle,
/*01*/  swat_signalling_remote_connecting,
/*02*/  swat_signalling_local_connecting,
/*03*/  swat_signalling_local_connecting_xover,
/*04*/  swat_signalling_connected,
/*05*/  swat_signalling_disconnecting
} swatSignallingState;

media的狀態:

typedef enum
{
/*00*/  swat_media_closed = 0,          /*< The media channel is closed (not active) */
/*01*/  swat_media_opening,         /*< The media channel is in the process of opening */
/*02*/  swat_media_open,            /*< The media chaneel is open, but not streaming */
/*03*/  swat_media_starting,        /*< The media channel is in the process of starting to stream */
/*04*/  swat_media_streaming,       /*< The media channel is open and contains streaming audio data */
/*05*/  swat_media_suspending,      /*< The media channel is in the process of suspending a stream */
/*06*/  swat_media_closing,         /*< The media channel is in the process of closing down */
/*ff*/  swat_media_invalid = 0xff   /*< invalid value for swat media channel state */
} swatMediaState;

media的類型有兩種:

typedef enum
{
    SWAT_MEDIA_NONE = 0x0,          /*< no media type */
    SWAT_MEDIA_STANDARD = 0x1,      /*< Standard latency mode */
    SWAT_MEDIA_LOW_LATENCY = 0x2    /*< Low latency mode */
} swatMediaType;

STANDARD類型對應BT源,LOW_LATENCY類型對應AUX等有線源。

2.4. swat_profile_handler.c/.h

swat_profile_handler.c只定義了一個鉤子函數swatProfileHandler。這個鉤子函數在初始化swat時被掛在profile_task.handler上。

// swat_init.c
// SwatInit
swat->l2cap_task.handler = swatL2capHandler;
swat->profile_task.handler = swatProfileHandler;
swat->command_task.handler = swatCommandHandler;

應用層調用swat_api.c文件裏的接口函數向swat_profile_handler發送消息,swat_profile_handler調用swat_command_handler.c、swat_l2cap_handler.c和swat_audio_handler.c裏的函數執行消息處理函數。

2.5. swat_packet_handler.c/.h

swat_packet_handler.c只定義了swatSendData和swatHandleSwatSignallingData兩個函數。

swatSendData只被swat_command_handler.c文件裏的函數調用,作用是向l2cap sink stream中寫入數據,寫入的數據通過l2cap連接發送給另一端。在swat_l2cap_handler.c中有一個handleL2capConnectCfm函數,此函數在l2cap連接成功後將sink保存下來。

/* Which PSM was the message recieved on? */
if (cfm->psm_local == SWAT_SIGNALLING_PSM)
{
    handleL2capConnectCfmSignalling(cfm->status, cfm->sink, device);
}
else if (cfm->psm_local == SWAT_MEDIA_PSM)
{
    handleL2capConnectCfmMedia(cfm->status, cfm->sink, device);
}

swatHandleSwatSignallingData處理BlueCore層向swat庫層提交的l2cap數據,數據被解析成SWAT_COMMAND發送給swat_command_handler.c處理。

2.6. swat_l2cap_handler.c/.h

swat_l2cap_handler.h聲明瞭8個函數,這8個函數被swat_profile_handler.c、swat_api.c、swat_audio_manager.c、swat_command_handler.c、swat_init.c調用,可以看出swat_l2cap_handler.c處在swat代碼架構的較低層。

那swat_l2cap_handler的下一層是哪裏呢?答案就在swatL2capSignallingConnectResponse函數中:

void swatL2capSignallingConnectResponse(uint16 device_id, uint16 connection_id, uint8 identifier, bool accept)
{
    /* If rejecting the signalling connection, also remove the device */
    /* Don't remove the device if we are in local_connecting state as means it's the crossover connection from the SINK device that's being rejected */
    if ( !accept && (swat->remote_devs[device_id].signalling_state != swat_signalling_local_connecting) )
    {
        /* Remove the device */
        swatRemoveDevice(device_id);
    }

    ConnectionL2capConnectResponse(&swat->l2cap_task, accept, SWAT_SIGNALLING_PSM, connection_id, identifier, sizeof(swat_signalling_conftab), (uint16 *)swat_signalling_conftab);
}

ConnectionL2capConnectResponse函數在.src\lib\connection\l2cap.c文件中,connection庫負責管理較低層級的藍牙連接,例如RFCOMM、L2CAP、Bluetooth Smart等。
這裏寫圖片描述

2.7. swat_init.c

swat_init.c裏只有一個函數SwatInit。這個函數主要做了以下幾件事:

  • 初始化swat結構體,包括掛接幾個handler鉤子函數swatL2capHandler、swatProfileHandler、swatCommandHandler
  • 註冊service record(定義在swat_service_record.h)

2.8. swat_device_manager.c/.h

swat_device_manager.c中最主要的函數是swatAddDevice,其是對端設備的實例化。SWAT協議只支持一個對端設備。

soundbar在搜索到subwoofer後,嘗試與其建立signalling通道的連接。此時soundbar和subwoofer會創建對方的實例。

2.9. swat_command_handler.c/.h

swat_command_handler.c中的swatCommandHandler函數處理來自swatL2capHandler的消息。

2.10. swat_audio_manager.c/.h

swat_audio_manager.c中的函數都是與esco連接有關,esco連接用在AUX等有線源的音頻傳輸。

2.11. swat_api.c

swat_api.c中的函數被swat_l2cap_handler.c和swat_command_handler.c調用。

swat_api.c中的函數都是對消息的封裝:

void swatSendInitCfmToClient(swat_status_code status)
{
    MAKE_SWAT_MESSAGE(SWAT_INIT_CFM);
    message->status = status;
    MessageSend(swat->clientTask, SWAT_INIT_CFM, message);

    /* If the initialisation failed, free the allocated task */
    if (status != swat_success)
    {
        free(swat);
        swat = 0;
    }
}

這些消息最後都發送給了應用層的sink_swat.c。

3. 爲什麼SWAT的延遲低?

當SWAT協議啓用後,soundbar在傳輸sub信號前將sub信號的採樣率從48KHz降低到了1.2KHz,從而大幅減少了sub通道的數據量,減少了傳輸前所需緩衝的數據量,降低了系統延遲。

在有線源的情況下,SWAT會啓用LOW LATENCY模式。此模式使用esco同步信道傳輸音頻數據,延遲優於L2CAP連接時的ACL信道。

4. 總結

SWAT可以降低soundbar的系統音頻延遲,在有線源的情況下可以做到40ms,在藍牙源的情況下可以做到200ms以下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章