LDACエンコーダを使ってみた

AOSPにあるオープンソースなソニー製のLDACエンコーダ [libldac](https://android.googlesource.com/platform/external/libldac/) を使ってみました。

ソースコードはgithubに置いときました。　https://github.com/eggman/ldacenc

# libldac の コンパイル 
* 普通のMakefileが無いので、普通にコンパイルして静的ライブラリを作ります。

```
$ git clone https://android.googlesource.com/platform/external/libldac
$ cd libldac/src
$ gcc -O2 -Werror -I../inc -c ldaclib.c 
$ gcc -O2 -Werror -I../inc -c ldacBT.c 
$ ar rcs libldacBT_enc.a ldaclib.o ldacBT.o
```
* macbookでコンパイルした場合のサイズ

```
$ size libldacBT_enc.a 
__TEXT  __DATA  __OBJC  others  dec     hex
36292   192     0       1120    37604   92e4    libldacBT_enc.a(ldaclib.o)
7134    0       0       480     7614    1dbe    libldacBT_enc.a(ldacBT.o)
```

# libldac の API
* APIは ldacBT.h に定義してあります。
* このヘッダファイルには親切な感じで使い方の記載があります。

```
LDACBT_API HANDLE_LDAC_BT ldacBT_get_handle( void );
LDACBT_API void ldacBT_free_handle( HANDLE_LDAC_BT hLdacBt );
LDACBT_API void ldacBT_close_handle( HANDLE_LDAC_BT hLdacBt );
LDACBT_API int  ldacBT_get_version( void );
LDACBT_API int  ldacBT_get_sampling_freq( HANDLE_LDAC_BT hLdacBt );
LDACBT_API int  ldacBT_get_bitrate( HANDLE_LDAC_BT hLdacBt );
LDACBT_API int  ldacBT_init_handle_encode( HANDLE_LDAC_BT hLdacBt, int mtu, int eqmid, int cm,
                                           LDACBT_SMPL_FMT_T fmt, int sf );
LDACBT_API int  ldacBT_set_eqmid( HANDLE_LDAC_BT hLdacBt, int eqmid );
LDACBT_API int  ldacBT_get_eqmid( HANDLE_LDAC_BT hLdacBt );
LDACBT_API int  ldacBT_alter_eqmid_priority( HANDLE_LDAC_BT hLdacBt, int priority );
LDACBT_API int  ldacBT_encode( HANDLE_LDAC_BT hLdacBt, void *p_pcm, int *pcm_used,
                               unsigned char *p_stream, int *stream_sz, int *frame_num );
LDACBT_API int  ldacBT_get_error_code( HANDLE_LDAC_BT hLdacBt );

```

* ストリームのエンコード開始後に、ldacBT_set_eqmid()のemqidを設定することでeqmidに対応したビットレートを動的に変更できます。


# Initialize
* 初期化のサンプルコード

```
#include <stdio.h>
#include "ldacBT.h"

int main(void)
{
    HANDLE_LDAC_BT h;
    int ret, err;

    h = ldacBT_get_handle();
    printf("version       %08x\n",ldacBT_get_version());

    ret = ldacBT_init_handle_encode(h, 679, LDACBT_EQMID_HQ, LDACBT_CHANNEL_MODE_STEREO, LDACBT_SMPL_FMT_S24, 96000);
    err=ldacBT_get_error_code(h); 

    printf("ret=%d api_error=%d\n", ret, LDACBT_API_ERR(err));
    printf("sampling_freq %08x\n",ldacBT_get_sampling_freq(h));
    printf("bit_rate      %08x\n",ldacBT_get_bitrate(h));
    return 0;
}
```
* 実行例

```
version       00020002
ret=0 api_error=0
sampling_freq 00017700
bit_rate      000003de
```

# Encode
* 256サンプルをencodeする。
* 128サンプルだと何も出力されませんでした。
* 256サンプルを入力すると、660バイトのエンコードデータが得られました。

```
#include <stdio.h>
#include "ldacBT.h"

int main(void)
{
    HANDLE_LDAC_BT h;
    int ret, err, pcm_used, ldac_stream_size, ldac_frame_num;
    unsigned char pcm[256*2*2];　/* 256 samples * 2ch * 16bit */
    unsigned char ldac[1024];
    h = ldacBT_get_handle();

    ret = ldacBT_init_handle_encode(h, 679, LDACBT_EQMID_HQ, LDACBT_CHANNEL_MODE_STEREO, LDACBT_SMPL_FMT_S16, 48000);
    err=ldacBT_get_error_code(h); 
    printf("ret=%d api_error=%d handle_error=%d block_error=%d\n", ret, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err), LDACBT_BLOCK_ERR(err));

    ret = ldacBT_encode(h, pcm, &pcm_used, ldac, &ldac_stream_size, &ldac_frame_num);
    err=ldacBT_get_error_code(h); 
    printf("ret=%d api_error=%d handle_error=%d block_error=%d\n", ret, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err), LDACBT_BLOCK_ERR(err));
    printf("  pcm_used=%d ldac_stream_size=%d ldac_frame_num=%d\n", pcm_used, ldac_stream_size, ldac_frame_num);
    
    ret = ldacBT_encode(h, &pcm[128*2*2], &pcm_used, ldac, &ldac_stream_size, &ldac_frame_num);
    err=ldacBT_get_error_code(h); 
    printf("ret=%d api_error=%d handle_error=%d block_error=%d\n", ret, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err), LDACBT_BLOCK_ERR(err));
    printf("  pcm_used=%d ldac_stream_size=%d ldac_frame_num=%d\n", pcm_used, ldac_stream_size, ldac_frame_num);

    return 0;
}
```

* 実行例

```
ret=0 api_error=0 handle_error=0 block_error=0
ret=0 api_error=0 handle_error=0 block_error=0
  pcm_used=512 ldac_stream_size=0 ldac_frame_num=0
ret=0 api_error=0 handle_error=0 block_error=0
  pcm_used=512 ldac_stream_size=660 ldac_frame_num=2
```

# Encode wav file
* 16bit stereo な wavファイルを読み込んでエンコードして出力します。
* ファイルの最後の端数の部分はエンコードされません。

```
#include <stdio.h>
#include "ldacBT.h"

int main(int argc, char *argv[])
{
    HANDLE_LDAC_BT h;
    int ret, err, pcm_used, ldac_stream_size, ldac_frame_num;
    unsigned char pcm[128*2*2];
    unsigned char ldac[1024];
    FILE *infp, *outfp;
    unsigned char header[44];

    if ((infp = fopen(argv[1], "r"))==NULL) {
        printf("can't open input file\n");
        return -1;
    }

    if ((outfp = fopen(argv[2], "w"))==NULL) {
        printf("can't open output file\n");
        return -1;
    }

    //skip wave header
    fread(header, 44, 1, infp);
    
    h = ldacBT_get_handle();
    ret = ldacBT_init_handle_encode(h, 679, LDACBT_EQMID_HQ, LDACBT_CHANNEL_MODE_STEREO, LDACBT_SMPL_FMT_S16, 48000);

    while(fread(pcm, 512, 1, infp))
    {
        ret = ldacBT_encode(h, &pcm[0], &pcm_used, ldac, &ldac_stream_size, &ldac_frame_num);
        printf("  pcm_used=%d ldac_stream_size=%d ldac_frame_num=%d\n", pcm_used, ldac_stream_size, ldac_frame_num);
        fwrite(ldac, 1, ldac_stream_size, outfp);
    }

    ldacBT_close_handle(h);

    return 0;
}
```
* 実行例

```
$./ldacenc 1kHz-0-dither.wav 1khz.ldac_hq
  pcm_used=512 ldac_stream_size=0 ldac_frame_num=0
  pcm_used=512 ldac_stream_size=660 ldac_frame_num=2
  pcm_used=512 ldac_stream_size=0 ldac_frame_num=0
  pcm_used=512 ldac_stream_size=660 ldac_frame_num=2
  pcm_used=512 ldac_stream_size=0 ldac_frame_num=0
  pcm_used=512 ldac_stream_size=660 ldac_frame_num=2
```

* サイズ
* HQ、SQ、MQでエンコードした場合のサイズです。　それぞれ990kbps, 660kbps, 330kbpsになっています。

```
  2880044  1kHz-0-dither.wav
  1855920  1khz.ldac_hq
  1237500  1khz.ldac_sq
   618420  1khz.ldac_mq
```