chore: migrate project into clean repository

This commit is contained in:
yuuux
2026-08-13 16:50:52 +08:00
commit d1d25a09e7
27405 changed files with 9422808 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
if (DEFINED CONFIG_LIS_ALGO)
listenai_library_named(lis_algo)
listenai_library_sources(
lis_ocr.c
lis_trans.c
lis_tts.c
lis_algo.c
algo/img_stream/img_stream.c
#algo
algo/algo_lsf_client.c
#algo: xtts
algo/xttsservice/xtts_app.c
algo/xttsservice/xtts_server.c
algo/xttsservice/xtts_service.c
algo/xttsservice/xtts_stream.c
)
listenai_include_directories(
./
./algo
./algo/xttsservice
./algo/img_stream
)
if (DEFINED CONFIG_LIS_ALGO_WORDSEG)
listenai_library_sources(
#algo: wordseg
algo/wordseg/source/friso_array.c
algo/wordseg/source/friso_ctype.c
algo/wordseg/source/friso_GBK.c
algo/wordseg/source/friso_hash.c
algo/wordseg/source/friso_lexicon.c
algo/wordseg/source/friso_link.c
algo/wordseg/source/friso_memory.c
algo/wordseg/source/friso_string.c
algo/wordseg/source/friso_UTF8.c
algo/wordseg/source/friso.c
algo/wordseg/source/ipa2iflyphone.c
algo/wordseg/source/wordseg_engine.c
algo/wordseg/source/wordseg_res.c
algo/wordseg/source/wordseg_wordcorrection.c
algo/wordseg/source/wordseg_wrapper.c
algo/wordseg/source/wordseg.c
)
listenai_include_directories(
./algo/wordseg/include/
./algo/wordseg/source
)
endif()
endif()

View File

@@ -0,0 +1,11 @@
menu "lis algo"
config LIS_ALGO
bool "lis algo"
default n
config LIS_ALGO_WORDSEG
bool "lis algo"
default n
endmenu

View File

@@ -0,0 +1,255 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "log_print.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"
#include "ic_message.h"
#include <assert.h>
#include "lis_algo.h"
#include "lis_trans.h"
#include "lis_tts.h"
#include "lis_ocr.h"
#include "sysutils.h"
#include "cache.h"
#define LOG_HEXDUMP(data, len, line_len) \
do { \
for (int i = 0; i < len; i++) { \
logDbg("%02x ", data[i]); \
if ((i + 1) % line_len == 0) { \
logDbg("\n"); \
} \
} \
logDbg("\n"); \
} while (0)
#define IC_MSG_QUEUE_SIZE 16
typedef enum {
e_algo_status_idle = 0x0,
e_xtts_synth_begin = 0x01,
e_xtts_synth_doing = 0x02,
e_xtts_synth_end = 0x03,
e_xtts_play_begin = 0x04,
e_xtts_play_playing = 0x05,
e_xtts_play_end = 0x06,
e_xtts_synth_stop = 0x07,
e_trans_begin = 0x08,
e_trans_stop = 0x09,
e_trans_end = 0x0A,
e_trans_result = 0x0B,
e_ocr_scan_begin = 0x0C,
e_ocr_scan_end = 0x0D,
e_roi_start,
e_roi_stop,
} algo_status_cp2ap_e;
#define CLIENT_MESSAGE_TASK_STACKSIZE (4 * 1024)
#define CLIENT_MESSAGE_TASK_PRIORITY (1)
static QueueHandle_t rx_queue;
#define TYPE_CNT 4
static EventGroupHandle_t func_evt[TYPE_CNT];
static int algo_ic_msg_id[TYPE_CNT] = {IC_MESSAGE_ID_TRANS, IC_MESSAGE_ID_TTS, IC_MESSAGE_ID_OCR, IC_MESSAGE_ID_COMMON};// id must match algo_func_type_e
__psram_data__ static func_ack_t func_ack[TYPE_CNT] = {0};
__psram_data__ static int g_algo_status[TYPE_CNT] = {0};
int lsf_cp2ap_func(uint32_t type, uint8_t *send_data, uint32_t send_size, uint8_t *rx, uint32_t rx_size, TickType_t xTicksToWait)
{
func_descriptors_t *func = (func_descriptors_t*)send_data; // just for debug
// if(func) LOGD("name:%s, func:%d, enter", ((const char *[]){"trans", "xtts", "ocr"})[type], func->func);
if((TYPE_TRANS == type && func->func == FUNC_TRANS_STATUS_E) ||
(TYPE_TTS == type && func->func == FUNC_XTTS_STATUS_E) ||
(TYPE_OCR == type && func->func == OCR_IOCTL_GET && func->ocr.arg == OCR_STATUS)
) {
// 算法状态获取接口使用AP通知的方式减少核间通讯频次
func_ack_t *ack = (func_ack_t *)rx;
ack->status = g_algo_status[type];
return 1;
}
HAL_FlushDCache_by_Addr((uint32_t *)func, sizeof(func_descriptors_t));
xEventGroupClearBits(func_evt[type], func->func);
int ret = ic_message_msg_send_by_id(algo_ic_msg_id[type], IC_MESSAGE_MSG_TYPE_CMD, &send_data, sizeof(uint8_t*));
ASSERT(ret == 0, "send_data failed, ret=%d", ret);
//wait ack
xEventGroupWaitBits(func_evt[type], func->func, true, false, portMAX_DELAY);
memcpy(rx, &func_ack[type], sizeof(func_ack_t));
// if(func) LOGD("name:%s, func:%d, exit\n", ((const char *[]){"trans", "xtts", "ocr"})[type], func->func);
return 1;
}
static int32_t translation_message_cb(ic_message_handle_info_t *handle, ic_message_msg_info_t* msg){
uint32_t data = *(uint32_t*)msg->msg;
func_ack_t *func = (func_ack_t*)data;
if(func == NULL) return 0;
HAL_InvalidateDCache_by_Addr((uint32_t *)func, sizeof(func_ack_t));
// LOGD("func_ack.func:%p, func:%d, status:%x", func, func->func, func->status);
// LOGD("trans ack:%p, func:%s, status:%x", func,
// ((const char *[]){"NULL", "start", "status", "result", "stop"})[func->func], func->status);
switch (func->func) {
case FUNC_TRANS_STATUS_E: {
if(func->status == e_trans_begin) g_algo_status[TYPE_TRANS] = LIS_TRANS_STATE_ING;
if(func->status == e_trans_stop) g_algo_status[TYPE_TRANS] = LIS_TRANS_STATE_EARLY_OVER;
if(func->status == e_trans_end) g_algo_status[TYPE_TRANS] = LIS_TRANS_STATE_OVER;
if(func->status == e_trans_result) g_algo_status[TYPE_TRANS] = LIS_TRANS_STATE_RESULT;
LOGD("trans ack status:%x", g_algo_status[TYPE_TRANS]);
break;
}
case FUNC_TRANS_START_E:
case FUNC_TRANS_STOP_E:{
memcpy(&func_ack[TYPE_TRANS], func, sizeof(func_ack_t));
break;
}
case FUNC_TRANS_RESULT_E: {
LOGD("trans ack result:%s", func->result);
memcpy(&func_ack[TYPE_TRANS], func, sizeof(func_ack_t));
break;
}
default:
LOGD("Unknown func:%d", func->func);
break;
}
xEventGroupSetBits(func_evt[TYPE_TRANS], func->func);
}
static int32_t ocr_message_cb(ic_message_handle_info_t *handle, ic_message_msg_info_t* msg)
{
uint32_t data = *(uint32_t*)msg->msg;
func_ack_t *func = (func_ack_t*)data;
if(func == NULL) return 0;
HAL_InvalidateDCache_by_Addr((uint32_t *)func, sizeof(func_ack_t));
// LOGD("ocr ack:%p, func:%s, status:%x", func,
// ((const char *[]){"NULL", "start", "status", "set", "get", "stop", "result"})[func->func], func->status);
switch (func->func) {
case OCR_IOCTL_STATUS: {
g_algo_status[TYPE_OCR] = func->status;
LOGD("ocr ack status:%x", g_algo_status[TYPE_OCR]);
break;
}
case OCR_IOCTL_START:
case OCR_IOCTL_SET:
case OCR_IOCTL_GET:
case OCR_IOCTL_SYS_START_UP:
case OCR_IOCTL_STOP: {
memcpy(&func_ack[TYPE_OCR], func, sizeof(func_ack_t));
break;
}
case OCR_IOCTL_RESULT: {
memcpy(&func_ack[TYPE_OCR], func, sizeof(func_ack_t));
LOGD("ocr ack result:%s", func->result);
break;
}
case OCR_IOCTL_RESULT_NOTIFY: {
LOGD("ocr notify result:%s", func->result);
extern void lis_ocr_result_callback(void *rslt);
lis_ocr_result_callback(func->result);
break;
}
default:
LOGD("Unknown func:%d", func->func);
break;
}
xEventGroupSetBits(func_evt[TYPE_OCR], func->func);
}
static int32_t xtts_message_cb(ic_message_handle_info_t *handle, ic_message_msg_info_t* msg)
{
uint32_t data = *(uint32_t*)msg->msg;
func_ack_t *func = (func_ack_t*)data;
if(func == NULL) return 0;
HAL_InvalidateDCache_by_Addr((uint32_t *)func, sizeof(func_ack_t));
// LOGD("xtts ack:%p, func:%s, status:%x", func,
// ((const char *[]){"NULL", "start", "status", "stop"})[func->func], func->status);
switch (func->func) {
case FUNC_XTTS_STATUS_E: {
// LOGD("xtts get");
switch (func->status) {
case e_xtts_synth_begin:
case e_xtts_synth_doing:
case e_xtts_play_begin:
case e_xtts_play_playing:
g_algo_status[TYPE_TTS] = LIS_TTS_STATE_ING;
break;
case e_xtts_synth_end:
g_algo_status[TYPE_TTS] = LIS_TTS_STATE_TTS_END;
break;
case e_xtts_play_end:
g_algo_status[TYPE_TTS] = LIS_TTS_STATE_OVER;
break;
case e_xtts_synth_stop:
g_algo_status[TYPE_TTS] = LIS_TTS_STATE_EARLY_OVER;
break;
default:
g_algo_status[TYPE_TTS] = LIS_TTS_STATE_ERR;
LOGD("xtts unkown st:%d", func->status);
break;
}
LOGD("xtts ack status:%x", g_algo_status[TYPE_TTS]);
break;
}
case FUNC_XTTS_START_E:
case FUNC_XTTS_STOP_E:
case FUNC_XTTS_SET_PARAM_E:
{
memcpy(&func_ack[TYPE_TTS], func, sizeof(func_ack_t));
break;
}
default:
LOGD("Unknown func:%d", func->func);
break;
}
xEventGroupSetBits(func_evt[TYPE_TTS], func->func);
}
void algo_lsf_ocr_init(void)
{
uint32_t send_data = 0;
ic_message_register_by_id(IC_MESSAGE_ID_OCR, ocr_message_cb, NULL);
while(!ic_message_remote_is_connected(IC_MESSAGE_ID_OCR)) {
vTaskDelay(1);
}
}
void algo_lsf_xtts_init(void)
{
uint32_t send_data = 0;
ic_message_register_by_id(IC_MESSAGE_ID_TTS, xtts_message_cb, NULL);
do {
int ret = ic_message_msg_send_by_id(IC_MESSAGE_ID_TTS, IC_MESSAGE_MSG_TYPE_CMD, &send_data, sizeof(send_data));
if (ret == 0) {
break;
}
} while (1);
extern int xtts_app_task(void);
xtts_app_task();
}
void algo_lsf_trans_init(void)
{
uint32_t send_data = 0;
ic_message_register_by_id(IC_MESSAGE_ID_TRANS, translation_message_cb, NULL);
do {
int ret = ic_message_msg_send_by_id(IC_MESSAGE_ID_TRANS, IC_MESSAGE_MSG_TYPE_CMD, &send_data, sizeof(send_data));
if (ret == 0) {
break;
}
} while (1);
}
void algo_lsf_client_start(void)
{
rx_queue = xQueueCreate(IC_MSG_QUEUE_SIZE, sizeof(uint32_t));
for(int i = 0; i < TYPE_CNT; i++) {
func_evt[i] = xEventGroupCreate();
}
}

View File

@@ -0,0 +1,30 @@
/*
* @Author: fhhao fhhao@listeanai.com
* @Date: 2025-03-05 19:40:53
* @LastEditors: fhhao fhhao@listeanai.com
* @LastEditTime: 2025-03-24 16:23:35
* @FilePath: /toycloud-cp/projects/scanpen-cp/src/algo/algo_lsf_client.h
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
#ifndef ALGO_LSF_CLIENT_H
#define ALGO_LSF_CLIENT_H
#include <stdint.h>
typedef enum
{
ap2cp_play_stream_id = 0,
cp2ap_record_stream_id,
cp2ap_scan_stream_id,
cp2ap_xtts_stream_id,
cp2ap_trans_stream_id,
cp2ap_scan_adjust_stream_id,
} ic_stream_id_e;
void algo_lsf_client_start(void);
void algo_lsf_ocr_init(void);
void algo_lsf_xtts_init(void);
void algo_lsf_trans_init(void);
#endif /* ALGO_LSF_CLIENT_H */

View File

