1.介绍

本文开源LC3编解码器实现来自于Zephyr项目下的一个模块,github仓库:

https://github.com/zephyrproject-rtos/liblc3codec

2.使用 

使用本demo只需复制test.c和Makefile到clone下来的文件夹内,使用的测试文件名为test_48K_16.pcm,编码后的文件名为encodedata,解码后的文件名为decode.pcm

音频测试文件格式为:

如果你想使用自己的音频文件测试,请根据实际文件修改代码。 

3.修改配置

3.1PCM格式

本库LC3支持两种格式:

16bit格式

LC3_PCM_FORMAT_S16

24bit格式

LC3_PCM_FORMAT_S24

16bit格式下每个采样占用2个字节,24bit没使用过,但是每个采样应该占用4个字节。

3.2帧长

通常单帧音频长10ms,也支持7.5ms(应该是为了兼容经典蓝牙音频)

3.3采样率

采样率支持 8000, 16000, 24000, 32000, 48000。正常LC3应该还支持44.1KHz的采样率,但是笔者发现官方测试demo中把44100注释掉了,不知是否本库还未支持,建议先不要用44100测试。

3.4编码输出字节

范围是20~400,但是BAP其实规定了一些固定规格,如下图所示:

本demo中使用120字节,也就是单帧被编码为120字节。

4.代码

4.1test.c

#include "include/lc3.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    //PCM格式
    enum lc3_pcm_format pcmFormat = LC3_PCM_FORMAT_S16;
    //帧长10ms
    int dtUs = 10000;
    //采样率48K
    int srHz =48000;
    //单帧编码后输出字节数
    uint16_t output_byte_count = 120;
    //编码器需占用缓存大小
    unsigned encodeSize = lc3_encoder_size(dtUs, srHz);
    //解码器需占用缓存大小
    unsigned decodeSize = lc3_decoder_size(dtUs, srHz);
    //单帧的采样数
    uint16_t sampleOfFrames = lc3_frame_samples(dtUs, srHz);
    //单帧字节数,一个采样占用两个字节
    uint16_t bytesOfFrames = sampleOfFrames*2;
    //编码器缓存
    void* encMem = NULL;
    //解码器缓存
    void* decMem = NULL;
    //输入文件的文件描述符
    int inFd = -1;
    //输出文件的文件描述符
    int outFd = -1;
    //输入帧缓冲
    unsigned char *inBuf = (unsigned char *)malloc(bytesOfFrames);
    //输出帧缓冲
    unsigned char *outBuf = (unsigned char *)malloc(bytesOfFrames);
    encMem = malloc(encodeSize);
    /*                      编码                           */
    lc3_encoder_t lc3_encoder = lc3_setup_encoder(dtUs, srHz, 0, encMem);
    if((inFd = open("./test_48K_16.pcm", O_RDONLY))<=0)
    {
        printf("encode open inFd err\n");
        return -1;
    }
    if((outFd = open("./encodedata", O_CREAT|O_WRONLY|O_TRUNC, 0666))<=0)
    {
        printf("encode open outFd err\n");
        return -1;
    }
    while(read(inFd,inBuf,bytesOfFrames)==bytesOfFrames)
    {
        lc3_encode(lc3_encoder, pcmFormat, (const int16_t*)inBuf, 1,output_byte_count, outBuf);
        // memcpy(outBuf,inBuf,bytesOfFrames);
        if(write(outFd,outBuf,output_byte_count)!=output_byte_count)
        {
            printf("encode write err\n");
            break;
        }
        memset(inBuf,0,bytesOfFrames);
        memset(outBuf,0,bytesOfFrames);
    }
    free(encMem);
    encMem = NULL;
    close(inFd);
    close(outFd);
    /*                      解码                           */
    decMem = malloc(decodeSize);
    lc3_decoder_t lc3_decoder = lc3_setup_decoder(dtUs, srHz, 0, decMem);    
    if((inFd = open("./encodedata", O_RDONLY))<=0)
    {
        printf("decode open inFd err\n");
        return -1;
    }
    if((outFd = open("./decode.pcm", O_CREAT|O_WRONLY|O_TRUNC, 0666))<=0)
    {
        printf("decode open outFd err\n");
        return -1;
    }  
    while(read(inFd,inBuf,output_byte_count)==output_byte_count)
    {
        lc3_decode(lc3_decoder, inBuf, output_byte_count, pcmFormat,outBuf, 1);
        if(write(outFd,outBuf,bytesOfFrames)!=bytesOfFrames)
        {
            printf("decode write err\n");
            break;
        }
        memset(inBuf,0,bytesOfFrames);
        memset(outBuf,0,bytesOfFrames);
    }
    free(decMem);
    close(inFd);
    close(outFd);
    free(inBuf);
    free(outBuf);
    inBuf = NULL;
    outBuf = NULL;
    return 0;
}

4.2Makefile

TARGET = test
BUILD_DIR = build
CC = gcc
CFLAGS = $(INCLUDES)
OFLAGS = -lm

SOURCES = \
test.c \
$(wildcard ./src/*.c)

INCLUDES =  \
-Iinclude/ \
-Isrc/

FILE_PATH = \
./ \
./src/

TEST_OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(SOURCES:.c=.o)))

vpath %.c $(FILE_PATH)

all: $(TARGET)

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR) 
	$(CC) -c $(CFLAGS) $< -o $@
	
$(TARGET) : $(TEST_OBJECTS)
	$(CC) $(CFLAGS) $(TEST_OBJECTS) $(OFLAGS) -o $@ 

$(BUILD_DIR):
	mkdir $@

clean:
	rm -rf $(BUILD_DIR)
	rm $(TARGET)

懒人专用链接:

https://github.com/CXsCode/lc3test

官方LC3文档:

Low Complexity Communication Codec 1.0 – Bluetooth® Technology Website

ETSI提供的开源LC3plus实现:

https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.03.01_60/ts_103634v010301p0.zip

实现方案:

https://www.etsi.org/deliver/etsi_ts/103600_103699/103634/01.03.01_60/ts_103634v010301p.pdf

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