doubao_chat/lib/net/libduer-device/modules/voice_engine/lightduer_speex.c

205 lines
6.1 KiB
C

/**
* Copyright (2017) Baidu Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* File: lightduer_speex.c
* Auth: Su Hao (suhao@baidu.com)
* Desc: Light duer Speex encoder.
*/
#include "duerapp_config.h"
#if USE_OWN_SPEEX_LIB == 0
#include "lightduer_speex.h"
#include "lightduer_log.h"
#include "lightduer_lib.h"
#include "lightduer_memory.h"
#include "speex/speex.h"
#include "lightduer_ds_log_recorder.h"
#include "lightduer_timestamp.h"
#define DUER_OUTPUT_HEADER_MAGIC_1 'P'
#define DUER_OUTPUT_HEADER_MAGIC_2 'X'
typedef enum _duer_sample_rate_e {
DUER_SR_8000,
DUER_SR_16000,
DUER_SR_TOTAL,
} duer_sample_rate_t;
typedef struct _duer_speex_s {
duer_sample_rate_t _rate;
void *_encoder;
size_t _frame_size;
size_t _frame_cur;
short *_buffer_in;
char *_buffer_out;
SpeexBits _bits;
} duer_speex_t;
static const char DUER_OUTPUT_HEADER_MAGIC_0[DUER_SR_TOTAL] = {
'S', //< DUER_SR_8000
'T', //< DUER_SR_16000
};
duer_speex_handler duer_speex_create(int rate, int quality)
{
int rs = -1;
duer_speex_t *speex = NULL;
do {
speex = (duer_speex_t *)DUER_MALLOC(sizeof(duer_speex_t)); // ~= 60
if (speex == NULL) {
DUER_LOGE("Memory overflow!!! Alloc speex handler failed.");
break;
}
DUER_MEMSET(speex, 0, sizeof(duer_speex_t));
if (rate == 8000) {
speex->_rate = DUER_SR_8000;
} else if (rate == 16000) {
speex->_rate = DUER_SR_16000;
} else {
DUER_LOGE("Not supported sample rate: %d", rate);
break;
}
speex->_encoder = speex_encoder_init(speex_mode_list[speex->_rate]);
if (speex->_encoder == NULL) {
DUER_LOGE("Speex encoder state init failed!");
break;
}
speex_encoder_ctl(speex->_encoder, SPEEX_SET_QUALITY, &quality);
speex_encoder_ctl(speex->_encoder, SPEEX_GET_FRAME_SIZE, &speex->_frame_size);
DUER_LOGV("Speex frame size: %d", speex->_frame_size);
speex->_buffer_out = DUER_MALLOC(speex->_frame_size + sizeof(int)); // ~= 324
if (speex->_buffer_out == NULL) {
DUER_LOGE("Memory overflow!!! Alloc speex buffer out failed.");
break;
}
speex->_buffer_in = DUER_MALLOC(sizeof(short) * speex->_frame_size); // ~= 640
if (speex->_buffer_in == NULL) {
DUER_LOGE("Memory overflow!!! Alloc speex buffer in failed.");
break;
}
speex_bits_init(&speex->_bits);
rs = 0;
} while (0);
if (rs < 0) {
duer_speex_destroy(speex);
speex = NULL;
}
return speex;
}
static void duer_speex_encode_frame(duer_speex_t *speex, duer_encoded_func func)
{
duer_u32_t timestamp = duer_timestamp();
speex_bits_reset(&speex->_bits);
/*Encode the frame*/
speex_encode_int(speex->_encoder, speex->_buffer_in, &speex->_bits);
/*Copy the bits to an array of char that can be written*/
int written = speex_bits_write(&speex->_bits, speex->_buffer_out + sizeof(int), speex->_frame_size);
DUER_LOGV("Speex encoded written: %d", written);
speex->_buffer_out[0] = written & 0xFF;
speex->_buffer_out[1] = DUER_OUTPUT_HEADER_MAGIC_0[speex->_rate];
speex->_buffer_out[2] = DUER_OUTPUT_HEADER_MAGIC_1;
speex->_buffer_out[3] = DUER_OUTPUT_HEADER_MAGIC_2;
if (func) {
func(speex->_buffer_out, sizeof(int) + written);
}
duer_ds_rec_compress_info_update(timestamp, duer_timestamp());
}
void duer_speex_encode(duer_speex_handler handler, const void *data, size_t size, duer_encoded_func func)
{
duer_speex_t *speex = (duer_speex_t *)handler;
size_t cur = 0;
DUER_LOGD("duer_speex_encode, speex:%p", speex);
if (speex && speex->_encoder && speex->_buffer_in && speex->_buffer_out) {
size_t framesize = speex->_frame_size * sizeof(short);
char *buffer_in = (char *)speex->_buffer_in;
DUER_LOGD("duer_speex_encode: size = %d, framesize = %d", size, framesize);
while (cur + framesize - speex->_frame_cur <= size) {
DUER_LOGV("duer_speex_encode: cur = %d, speex->_frame_cur = %d", cur, speex->_frame_cur);
DUER_MEMCPY(buffer_in + speex->_frame_cur, (const char *)data + cur, framesize - speex->_frame_cur);
cur += framesize - speex->_frame_cur;
speex->_frame_cur = 0;
duer_speex_encode_frame(speex, func);
}
DUER_LOGV("duer_speex_encode: cur = %d, speex->_frame_cur = %d", cur, speex->_frame_cur);
if (speex->_frame_cur > 0 && (data == NULL || size == 0)) {
DUER_MEMSET(buffer_in + speex->_frame_cur, 0, framesize - speex->_frame_cur);
duer_speex_encode_frame(speex, func);
} else if (cur < size) {
DUER_MEMCPY(buffer_in + speex->_frame_cur, (const char *)data + cur, size - cur);
speex->_frame_cur += size - cur;
}
DUER_LOGV("duer_speex_encode: speex->_frame_cur = %d", speex->_frame_cur);
}
}
void duer_speex_destroy(duer_speex_handler handler)
{
duer_speex_t *speex = (duer_speex_t *)handler;
if (speex) {
if (speex->_encoder) {
speex_encoder_destroy(speex->_encoder);
speex->_encoder = NULL;
}
if (speex->_buffer_out) {
DUER_FREE(speex->_buffer_out);
speex->_buffer_out = NULL;
}
if (speex->_buffer_in) {
DUER_FREE(speex->_buffer_in);
speex->_buffer_in = NULL;
}
speex_bits_destroy(&speex->_bits);
DUER_FREE(speex);
}
}
#endif