@@ -0,0 +1,89 @@
#include "cache.h"
#include "ic_message.h"
#include <stdint.h>
#include <string.h>
#include "sysheap.h"
#include "log_print.h"
#include "lsfs_interface.h"
#include "img_stream.h"
static img_stream_t *_img_stream = NULL;
static int32_t _img_stream_cb(ic_message_handle_info_t *handle, ic_message_msg_info_t *msg)
{
img_stream_t *img_stream = (void *)handle->user_datas;
struct img_stream_msg *img_msg = (void *)msg->msg;
uint32_t img_size = img_msg->wdith * img_msg->height;
int ret = 0;
if (img_msg->addr && img_size)
HAL_InvalidateDCache_by_Addr((uint32_t *)img_msg->addr, img_size);
for (int i = 0; i < sizeof(img_stream->observers)/sizeof(img_stream->observers[0]); i++) {
if (img_stream->observers[i] != NULL) {
img_stream->observers[i](img_msg);
}
}
end:
ic_message_msg_send_by_id(IC_MESSAGE_ID_STREAM, IC_MESSAGE_MSG_TYPE_CMD, &ret, sizeof(ret));
return 0;
}
img_stream_t *img_stream_init(void)
{
img_stream_t *img_stream = exram_malloc(4, sizeof(img_stream_t));
if (!img_stream) {
CLOGE("malloc img_stream failed");
return NULL;
}
memset(img_stream, 0x00, sizeof(img_stream_t));
uint32_t send_data = 0;
ic_message_register_by_id(IC_MESSAGE_ID_STREAM, _img_stream_cb, img_stream);
do {
int ret = ic_message_msg_send_by_id(IC_MESSAGE_ID_STREAM, IC_MESSAGE_MSG_TYPE_CMD, &send_data, sizeof(send_data));
if (ret == 0) break;
} while (1);
_img_stream = img_stream;
return img_stream;
}
void img_stream_register_on_recv(on_data_received cbs_recv)
{
if (!_img_stream)
return;
for (int i = 0; i < sizeof(_img_stream->observers) / sizeof(_img_stream->observers[0]); i++) {
if (_img_stream->observers[i] == NULL) {
_img_stream->observers[i] = cbs_recv;
break;
}
}
}
void img_stream_unregister_on_recv(on_data_received cbs_recv)
{
if (!_img_stream)
return;
for (int i = 0; i < sizeof(_img_stream->observers) / sizeof(_img_stream->observers[0]); i++) {
if (_img_stream->observers[i] == cbs_recv) {
_img_stream->observers[i] = NULL;
break;
}
}
}
void img_stream_unregister_all(void)
{
for (int i = 0; i < sizeof(_img_stream->observers) / sizeof(_img_stream->observers[0]); i++) {
if (_img_stream->observers[i] != NULL) {
_img_stream->observers[i] = NULL;
}
}
}

View File

