本文开源LC3编解码器实现来自于Zephyr项目下的一个模块,github仓库:
https://github.com/zephyrproject-rtos/liblc3codec
使用本demo只需复制test.c和Makefile到clone下来的文件夹内,使用的测试文件名为test_48K_16.pcm,编码后的文件名为encodedata,解码后的文件名为decode.pcm
音频测试文件格式为:
如果你想使用自己的音频文件测试,请根据实际文件修改代码。
本库LC3支持两种格式:
16bit格式
LC3_PCM_FORMAT_S16
24bit格式
LC3_PCM_FORMAT_S24
16bit格式下每个采样占用2个字节,24bit没使用过,但是每个采样应该占用4个字节。
通常单帧音频长10ms,也支持7.5ms(应该是为了兼容经典蓝牙音频)
采样率支持 8000, 16000, 24000, 32000, 48000。正常LC3应该还支持44.1KHz的采样率,但是笔者发现官方测试demo中把44100注释掉了,不知是否本库还未支持,建议先不要用44100测试。
范围是20~400,但是BAP其实规定了一些固定规格,如下图所示:
本demo中使用120字节,也就是单帧被编码为120字节。
- #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;
- }
- 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
一个LC3plus实现:
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