@@ -0,0 +1,45 @@
#ifndef __IMG_STREAM_H__
#define __IMG_STREAM_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define IMG_STREAM_START 0x00
#define IMG_STREAM_END 0xFFFFFFFF
enum img_type {
IMG_RAW,
IMG_STITCH,
IMG_CUTLINE,
};
struct img_stream_msg {
uint8_t type;
uint32_t addr;
uint16_t wdith;
uint16_t height;
}__attribute__((packed));
typedef struct {
} img_stream_cbs_t;
typedef void (*on_data_received)(struct img_stream_msg *msg);
typedef struct {
img_stream_cbs_t cbs;
on_data_received observers[5];
} img_stream_t;
img_stream_t *img_stream_init(void);
void img_stream_register_on_recv(on_data_received cbs_recv);
void img_stream_unregister_on_recv(on_data_received cbs_recv);
void img_stream_unregister_all(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,202 @@
#include "play_ring_buffer.h"
#include "log_print.h"
//初始化缓冲区
struct play_ring_buffer *play_ring_buffer_init(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size)
{
//struct play_ring_buffer *ring_buf = NULL;
if (!buffer)
{
CLOGE("buffer null");
return NULL;
}
if (!is_power_of_2(size))
{
CLOGE("size must be power of 2.");
return ring_buf;
}
if (!ring_buf)
{
ring_buf = (struct play_ring_buffer *)malloc(sizeof(struct play_ring_buffer));
if(!ring_buf)
{
CLOGE("Failed to alloct memory");
return ring_buf;
}
}
memset(ring_buf, 0, sizeof(struct play_ring_buffer));
ring_buf->buffer = buffer;
ring_buf->size = size;
ring_buf->in = 0;
ring_buf->out = 0;
#if RING_BUFFER_BLOCK_ENABLE
ring_buf->ringbuff_sta = xEventGroupCreate();
#endif
return ring_buf;
}
//重置缓冲区
void play_ring_buffer_reset(struct play_ring_buffer *ring_buf)
{
if (ring_buf)
{
if (ring_buf->buffer)
{
//memset(ring_buf->buffer, 0, ring_buf->size);
}
ring_buf->in = 0;
ring_buf->out = 0;
#if RING_BUFFER_BLOCK_ENABLE
vEventGroupDelete(ring_buf->ringbuff_sta);
ring_buf->ringbuff_sta = xEventGroupCreate();
#endif
}
}
//释放缓冲区
void play_ring_buffer_free(struct play_ring_buffer *ring_buf)
{
if (ring_buf)
{
if(ring_buf->buffer)
{
free(ring_buf->buffer);
}
free(ring_buf);
ring_buf = NULL;
}
}
//缓冲区的长度
unsigned int __play_ring_buffer_len(const struct play_ring_buffer *ring_buf)
{
return (ring_buf->in - ring_buf->out);
}
//从缓冲区中取数据
static unsigned int __play_ring_buffer_get(struct play_ring_buffer *ring_buf, unsigned char *buffer, unsigned int size)
{
if (!ring_buf || !buffer)
return 0;
unsigned int len = 0;
size = min(size, ring_buf->in - ring_buf->out);
/* first get the data from fifo->out until the end of the buffer */
len = min(size, ring_buf->size - (ring_buf->out & (ring_buf->size - 1)));
memcpy(buffer, ring_buf->buffer + (ring_buf->out & (ring_buf->size - 1)), len);
/* then get the rest (if any) from the beginning of the buffer */
memcpy(buffer + len, ring_buf->buffer, size - len);
ring_buf->out += size;
return size;
}
//向缓冲区中存放数据
unsigned int play_ring_buffer_put(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size)
{
if (!ring_buf || !buffer)
return 0;
unsigned int len = 0;
size = min(size, ring_buf->size - ring_buf->in + ring_buf->out);
/* first put the data starting from fifo->in to buffer end */
len = min(size, ring_buf->size - (ring_buf->in & (ring_buf->size - 1)));
memcpy(ring_buf->buffer + (ring_buf->in & (ring_buf->size - 1)), buffer, len);
/* then put the rest (if any) at the beginning of the buffer */
memcpy(ring_buf->buffer, buffer + len, size - len);
ring_buf->in += size;
return size;
}
unsigned int play_ring_buffer_len(const struct play_ring_buffer *ring_buf)
{
unsigned int len = 0;
len = __play_ring_buffer_len(ring_buf);
return len;
}
unsigned int play_ring_buffer_avail(const struct play_ring_buffer *ring_buf)
{
int availabe = 0;
availabe = ring_buf->size - play_ring_buffer_len(ring_buf);
return availabe;
}
unsigned int play_ring_buffer_get(struct play_ring_buffer *ring_buf, unsigned char *buffer, unsigned int size)
{
unsigned int ret;
ret = __play_ring_buffer_get(ring_buf, buffer, size);
// buffer中没有数据
if (ring_buf->in == ring_buf->out)
ring_buf->in = ring_buf->out = 0;
return ret;
}
unsigned int play_ring_buffer_put_block(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size, int wait_ms)
{
if (!ring_buf || !buffer)
return 0;
int write_len = 0;
int ret = 0;
while(write_len < size)
{
ret = play_ring_buffer_put(ring_buf, buffer+write_len, size-write_len);
if(ret > 0)
{
#if RING_BUFFER_BLOCK_ENABLE
xEventGroupSetBits(ring_buf->ringbuff_sta,RINGBUFF_CAN_GET);
#endif
}
write_len += ret;
if(write_len < size)
{
#if RING_BUFFER_BLOCK_ENABLE
// to do理论上应该等待portMAX_DELAY但是为了阻塞在这里而不自知暂定等待200ms.
EventBits_t uxBits = xEventGroupWaitBits(ring_buf->ringbuff_sta, RINGBUFF_CAN_PUT, pdTRUE, pdFALSE, pdMS_TO_TICKS(wait_ms));
if( ( wait_ms > 0) && ((uxBits & RINGBUFF_CAN_PUT) == 0))
{
CLOGW("%s: wait for RINGBUFF_CAN_PUT bit time out!", __FUNCTION__);
return write_len;
}
#endif
}
}
return size;
}
unsigned int play_ring_buffer_get_block(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size, int wait_ms)
{
if (!ring_buf || !buffer)
return 0;
int read_len = 0;
int ret = 0;
while(read_len < size)
{
ret = play_ring_buffer_get(ring_buf, buffer+read_len, size-read_len);
if(ret > 0)
{
#if RING_BUFFER_BLOCK_ENABLE
xEventGroupSetBits(ring_buf->ringbuff_sta,RINGBUFF_CAN_PUT);
#endif
}
read_len += ret;
if(read_len < size)
{
#if RING_BUFFER_BLOCK_ENABLE
EventBits_t uxBits = xEventGroupWaitBits(ring_buf->ringbuff_sta, RINGBUFF_CAN_GET, pdTRUE, pdFALSE, pdMS_TO_TICKS(wait_ms));
if( ( wait_ms > 0) && ( (uxBits & RINGBUFF_CAN_GET) == 0) )
{
CLOGW("%s: wait for RINGBUFF_CAN_GET bit time out!", __FUNCTION__);
return read_len;
}
#endif
}
}
return size;
}

View File

@@ -0,0 +1,53 @@
#ifndef _play_ring_buffer_HEADER_H_
#define _play_ring_buffer_HEADER_H_
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "FreeRTOS.h"
#include "event_groups.h"
// 缓冲区大小
//判断x是否是2的次方
#define is_power_of_2(x) ((x) != 0 && (((x) & ((x)-1)) == 0))
//取a和b中最小值
#define min(a, b) (((a) < (b)) ? (a) : (b))
// 阻塞ringbuffer使能
#define RING_BUFFER_BLOCK_ENABLE 0
#define RINGBUFF_CAN_PUT (1<<0)
#define RINGBUFF_CAN_GET (1<<1)
struct play_ring_buffer
{
uint8_t *buffer; //缓冲区
unsigned int size; //大小
unsigned int in; //入口位置
unsigned int out; //出口位置
#if RING_BUFFER_BLOCK_ENABLE
EventGroupHandle_t ringbuff_sta;
#endif
};
//初始化缓冲区
struct play_ring_buffer *play_ring_buffer_init(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size);
//释放缓冲区
void play_ring_buffer_free(struct play_ring_buffer *ring_buf);
void play_ring_buffer_reset(struct play_ring_buffer *ring_buf);
unsigned int play_ring_buffer_len(const struct play_ring_buffer *ring_buf);
unsigned int play_ring_buffer_get(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size);
unsigned int play_ring_buffer_put(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size);
unsigned int play_ring_buffer_avail(const struct play_ring_buffer *ring_buf);
unsigned int play_ring_buffer_get_block(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size, int wait_timeout);
unsigned int play_ring_buffer_put_block(struct play_ring_buffer *ring_buf, uint8_t *buffer, unsigned int size, int wait_timeout);
#endif

View File

@@ -0,0 +1,499 @@
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "stream_buffer.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include "systick.h"
#include "ClockManager.h"
#include "sysheap.h"
// 核间 ------------------------------------------------
#include "ic_stream.h"
#include "ic_proxy.h"
#include "rpc_client.h"
// xttsService 相关 ------------------------------------------------
#include "xtts_service.h"
#include "xtts_stream.h"
#include "lis_tts.h"
// xtts PCM流 ------------------------------------------------
// 格式: 160 samples/frame, 16 bits/sample
#define XTTS_FRAME_WORD_COUNT (160 * 2 / 4)
#define XTTS_PCM_FRAME_SIZE (320)
volatile uint32_t App_Test_xttsFrameSum;
void App_Test_readXttsFrame(void *pFrame)
{
uint32_t *pUint32 = (uint32_t *)pFrame;
for (int i = 0; i < XTTS_FRAME_WORD_COUNT; ++i) {
App_Test_xttsFrameSum += pUint32[i];
// LOGD("[App_Test] readFrame from psram\r\n");
}
return;
}
// 保存数据流到fs
#if 0
static void save_audio_to_file_test(uint32_t *audio_addr, int len)
{
f_chdrive("1:/");
uint8_t cul_path[20];
static uint32_t file_id = 0;
sprintf(cul_path, "%s_%05d.pcm", "xtts/", file_id);
int fd_pcm = low_fs_open(cul_path, FS_O_RDWR | FS_O_CREAT | FS_O_TRUNC);
if (fd_pcm < 0) {
LOGE("fd pcm open fail");
}
int ret = low_fs_write(fd_pcm, audio_addr, len);
if (ret != len) {
LOGE("fd pcm write error:%d", ret);
}
low_fs_close(fd_pcm);
fd_pcm = -1;
f_chdrive("0:/");
file_id++;
}
#endif
// xtts PCM流 的引用
static XttsStream *App_xtts_stream;
#define AUDIO2FS_LOG_DISABLE (0)
#define AUDIO2FS_LOG_XTTS (1)
#define AUDIO2FS_LOG_LOOPBACK (2)
#ifndef XTTS_AUDIO2FS_LOG
#define XTTS_AUDIO2FS_LOG (0)
#endif
#define XTTS_AUDIO_CRC 0
#define XTTS_AUDIO_RINGBUFFER 1
#if XTTS_AUDIO_RINGBUFFER
#define TTS_RING_BUF (256 * 1024)
#define TTS_STREAM_RX_TIMEOUT_MS (100)
#define TTS_STREAM_TX_TIMEOUT_MS (1000)
StreamBufferHandle_t xtts_stream_buffer_inst = NULL;
#endif
#if XTTS_AUDIO2FS_LOG
uint8_t g_audio2fs_type = AUDIO2FS_LOG_DISABLE;
#endif
int xtts_stream_frame_get(void *data, int size)
{
#if XTTS_AUDIO_RINGBUFFER
if(xtts_stream_buffer_inst == NULL) {
LOGE("xtts stream buffer not initialized");
return -1;
}
return xStreamBufferReceive(xtts_stream_buffer_inst, data, size, TTS_STREAM_RX_TIMEOUT_MS);
#else
return 0;
#endif
}
int xtts_stream_frame_reset(void)
{
#if XTTS_AUDIO_RINGBUFFER
if(xtts_stream_buffer_inst == NULL) {
LOGE("xtts stream buffer not initialized");
return -1;
}
return (pdPASS == xStreamBufferReset(xtts_stream_buffer_inst)) ? 1 : 0;
#endif
return 0;
}
// 后台 xtts PCM流 task, 读取流
static void App_xttsStreamTask(void *param)
{
(void)param;
int ret;
uint32_t msg;
static int frameSeq = 0;
void *xttsPCMFrame = NULL;
int frameType = 0;
#if XTTS_AUDIO_CRC
uint8_t *xtts_buffer_storage = (uint8_t *)exram_malloc(32, 300*1024);
int offset = 0;
#endif
#if XTTS_AUDIO2FS_LOG
#define XTTS_SDIO_BUFFER (10 * 1024)
uint8_t *xtts_buffer_storage = (uint8_t *)heap_psram_malloc(XTTS_SDIO_BUFFER);
int offset = 0;
int fd = -1;
uint32_t index = 0;
#endif // XTTS_AUDIO2FS_LOG
#if XTTS_AUDIO_RINGBUFFER
xtts_stream_buffer_inst = xStreamBufferCreate(TTS_RING_BUF, 1);
ASSERT(xtts_stream_buffer_inst, "Failed to create stream buffer");
int stream_ret = 0;
#endif
while (1) {
// 阻塞式接口, 非copy, 用完归还 空frame.
// 结尾帧时, xttsPCMFrame 不是有效帧, 无视其中数据.
// TODO: 接口待改进?
ret = XttsStream_Consumer_acquireFrame(App_xtts_stream, &xttsPCMFrame, &frameType);
ASSERT(XTTSSTREAM_OK == ret, "%s", __FUNCTION__);
switch (frameType) {
case XTTSSTREAM_FRAMETYPE_BEGIN_OF_STREAM: {
LOGD("[xttsstream_task] begin of stream");
App_Test_readXttsFrame(xttsPCMFrame);
#if XTTS_AUDIO_RINGBUFFER
stream_ret = xStreamBufferSend(xtts_stream_buffer_inst, xttsPCMFrame, XTTS_PCM_FRAME_SIZE, pdMS_TO_TICKS(TTS_STREAM_TX_TIMEOUT_MS));
if(stream_ret != XTTS_PCM_FRAME_SIZE) {
LOGE("xStreamBufferSend failed, ret:%d", stream_ret);
}
#endif
#if XTTS_AUDIO_CRC
memcpy(xtts_buffer_storage, xttsPCMFrame, XTTS_PCM_FRAME_SIZE);
#endif
#if XTTS_AUDIO2FS_LOG
memset(xtts_buffer_storage, 0, XTTS_SDIO_BUFFER);
memcpy(xtts_buffer_storage, xttsPCMFrame, XTTS_PCM_FRAME_SIZE);
offset = 0;
if (AUDIO2FS_LOG_XTTS == g_audio2fs_type) {
char path[100];
sprintf(path, "firmware/listenai/xtts/%d.pcm", index++);
fd = low_fs_open(path, FS_O_CREAT | FS_O_WRITE);
if (fd < 0) {
LOGW("%s: open xtts file failed(%d)", __func__, fd);
}
} else if (AUDIO2FS_LOG_LOOPBACK == g_audio2fs_type) {
char path[100];
sprintf(path, "firmware/listenai/saved/%d.pcm", index++);
fd = low_fs_open(path, FS_O_CREAT | FS_O_WRITE);
if (fd < 0) {
LOGW("%s: open eq audio file failed(%d)", __func__, fd);
}
}
#endif // XTTS_AUDIO2FS_LOG
++frameSeq;
break;
}
case XTTSSTREAM_FRAMETYPE_NORMAL: {
// LOGD("[xttsstream_task] normal frame");
App_Test_readXttsFrame(xttsPCMFrame);
#if XTTS_AUDIO_RINGBUFFER
stream_ret = xStreamBufferSend(xtts_stream_buffer_inst, xttsPCMFrame, XTTS_PCM_FRAME_SIZE, pdMS_TO_TICKS(TTS_STREAM_TX_TIMEOUT_MS));
if(stream_ret != XTTS_PCM_FRAME_SIZE) {
LOGE("xStreamBufferSend failed, ret:%d", stream_ret);
}
#endif
#if XTTS_AUDIO_CRC
offset = (frameSeq) * XTTS_PCM_FRAME_SIZE;
// LOGD("%d, %d", frameSeq, offset);
memcpy(xtts_buffer_storage + offset, xttsPCMFrame, XTTS_PCM_FRAME_SIZE);
#endif
#if XTTS_AUDIO2FS_LOG
offset = (frameSeq % 32) * XTTS_PCM_FRAME_SIZE;
// LOGD("%d, %d", frameSeq, offset);
memcpy(xtts_buffer_storage + offset, xttsPCMFrame, XTTS_PCM_FRAME_SIZE);
if ((offset + XTTS_PCM_FRAME_SIZE) == XTTS_SDIO_BUFFER) {
if (AUDIO2FS_LOG_XTTS == g_audio2fs_type || AUDIO2FS_LOG_LOOPBACK == g_audio2fs_type) {
if (fd >= 0) {
low_fs_write(fd, xtts_buffer_storage, XTTS_SDIO_BUFFER);
}
}
}
#endif // XTTS_AUDIO2FS_LOG
++frameSeq;
break;
}
case XTTSSTREAM_FRAMETYPE_END_OF_STREAM: {
LOGD("[xttsstream_task] end of stream. frameSeq = %d, total len:%d\n\n", frameSeq, frameSeq * XTTS_PCM_FRAME_SIZE);
#if XTTS_AUDIO_CRC
LOGD("xtts_buffer_storage crc :0x%x", crc32_calc((uint8_t *)xtts_buffer_storage, 246960, 0));
#endif
#if XTTS_AUDIO2FS_LOG
// save_audio_to_file_test(xtts_buffer_storage, frameSeq*XTTS_PCM_FRAME_SIZE);
if (AUDIO2FS_LOG_XTTS == g_audio2fs_type || AUDIO2FS_LOG_LOOPBACK == g_audio2fs_type) {
if (fd >= 0) {
low_fs_close(fd);
fd = -1;
}
}
#endif // XTTS_AUDIO2FS_LOG
// 一轮xtts PCM流后, 计数重置
frameSeq = 0;
break;
}
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
// 处理: 上传网络...
// LOGD("[xttsstream_task] frame %d", frameSeq);
// 用过 归还 frame
ret = XttsStream_Consumer_releaseFrame(App_xtts_stream, xttsPCMFrame);
ASSERT(XTTSSTREAM_OK == ret, "%s", __FUNCTION__);
}
// TODO: 测试运行中open/close
// 关闭图流, 当不需要时.
XttsStream_close(App_xtts_stream);
// 语义上: 归还, XttsStream是XttsService的一个外露口, 用完还回去.
// TODO: 不需要了吧, 作为一个内外通道, 不用有归还语义. close已经足够. 且close中已经有了producer端通知功能
// XttsService_returnXttsStream(App_xtts_stream);
vTaskSuspend(NULL);
}
// ScanService 测试------------------------------------------------------------
int xtts_app_task(void)
{
XttsService_remote_sync();
XttsService_initialize();
XttsService_register(NULL, 0);
// 获取内部对象引用, close 状态. 返回 open 的?
XttsService_getXttsStream(&App_xtts_stream);
// 预先打开 Xtts pcm流, XttsService运行后, 才可读取到pcm帧
XttsStream_open(App_xtts_stream);
// 后台 Xtts pcm流 task
BaseType_t result = xTaskCreate(App_xttsStreamTask, /* The function that implements the task. */
"App_xttsStreamTask", /* Text name for the task. */
DEF_TASK_STACK, /* Stack depth in words. */
NULL, /* Task parameters. */
7, /* Priority and mode (user in this case). */
NULL /* Handle. */
);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
XttsService_start();
return 0;
}
#if STRESS_TEST
static TimerHandle_t timer = NULL;
#define XTTS_TEST_TIMER_PERIOD (MS2TICK(2000))
typedef struct {
int type;
int start;
int end;
} stress_test_info_t;
stress_test_info_t test_info = {0};
static void twdt_tmr_proc(TimerHandle_t timer)
{
if (test_info.type == 1) {
ap2cp_trans_stop();
} else if (2 == test_info.type) {
ap2cp_xtts_stop();
} else {
LOGE("not type");
}
sysevt_set(EVT_STAT_OCR_DONE);
LOGD("########");
xTimerStop(timer, portMAX_DELAY);
}
void Stress_Test_Task(void *param)
{
LOGD("[xttstask]Stress_Test_Task start");
char *test_buf_en =
"就是法国国歌:前进,前进,祖国的儿郎,光荣的时刻已经来临……好友、斯特拉斯堡市长迪特里希让他为即将奔赴战场与";
// char *test_buf_en = "We all know that environment is so important to ourselves and our future generations.
// Natural resources have been depleted in an unprecedented scale.";
play_set_pcm_volume(50);
int period_time_ms = 100;
LOGD("type:%d, start:%d, end:%d", test_info.type, test_info.start, test_info.end);
timer = xTimerCreate("tts_T", XTTS_TEST_TIMER_PERIOD, pdTRUE, NULL, twdt_tmr_proc);
ASSERT(timer, "create");
int cnt = 0;
int delay_ms = 80;
while (true) {
LOGD("test cnt:%d", cnt++);
sysevt_clr(EVT_STAT_OCR_DONE);
if (test_info.type == 1) {
scanservice_trans_text(test_buf_en, strlen(test_buf_en));
delay_ms = 80; // 翻译时间间隔过短,会导致看门狗复位
} else if (2 == test_info.type) {
ap2cp_play_audio_type(AUDIO_STREAM_TYPE_XTTS);
scanservice_tts_text(test_buf_en, strlen(test_buf_en));
delay_ms = 30;
} else {
LOGE("not type");
break;
}
xTimerStart(timer, portMAX_DELAY);
sysevt_wait(EVT_STAT_OCR_DONE, portMAX_DELAY);
period_time_ms = rand() % (test_info.end - test_info.start) + test_info.start;
LOGD("%d ms", period_time_ms);
if (period_time_ms <= 0) {
period_time_ms = 100;
}
xTimerChangePeriod(timer, MS2TICK(period_time_ms), portMAX_DELAY);
vTaskDelay(delay_ms);
}
vTaskSuspend(NULL);
}
int stress_test_task(void *param)
{
memcpy(&test_info, param, sizeof(stress_test_info_t));
BaseType_t result;
result = xTaskCreate(Stress_Test_Task, /* The function that implements the task. */
"stress", /* Text name for the task. */
DEF_TASK_STACK, /* Stack depth in words. */
param, /* Task parameters. */
7, /* Priority and mode (user in this case). */
NULL /* Handle. */
);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
return 0;
}
#include "task_play.h"
void Stress_Test_Trans_Xtss_Task(void *param)
{
LOGD("[%s] start", __FUNCTION__);
char *test_buf_en = "[g0]生生世世厚德载物地制宜";
// char *test_buf_en = "We all know that environment is so important to ourselves and our future generations.
// Natural resources have been depleted in an unprecedented scale.";
play_set_pcm_volume(60);
int cnt = 0;
int status = 0;
int delay_ms = 70;
while (true) {
LOGD("test cnt:%d", cnt++);
scan_utils_xtts_set_st(e_xtts_synth_begin);
play_module_config(samplerate_48000, pcm_mono, pcm_16bits, 0);
ap2cp_play_audio_type(AUDIO_STREAM_TYPE_XTTS);
scanservice_tts_text(test_buf_en, strlen(test_buf_en));
while (true) {
uint32_t sta = scan_utils_xtts_get_st();
switch (sta) {
case e_xtts_synth_begin:
case e_xtts_synth_doing:
case e_xtts_play_begin:
case e_xtts_play_playing:
status = LIS_TTS_STATE_ING;
break;
case e_xtts_synth_end:
status = LIS_TTS_STATE_TTS_END;
break;
case e_xtts_play_end:
status = LIS_TTS_STATE_OVER;
break;
case e_xtts_synth_stop:
status = LIS_TTS_STATE_EARLY_OVER;
break;
default:
status = LIS_TTS_STATE_ERR;
LOGE("unkown st:%d", sta);
break;
}
LOGD("st:%d", status);
if (LIS_TTS_STATE_TTS_END == status) {
break;
}
vTaskDelay(10);
}
scanservice_trans_text(test_buf_en, strlen(test_buf_en));
while (true) {
uint32_t sta = scan_utils_xtts_get_st();
switch (sta) {
case e_xtts_synth_begin:
case e_xtts_synth_doing:
case e_xtts_play_begin:
case e_xtts_play_playing:
status = LIS_TTS_STATE_ING;
break;
case e_xtts_synth_end:
status = LIS_TTS_STATE_TTS_END;
break;
case e_xtts_play_end:
status = LIS_TTS_STATE_OVER;
break;
case e_xtts_synth_stop:
status = LIS_TTS_STATE_EARLY_OVER;
break;
default:
status = LIS_TTS_STATE_ERR;
LOGE("unkown st:%d", sta);
break;
}
LOGD("st:%d", status);
vTaskDelay(10);
if (LIS_TTS_STATE_OVER == status) {
ap2cp_xtts_stop();
break;
}
}
delay_ms = rand() % (5) + 200;
LOGD("delay_ms:%d", delay_ms);
vTaskDelay(delay_ms);
ap2cp_trans_stop();
vTaskDelay(20);
// break;
}
vTaskSuspend(NULL);
}
int stress_test_trans_xtts_task(void *param)
{
memcpy(&test_info, param, sizeof(stress_test_info_t));
BaseType_t result;
result = xTaskCreate(Stress_Test_Trans_Xtss_Task, /* The function that implements the task. */
"stress", /* Text name for the task. */
DEF_TASK_STACK, /* Stack depth in words. */
param, /* Task parameters. */
7, /* Priority and mode (user in this case). */
NULL /* Handle. */
);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
return 0;
}
#endif

View File

@@ -0,0 +1,265 @@
#include "xtts_server.h"
#include "xtts_stream.h"
//------------------------------------------------
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
//------------------------------------------------
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// 内外部通路------------------------------------------------
// 引用
static XttsStream *_XttsServer_xttsStream;
// 内部状态 - 配置 ------------------------------------------------
// observer
static XttsServer_SignalEvent_t _XttsServer_cb;
static uint32_t _XttsServer_userData;
static int _XttsServer_session;
// 事件和状态迁移 ------------------------------------------------
enum XTTSSERVER_MSG
{
// 用户API事件
XTTSSERVER_MSG_START, // 开始运行服务
XTTSSERVER_MSG_STOP, // 停止运行服务
// 内部事件
XTTSSERVER_MSG_STREAM_OPEN, // XttsStream对端open
XTTSSERVER_MSG_STREAM_CLOSE, // XttsStream对端close
};
// TODO: 需要同步cp侧扫描服务的实际状态么? 不用吧. 能获取结果就够.
enum XTTSSERVER_STATE
{
XTTSSERVER_STATE_NO_INIT = 0, // 未初始化状态
XTTSSERVER_STATE_INITED, // 已初始化
XTTSSERVER_STATE_RUNNING, // 运行中
};
// pending的事件, 运行时处理
static int _XttsServer_pending_open_stream;
// 单变量共享: 一写多读, api在用户task使用?
// TODO: 增加acquire/release barrier语义? COMPILER_BARRIER?
static volatile int _XttsServer_state;
// 附加状态
static int _XttsServer_state_stream_open;
// 私有成员 ------------------------------------------------
// 队列句柄 16 messages
static QueueHandle_t _XttsServer_msgQ;
// 主task
static void _XttsServer_task(void *arg);
// #define XTTSSERVER_STACK_SIZE (2048)
// 实际图像才用? 或者只需要产生顺序增长的字节数据.
// static MEMSrc _XttsServer_memSrc;
// 接口 ------------------------------------------------
static uint32_t _XttsServer_xttsStreamProducerCB(uint32_t event, uint32_t param, uint32_t user_data)
{
(void)param;
(void)user_data;
BaseType_t result;
if (XTTSSTREAM_EVENT_CONSUMER_OPEN == event)
{
// 消息发给XttsServer的msgQ: consumer端已open
// XttsServer根据inited/running: 做不同处理
uint32_t msg = XTTSSERVER_MSG_STREAM_OPEN;
result = xQueueSendToBack(_XttsServer_msgQ, &msg, 0);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
}
else if (XTTSSTREAM_EVENT_CONSUMER_CLOSE == event)
{
// 消息发给XttsServer的msgQ: consumer端已close
// XttsServer根据inited/running: 做不同处理
uint32_t msg = XTTSSERVER_MSG_STREAM_CLOSE;
result = xQueueSendToBack(_XttsServer_msgQ, &msg, 0);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
}
return 0;
}
// server initialize. 给入XttsStream, 作为其producer
int32_t XttsServer_initialize(XttsStream *xttsStream)
{
// LOGD("[XttsServer::api] XttsServer_initialize\r\n");
ASSERT(XTTSSERVER_STATE_NO_INIT == _XttsServer_state, "%s", __FUNCTION__);
BaseType_t result;
_XttsServer_pending_open_stream = 0;
_XttsServer_state_stream_open = 0;
_XttsServer_xttsStream = xttsStream;
// MEMSrc_ctor(& _XttsServer_memSrc);
_XttsServer_msgQ = xQueueCreate(16, sizeof(uint32_t));
// TODO: task 栈 大小, 创建后台task
result = xTaskCreate(_XttsServer_task, /* The function that implements the task. */
"_XttsServer_task", /* Text name for the task. */
DEF_TASK_STACK, /* Stack depth in words. */
NULL, /* Task parameters. */
7, /* Priority and mode (user in this case). */
NULL /* Handle. */
);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
// 监听 XttsStream consumer端 的open/close事件,
// 发回消息, 因为影响流程. 需要考虑, 消息的延后, 那么producer端可能填不进去.
// acquire时返回closed-error
// 管道残留需要清理吧
XttsStream_register(_XttsServer_xttsStreamProducerCB, 0);
// TODO: 初始化cp服务? 但是XttsServer本身就代表了cp?
_XttsServer_state = XTTSSERVER_STATE_INITED;
// LOGD("[XttsServer::state] -> XTTSSERVER_STATE_INITED\r\n");
return XTTSSERVER_OK;
}
int32_t XttsServer_register(XttsServer_SignalEvent_t cb_event, uint32_t user_data)
{
// LOGD("[XttsServer::api] XttsServer_register\r\n");
_XttsServer_cb = cb_event;
_XttsServer_userData = user_data;
return XTTSSERVER_OK;
}
// server start
int32_t XttsServer_start(void)
{
// 开启后台task
// LOGD("[XttsServer::api] XttsServer_start\r\n");
ASSERT(XTTSSERVER_STATE_INITED == _XttsServer_state, "%s", __FUNCTION__);
BaseType_t result;
// 发事件给server task: 开始运行
uint32_t msg = XTTSSERVER_MSG_START;
result = xQueueSendToBack(_XttsServer_msgQ, &msg, 0);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
return XTTSSERVER_OK;
}
static void _XttsServer_task(void *arg)
{
(void)arg;
BaseType_t result;
uint32_t msg;
while (1)
{
result = xQueueReceive(_XttsServer_msgQ, &msg, portMAX_DELAY);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
switch (_XttsServer_state)
{
// 初始态
case XTTSSERVER_STATE_INITED:
{
// 事件
switch (msg)
{
// TODO: 新ocr结果. 可能为0长字符串. param 为字符串长度
case XTTSSERVER_MSG_STREAM_OPEN:
{
// LOGD("[XttsServer] XTTSSERVER_MSG_STREAM_OPEN\r\n");
// 未运行时的消息: 先记录, 待运行时处理
_XttsServer_pending_open_stream = 1;
break;
}
// 开始运行服务
case XTTSSERVER_MSG_START:
{
// LOGD("[XttsServer] XTTSSERVER_MSG_START\r\n");
// 连接cp扫描服务, 配置而不启动 raw图流
// 之后可根据XttsStream的consumer端是否open, 来决定是否向其中生产图像帧
if (_XttsServer_pending_open_stream)
{
// 状态设置: 需要产生图流
_XttsServer_state_stream_open = 1;
// pending事件已处理, 复位
_XttsServer_pending_open_stream = 0;
}
// XttsServer运行中
_XttsServer_state = XTTSSERVER_STATE_RUNNING;
// LOGD("[XttsServer::state] -> XTTSSERVER_STATE_RUNNING\r\n");
break;
}
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
break;
}
// 运行中, 可能有stream open/close事件
// TODO: mock版不需要处理
case XTTSSERVER_STATE_RUNNING:
{
// 事件
switch (msg)
{
case XTTSSERVER_MSG_STREAM_OPEN:
{
LOGD("[XttsServer] XTTSSERVER_MSG_STREAM_OPEN\r\n");
break;
}
case XTTSSERVER_MSG_STREAM_CLOSE:
{
LOGD("[XttsServer] XTTSSERVER_MSG_STREAM_CLOSE\r\n");
break;
}
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
break;
}
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
};
return;
}

View File

@@ -0,0 +1,93 @@
#ifndef __XTTSSERVER_H
#define __XTTSSERVER_H
#include <stddef.h>
#include <stdint.h>
// #include <stdbool.h>
#include "xtts_stream.h"
/****** XttsServer specific error codes *****/
#define XTTSSERVER_OK (0) ///< 成功
#define XTTSSERVER_ERROR_GENERAL (-1) ///< 一般错误
/****** XttsServer Event *****/
///< TTS成功, 其param (session | 字节数)
// #define XTTSSERVICE_EVENT_TTS_COMPLETE (1UL << 0)
/**
\brief 初始化
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_initialize(XttsStream * xttsStream);
/**
\brief 销毁
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_uninitialize(void);
/**
\brief 用户提供的事件回调
\param[in] event 事件
\param[in] param 事件参数
\param[in] user_data 用户自定义参数
*/
typedef uint32_t (*XttsServer_SignalEvent_t) (uint32_t event, uint32_t param, uint32_t user_data);
/**
\brief 注册 事件的监听回调
\param[in] cb_event 用户提供的事件回调
\param[in] user_data 事件回调的用户自定义参数
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_register(XttsServer_SignalEvent_t cb_event, uint32_t user_data);
/**
\brief 注销扫描事件的监听回调
\param[in] cb_event 用户提供的事件回调
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_unregister(XttsServer_SignalEvent_t cb_event);
/**
\brief 启动, 在使能调度器之后, task中调用.
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_start(void);
/**
\brief 停止服务, 停止内置的外设
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_stop(void);
/**
\brief 暂时挂起服务, 内置的外设挂起
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_suspend(void);
/**
\brief 继续一个已经挂起的服务, 快速继续外设
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsServer_resume(void);
#endif /* __XTTSSERVER_H */

View File

@@ -0,0 +1,252 @@
#include "xtts_service.h"
#include "xtts_stream.h"
#include "xtts_server.h"
#include "algo_lsf_client.h"
//------------------------------------------------
#include "ic_stream.h"
#include "ic_proxy.h"
#include "rpc_client.h"
//------------------------------------------------
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
//------------------------------------------------
#include <stdint.h>
#include <stddef.h>
#include <string.h>
//------------------------------------------------
// #include "venus_ap.h"
// #include "cache.h"
//------------------------------------------------
// service与外部用户代码 连接
// XttsStream 数据流管道, open/close事件 通知对端, 数据流 一读一写
XttsStream _XttsService_xttsStream;
//------------------------------------------------
// 内部状态 - 配置
// observer
static XttsService_SignalEvent_t _XttsService_cb;
static uint32_t _XttsService_userData;
// 事件和状态迁移 ------------------------------------------------
enum XTTSSERVICE_MSG
{
// 用户API事件
XTTSSERVICE_MSG_START, // 开始运行服务
XTTSSERVICE_MSG_STOP, // 停止运行服务
// 内部事件
};
// TODO: 需要同步cp侧扫描服务的实际状态么? 不用吧. 能获取结果就够.
enum XTTSSERVICE_STATE
{
XTTSSERVICE_STATE_NO_INIT = 0, // 未初始化状态
XTTSSERVICE_STATE_INITED, // 已初始化
XTTSSERVICE_STATE_RUNNING, // 运行中
};
// 单变量共享: 一写多读, api在用户task使用?
// TODO: 增加acquire/release barrier语义? COMPILER_BARRIER?
static volatile int _XttsService_state;
// cp端连接, 隐含成员 ------------------------------------------------
// ap <- cp, XttsStream中使用
ICStream *g_ic_stream_0;
static ICStream icStream_0;
// 私有成员 ------------------------------------------------
// 队列句柄 16 messages
static QueueHandle_t _XttsService_msgQ;
// 主task
static void _XttsService_task(void *arg);
// #define XTTSSERVICE_STACK_SIZE (2048)
//------------------------------------------------
static void _XttsService_IC_connect(void);
//------------------------------------------------
// 接口
// resource acquisition is initialization
int32_t XttsService_initialize(void)
{
// LOGD("[XttsService::api] XttsService_initialize\r\n");
ASSERT(XTTSSERVICE_STATE_NO_INIT == _XttsService_state, "%s", __FUNCTION__);
BaseType_t result;
_XttsService_msgQ = xQueueCreate(16, sizeof(uint32_t));
// TODO: task 栈 大小, 创建后台task
result = xTaskCreate(_XttsService_task, /* The function that implements the task. */
"_XttsService_task", /* Text name for the task. */
DEF_TASK_STACK, /* Stack depth in words. */
NULL, /* Task parameters. */
7, /* Priority and mode (user in this case). */
NULL /* Handle. */
);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
// 创建XttsStream管道, consumer端给外部, producer端给XttsServer
XttsStream_ctor((void *)&_XttsService_xttsStream);
// TODO 有了核间流, 不需要这个了吧, 远程调用还是要的?
// 初始化cp服务
XttsServer_initialize(&_XttsService_xttsStream);
// 注册以监听: ocr识别通知
XttsServer_register(NULL, 0);
_XttsService_state = XTTSSERVICE_STATE_INITED;
// LOGD("[XttsService::state] -> XTTSSERVICE_STATE_INITED\r\n");
return XTTSSERVICE_OK;
}
// 用户task中使用, 异步开始, 不等待结果返回
// 在使能调度器之后, boot task中调用, 之前调用不行.
// service是os中的概念, 当然不能在os前存在.
int32_t XttsService_start(void)
{
// LOGD("[XttsService::api] XttsService_start\r\n");
ASSERT(XTTSSERVICE_STATE_INITED == _XttsService_state, "%s", __FUNCTION__);
BaseType_t result;
// 发送事件给service task: 开始运行服务
uint32_t msg = XTTSSERVICE_MSG_START;
result = xQueueSendToBack(_XttsService_msgQ, &msg, 0);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
return XTTSSERVICE_OK;
}
// 请求发送至service的消息队列? 那么task未开始调度时, 无法使用了.
// 此处似乎有race condition? 但是start之后才会有callback, 所以没事吧.
// 或者, 用原子操作: 属性赋值, 或者队列入队
// 就算注册了, 真正的回调仍然要等到调度器运行起来后, 才能触发吧, 所以这里先记下.
// 不支持动态 register/unregister
int32_t XttsService_register(XttsService_SignalEvent_t cb_event, uint32_t user_data)
{
LOGV("[XttsService::api] XttsService_register\r\n");
_XttsService_cb = cb_event;
_XttsService_userData = user_data;
return XTTSSERVICE_OK;
}
// xtts task
static void _XttsService_task(void *arg)
{
(void)arg;
BaseType_t result;
uint32_t msg;
while (1)
{
result = xQueueReceive(_XttsService_msgQ, &msg, portMAX_DELAY);
ASSERT(pdPASS == result, "%s", __FUNCTION__);
switch (_XttsService_state)
{
// 初始态
case XTTSSERVICE_STATE_INITED:
{
// 事件
switch (msg)
{
// 开始运行服务
case XTTSSERVICE_MSG_START:
{
// LOGD("[XttsService] XTTSSERVICE_MSG_START\r\n");
// 连接cp服务, 配置而不启动 流
// 之后可根据XttsStream的consumer端是否open, 来决定是否向其中生产 数据帧
XttsServer_start();
// XttsService运行中
_XttsService_state = XTTSSERVICE_STATE_RUNNING;
// LOGD("[XttsService::state] -> XTTSSERVICE_STATE_RUNNING\r\n");
break;
}
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
break;
}
// 运行中
case XTTSSERVICE_STATE_RUNNING:
{
// 事件
switch (msg)
{
// 目前没有事件需要处理
// case XTTSSERVICE_MSG_something: {
// LOGD("[XttsService] XTTSSERVICE_MSG_something\r\n");
// break;
// }
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
break;
}
default:
LOGE("%s, %d", __FUNCTION__, __LINE__);
break;
}
};
return;
}
// 获取数据流管道引用, close 状态.
int32_t XttsService_getXttsStream(XttsStream **out_xttsStream)
{
LOGD("[XttsService::api] XttsService_getXttsStream\r\n");
// TODO: 根据XttsService状态, 共享单变量?
ASSERT(XTTSSERVICE_STATE_INITED == _XttsService_state, "%s", __FUNCTION__);
*out_xttsStream = &_XttsService_xttsStream;
return 0;
}
void XttsService_remote_sync(void)
{
// 挂接到已经创建好的核间数据流通道上
// 此时server端的ICStream对象已就绪
g_ic_stream_0 = IC_Proxy_getRemoteICStream(&icStream_0, cp2ap_xtts_stream_id);
// 对象级的核间同步, 以确认对端已经就绪.
ICStream_Consumer_syncWithProducer(g_ic_stream_0);
return;
}

View File

@@ -0,0 +1,90 @@
#ifndef __XTTSSERVICE_H
#define __XTTSSERVICE_H
#include <stddef.h>
#include <stdint.h>
// #include <stdbool.h>
#include "xtts_stream.h"
/****** XttsService specific error codes *****/
#define XTTSSERVICE_OK (0) ///< 成功
#define XTTSSERVICE_ERROR_GENERAL (-1) ///< 一般错误
/****** XttsService Event *****/
///< TTS成功, 其param (session | 字节数)
// #define XTTSSERVICE_EVENT_TTS_COMPLETE (1UL << 0)
/**
\brief 服务初始化, 内置核间通信初始化(一次性)
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_initialize(void);
/**
\brief 服务销毁
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_uninitialize(void);
/**
\brief 用户提供的事件回调
\param[in] event 事件
\param[in] param 事件参数
\param[in] user_data 用户自定义参数
*/
typedef uint32_t (*XttsService_SignalEvent_t) (uint32_t event, uint32_t param, uint32_t user_data);
/**
\brief 注册事件的监听回调
\param[in] cb_event 用户提供的事件回调
\param[in] user_data 事件回调的用户自定义参数
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_register(XttsService_SignalEvent_t cb_event, uint32_t user_data);
/**
\brief 注销事件的监听回调
\param[in] cb_event 用户提供的事件回调
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_unregister(XttsService_SignalEvent_t cb_event);
/**
\brief 获取内部对象引用, close 状态
\param[in]
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_getXttsStream(XttsStream **out_xtts_stream);
/**
\brief 启动服务, 在使能调度器之后, task中调用.
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_start(void);
/**
\brief 停止服务, 停止内置的外设
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsService_stop(void);
int xtts_app_task(void);
void XttsService_remote_sync(void);
#endif /* __XTTSSERVICE_H */

View File

@@ -0,0 +1,294 @@
#include "xtts_stream.h"
//------------------------------------------------
#include "ic_stream.h"
//------------------------------------------------
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
//------------------------------------------------
#include "FreeRTOS.h"
#include "semphr.h"
//------------------------------------------------
#undef LOGD
#define LOGD(format, ...) CLOG(format, ##__VA_ARGS__)
//------------------------------------------------
// xtts pcm数据帧
// 空间大小4帧, 实际可用为(4-1=3)帧, 信号量按3帧
#define XTTSSTREAM_FRAME_COUNT (3)
// 160*2 bytes/frame
#define XTTSSTREAM_BUF_BYTES (160 * XTTSSTREAM_FRAME_HEIGHT * XTTSSTREAM_FRAME_COUNT)
extern ICStream *g_ic_stream_0;
static void *p_stream_frame_0;
// observer ------------------------------------------------
// 只需要通知producer端: 接收consumer端的open/close事件
static XttsStream_SignalEvent_t _XttsStream_cb;
static uint32_t _XttsStream_userData;
// TODO: 增加acquire/release barrier语义? COMPILER_BARRIER?
static volatile int _XttsStream_isConsumerOpen; // 1: open; 0: close;
// 状态 ------------------------------------------------
enum XTTSSTREAM_STATE {
XTTSSTREAM_STATE_NO_INIT = 0, // 未初始化状态
XTTSSTREAM_STATE_IDLE, // 会话空闲
XTTSSTREAM_STATE_PREPARE, // 会话起头
XTTSSTREAM_STATE_NORMAL, // 正常数据中
};
int _XttsStream_producerState;
int _XttsStream_consumerState;
// 帧类型 ------------------------------------------------
#define XTTSSTREAM_PATTERN_BEGIN_OF_STREAM (0x7b)
#define XTTSSTREAM_PATTERN_END_OF_STREAM (0x5a)
// utils ------------------------------------------------
static int XttsStream_setFrameType(void *pFrame, int frameType);
static int XttsStream_checkFrameType(void *pFrame, int *out_frameType);
// API ------------------------------------------------
XttsStream * XttsStream_ctor(void *self_mem)
{
LOGD("[XttsStream::api] XttsStream_ctor\r\n");
_XttsStream_isConsumerOpen = 0;
_XttsStream_consumerState = XTTSSTREAM_STATE_IDLE;
return (XttsStream *) self_mem;
}
int32_t XttsStream_register(XttsStream_SignalEvent_t cb_event, uint32_t user_data)
{
// 记录 监听回调, 以便对端变化及时获知
LOGD("[XttsStream::api] XttsStream_register\r\n");
_XttsStream_cb = cb_event;
_XttsStream_userData = user_data;
return XTTSSTREAM_OK;
}
int32_t XttsStream_open(XttsStream *self)
{
(void) self;
LOGD("[XttsStream::api] XttsStream_open\r\n");
// consumer端: 标记设置 open
_XttsStream_isConsumerOpen = 1;
// open事件, 通知给producer端cb
if (NULL != _XttsStream_cb) {
_XttsStream_cb(XTTSSTREAM_EVENT_CONSUMER_OPEN, 0, _XttsStream_userData);
}
return 0;
}
int32_t XttsStream_close(XttsStream *self)
{
(void) self;
LOGD("[XttsStream::api] XttsStream_close\r\n");
// consumer端: 标记设置 close
_XttsStream_isConsumerOpen = 0;
// close事件, 通知给producer端cb
if (NULL != _XttsStream_cb) {
_XttsStream_cb(XTTSSTREAM_EVENT_CONSUMER_CLOSE, 0, _XttsStream_userData);
}
return 0;
}
// TODO 传出参数 void **out_frame
static void XttsStream_Consumer_acquireInterCoreFrame(void)
{
int ret;
// 获取最新的共享状态
ICStream_Consumer_fetchRemote(g_ic_stream_0);
// 先检查是否有数据帧
if (ICStream_Consumer_isEmpty(g_ic_stream_0)) {
// 等待数据帧出现, 如果已经有, 则立即返回, 否则阻塞等待让出调度
// TODO: 通信机制出错码
ICStream_Consumer_waitFrame(g_ic_stream_0);
// 再获取最新的共享状态
ICStream_Consumer_fetchRemote(g_ic_stream_0);
}
else {
// 就算内存先看见指针更新, 还是要等对端通知, 以保持流程同步
ICStream_Consumer_waitFrame(g_ic_stream_0);
}
// 失败则返回出错码, 无关远程
ret = ICStream_Consumer_acquireFrame(g_ic_stream_0, & p_stream_frame_0);
ASSERT(IC_OK == ret, "");
return;
}
// TODO 传出参数 void **out_frame
static void XttsStream_Consumer_releaseInterCoreFrame(void)
{
int ret;
// 失败则返回出错码, 无关远程
ret = ICStream_Consumer_releaseFrame(g_ic_stream_0, p_stream_frame_0);
ASSERT(IC_OK == ret, "");
// 发布最新状态至共享, 通信机制出错码
ret = ICStream_Consumer_commitRemote(g_ic_stream_0);
ASSERT(IC_OK == ret, "");
return;
}
// TODO: return: 已关闭; ok
int32_t XttsStream_Consumer_acquireFrame(XttsStream *self, void **out_frame, int *out_frameType)
{
// TODO: 非mock直接接到核间流的信号量
BaseType_t retOS;
int ret;
void *frame; // TODO 多余
int frameType;
// LOGV("[XttsStream::api] XttsStream_Consumer_acquireFrame\r\n");
if (XTTSSTREAM_STATE_IDLE == _XttsStream_consumerState) {
XttsStream_Consumer_acquireInterCoreFrame();
frame = p_stream_frame_0;
// 必须是BOS, 略过这帧, 再拿1帧
XttsStream_checkFrameType(frame, & frameType);
// TODO: 内部使用的frame类型 最好 与给外部的frame类型区分开, 目前是混用的
ASSERT(XTTSSTREAM_FRAMETYPE_BEGIN_OF_STREAM == frameType, "");
XttsStream_Consumer_releaseInterCoreFrame();
_XttsStream_consumerState = XTTSSTREAM_STATE_PREPARE;
XttsStream_Consumer_acquireInterCoreFrame();
frame = p_stream_frame_0;
// 必须是数据帧
XttsStream_checkFrameType(frame, & frameType);
ASSERT(XTTSSTREAM_FRAMETYPE_NORMAL == frameType, "");
*out_frameType = XTTSSTREAM_FRAMETYPE_BEGIN_OF_STREAM;
*out_frame = frame;
// TODO 状态更新的位置有点怪, 似乎应该放在1帧已release后
_XttsStream_consumerState = XTTSSTREAM_STATE_NORMAL;
return 0;
}
ASSERT(XTTSSTREAM_STATE_NORMAL == _XttsStream_consumerState, "");
// case 状态: XTTSSTREAM_STATE_NORMAL
// wait: 信号量: 数据frame
// audioFrame_acquire_frame
// 如果数据帧
// frameType = XTTSSTREAM_FRAMETYPE_NORMAL;
// 如果这是EOS
// nothing
// frameType = XTTSSTREAM_FRAMETYPE_END_OF_STREAM;
// 状态 = XTTSSTREAM_STATE_IDLE
XttsStream_Consumer_acquireInterCoreFrame();
frame = p_stream_frame_0;
XttsStream_checkFrameType(frame, & frameType);
// TODO: 内部使用的frame类型 最好 与给外部的frame类型区分开, 目前是混用的
if (XTTSSTREAM_FRAMETYPE_NORMAL == frameType) {
*out_frameType = XTTSSTREAM_FRAMETYPE_NORMAL;
*out_frame = frame;
// 状态不变
}
else if (XTTSSTREAM_FRAMETYPE_END_OF_STREAM == frameType) {
*out_frameType = XTTSSTREAM_FRAMETYPE_END_OF_STREAM;
*out_frame = frame;
// 状态变idle
_XttsStream_consumerState = XTTSSTREAM_STATE_IDLE;
}
return 0;
}
int32_t XttsStream_Consumer_releaseFrame(XttsStream *self, void *frame)
{
// TODO: 非mock直接接到核间流的信号量
BaseType_t retOS;
int ret;
// LOGV("[XttsStream::api] XttsStream_Consumer_releaseFrame\r\n");
XttsStream_Consumer_releaseInterCoreFrame();
return 0;
}
// BOS/DATA/EOS
static int XttsStream_checkFrameType(void *pFrame, int *out_frameType)
{
bool bos_found = true;
bool eos_found = true;
// LOGV("[XttsStream] XttsStream_checkFrameType\r\n");
// 尝试查找BOS标记
uint8_t *pUint8 = (uint8_t *) pFrame;
for (int i = 0; i < 8; ++i) {
if (pUint8[i] != XTTSSTREAM_PATTERN_BEGIN_OF_STREAM) {
bos_found = false;
break;
}
}
if (bos_found) {
*out_frameType = XTTSSTREAM_FRAMETYPE_BEGIN_OF_STREAM;
LOGD("[XttsStream] type: BOS\r\n");
return 0;
}
// 尝试查找EOS标记
pUint8 = (uint8_t *) pFrame;
for (int i = 0; i < 8; ++i) {
if (pUint8[i] != XTTSSTREAM_PATTERN_END_OF_STREAM) {
eos_found = false;
break;
}
}
if (eos_found) {
*out_frameType = XTTSSTREAM_FRAMETYPE_END_OF_STREAM;
LOGD("[XttsStream] type: EOS\r\n");
return 0;
}
// 只能是DATA帧
*out_frameType = XTTSSTREAM_FRAMETYPE_NORMAL;
// LOGD("[XttsStream] type: NORMAL\r\n");
return 0;
}

View File

@@ -0,0 +1,87 @@
#ifndef __XTTSSTREAM_H
#define __XTTSSTREAM_H
#include <stddef.h>
#include <stdint.h>
// #include <stdbool.h>
#include "ic_message.h"
#include "lis_algo.h"
#define DEF_TASK_STACK 1024
/****** XttsStream specific error codes *****/
#define XTTSSTREAM_OK (0) ///< 成功
#define XTTSSTREAM_ERROR (-1) ///< 一般错误
// TODO: 任何连接异常都应该引起错误
/****** XttsStream Event *****/
#define XTTSSTREAM_EVENT_CONSUMER_OPEN (1UL << 0) ///< consumer端打开
#define XTTSSTREAM_EVENT_CONSUMER_CLOSE (1UL << 0) ///< consumer端关闭
/****** XttsStream Frame Type *****/
enum {
XTTSSTREAM_FRAMETYPE_ERROR = 0,
XTTSSTREAM_FRAMETYPE_BEGIN_OF_STREAM,
XTTSSTREAM_FRAMETYPE_NORMAL,
XTTSSTREAM_FRAMETYPE_END_OF_STREAM,
};
typedef struct _tag_XttsStream {
int dummy;
} XttsStream;
//------------------------------------------------
/**
\brief 初始化, 传入已分配的内存
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern XttsStream * XttsStream_ctor(void *self_mem);
/**
\brief 销毁
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsStream_dtor(XttsStream *self);
/**
\brief 用户提供的事件回调
\param[in] event 事件
\param[in] param 事件参数
\param[in] user_data 用户自定义参数
*/
typedef uint32_t (*XttsStream_SignalEvent_t) (uint32_t event, uint32_t param, uint32_t user_data);
extern int32_t XttsStream_register(XttsStream_SignalEvent_t cb_event, uint32_t user_data);
/**
\brief 打开流
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsStream_open(XttsStream *self);
/**
\brief 关闭流
\returns
- \b 0: function succeeded
- \b -1: function failed
*/
extern int32_t XttsStream_close(XttsStream *self);
// 阻塞式接口, 非copy, 用完归还 空frame.
// TODO: 接口待改进. 结尾帧时, 不是有效数据帧, 无视其中数据.
extern int32_t XttsStream_Consumer_acquireFrame(XttsStream *self, void **out_frame, int *out_frameType);
// 阻塞式接口, 用完归还 frame.
extern int32_t XttsStream_Consumer_releaseFrame(XttsStream *self, void *frame);
extern int32_t XttsStream_Producer_acquireFrame(XttsStream *self, void **out_frame, int frameType);
extern int32_t XttsStream_Producer_releaseFrame(XttsStream *self, void *frame, int frameType);
#endif /* __XTTSSTREAM_H */

View File

@@ -0,0 +1,28 @@
#include <string.h>
#include "lis_algo.h"
#define MEM_DEBUG 0
#if MEM_DEBUG
static long sram_allocated=0,psram_allocated=0;
#endif
// extern uint32_t get_time_us(void);
// uint32_t os_ticks_get(void)
// {
// return get_time_us()/1000;
// }
//psram strdup
char *pendup(char *s)
{
if(s == NULL||*s==0)
return NULL;
uint32_t len = strlen(s) + 1;
if(len>1024){
LOGW("[pendup]big str,len>1k:%lu\n",len);
}
void *new = os_mem_alloc(len);
if(new == NULL) return NULL;
return (char *)memcpy(new, s, len);
}

View File

@@ -0,0 +1,213 @@
#ifndef __lis_algo_h__
#define __lis_algo_h__
#include "FreeRTOS.h"
#include "task.h"
#include "ic_message.h"
#include "log_print.h"
#include "lisa_log.h"
#define OS_NO_WAIT 0
#define OS_WAIT_FOREVER 0xFFFFFFFF
#define OS_EFAIL -1
#define OS_EOK 0
#define OS_MEM_IRAM 0 //芯片内部ram
#define OS_MEM_ERAM 1 //芯片外部ram
#define ASSERT(exp, fmt, ...) do { if (!(exp)) { LOGE("ASSERED: " fmt, ##__VA_ARGS__); assert(exp); } } while (0)
#define ESP_LOGE(tag, fmt, ...) \
do { \
LOGE("[%s] " fmt, tag, ##__VA_ARGS__); \
} while (0)
#define ESP_LOGW(tag, fmt, ...) \
do { \
LOGW("[%s] " fmt, tag, ##__VA_ARGS__); \
} while (0)
#define ESP_LOGI(tag, fmt, ...) \
do { \
LOGI("[%s] " fmt, tag, ##__VA_ARGS__); \
} while (0)
#define ESP_LOGD(tag, fmt, ...) \
do { \
LOGD("[%s] " fmt, tag, ##__VA_ARGS__); \
} while (0)
#define TAOYUN_OS 1
#define TRANSLATION_RESULT_SIZE (1024)
#define TRANSLATION_INPUT_SIZE (1024)
typedef enum
{
lis_err_ok = 0,
lis_err_busy,
lis_err_err,
lis_err_failed
//...
} lis_err_t;
typedef enum
{
TYPE_TRANS = 0,
TYPE_TTS,
TYPE_OCR,
TYPE_REC,
TYPE_AUDIO,
TYPE_FS,
TYPE_UTIL,
TYPE_SDIO,
TYPE_DB,
TYPE_FENCI,
} algo_func_type_e;
#define _PACKED_ __attribute__((packed))
typedef struct
{
uint8_t buff[TRANSLATION_INPUT_SIZE];
uint32_t size;
uint32_t type;
} _PACKED_ trans_item_t;
typedef struct
{
void *buff;
uint32_t size;
uint32_t speed;
uint32_t vol;
uint8_t role;
int param;
int param_value;
} _PACKED_ xtts_item_t;
typedef struct {
uint16_t x;
uint16_t y;
uint16_t w;
uint16_t h;
} ocr_roi_t;
typedef struct
{
uint32_t ioctl_type;
uint32_t arg;
union {
uint32_t value;
ocr_roi_t ocr_roi;
};
} _PACKED_ ocr_ioctl_t;
typedef struct
{
uint32_t func;
union
{
trans_item_t trans;
xtts_item_t xtts;
ocr_ioctl_t ocr;
};
} _PACKED_ __attribute__((aligned(32))) func_descriptors_t;
typedef enum
{
FUNC_TRANS_START_E = 1,
FUNC_TRANS_STATUS_E,
FUNC_TRANS_RESULT_E,
FUNC_TRANS_STOP_E,
} translation_func_e;
typedef enum
{
OCR_SCAN_MODE = 0,
OCR_HANDMODE,
OCR_IMG_320X240,
OCR_IMG_128X180,
OCR_IMG_STICH, // 拼接图片
OCR_RESULT,
OCR_STATUS,
OCR_SCAN_KEY_DURATION,
OCR_STITCH_ROI,
OCR_SENSOR_GAIN,
OCR_SCAN_LED,
} ocr_arg_e;
typedef enum
{
OCR_IOCTL_START = 1,
OCR_IOCTL_STATUS,
OCR_IOCTL_SET,
OCR_IOCTL_GET,
OCR_IOCTL_STOP,
OCR_IOCTL_RESULT, // 业务主动获取OCR结果
OCR_IOCTL_RESULT_NOTIFY, // 算法主动通知OCR结果
OCR_IOCTL_SYS_START_UP, // 系统启动方式
} ocr_func_e;
typedef enum
{
XTTS_PARAM_ROLE,
XTTS_PARAM_VOLUME,
XTTS_PARAM_VOICE_SPEED,
XTTS_PARAM_VOLUME_INCREASE,
} xtts_func_param_e;
typedef enum
{
FUNC_XTTS_START_E = 1,
FUNC_XTTS_STATUS_E,
FUNC_XTTS_STOP_E,
FUNC_XTTS_SET_PARAM_E,
} xtts_func_e;
typedef enum
{
UTIL_GET_SM_E = 1,
UTIL_GET_FY_E,
UTIL_GET_KEY_E,
UTIL_LED_CTRL_E,
UTIL_CSK_DEEP_SLEEP_E,
UTIL_CSK_VERSION_E,
UTIL_CAM_ROI_E,
UTIL_SCAN_CTRL_E,
UTIL_RESOURCE_COPY_CMD_E,
UTIL_RESOURCE_COPY_STATUS_E,
UTIL_DISK_CHECK_E,
UTIL_GET_ROI_INFO_E,
UTIL_REWRITE_240X320_REG_E, // rewrite camea register before reboot csk
UTIL_GET_ROI_REGISTER_E,
UTIL_IMG_REFLOW_CTRL_E,
UTIL_IMG_REFLOW_CLEAR_E,
UTIL_IMG_REFLOW_GET_CONFIG,
UTIL_LOGDFILE_GET_CONFIG_E,
UTIL_LOGDFILE_SET_ESP_LOGID_E,
UTIL_LOGDFILE_CLEAR_E,
UTIL_LOGDFILE_INIT_E,
UTIL_LOGDFILE_DEINIT_E,
UTIL_XTTS_RESOURCE_COPY_CMD_E,
UTIL_SDIO_SRCTOR_RD_E,
UTIL_SDIO_SRCTOR_WR_E,
UTIL_ESP_RESET_CSK_E,
UTIL_SET_LED_CTRL_DELAY_E,
} util_func_e;
typedef struct
{
uint32_t func;
uint32_t status;
char result[TRANSLATION_RESULT_SIZE];
} __attribute__((aligned(32))) func_ack_t;
extern int lsf_cp2ap_func(uint32_t type, uint8_t *tx, uint32_t tx_size, uint8_t *rx, uint32_t rx_size, TickType_t xTicksToWait);
extern void* os_mem_alloc(unsigned int size);
extern void* os_mem_calloc(unsigned int num, unsigned int size);
extern void* os_mem_realloc(void* old_ptr, unsigned int new_size);
extern void os_mem_free(void* ptr);
// uint32_t os_ticks_get(void);
//psram strdup
char *pendup(char *s);
#endif

View File

@@ -0,0 +1,537 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "lis_ocr.h"
#include "lis_algo.h"
#include "log_print.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "event_groups.h"
#include "IOMuxManager.h"
#include "Driver_GPIO.h"
#define EV_result_READY (1 << 0) // result ready
#define WAIT_PIC_TIME (pdMS_TO_TICKS(3000)) // ms
#define WAIT_CTRL_ACK (pdMS_TO_TICKS(1500)) // ms
#define RESULT_SIZE (1024)
#define OCR_WIDTH (180)
#define OCR_HEIGHT (128)
#define CAM_WIDTH (240)
#define CAM_HEIGHT (320)
#define OCR_DEPTH (1) //
#define OCR_RX_SIZE (OCR_WIDTH * OCR_HEIGHT * OCR_DEPTH) //
#define CAM_RX_SIZE (CAM_WIDTH * CAM_HEIGHT * OCR_DEPTH)
#define SCAN_KEY_PAD CSK_IOMUX_PAD_B
#define SCAN_KEY_PIN 8
#define SCAN_EVT_START (1 << 0)
#define SCAN_EVT_STOP (1 << 1)
#define SCAN_EVT_RESULT (1 << 2)
static struct
{
int ocr_st;
scan_mode_e mode;
uint32_t handle;
char *result;
SemaphoreHandle_t ctrl_sem;
} ocr;
static const char OCR_TAG[] = "ocr";
static EventGroupHandle_t scan_key_evt_hdl;
static void _scan_key_event(uint32_t event, void* workspace)
{
BaseType_t yield = pdFALSE;
if (event & (1 << SCAN_KEY_PIN)) {
if (GPIO_PinRead(GPIOB(), 1 << SCAN_KEY_PIN)) {
xEventGroupSetBitsFromISR(scan_key_evt_hdl, SCAN_EVT_STOP, &yield);
}
else {
xEventGroupSetBitsFromISR(scan_key_evt_hdl, SCAN_EVT_START, &yield);
}
}
}
// 模式配置
lis_err_t lis_ocr_set_scan_mode(scan_mode_e mode)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_SET,
.ocr.arg = OCR_SCAN_MODE,
.ocr.value = mode,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
ocr.mode = mode;
}
else
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_set_scan_mode fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
lis_err_t lis_ocr_set_scan_led(scan_led_e val)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_SET,
.ocr.arg = OCR_SCAN_LED,
.ocr.value = val,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
ocr.mode = val;
}
else
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_set_scan_mode fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
lis_err_t lis_stitch_set_roi(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_SET,
.ocr.arg = OCR_STITCH_ROI,
.ocr.ocr_roi = {
.x = x,
.y = y,
.w = w,
.h = h,
},
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_stitch_set_roi fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
lis_err_t lis_ocr_set_exposure(uint8_t val)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_SET,
.ocr.arg = OCR_SENSOR_GAIN,
.ocr.value = val,
};
CLOGD("lis_ocr_set_exposure val:%d", val);
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_stitch_set_roi fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
lis_err_t lis_ocr_get_sys_startup(int *val)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_SYS_START_UP,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_start fail");
}
*val = ocr_ioctl_ack.status;
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
// 产测或调试接口
lis_err_t lis_ocr_start(void)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_START,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_start fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
lis_err_t lis_ocr_stop(void)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_STOP,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_stop fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
// 获取ocr的状态
lis_ocr_status lis_ocr_get_status(void)
{
lis_ocr_status st = LIS_OCR_STATE_IDLE;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_GET,
.ocr.arg = OCR_STATUS,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
st = ocr_ioctl_ack.status;
}
else
{
st = LIS_OCR_STATE_ERR;
ESP_LOGE(OCR_TAG, "lis_ocr_get_status fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return st;
}
// 获取ocr 扫描按键时间
int lis_ocr_get_scan_key_duration(void)
{
int ret = 0;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_GET,
.ocr.arg = OCR_SCAN_KEY_DURATION,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
ret = ocr_ioctl_ack.status;
}
else
{
ret = -1;
ESP_LOGE(OCR_TAG, "lis_ocr_get_status fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
// 设置OCR扫描左右手模式,默认右手模式
lis_err_t lis_ocr_set_handmode(lis_ocr_handmode mode)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_SET,
.ocr.arg = OCR_HANDMODE,
.ocr.value = mode,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_set_handmode fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
lis_err_t lis_ocr_get_handmode(lis_ocr_handmode *mode)
{
lis_err_t ret = lis_err_ok;
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_GET,
.ocr.arg = OCR_HANDMODE,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
*mode = ocr_ioctl_ack.status;
}
else
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_get_status fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
static uint16_t data_pack_check(const void *dat, uint32_t len)
{
if (NULL == dat)
{
ESP_LOGE(OCR_TAG, "image pack check null\n");
return 0;
}
uint16_t check = 0;
uint8_t *data = (uint8_t *)dat;
while (len--)
check += *data++;
return check;
}
// 获取拼接前摄像头采集的每帧图片
lis_err_t lis_ocr_get_perframe_image(char *buf, int *buf_size)
{
lis_err_t ret = lis_err_ok;
int pic_size = e_scan_mode_adjust == ocr.mode ? CAM_RX_SIZE : OCR_RX_SIZE;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_GET,
.ocr.arg = e_scan_mode_adjust == ocr.mode ? OCR_IMG_320X240 : OCR_IMG_128X180,
};
*buf_size = 0;
if (NULL == buf)
{
*buf_size = pic_size;
ESP_LOGE(OCR_TAG, "lis_ocr_get_perframe_image fail, buf null");
return lis_err_err;
}
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
#if 0
*buf_size = lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)buf, pic_size, WAIT_CTRL_ACK);
if (*buf_size < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_get_perframe_image fail");
}
uint16_t esp_checksum = data_pack_check(buf, *buf_size);
uint16_t checksum = 0;
int rx_len = spi_trans_recv_data(ocr.handle, (uint8_t *)&checksum, sizeof(uint16_t), WAIT_CTRL_ACK);
if (rx_len < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_get_perframe_image fail");
}
if(checksum != esp_checksum) {
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "checksum error, csk:0x%x, esp:0x%x", checksum, esp_checksum);
}
#endif
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
// 获取经过拼接算法拼接后送入OCR的图片
// TODO 拼接长度
lis_err_t lis_ocr_get_stitched_image(char *buf, int *buf_size)
{
lis_err_t ret = lis_err_ok;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_GET,
.ocr.arg = OCR_IMG_STICH,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)buf, CAM_RX_SIZE, WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(OCR_TAG, "lis_ocr_get_stitched_image fail");
}
xSemaphoreGive(ocr.ctrl_sem);
return ret;
}
ocr_result_callback_t g_ocr_rslt_cb = NULL;
void lis_ocr_result_callback(void *rslt)
{
if(g_ocr_rslt_cb)
g_ocr_rslt_cb(rslt, strlen(rslt));
}
int32_t lis_ocr_result_register(ocr_result_callback_t cb)
{
if(cb) g_ocr_rslt_cb = cb;
}
// 当lis_ocr_status_get获取到OCR_STATE_RESULT后会调用该接口获取OCR结果
char *lis_ocr_get_result(void)
{
func_ack_t ocr_ioctl_ack;
func_descriptors_t ocr_ctrl = {
.func = OCR_IOCTL_RESULT,
.ocr.arg = OCR_RESULT,
};
xSemaphoreTake(ocr.ctrl_sem, portMAX_DELAY);
if (lsf_cp2ap_func(ocr.handle, (uint8_t *)&ocr_ctrl, sizeof(func_descriptors_t),
(uint8_t *)&ocr_ioctl_ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
memset(ocr.result, 0, RESULT_SIZE);
ESP_LOGE(OCR_TAG, "ocr get result fail");
} else {
memcpy(ocr.result, ocr_ioctl_ack.result, RESULT_SIZE);
}
// ESP_LOGD(OCR_TAG, "low ocr result:%s\n", ocr.result?:"");
xSemaphoreGive(ocr.ctrl_sem);
return ocr.result;
}
lis_err_t lis_ocr_init(void)
{
memset(&ocr, 0, sizeof(ocr));
ocr.ctrl_sem = xSemaphoreCreateBinary();
ocr.handle = TYPE_OCR;
ocr.mode = e_scan_mode_factory;
#if TAOYUN_OS
ocr.result = os_mem_alloc(RESULT_SIZE);
#else
ocr.result = heap_caps_malloc(RESULT_SIZE, MALLOC_CAP_SPIRAM);
#endif
if (ocr.result && ocr.ctrl_sem)
{
ocr.ocr_st = 1;
xSemaphoreGive(ocr.ctrl_sem);
ESP_LOGI(OCR_TAG, "lis_ocr_init ok");
}
else
{
if (ocr.result)
{
#if TAOYUN_OS
os_mem_free(ocr.result);
#else
heap_caps_free(ocr.result);
#endif
}
ESP_LOGE(OCR_TAG, "lis_ocr_init fail");
return lis_err_err;
}
return lis_err_ok;
}
void lis_ocr_deinit(void)
{
if (ocr.ocr_st)
{
ocr.ocr_st = 0;
#if TAOYUN_OS
os_mem_free(ocr.result);
#else
heap_caps_free(ocr.result);
#endif
vSemaphoreDelete(ocr.ctrl_sem);
}
ESP_LOGW(OCR_TAG, "lis_ocr_deinit ok");
return;
}
// OCR 测试任务
void lis_ocr_task(void)
{
int count = 0;
IOMuxManager_PinConfigure(SCAN_KEY_PAD, SCAN_KEY_PIN, CSK_IOMUX_FUNC_DEFAULT);
GPIO_SetCallback(GPIOB(), 1 << SCAN_KEY_PIN, _scan_key_event, NULL);
GPIO_SetDir(GPIOB(), 1 << SCAN_KEY_PIN, CSK_GPIO_DIR_INPUT);
GPIO_Control(GPIOB(), CSK_GPIO_DEBOUNCE_DISABLE | CSK_GPIO_SET_INTR_DUAL_EDGE |
CSK_GPIO_INTR_ENABLE, (1UL << SCAN_KEY_PIN));
scan_key_evt_hdl = xEventGroupCreate();
lis_ocr_init();
while (1) {
EventBits_t evt_bits = xEventGroupWaitBits(scan_key_evt_hdl, SCAN_EVT_START | SCAN_EVT_STOP | SCAN_EVT_RESULT, false, false, portMAX_DELAY);
if (evt_bits & SCAN_EVT_START)
{
xEventGroupClearBits(scan_key_evt_hdl, SCAN_EVT_START);
xEventGroupSetBits(scan_key_evt_hdl, SCAN_EVT_RESULT);
lis_ocr_start();
}
else if (evt_bits & SCAN_EVT_STOP)
{
xEventGroupClearBits(scan_key_evt_hdl, SCAN_EVT_STOP);
xEventGroupSetBits(scan_key_evt_hdl, SCAN_EVT_RESULT);
lis_ocr_stop();
}
else if (evt_bits & SCAN_EVT_RESULT)
{
lis_ocr_status ocr_status = lis_ocr_get_status();
if (ocr_status != LIS_OCR_STATE_RESULT) {
vTaskDelay(10);
}
else {
xEventGroupClearBits(scan_key_evt_hdl, SCAN_EVT_RESULT);
char * rslt = lis_ocr_get_result();
ESP_LOGD(OCR_TAG, "ocr rslt:%s", rslt);
}
}
}
// lis_ocr_start();
// while (1)
// {
// ESP_LOGD(OCR_TAG, "ocr status:%d", lis_ocr_get_status());
// vTaskDelay(pdMS_TO_TICKS(1000));
// if(count++ > 3) break;
// }
// lis_ocr_stop();
// while(1)
// {
// if(lis_ocr_get_status() == LIS_OCR_STATE_RESULT) {
// char * rslt = lis_ocr_get_result();
// ESP_LOGD(OCR_TAG, "ocr rslt:%s", rslt);
// break;
// }
// vTaskDelay(10);
// }
lis_ocr_deinit();
}

View File

@@ -0,0 +1,145 @@
#ifndef __LIS_OCR_H__
#define __LIS_OCR_H__
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus/*需要被.c文件使用的函数声明*/
#include "lis_algo.h"
#define OCR_PIC_ONE_SHOT (0)
#define OCR_PIC_CONTINUE_SHOT (1)
#define OCR_MODE_RIGHT_HAND (0)
#define OCR_MODE_LEFT_HAND (1)
#define OCR_SCAN_MODE_SINGLE_LINE (0)
#define OCR_SCAN_MODE_MULTI_LINE (1)
#define OCR_KEY_DOWN (0)
#define OCR_KEY_UP (1)
#define OCR_IMG_WIDTH (96)
#define OCR_IMG_HEIGHT (240)
typedef enum
{
e_scan_mode_singleline = 0, // 单行模式
e_scan_mode_multiline,
e_scan_mode_invalid,
e_scan_mode_factory,
e_scan_mode_debug = 4,
e_scan_mode_age,
e_scan_mode_adjust, // 校准
e_scan_mode_offline_ocr,
e_scan_mode_adjust_serial, // 成像测试
e_scan_mode_maximum_picture,
e_scan_mode_reboot = 10,
e_scan_mode_offline_image_uart, // 一致性
e_scan_mode_max,
} scan_mode_e;
typedef enum
{
SCAN_LED_OFF=0,
SCAN_LED_ON,
}scan_led_e;
typedef enum
{
CV_IMAGE_TYPE_INCRE = 0,
CV_IMAGE_TYPE_ORIGINAL,
CV_IMAGE_TYPE_ADJUST,
CV_IMAGE_TYPE_INVALID,
} image_type;
typedef enum
{
CV_IMAGE_FORMAT_ONLY_Y = 0, // 灰度图
CV_IMAGE_FORMAT_INVALID,
} image_format;
typedef struct
{
int8_t x_shift;
int8_t y_shift;
uint16_t width;
uint16_t height;
uint16_t uid;
uint16_t checksum;
image_type type;
image_format format;
uint16_t data_len;
void *data;
} __attribute__((packed)) cv_image_send_para;
/* --------------------------------------------------- */
typedef enum
{
LIS_OCR_STATE_IDLE = 0, // 未启动
LIS_OCR_STATE_ING, // 进行中
LIS_OCR_STATE_ERR, // 内部出错
LIS_OCR_STATE_RESULT, // 可获取结果
LIS_OCR_STATE_OVER, // 正常结束
LIS_OCR_STATE_EARLY_OVER // 提前结束
} lis_ocr_status;
typedef enum
{
LIS_OCR_MODE_RIGHT_HAND = 0,
LIS_OCR_MODE_LEFT_HAND
} lis_ocr_handmode;
lis_err_t lis_ocr_init(void);
void lis_ocr_deinit(void);
// 获取ocr的状态
lis_ocr_status lis_ocr_get_status(void);
// 当lis_ocr_status_get获取到OCR_STATE_RESULT后会调用该接口获取OCR结果
char *lis_ocr_get_result(void);
// 设置OCR扫描左右手模式,默认右手模式
lis_err_t lis_ocr_set_handmode(lis_ocr_handmode mode);
lis_err_t lis_ocr_get_handmode(lis_ocr_handmode *mode);
// 设置拼接ROI区域
lis_err_t lis_stitch_set_roi(uint32_t x, uint32_t y, uint32_t width, uint32_t height);
lis_err_t lis_ocr_set_exposure(uint8_t val);
// 获取拼接前摄像头采集的每帧图片
lis_err_t lis_ocr_get_perframe_image(char *buf, int *buf_size); // 非当前业务强相关接口,可后续讨论实现!!!
// 获取经过拼接算法拼接后送入OCR的图片
lis_err_t lis_ocr_get_stitched_image(char *buf, int *buf_size); // 非当前业务强相关接口,可后续讨论实现!!!
/* ------------------OCR产测接口------------------------ */
// 产测模式图片size:320*240
lis_err_t lis_ocr_set_scan_mode(scan_mode_e mode);
// 产测设置灯亮
lis_err_t lis_ocr_set_scan_led(scan_led_e onoff);
//
lis_err_t lis_ocr_start(void);
//
lis_err_t lis_ocr_stop(void);
// 获取ocr 扫描按键时间
int lis_ocr_get_scan_key_duration(void);
// OCR结果回调函数
typedef int32_t (*ocr_result_callback_t)(void *rslt, uint32_t size);
// 注册ocr结果回调函数
int32_t lis_ocr_result_register(ocr_result_callback_t cb);
// 获取系统启动方式
lis_err_t lis_ocr_get_sys_startup(int *val);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __LIS_OCR_H__

View File

@@ -0,0 +1,188 @@
#include <string.h>
#include "lis_trans.h"
#include "log_print.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#define TRANS_STOP_SYNC (1)
#define WAIT_CTRL_ACK (pdMS_TO_TICKS(500)) // ms
#define POLL_TIME (5 / portTICK_PERIOD_MS)
#define POLL_CNT (200)
typedef struct
{
uint8_t inited : 1;
uint8_t started : 1;
int comm_json_id;
char *result;
uint32_t handle;
QueueHandle_t sync_que;
SemaphoreHandle_t ctrl_sem;
SemaphoreHandle_t txt_sem; // trans文本发送完成信号量
} trans_t;
static const char TRANS_TAG[] = "trans";
static trans_t trans;
lis_err_t lis_trans_start(char *txt, uint32_t txt_size, lis_trans_type type)
{
if(txt == NULL || txt_size == 0) {
ESP_LOGE(TRANS_TAG, "lis_trans_start fail txt is NULL");
return lis_err_err;
}
if(txt_size > TRANSLATION_INPUT_SIZE) {
ESP_LOGE(TRANS_TAG, "lis_trans_start fail txt_size(%d) is too large", txt_size);
return lis_err_err;
}
xSemaphoreTake(trans.ctrl_sem, portMAX_DELAY);
lis_err_t ret = lis_err_ok;
func_ack_t ack = {0};
func_descriptors_t func = {
.func = FUNC_TRANS_START_E,
.trans.size = txt_size,
.trans.type = type,
};
memcpy(func.trans.buff, txt, txt_size);
if (lsf_cp2ap_func(trans.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
if (ack.status)
{
ret = lis_err_busy;
ESP_LOGE(TRANS_TAG, "lis_trans_start fail status: 0x%x", ack.status);
}
}
else
{
ret = lis_err_err;
ESP_LOGE(TRANS_TAG, "lis_trans_start fail");
}
xSemaphoreGive(trans.ctrl_sem);
return ret;
}
lis_err_t lis_trans_stop(void)
{
lis_err_t ret = lis_err_ok;
uint32_t now = xTaskGetTickCount();
xSemaphoreTake(trans.ctrl_sem, portMAX_DELAY);
func_ack_t ack;
func_descriptors_t func = {
.func = FUNC_TRANS_STOP_E,
};
if (lsf_cp2ap_func(trans.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(TRANS_TAG, "lis_trans_stop fail");
}
xSemaphoreGive(trans.ctrl_sem);
ESP_LOGI(TRANS_TAG, "stop cost:%d", xTaskGetTickCount() - now);
return ret;
}
lis_trans_status lis_trans_get_status(void)
{
lis_trans_status st;
xSemaphoreTake(trans.ctrl_sem, portMAX_DELAY);
func_ack_t ack;
func_descriptors_t func = {
.func = FUNC_TRANS_STATUS_E,
};
if (lsf_cp2ap_func(trans.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
st = ack.status;
}
else
{
st = LIS_TRANS_STATE_ERR;
ESP_LOGE(TRANS_TAG, "lis_trans_get_status fail");
}
// ESP_LOGI(TRANS_TAG, "trans status:%d", ack.status);
xSemaphoreGive(trans.ctrl_sem);
return st;
}
char *lis_trans_get_result(void)
{
xSemaphoreTake(trans.ctrl_sem, portMAX_DELAY);
func_descriptors_t func = {
.func = FUNC_TRANS_RESULT_E,
};
func_ack_t ack;
if (lsf_cp2ap_func(trans.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
memset(trans.result, 0, TRANSLATION_RESULT_SIZE);
ESP_LOGE(TRANS_TAG, "lis_trans_get_status fail");
} else {
memcpy(trans.result, ack.result, TRANSLATION_RESULT_SIZE);
}
xSemaphoreGive(trans.ctrl_sem);
CLOGD("result:%s\n", trans.result?:"");
return trans.result;
}
void lis_trans_init(void)
{
if (trans.inited)
{
ESP_LOGW(TRANS_TAG, "already inited");
return;
}
memset(&trans, 0, sizeof(trans));
trans.ctrl_sem = xSemaphoreCreateBinary();
if(trans.result == NULL)
{
#if TAOYUN_OS
trans.result = os_mem_alloc(TRANSLATION_RESULT_SIZE);
#else
trans.result = heap_caps_malloc(TRANSLATION_RESULT_SIZE, MALLOC_CAP_SPIRAM);
#endif
}
trans.handle = TYPE_TRANS;
xSemaphoreGive(trans.ctrl_sem);
trans.inited = 1;
return;
}
void lis_trans_deinit(void)
{
#if TAOYUN_OS
os_mem_free(trans.result);
#else
heap_caps_free(trans.result);
#endif
vSemaphoreDelete(trans.ctrl_sem);
trans.inited = 0;
return;
}
// 翻译测试任务
#define TXT "融合位置编码是一种结合绝对位置编码和相对位置编码优点的位置编码方法。"
#define SIZE (sizeof(TXT) - 1)
void lis_trans_task(void)
{
lis_trans_init();
lis_trans_start(TXT, SIZE, LIS_TRANS_CN2EN);
while (1)
{
int sta = lis_trans_get_status();
if(LIS_TRANS_STATE_OVER == sta) break;
vTaskDelay(pdMS_TO_TICKS(300));
}
lis_trans_get_result();
lis_trans_stop();
lis_trans_deinit();
}

View File

@@ -0,0 +1,50 @@
#ifndef __LIS_TRANS_H__
#define __LIS_TRANS_H__
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus/*需要被.c文件使用的函数声明*/
#include "lis_algo.h"
// ret:小于0超时大于0代表结果的长度
int lis_trans_wait_result(char *result, TickType_t xTicksToWait);
/*******************************************/
typedef enum
{
LIS_TRANS_AUTO = 0,
LIS_TRANS_CN2EN,
LIS_TRANS_EN2CN
} lis_trans_type;
typedef enum
{
LIS_TRANS_STATE_ING = 0, //进行中
LIS_TRANS_STATE_ERR, //内存出错
LIS_TRANS_STATE_RESULT, //有翻译结果,可能有多次结果,如按句输出译文等
LIS_TRANS_STATE_OVER, //正常结束
LIS_TRANS_STATE_EARLY_OVER, //提前结束如调用了lis_trans_stop
} lis_trans_status;
//
void lis_trans_init(void);
//
void lis_trans_deinit(void);
//启动翻译,支持的文本长度至少512字节
lis_err_t lis_trans_start(char *txt, uint32_t txt_size, lis_trans_type type);
//查询翻译状态在lis_trans_start后会轮询调用该函数获取状态
lis_trans_status lis_trans_get_status();
//当lis_trans_get_status获取到TRANS_STATE_RESULT后调用该接口获取译文
char *lis_trans_get_result();
//强制结束,阻塞式
lis_err_t lis_trans_stop();
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __LIS_TRANS_H__

View File

@@ -0,0 +1,263 @@
#include <string.h>
#include "lis_tts.h"
#include "log_print.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "cache.h"
#define WAIT_CTRL_ACK (pdMS_TO_TICKS(2000)) // ms
#define PCM_MAX_RECV_LEN (320 * 10) // 最大的接收buf
#define TTS_TX_SIZE (10 * 1024) // tts 文本
typedef struct
{
uint8_t inited : 1;
uint8_t started : 1;
int pcm_len;
int pcm_back;
int comm_json_id;
uint32_t handle;
QueueHandle_t sync_que; //
SemaphoreHandle_t ctrl_sem; //
SemaphoreHandle_t txt_sem; // tts文本发送完成信号量
SemaphoreHandle_t pcm_rx_sem; // tts pcm
} tts_t;
static const char TTS_TAG[] = "tts";
static tts_t tts;
// 增强音量设置[0-10]
lis_err_t lis_tts_enhance_vol(int32_t vol)
{
//char *pjstr;
lis_err_t ret = lis_err_ok;
if (vol < 0 || vol > 10)
{
ret = lis_err_err;
ESP_LOGE(TTS_TAG, "enhance vol=%d, must be in[0-10]", vol);
goto RET;
}
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
xSemaphoreGive(tts.ctrl_sem);
RET:
return ret;
}
// tts合成播放
lis_err_t lis_tts_start(char *txt, uint32_t txt_size, uint32_t speed, uint32_t vol, uint8_t role)
{
//int ctrl_ack;
lis_err_t ret = lis_err_ok;
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
func_ack_t ack;
func_descriptors_t func = {
.func = FUNC_XTTS_START_E,
.xtts.buff = txt,
.xtts.size = txt_size,
.xtts.speed = speed,
.xtts.vol = vol,
.xtts.role = role,
};
HAL_FlushDCache_by_Addr((uint32_t *)func.xtts.buff, txt_size);
if (lsf_cp2ap_func(tts.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
if (ack.status)
{
ret = lis_err_busy;
ESP_LOGE(TTS_TAG, "lis_tts_start fail status: 0x%x", ack.status);
}
}
else
{
ret = lis_err_err;
ESP_LOGE(TTS_TAG, "lis_tts_start fail");
}
xSemaphoreGive(tts.ctrl_sem);
return ret;
}
// 停止tts合成播放
lis_err_t lis_tts_stop(void)
{
//int ctrl_ack;
lis_err_t ret = lis_err_ok;
uint32_t now = xTaskGetTickCount();
// ESP_LOGI(TTS_TAG, "lis_tts_stop 1\n"); //用于测试
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
func_ack_t ack;
func_descriptors_t func = {
.func = FUNC_XTTS_STOP_E,
};
if (lsf_cp2ap_func(tts.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) < 0)
{
ret = lis_err_err;
ESP_LOGE(TTS_TAG, "lis_tts_stop fail");
}
// ESP_LOGI(TTS_TAG, "lis_tts_stop 2\n");//用于测试
xSemaphoreGive(tts.ctrl_sem);
ESP_LOGI(TTS_TAG, "stop cost:%d", xTaskGetTickCount() - now);
return ret;
}
lis_err_t lis_tts_set_param(int param, int param_value)
{
lis_err_t ret = lis_err_ok;
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
func_ack_t ack;
func_descriptors_t func = {
.func = FUNC_XTTS_SET_PARAM_E,
.xtts.param = param,
.xtts.param_value = param_value,
};
if (lsf_cp2ap_func(tts.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
if (ack.status)
{
ret = lis_err_busy;
ESP_LOGE(TTS_TAG, "lis_tts_set_param fail status: 0x%x", ack.status);
}
}
else
{
ret = lis_err_err;
ESP_LOGE(TTS_TAG, "lis_tts_set_param fail");
}
xSemaphoreGive(tts.ctrl_sem);
return ret;
}
lis_err_t lis_tts_set_pcm_back(int is_back)
{
lis_err_t ret = lis_err_ok;
if (tts.pcm_back == is_back)
{
goto RET;
}
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
tts.pcm_back = is_back;
xSemaphoreGive(tts.ctrl_sem);
RET:
return ret;
}
int lis_tts_get_pcm(char *buf, uint32_t buf_size)
{
lis_err_t ret = lis_err_ok;
// *buf_size = 0;
if (NULL == buf)
{
return ret;
}
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
extern int xtts_stream_frame_get(void *data, uint32_t size);
ret = xtts_stream_frame_get(buf, buf_size);
xSemaphoreGive(tts.ctrl_sem);
return ret;
}
lis_err_t lis_tts_clear_pcm(void)
{
lis_err_t ret = lis_err_ok;
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
extern int xtts_stream_frame_reset(void);
ret = xtts_stream_frame_reset();
xSemaphoreGive(tts.ctrl_sem);
return ret;
}
// 获取tts的状态
lis_tts_status lis_tts_get_status(void)
{
lis_tts_status st;
xSemaphoreTake(tts.ctrl_sem, portMAX_DELAY);
func_ack_t ack;
func_descriptors_t func = {
.func = FUNC_XTTS_STATUS_E,
};
if (lsf_cp2ap_func(tts.handle, (uint8_t *)&func, sizeof(func_descriptors_t),
(uint8_t *)&ack, sizeof(func_ack_t), WAIT_CTRL_ACK) > 0)
{
st = ack.status;
}
else
{
st = LIS_TTS_STATE_ERR;
ESP_LOGE(TTS_TAG, "lis_tts_get_status timeout");
}
// ESP_LOGI(TTS_TAG, "lis_tts_get_status:%d\n", st);
xSemaphoreGive(tts.ctrl_sem);
return st;
}
void lis_tts_init(void)
{
if (tts.inited)
{
ESP_LOGW(TTS_TAG, "already inited");
return;
}
memset(&tts, 0, sizeof(tts));
tts.handle = TYPE_TTS;
tts.ctrl_sem = xSemaphoreCreateBinary();
xSemaphoreGive(tts.ctrl_sem);
tts.inited = 1;
return;
}
void lis_tts_deinit(void)
{
return;
}
void lis_tts_task(void)
{
#if 0
#include "low_play.h"
#include "lis_tts.h"
#define TXT "上海把高标准高质量抓好主题教育作为一项重大政治任务。"
low_play_init();
low_play_cfg_t cfg = {.channel=1, .rate=24000, .bit=16, .vol=100};
low_play_t *play = low_play_start(&cfg);
lis_tts_init();
lis_tts_start(TXT, strlen(TXT), 50, 50, 1);
uint32_t size = 1024;
char *buffer = (char *)os_mem_alloc(size);
uint32_t count = 0;
while (1)
{
int len = lis_tts_get_pcm(buffer, size);
if(len > 0) {
count += len;
printf("count:%d len:%d\n", count, len);
low_play_pcm_write(play, (int16_t *)buffer, len>>1);
}
int sta = lis_tts_get_status();
if((LIS_TTS_STATE_TTS_END == sta) && (len == 0)) {
break;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
printf("total:%d\n", count);
vTaskDelay(1000);
low_play_term();
lis_tts_stop();
lis_tts_deinit();
if(buffer) os_mem_free(buffer);
#endif
}

View File

@@ -0,0 +1,54 @@
#ifndef __LIS_TTS_H__
#define __LIS_TTS_H__
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus/*需要被.c文件使用的函数声明*/
#include "lis_algo.h"
typedef enum
{
LIS_TTS_STATE_ING = 0, // 进行中
LIS_TTS_STATE_TTS_END, // TTS 合成结束, 此时,播放一般还在继续
LIS_TTS_STATE_ERR, // 内部出错
LIS_TTS_STATE_OVER, // 正常结束
LIS_TTS_STATE_EARLY_OVER // 提前结束如esp32调用了lis_tts_stop或者按下笔头需要启动ocrtts就会被强制结束
} lis_tts_status;
#define XTTS_ROLE_LINGXIAOQI (1) // 中文
#define XTTS_ROLE_LUCY (2) // 英文
#define XTTS_ROLE_YILIN (3) // 英文
// init
void lis_tts_init(void);
// init
void lis_tts_deinit(void);
// 增强音量设置[0-10]
lis_err_t lis_tts_enhance_vol(int32_t vol);
// 启动tts,并将和应用相关项在此通过参数传入,参数可设置范围需提供
lis_err_t lis_tts_start(char *txt, uint32_t txt_size, uint32_t speed, uint32_t vol, uint8_t role);
// 查询tts状态在lis_tts_start后会轮询调用该函数获取tts状态
lis_tts_status lis_tts_get_status();
// 强制结束,阻塞式,
lis_err_t lis_tts_stop();
// 将原TTS引擎支持的参数和参数值进行开放支持可设置具体哪些参数可设置需要算法层提供如1的读法语种等
// 在lis_tts_start之前调用
lis_err_t lis_tts_set_param(int param, int param_value); // 非当前业务强相关接口,可后续讨论实现!!!
// 音频数据回传接口,非当前业务强相关接口,可后续讨论实现!!!
lis_err_t lis_tts_set_pcm_back(int is_back); //
// 获取合成音频
int lis_tts_get_pcm(char *buf, uint32_t buf_size);
// 清除合成流中的剩余音频
lis_err_t lis_tts_clear_pcm(void);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __LIS_TTS_H__