second commit
This commit is contained in:
parent
3444880293
commit
5a4277dfad
|
|
@ -1,405 +0,0 @@
|
|||
#include "server/audio_server.h"
|
||||
#include "server/server_core.h"
|
||||
#include "system/app_core.h"
|
||||
#include "generic/circular_buf.h"
|
||||
#include "os/os_api.h"
|
||||
#include "app_config.h"
|
||||
#include "system/includes.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "syscfg/syscfg_id.h"
|
||||
#include "storage_device.h"
|
||||
#include "fs/fs.h"
|
||||
#include <time.h>
|
||||
#include "aduio_record.h"
|
||||
#include "device/device.h"
|
||||
#include "asm/clock.h"
|
||||
|
||||
//#define RECORD
|
||||
#ifdef RECORD
|
||||
|
||||
//全局变量
|
||||
OS_SEM voice_start_sem;
|
||||
OS_SEM voice_msg_sem;
|
||||
struct record_message record_msg;
|
||||
|
||||
//静态全局变量
|
||||
static struct timer_hdl timer4_hal;
|
||||
|
||||
static const u32 timer_div[] = {
|
||||
/*0000*/ 1,
|
||||
/*0001*/ 4,
|
||||
/*0010*/ 16,
|
||||
/*0011*/ 64,
|
||||
/*0100*/ 2,
|
||||
/*0101*/ 8,
|
||||
/*0110*/ 32,
|
||||
/*0111*/ 128,
|
||||
/*1000*/ 256,
|
||||
/*1001*/ 4 * 256,
|
||||
/*1010*/ 16 * 256,
|
||||
/*1011*/ 64 * 256,
|
||||
/*1100*/ 2 * 256,
|
||||
/*1101*/ 8 * 256,
|
||||
/*1110*/ 32 * 256,
|
||||
/*1111*/ 128 * 256,
|
||||
};
|
||||
|
||||
//静态函数
|
||||
static struct record_hdl record_handler;
|
||||
|
||||
#define __this (&record_handler)
|
||||
|
||||
static SEC_USED(.volatile_ram) JL_TIMER_TypeDef *TMR = JL_TIMER4;//选择定时器4
|
||||
static SEC_USED(.volatile_ram) u8 timer_irq = IRQ_TIMER4_IDX;//选择定时器4
|
||||
|
||||
static SEC_USED(.volatile_ram) u32 timer_clk = 0;
|
||||
|
||||
static AWINLINE TSEC void timer_cfg(u32 freq, u32 us)
|
||||
{
|
||||
u8 timer_index[16] = {0, 4, 1, 5, 2, 6, 3, 7, 8, 12, 9, 13, 10, 14, 11, 15};
|
||||
u32 timer_table[16] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768};
|
||||
u32 clock = timer_clk;
|
||||
u8 psc = 0;
|
||||
u8 tmp = 0;
|
||||
u32 prd = 0;
|
||||
u32 ts = us / (1000 * 1000);//计算秒
|
||||
u32 tu = us % (1000 * 1000);//计算秒剩余us
|
||||
u8 i;
|
||||
float tp = 0;
|
||||
|
||||
if (freq >= clock) {
|
||||
freq = clock;
|
||||
} else if (freq <= 1) {
|
||||
freq = 1;
|
||||
if (ts) {
|
||||
tp = (float)tu / (1000 * 1000);
|
||||
}
|
||||
}
|
||||
/*求prd值*/
|
||||
prd = clock / freq;
|
||||
if (prd > 65535) {
|
||||
for (psc = 0; psc < 16; psc++) {
|
||||
prd = (u32)(clock / (timer_table[psc]) / freq);
|
||||
if (prd <= 65535) {
|
||||
break;
|
||||
} else if (psc == 15) {
|
||||
prd = 65535;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prd = ts ? (prd * ts + tp * prd) : prd;
|
||||
psc = timer_index[psc];
|
||||
TMR->CON = 0;
|
||||
TMR->CNT = 0;
|
||||
TMR->CON |= BIT(14);
|
||||
TMR->PRD = prd;
|
||||
TMR->CON |= psc << 4; //lsb_clk分频
|
||||
TMR->CON |= BIT(0);
|
||||
}
|
||||
|
||||
static void TSEC timer_set(u32 us)//设置时间,该函数可以在中断调用重新设置定时器值
|
||||
{
|
||||
u32 freq = 1000000 / us;
|
||||
timer_cfg(freq, us);
|
||||
}
|
||||
|
||||
extern void timer4_stop(void);
|
||||
static ___interrupt TSEC void timer_isr(void)//中断
|
||||
{
|
||||
if (TMR->CON & BIT(15)) {
|
||||
TMR->CON |= BIT(14);
|
||||
//todo,中断函数执行程序...
|
||||
if(!__this->start_flag){
|
||||
__this->end_flag = 1;
|
||||
printf("---->end\r\n");
|
||||
timer4_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void timer4_resume(void) //定时器继续
|
||||
{
|
||||
if (timer4_hal.run == 0) {
|
||||
return;
|
||||
}
|
||||
u32 prd_cnt;
|
||||
u8 index;
|
||||
//precesion = PRD * index / CLK
|
||||
for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
|
||||
prd_cnt = timer4_hal.precesion * (APP_TIMER_CLK / 1000) / timer_div[index];
|
||||
if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timer4_hal.index = index;
|
||||
timer4_hal.prd = prd_cnt;
|
||||
JL_TIMER4->CNT = timer4_hal.ticks;
|
||||
JL_TIMER4->PRD = prd_cnt;
|
||||
JL_TIMER4->CON = (index << 4) | BIT(0) | BIT(3);
|
||||
}
|
||||
|
||||
void timer4_stop(void) //定时器停止
|
||||
{
|
||||
printf("--->Stop\r\n");
|
||||
JL_TIMER4->CON = BIT(14);
|
||||
timer4_hal.run = 0;
|
||||
timer4_hal.precesion = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void timer4_run(u32 precesion) //定时器开始
|
||||
{
|
||||
if (timer4_hal.precesion == precesion) {
|
||||
return;
|
||||
}
|
||||
printf("-->Run %d\r\n", precesion);
|
||||
JL_TIMER4->CON = BIT(14);
|
||||
timer4_hal.precesion = precesion;
|
||||
timer4_hal.run = 1;
|
||||
timer4_hal.ticks = 0;
|
||||
timer4_resume();
|
||||
}
|
||||
|
||||
|
||||
//编码器输出MP3数据
|
||||
static int record_vfs_fwrite(void *file, void *data, u32 len)
|
||||
{
|
||||
static u8 *recognition_buffer = NULL;
|
||||
static u32 buffer_filled = 0;
|
||||
|
||||
if (!recognition_buffer) {
|
||||
recognition_buffer = malloc(SPEECH_RECOGNITION_BUFFER_SIZE);
|
||||
if (!recognition_buffer) {
|
||||
log_e("Failed to allocate recognition buffer\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 将新数据添加到缓冲区
|
||||
if (buffer_filled + len <= SPEECH_RECOGNITION_BUFFER_SIZE) {
|
||||
memcpy(recognition_buffer + buffer_filled, data, len);
|
||||
buffer_filled += len;
|
||||
} else {
|
||||
// 改变全局变量,发送信号量
|
||||
record_msg.msg = recognition_buffer;
|
||||
record_msg.msg_size = buffer_filled;
|
||||
if(__this->end_flag){
|
||||
__this->end_flag = 0;
|
||||
record_msg.end = 1;
|
||||
os_sem_post(&__this->stop_sem);
|
||||
}
|
||||
else
|
||||
record_msg.end = 0;
|
||||
//发送信号量
|
||||
if(__this->start_flag||record_msg.end){
|
||||
os_sem_post(&voice_msg_sem);
|
||||
}
|
||||
|
||||
if(__this->stop_flag){
|
||||
__this->start_flag = 0;
|
||||
__this->stop_flag = 0;
|
||||
}
|
||||
|
||||
// 重置缓冲区
|
||||
buffer_filled = 0;
|
||||
memcpy(recognition_buffer, data, len);
|
||||
buffer_filled = len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
//解码器读取MP3数据
|
||||
static int record_vfs_fread(void *file, void *data, u32 len)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
static int record_vfs_fclose(void *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int record_vfs_flen(void *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct audio_vfs_ops record_vfs_ops = {
|
||||
.fwrite = record_vfs_fwrite,
|
||||
.fread = record_vfs_fread,
|
||||
.fclose = record_vfs_fclose,
|
||||
.flen = record_vfs_flen,
|
||||
};
|
||||
|
||||
static int record_close(void)
|
||||
{
|
||||
union audio_req req = {0};
|
||||
|
||||
log_d("----------record close----------\n");
|
||||
|
||||
if (__this->enc_server) {
|
||||
req.enc.cmd = AUDIO_ENC_CLOSE;
|
||||
server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void enc_server_event_handler(void *priv, int argc, int *argv)
|
||||
{
|
||||
switch (argv[0]) {
|
||||
case AUDIO_SERVER_EVENT_ERR:
|
||||
case AUDIO_SERVER_EVENT_END:
|
||||
record_close();
|
||||
break;
|
||||
case AUDIO_SERVER_EVENT_SPEAK_START:
|
||||
log_i("speak start ! \n");
|
||||
__this->start_flag = 1;
|
||||
__this->end_flag = 0;
|
||||
os_sem_post(&voice_start_sem);
|
||||
timer_set(2000 * 1000);
|
||||
timer4_stop();
|
||||
break;
|
||||
case AUDIO_SERVER_EVENT_SPEAK_STOP:
|
||||
log_i("speak stop ! \n");
|
||||
__this->stop_flag = 1;
|
||||
timer4_run(2000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int encoder_init(int sample_rate, u8 channel)
|
||||
{
|
||||
int err;
|
||||
union audio_req req = {0};
|
||||
|
||||
log_d("----------encoder_init----------\n");
|
||||
|
||||
if (channel > 2) {
|
||||
channel = 2;
|
||||
}
|
||||
|
||||
os_sem_create(&__this->stop_sem, 0);
|
||||
os_sem_create(&voice_msg_sem, 0);
|
||||
os_sem_create(&voice_start_sem, 0);
|
||||
|
||||
__this->start_flag = 0;
|
||||
__this->stop_flag = 0;
|
||||
__this->end_flag = 0;
|
||||
|
||||
/****************初始化编码器*******************/
|
||||
memset(&req, 0, sizeof(union audio_req));
|
||||
|
||||
//BIT(x)用来区分上层需要获取哪个通道的数据
|
||||
if (channel == 2) {
|
||||
req.enc.channel_bit_map = BIT(CONFIG_AUDIO_ADC_CHANNEL_L) | BIT(CONFIG_AUDIO_ADC_CHANNEL_R);
|
||||
} else {
|
||||
req.enc.channel_bit_map = BIT(CONFIG_AUDIO_ADC_CHANNEL_L);
|
||||
}
|
||||
req.enc.frame_size = sample_rate / 100 * 4 * channel; //收集够多少字节MP3数据就回调一次fwrite
|
||||
req.enc.output_buf_len = req.enc.frame_size * 3; //底层缓冲buf至少设成3倍frame_size
|
||||
req.enc.cmd = AUDIO_ENC_OPEN;
|
||||
req.enc.channel = channel;
|
||||
req.enc.volume = __this->gain;
|
||||
req.enc.sample_rate = sample_rate;
|
||||
req.enc.format = "mp3";
|
||||
req.enc.sample_source = __this->sample_source;
|
||||
req.enc.vfs_ops = &record_vfs_ops;
|
||||
req.enc.file = NULL;
|
||||
if (channel == 1 && !strcmp(__this->sample_source, "mic") && (sample_rate == 8000 || sample_rate == 16000)) {
|
||||
req.enc.use_vad = 1; //打开VAD断句功能
|
||||
req.enc.dns_enable = 1; //打开降噪功能
|
||||
req.enc.vad_auto_refresh = 1; //VAD自动刷新
|
||||
}
|
||||
|
||||
err = server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int record_mode_init(void)
|
||||
{
|
||||
log_i("record_play_main\n");
|
||||
|
||||
memset(__this, 0, sizeof(struct record_hdl));
|
||||
|
||||
#ifdef CONFIG_STORE_VOLUME
|
||||
if (syscfg_read(CFG_MUSIC_VOL, &__this->volume, sizeof(__this->volume)) < 0 ||
|
||||
__this->volume < MIN_VOLUME_VALUE || __this->volume > MAX_VOLUME_VALUE) {
|
||||
__this->volume = INIT_VOLUME_VALUE;
|
||||
}
|
||||
#else
|
||||
__this->volume = INIT_VOLUME_VALUE;
|
||||
#endif
|
||||
if (__this->volume < 0 || __this->volume > MAX_VOLUME_VALUE) {
|
||||
__this->volume = INIT_VOLUME_VALUE;
|
||||
}
|
||||
|
||||
__this->sample_source = "mic";
|
||||
__this->channel = CONFIG_AUDIO_RECORDER_CHANNEL;
|
||||
__this->gain = CONFIG_AUDIO_ADC_GAIN;
|
||||
__this->sample_rate = CONFIG_AUDIO_RECORDER_SAMPLERATE;
|
||||
|
||||
__this->enc_server = server_open("audio_server", "enc");
|
||||
server_register_event_handler_to_task(__this->enc_server, NULL, enc_server_event_handler, "app_core");
|
||||
|
||||
return encoder_init(__this->sample_rate, __this->channel);
|
||||
}
|
||||
|
||||
static void record_run_task(void){
|
||||
|
||||
union audio_req req = {0};
|
||||
record_mode_init();
|
||||
timer_clk = clk_get("timer");//先获取定时器的时钟源
|
||||
request_irq(timer_irq, 3, timer_isr, 0);//注册中断函数定和中断优先级
|
||||
|
||||
while(1){
|
||||
//等待信号量
|
||||
os_sem_pend(&__this->stop_sem, 0);
|
||||
//os_time_dly(1000);
|
||||
req.enc.cmd = AUDIO_ENC_PP;
|
||||
server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void record_run_task_create(void)
|
||||
{
|
||||
thread_fork("record_run_task", 15, 1024 * 3 , 0, NULL, record_run_task, NULL);
|
||||
}
|
||||
|
||||
static void record_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(500);
|
||||
}
|
||||
record_run_task_create();
|
||||
}
|
||||
|
||||
static int record_init_task(void)
|
||||
{
|
||||
|
||||
if (thread_fork("record_start", 10, 512, 0, NULL, record_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
late_initcall(record_init_task);
|
||||
|
||||
#endif //RECORD
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,385 +0,0 @@
|
|||
#include "aduio_record.h"
|
||||
|
||||
//#define RECORD
|
||||
#ifdef RECORD
|
||||
|
||||
//全局变量
|
||||
OS_SEM voice_start_sem;
|
||||
|
||||
//静态全局变量
|
||||
static struct timer_hdl timer4_hal;
|
||||
|
||||
static const u32 timer_div[] = {
|
||||
/*0000*/ 1,
|
||||
/*0001*/ 4,
|
||||
/*0010*/ 16,
|
||||
/*0011*/ 64,
|
||||
/*0100*/ 2,
|
||||
/*0101*/ 8,
|
||||
/*0110*/ 32,
|
||||
/*0111*/ 128,
|
||||
/*1000*/ 256,
|
||||
/*1001*/ 4 * 256,
|
||||
/*1010*/ 16 * 256,
|
||||
/*1011*/ 64 * 256,
|
||||
/*1100*/ 2 * 256,
|
||||
/*1101*/ 8 * 256,
|
||||
/*1110*/ 32 * 256,
|
||||
/*1111*/ 128 * 256,
|
||||
};
|
||||
|
||||
//静态函数
|
||||
static struct record_hdl record_handler;
|
||||
|
||||
#define __this (&record_handler)
|
||||
|
||||
static SEC_USED(.volatile_ram) JL_TIMER_TypeDef *TMR = JL_TIMER4;//选择定时器4
|
||||
static SEC_USED(.volatile_ram) u8 timer_irq = IRQ_TIMER4_IDX;//选择定时器4
|
||||
|
||||
static SEC_USED(.volatile_ram) u32 timer_clk = 0;
|
||||
|
||||
|
||||
static AWINLINE TSEC void timer_cfg(u32 freq, u32 us)
|
||||
{
|
||||
u8 timer_index[16] = {0, 4, 1, 5, 2, 6, 3, 7, 8, 12, 9, 13, 10, 14, 11, 15};
|
||||
u32 timer_table[16] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768};
|
||||
u32 clock = timer_clk;
|
||||
u8 psc = 0;
|
||||
u8 tmp = 0;
|
||||
u32 prd = 0;
|
||||
u32 ts = us / (1000 * 1000);//计算秒
|
||||
u32 tu = us % (1000 * 1000);//计算秒剩余us
|
||||
u8 i;
|
||||
float tp = 0;
|
||||
|
||||
if (freq >= clock) {
|
||||
freq = clock;
|
||||
} else if (freq <= 1) {
|
||||
freq = 1;
|
||||
if (ts) {
|
||||
tp = (float)tu / (1000 * 1000);
|
||||
}
|
||||
}
|
||||
/*求prd值*/
|
||||
prd = clock / freq;
|
||||
if (prd > 65535) {
|
||||
for (psc = 0; psc < 16; psc++) {
|
||||
prd = (u32)(clock / (timer_table[psc]) / freq);
|
||||
if (prd <= 65535) {
|
||||
break;
|
||||
} else if (psc == 15) {
|
||||
prd = 65535;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
prd = ts ? (prd * ts + tp * prd) : prd;
|
||||
psc = timer_index[psc];
|
||||
TMR->CON = 0;
|
||||
TMR->CNT = 0;
|
||||
TMR->CON |= BIT(14);
|
||||
TMR->PRD = prd;
|
||||
TMR->CON |= psc << 4; //lsb_clk分频
|
||||
TMR->CON |= BIT(0);
|
||||
}
|
||||
|
||||
static void TSEC timer_set(u32 us)//设置时间,该函数可以在中断调用重新设置定时器值
|
||||
{
|
||||
u32 freq = 1000000 / us;
|
||||
timer_cfg(freq, us);
|
||||
}
|
||||
|
||||
extern void timer4_stop(void);
|
||||
static ___interrupt TSEC void timer_isr(void)//中断
|
||||
{
|
||||
if (TMR->CON & BIT(15)) {
|
||||
TMR->CON |= BIT(14);
|
||||
//todo,中断函数执行程序...
|
||||
if(!__this->start_flag){
|
||||
__this->end_flag = 1;
|
||||
printf("---->end\r\n");
|
||||
timer4_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void timer4_resume(void) //定时器继续
|
||||
{
|
||||
if (timer4_hal.run == 0) {
|
||||
return;
|
||||
}
|
||||
u32 prd_cnt;
|
||||
u8 index;
|
||||
//precesion = PRD * index / CLK
|
||||
for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
|
||||
prd_cnt = timer4_hal.precesion * (APP_TIMER_CLK / 1000) / timer_div[index];
|
||||
if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timer4_hal.index = index;
|
||||
timer4_hal.prd = prd_cnt;
|
||||
JL_TIMER4->CNT = timer4_hal.ticks;
|
||||
JL_TIMER4->PRD = prd_cnt;
|
||||
JL_TIMER4->CON = (index << 4) | BIT(0) | BIT(3);
|
||||
}
|
||||
|
||||
static void timer4_stop(void) //定时器停止
|
||||
{
|
||||
printf("--->Stop\r\n");
|
||||
JL_TIMER4->CON = BIT(14);
|
||||
timer4_hal.run = 0;
|
||||
timer4_hal.precesion = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void timer4_run(u32 precesion) //定时器开始
|
||||
{
|
||||
if (timer4_hal.precesion == precesion) {
|
||||
return;
|
||||
}
|
||||
printf("-->Run %d\r\n", precesion);
|
||||
JL_TIMER4->CON = BIT(14);
|
||||
timer4_hal.precesion = precesion;
|
||||
timer4_hal.run = 1;
|
||||
timer4_hal.ticks = 0;
|
||||
timer4_resume();
|
||||
}
|
||||
|
||||
|
||||
//编码器输出MP3数据
|
||||
static int record_vfs_fwrite(void *file, void *data, u32 len)
|
||||
{
|
||||
static u8 *recognition_buffer = NULL;
|
||||
static u32 buffer_filled = 0;
|
||||
int argv[4];
|
||||
int err;
|
||||
|
||||
if (!recognition_buffer) {
|
||||
recognition_buffer = malloc(SPEECH_RECOGNITION_BUFFER_SIZE);
|
||||
if (!recognition_buffer) {
|
||||
log_e("Failed to allocate recognition buffer\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 将新数据添加到缓冲区
|
||||
if (buffer_filled + len <= SPEECH_RECOGNITION_BUFFER_SIZE) {
|
||||
memcpy(recognition_buffer + buffer_filled, data, len);
|
||||
buffer_filled += len;
|
||||
} else {
|
||||
rec_msg *qmsg = calloc(1,sizeof(rec_msg));
|
||||
memcpy(qmsg->msg,recognition_buffer,buffer_filled);
|
||||
qmsg->msg_size = buffer_filled;
|
||||
if(__this->end_flag){
|
||||
__this->end_flag = 0;
|
||||
qmsg->end = 1;
|
||||
os_sem_post(&__this->stop_sem);
|
||||
}
|
||||
else
|
||||
qmsg->end = 0;
|
||||
|
||||
//发送消息队列
|
||||
if(__this->start_flag||qmsg->end){
|
||||
argv[0]=(int)qmsg;
|
||||
err = os_taskq_post_type("websockets_stt_client_main_thread", Q_USER, 4, argv);
|
||||
if (err == OS_Q_FULL) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
if(__this->stop_flag){
|
||||
__this->start_flag = 0;
|
||||
__this->stop_flag = 0;
|
||||
}
|
||||
|
||||
// 重置缓冲区
|
||||
buffer_filled = 0;
|
||||
memcpy(recognition_buffer, data, len);
|
||||
buffer_filled = len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
//解码器读取MP3数据
|
||||
static int record_vfs_fread(void *file, void *data, u32 len)
|
||||
{
|
||||
return len;
|
||||
}
|
||||
|
||||
static int record_vfs_fclose(void *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int record_vfs_flen(void *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct audio_vfs_ops record_vfs_ops = {
|
||||
.fwrite = record_vfs_fwrite,
|
||||
.fread = record_vfs_fread,
|
||||
.fclose = record_vfs_fclose,
|
||||
.flen = record_vfs_flen,
|
||||
};
|
||||
|
||||
static int record_close(void)
|
||||
{
|
||||
union audio_req req = {0};
|
||||
|
||||
log_d("----------record close----------\n");
|
||||
|
||||
if (__this->enc_server) {
|
||||
req.enc.cmd = AUDIO_ENC_CLOSE;
|
||||
server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void enc_server_event_handler(void *priv, int argc, int *argv)
|
||||
{
|
||||
switch (argv[0]) {
|
||||
case AUDIO_SERVER_EVENT_ERR:
|
||||
case AUDIO_SERVER_EVENT_END:
|
||||
record_close();
|
||||
break;
|
||||
case AUDIO_SERVER_EVENT_SPEAK_START:
|
||||
log_i("speak start ! \n");
|
||||
__this->start_flag = 1;
|
||||
__this->end_flag = 0;
|
||||
os_sem_set(&voice_start_sem,0);
|
||||
os_sem_post(&voice_start_sem);
|
||||
timer_set(2000 * 1000);
|
||||
timer4_stop();
|
||||
break;
|
||||
case AUDIO_SERVER_EVENT_SPEAK_STOP:
|
||||
log_i("speak stop ! \n");
|
||||
__this->stop_flag = 1;
|
||||
timer4_run(2000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int encoder_init(int sample_rate, u8 channel)
|
||||
{
|
||||
int err;
|
||||
union audio_req req = {0};
|
||||
|
||||
log_d("----------encoder_init----------\n");
|
||||
|
||||
if (channel > 2) {
|
||||
channel = 2;
|
||||
}
|
||||
|
||||
os_sem_create(&__this->stop_sem, 0);
|
||||
os_sem_create(&voice_start_sem, 0);
|
||||
|
||||
__this->start_flag = 0;
|
||||
__this->stop_flag = 0;
|
||||
__this->end_flag = 0;
|
||||
|
||||
/****************初始化编码器*******************/
|
||||
memset(&req, 0, sizeof(union audio_req));
|
||||
|
||||
//BIT(x)用来区分上层需要获取哪个通道的数据
|
||||
if (channel == 2) {
|
||||
req.enc.channel_bit_map = BIT(CONFIG_AUDIO_ADC_CHANNEL_L) | BIT(CONFIG_AUDIO_ADC_CHANNEL_R);
|
||||
} else {
|
||||
req.enc.channel_bit_map = BIT(CONFIG_AUDIO_ADC_CHANNEL_L);
|
||||
}
|
||||
req.enc.frame_size = sample_rate / 100 * 4 * channel; //收集够多少字节MP3数据就回调一次fwrite
|
||||
req.enc.output_buf_len = req.enc.frame_size * 3; //底层缓冲buf至少设成3倍frame_size
|
||||
req.enc.cmd = AUDIO_ENC_OPEN;
|
||||
req.enc.channel = channel;
|
||||
req.enc.volume = __this->gain;
|
||||
req.enc.sample_rate = sample_rate;
|
||||
req.enc.format = "mp3";
|
||||
req.enc.sample_source = __this->sample_source;
|
||||
req.enc.vfs_ops = &record_vfs_ops;
|
||||
req.enc.file = NULL;
|
||||
if (channel == 1 && !strcmp(__this->sample_source, "mic") && (sample_rate == 8000 || sample_rate == 16000)) {
|
||||
req.enc.use_vad = 1; //打开VAD断句功能
|
||||
req.enc.dns_enable = 1; //打开降噪功能
|
||||
req.enc.vad_auto_refresh = 1; //VAD自动刷新
|
||||
}
|
||||
|
||||
err = server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int record_mode_init(void)
|
||||
{
|
||||
log_i("record_play_main\n");
|
||||
|
||||
memset(__this, 0, sizeof(struct record_hdl));
|
||||
|
||||
__this->sample_source = "mic";
|
||||
__this->channel = CONFIG_AUDIO_RECORDER_CHANNEL;
|
||||
__this->gain = CONFIG_AUDIO_ADC_GAIN;
|
||||
__this->sample_rate = CONFIG_AUDIO_RECORDER_SAMPLERATE;
|
||||
|
||||
__this->enc_server = server_open("audio_server", "enc");
|
||||
server_register_event_handler_to_task(__this->enc_server, NULL, enc_server_event_handler, "app_core");
|
||||
|
||||
return encoder_init(__this->sample_rate, __this->channel);
|
||||
}
|
||||
|
||||
static void record_run_task(void){
|
||||
|
||||
union audio_req req = {0};
|
||||
record_mode_init();
|
||||
timer_clk = clk_get("timer");//先获取定时器的时钟源
|
||||
request_irq(timer_irq, 3, timer_isr, 0);//注册中断函数定和中断优先级
|
||||
|
||||
while(1){
|
||||
//等待信号量
|
||||
os_sem_pend(&__this->stop_sem, 0);
|
||||
//os_time_dly(1000);
|
||||
req.enc.cmd = AUDIO_ENC_PP;
|
||||
server_request(__this->enc_server, AUDIO_REQ_ENC, &req);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void record_run_task_create(void)
|
||||
{
|
||||
thread_fork("record_run_task", 15, 1024 * 3 , 0, NULL, record_run_task, NULL);
|
||||
}
|
||||
|
||||
static void record_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(500);
|
||||
}
|
||||
record_run_task_create();
|
||||
}
|
||||
//程序入口
|
||||
static int record_init_task(void)
|
||||
{
|
||||
|
||||
if (thread_fork("record_start", 10, 512, 0, NULL, record_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
late_initcall(record_init_task);
|
||||
|
||||
#endif //RECORD
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
主要问题是如何接收处理传过来的文字
|
||||
*/
|
||||
#include "system/includes.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "http/http_cli.h"
|
||||
#include "app_config.h"
|
||||
|
||||
//#define CHAT
|
||||
#ifdef CHAT
|
||||
//选择其中一种进行测试
|
||||
//#define HTTP_GET_TEST
|
||||
#define HTTP_POST_TEST
|
||||
//#define HTTP_PUT_TEST
|
||||
|
||||
#define HTTP_DEMO_URL "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||
|
||||
//#define HTTP_DEMO_URL "https://www.baidu.com/search/error.html"
|
||||
|
||||
//#define HTTP_DEMO_URL "https://jsonplaceholder.typicode.com/posts"
|
||||
|
||||
#define HTTP_DEMO_DATA "{\"model\":\"ep-20241010174456-nhgs5\", \"messages\":[{\"role\":\"user\", \"content\":\"我爱你豆包\"}]}"
|
||||
|
||||
//#define HTTP_DEMO_DATA "{\"name\":\"xiao ming\", \"age\":12}"
|
||||
|
||||
//#define HTTP_DEMO_DATA "{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}"
|
||||
|
||||
|
||||
#define LOG_HTTP_HEAD_OPTION \
|
||||
"POST /api/v3/chat/completions HTTP/1.1\r\n"\
|
||||
"Host: ark.cn-beijing.volces.com\r\n"\
|
||||
"Content-type: application/json\r\n"\
|
||||
"Content-length: %d\r\n"\
|
||||
"Authorization: Bearer f311cc81-1c5b-42c3-bb8a-1b10d1348080\r\n\r\n"
|
||||
|
||||
//#define LOG_HTTP_HEAD_OPTION \
|
||||
// "POST /search/error.html HTTP/1.1\r\n"\
|
||||
// "Host: www.baidu.com\r\n"\
|
||||
// "Connection: close\r\n"\
|
||||
// "Cache-Control: no-store\r\n"\
|
||||
// "Content-type: application/json\r\n"\
|
||||
// "Content-length: 30\r\n"\
|
||||
// "Accept: */*\r\n"\
|
||||
// "Accept-Language: zh-CN,zh,q=0.8\r\n"\
|
||||
// "Cookie: loginType=hardware; appId=1000000001; deviceSN=A4W0H02B000001; credential=56fb1f8df901f13fea8bf12ac2109c58; randNum=2dcf4629\r\n\r\n"
|
||||
|
||||
//#define LOG_HTTP_HEAD_OPTION \
|
||||
// "POST /post HTTP/1.1\r\n"\
|
||||
// "Host: jsonplaceholder.typicode.com\r\n"\
|
||||
// "Connection: close\r\n"\
|
||||
// "Cache-Control: no-store\r\n"\
|
||||
// "Content-type: application/json\r\n"\
|
||||
// "Content-length: 44\r\n"\
|
||||
// "Accept: */*\r\n"\
|
||||
// "Accept-Language: zh-CN,zh,q=0.8\r\n\r\n"\
|
||||
|
||||
|
||||
#define USER_HEADER_BUF_SIZE 2 * 1024
|
||||
|
||||
httpin_error httpcli_put(httpcli_ctx *ctx)
|
||||
{
|
||||
return httpcli_post(ctx);
|
||||
}
|
||||
|
||||
static void http_test_task(void *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
http_body_obj http_body_buf;
|
||||
char *user_header_buf = NULL;
|
||||
|
||||
user_header_buf = calloc(1, USER_HEADER_BUF_SIZE);
|
||||
if (!user_header_buf) {
|
||||
printf("calloc faile\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(user_header_buf, LOG_HTTP_HEAD_OPTION, strlen(HTTP_DEMO_DATA));
|
||||
|
||||
printf(" . user_header_buf :\r\n%s\r\n",user_header_buf);
|
||||
|
||||
httpcli_ctx *ctx = (httpcli_ctx *)calloc(1, sizeof(httpcli_ctx));
|
||||
if (!ctx) {
|
||||
printf("calloc faile\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&http_body_buf, 0x0, sizeof(http_body_obj));
|
||||
|
||||
http_body_buf.recv_len = 0;
|
||||
http_body_buf.buf_len = 4 * 1024;
|
||||
http_body_buf.buf_count = 1;
|
||||
http_body_buf.p = (char *) malloc(http_body_buf.buf_len * http_body_buf.buf_count);
|
||||
if (!http_body_buf.p) {
|
||||
free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->url = HTTP_DEMO_URL;
|
||||
ctx->timeout_millsec = 5000;
|
||||
ctx->priv = &http_body_buf;
|
||||
ctx->connection = "close";
|
||||
|
||||
// ctx->mode = MODE_HTTPS;
|
||||
|
||||
#if (defined HTTP_POST_TEST)
|
||||
printf("http post test start\n\n");
|
||||
ctx->data_format = "application/json";
|
||||
ctx->post_data = HTTP_DEMO_DATA;
|
||||
ctx->data_len = strlen(HTTP_DEMO_DATA);
|
||||
// ctx->user_http_header = LOG_HTTP_HEAD_OPTION; //添加该参数, 则自定义申请头部内容
|
||||
|
||||
ctx->user_http_header = user_header_buf; //添加该参数, 则自定义申请头部内容
|
||||
ret = httpcli_post(ctx);
|
||||
#elif (defined HTTP_GET_TEST)
|
||||
printf("http get test start\n\n");
|
||||
ret = httpcli_get(ctx);
|
||||
#elif (defined HTTP_PUT_TEST)
|
||||
printf("http put test start\n\n");
|
||||
ret = httpcli_put(ctx);
|
||||
#endif
|
||||
|
||||
if (ret != HERROR_OK) {
|
||||
printf("http client test faile\n");
|
||||
} else {
|
||||
if (http_body_buf.recv_len > 0) {
|
||||
printf("\nReceive %d Bytes from (%s)\n", http_body_buf.recv_len, HTTP_DEMO_URL);
|
||||
printf("%s\n", http_body_buf.p);
|
||||
}
|
||||
}
|
||||
|
||||
//关闭连接
|
||||
httpcli_close(ctx);
|
||||
|
||||
if (http_body_buf.p) {
|
||||
free(http_body_buf.p);
|
||||
http_body_buf.p = NULL;
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
free(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void http_test_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(1000);
|
||||
}
|
||||
|
||||
if (thread_fork("http_test_task", 10, 1024 * 10, 0, NULL, http_test_task, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
void c_main(void *priv)
|
||||
{
|
||||
if (thread_fork("http_test_start", 10, 512, 0, NULL, http_test_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(c_main);
|
||||
#endif //CHAT
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/*
|
||||
主要问题是如何接收处理传过来的文字
|
||||
*/
|
||||
#include "system/includes.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "http/http_cli.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#define CHAT
|
||||
#ifdef CHAT
|
||||
//选择其中一种进行测试
|
||||
//#define HTTP_GET_TEST
|
||||
#define HTTP_POST_TEST
|
||||
//#define HTTP_PUT_TEST
|
||||
|
||||
#define HTTP_POST_TEST
|
||||
|
||||
#define HTTP_DEMO_URL "http://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||
#define HTTP_DEMO_DATA "{\"model\": \"ep-20240807114601-mvhsx\", \"messages\": [{\"role\": \"system\",\"content\": \"你是豆包,是由字节跳动开发的 AI 人工智能助手.\"},{\"role\": \"user\",\"content\": \"常见的十字花科植物有哪些?\"}]}"
|
||||
|
||||
#define LOG_HTTP_HEAD_OPTION \
|
||||
"POST /api/v3/chat/completions HTTP/1.1\r\n"\
|
||||
"Host: ark.cn-beijing.volces.com\r\n"\
|
||||
"Connection: close\r\n"\
|
||||
"Content-Type: application/json\r\n"\
|
||||
"Authorization: Bearer 0b559a45-023b-4453-98bd-637daabe257a\r\n"\
|
||||
"Content-Length: 256\r\n"\
|
||||
"Accept: */*\r\n\r\n"
|
||||
|
||||
static void http_test_task(void *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
http_body_obj http_body_buf;
|
||||
httpcli_ctx *ctx = (httpcli_ctx *)calloc(1, sizeof(httpcli_ctx));
|
||||
if (!ctx) {
|
||||
printf("calloc faile\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&http_body_buf, 0x0, sizeof(http_body_obj));
|
||||
|
||||
http_body_buf.recv_len = 0;
|
||||
http_body_buf.buf_len = 4 * 1024;
|
||||
http_body_buf.buf_count = 1;
|
||||
http_body_buf.p = (char *) malloc(http_body_buf.buf_len * http_body_buf.buf_count);
|
||||
if (!http_body_buf.p) {
|
||||
free(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->url = HTTP_DEMO_URL;
|
||||
ctx->timeout_millsec = 5000;
|
||||
ctx->priv = &http_body_buf;
|
||||
ctx->connection = "close";
|
||||
|
||||
printf("http post test start\n\n");
|
||||
ctx->data_format = "application/json";
|
||||
ctx->post_data = HTTP_DEMO_DATA;
|
||||
ctx->data_len = strlen(HTTP_DEMO_DATA);
|
||||
ctx->user_http_header = LOG_HTTP_HEAD_OPTION; // 添加自定义头部内容
|
||||
ret = httpcli_post(ctx);
|
||||
|
||||
if (ret != HERROR_OK) {
|
||||
printf("http client test faile\n");
|
||||
} else {
|
||||
if (http_body_buf.recv_len > 0) {
|
||||
printf("\nReceive %d Bytes from (%s)\n", http_body_buf.recv_len, HTTP_DEMO_URL);
|
||||
printf("%s\n", http_body_buf.p);
|
||||
}
|
||||
}
|
||||
|
||||
//关闭连接
|
||||
httpcli_close(ctx);
|
||||
|
||||
if (http_body_buf.p) {
|
||||
free(http_body_buf.p);
|
||||
http_body_buf.p = NULL;
|
||||
}
|
||||
|
||||
if (ctx) {
|
||||
free(ctx);
|
||||
ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void http_test_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(1000);
|
||||
}
|
||||
|
||||
if (thread_fork("http_test_task", 10, 1024 * 10, 0, NULL, http_test_task, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
void c_main(void *priv)
|
||||
{
|
||||
if (thread_fork("http_test_start", 10, 512, 0, NULL, http_test_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(c_main);
|
||||
#endif //CHAT
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
#include "system/includes.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "apps/http_client.h"
|
||||
#include "app_config.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
//#define chat_lwip
|
||||
#ifdef chat_lwip
|
||||
|
||||
#define HTTP_DEMO_URL "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||
|
||||
#define HTTP_DEMO_DATA "{\"model\":\"ep-20241018142738-n7wv9\", \"messages\":[{\"role\":\"user\", \"content\":\"豆包,介绍一下上甘岭战役\"}]}"
|
||||
|
||||
#define AUTHORIZATION "Bearer f311cc81-1c5b-42c3-bb8a-1b10d1348080"
|
||||
|
||||
#define BUFFER_SIZE 4096 * 3
|
||||
|
||||
static char buffer[BUFFER_SIZE]={0};
|
||||
static uint16_t total_received = 0;
|
||||
|
||||
extern unsigned char chat_msg[4 * 1024];
|
||||
|
||||
extern OS_SEM chat_sem;
|
||||
|
||||
static bool is_json_complete(const char *buffer)
|
||||
{
|
||||
// 尝试解析 JSON
|
||||
cJSON *json = cJSON_Parse(buffer);
|
||||
if (json) {
|
||||
cJSON_Delete(json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void process_json(cJSON *json)
|
||||
{
|
||||
// 处理 JSON 数据
|
||||
cJSON *choices = cJSON_GetObjectItem(json, "choices");
|
||||
if (cJSON_IsArray(choices)) {
|
||||
int array_size = cJSON_GetArraySize(choices);
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON *choice = cJSON_GetArrayItem(choices, i);
|
||||
if (cJSON_IsObject(choice)) {
|
||||
cJSON *message = cJSON_GetObjectItem(choice, "message");
|
||||
if (cJSON_IsObject(message)) {
|
||||
cJSON *content = cJSON_GetObjectItem(message, "content");
|
||||
if (cJSON_IsString(content) && content->valuestring != NULL) {
|
||||
printf("content: %s \r\n", content->valuestring);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static err_t http_rev_func(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
LWIP_PLATFORM_DIAG(("this is http_rev_func\r\n"));
|
||||
|
||||
if (err != ERR_OK || p == NULL) {
|
||||
LWIP_DEBUGF(LWIP_DEBUG, ("http_rev_func: ERR_OK=%d, p=%p\n", err, p));
|
||||
return err;
|
||||
}
|
||||
|
||||
// 将接收到的数据复制到静态缓冲区
|
||||
uint16_t len = p->len;
|
||||
memcpy(buffer + total_received, p->payload, len);
|
||||
total_received += len;
|
||||
|
||||
printf("payload: %s\r\n",(char *)p->payload);
|
||||
|
||||
// 检查是否接收完整条消息
|
||||
if (is_json_complete(buffer)) {
|
||||
// 处理完整的消息
|
||||
cJSON *json = cJSON_Parse(buffer);
|
||||
if (json) {
|
||||
process_json(json);
|
||||
cJSON_Delete(json);
|
||||
} else {
|
||||
LWIP_DEBUGF(LWIP_DEBUG, ("Failed to parse JSON\n"));
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
total_received = 0;
|
||||
memset(buffer,0,sizeof(buffer));
|
||||
}
|
||||
tcp_recved(tpcb, p->len);//通知 lwIP 应用程序已处理了 len 字节的数据,增加接收窗口
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void http_chat_task(void *priv)
|
||||
{
|
||||
const char *server_name = "ark.cn-beijing.volces.com"; // 服务器名称
|
||||
u16_t port = 80; // 端口
|
||||
const char *uri = "/api/v3/chat/completions"; // 请求的 URI
|
||||
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "model", "ep-20241010174456-nhgs5");
|
||||
|
||||
cJSON *messages = cJSON_AddArrayToObject(root, "messages");
|
||||
|
||||
httpc_connection_t setting = { .use_proxy = 0,
|
||||
.result_fn = NULL,
|
||||
.headers_done_fn = NULL,
|
||||
.use_post = 1,
|
||||
.authorization =AUTHORIZATION,
|
||||
.arglen = strlen(HTTP_DEMO_DATA),
|
||||
.args = HTTP_DEMO_DATA
|
||||
};
|
||||
|
||||
while(1){
|
||||
|
||||
os_sem_pend(&chat_sem, 0);
|
||||
|
||||
cJSON *session = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(session, "role", "user");
|
||||
|
||||
cJSON_AddStringToObject(session, "content", chat_msg);
|
||||
|
||||
cJSON_AddItemToArray(messages, session);
|
||||
|
||||
char *http_data = cJSON_Print(root);
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
setting.arglen = strlen(http_data);
|
||||
|
||||
setting.args = http_data;
|
||||
|
||||
httpc_get_file_dns(server_name, port, uri, &setting,http_rev_func, NULL, NULL);
|
||||
|
||||
free(http_data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void http_chat_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(1000);
|
||||
}
|
||||
|
||||
if (thread_fork("http_chat_task", 10, 1024 * 10, 0, NULL, http_chat_task, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
static int chat_lwip_http(void)
|
||||
{
|
||||
if (thread_fork("http_chat_start", 15, 512, 0, NULL, http_chat_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(chat_lwip_http);
|
||||
#endif //CHAT
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
#include "system/includes.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "apps/http_client.h"
|
||||
#include "app_config.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
//#define chat_lwip
|
||||
#ifdef chat_lwip
|
||||
|
||||
#define HTTP_DEMO_URL "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||
|
||||
#define HTTP_DEMO_DATA "{\"model\":\"ep-20241018142738-n7wv9\", \"messages\":[{\"role\":\"user\", \"content\":\"讲一个简短的故事\"}]}"
|
||||
|
||||
#define AUTHORIZATION "Bearer f311cc81-1c5b-42c3-bb8a-1b10d1348080"
|
||||
|
||||
#define BUFFER_SIZE 4096 * 3
|
||||
|
||||
static char buffer[BUFFER_SIZE]={0};
|
||||
static uint16_t total_received = 0;
|
||||
|
||||
char *chat_req = NULL;
|
||||
|
||||
OS_SEM tts_sem;
|
||||
|
||||
static OS_SEM demo_sem;
|
||||
|
||||
static bool is_json_complete(const char *buffer)
|
||||
{
|
||||
// 尝试解析 JSON
|
||||
cJSON *json = cJSON_Parse(buffer);
|
||||
if (json) {
|
||||
cJSON_Delete(json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void process_json(cJSON *json)
|
||||
{
|
||||
// 处理 JSON 数据
|
||||
cJSON *choices = cJSON_GetObjectItem(json, "choices");
|
||||
if (cJSON_IsArray(choices)) {
|
||||
int array_size = cJSON_GetArraySize(choices);
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON *choice = cJSON_GetArrayItem(choices, i);
|
||||
if (cJSON_IsObject(choice)) {
|
||||
cJSON *message = cJSON_GetObjectItem(choice, "message");
|
||||
if (cJSON_IsObject(message)) {
|
||||
cJSON *content = cJSON_GetObjectItem(message, "content");
|
||||
if (cJSON_IsString(content) && content->valuestring != NULL) {
|
||||
printf("content: %s \r\n", content->valuestring);
|
||||
int content_len=strlen(content->valuestring);
|
||||
chat_req = calloc(1,content_len);
|
||||
memcpy(chat_req,content->valuestring,content_len);
|
||||
if(chat_req!=NULL){
|
||||
os_sem_post(&tts_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static err_t http_rev_func(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
LWIP_PLATFORM_DIAG(("this is http_rev_func\r\n"));
|
||||
|
||||
if (err != ERR_OK || p == NULL) {
|
||||
LWIP_DEBUGF(LWIP_DEBUG, ("http_rev_func: ERR_OK=%d, p=%p\n", err, p));
|
||||
return err;
|
||||
}
|
||||
|
||||
// 将接收到的数据复制到静态缓冲区
|
||||
uint16_t len = p->len;
|
||||
memcpy(buffer + total_received, p->payload, len);
|
||||
total_received += len;
|
||||
|
||||
printf("payload: %s\r\n",(char *)p->payload);
|
||||
|
||||
// 检查是否接收完整条消息
|
||||
if (is_json_complete(buffer)) {
|
||||
// 处理完整的消息
|
||||
cJSON *json = cJSON_Parse(buffer);
|
||||
if (json) {
|
||||
process_json(json);
|
||||
cJSON_Delete(json);
|
||||
} else {
|
||||
LWIP_DEBUGF(LWIP_DEBUG, ("Failed to parse JSON\n"));
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
total_received = 0;
|
||||
memset(buffer,0,sizeof(buffer));
|
||||
}
|
||||
tcp_recved(tpcb, p->len);//通知 lwIP 应用程序已处理了 len 字节的数据,增加接收窗口
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void http_chat_task(void *priv)
|
||||
{
|
||||
const char *server_name = "ark.cn-beijing.volces.com"; // 服务器名称
|
||||
u16_t port = 80; // 端口
|
||||
const char *uri = "/api/v3/chat/completions"; // 请求的 URI
|
||||
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddStringToObject(root, "model", "ep-20241010174456-nhgs5");
|
||||
|
||||
cJSON *messages = cJSON_AddArrayToObject(root, "messages");
|
||||
|
||||
httpc_connection_t setting = { .use_proxy = 0,
|
||||
.result_fn = NULL,
|
||||
.headers_done_fn = NULL,
|
||||
.use_post = 1,
|
||||
.authorization =AUTHORIZATION,
|
||||
.arglen = strlen(HTTP_DEMO_DATA),
|
||||
.args = HTTP_DEMO_DATA
|
||||
};
|
||||
|
||||
while(1){
|
||||
os_sem_pend(&demo_sem,0);
|
||||
|
||||
httpc_get_file_dns(server_name, port, uri, &setting,http_rev_func, NULL, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void http_chat_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
os_sem_create(&tts_sem,0);
|
||||
os_sem_create(&demo_sem,1);
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(1000);
|
||||
}
|
||||
|
||||
if (thread_fork("http_chat_task", 10, 1024 * 10, 0, NULL, http_chat_task, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
static int chat_lwip_http(void)
|
||||
{
|
||||
if (thread_fork("http_chat_start", 15, 512, 0, NULL, http_chat_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(chat_lwip_http);
|
||||
#endif //CHAT
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#include "system/includes.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "apps/http_client.h"
|
||||
#include "app_config.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
//#define chat_lwip_stream
|
||||
#ifdef chat_lwip_stream
|
||||
|
||||
#define HTTP_DEMO_URL "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||
|
||||
#define HTTP_DEMO_DATA "{\"model\":\"ep-20241018142738-n7wv9\",\"stream\": true,\"messages\":[{\"role\":\"user\", \"content\":\"写一篇六百字的作文\"}]}"
|
||||
|
||||
#define AUTHORIZATION "Bearer f311cc81-1c5b-42c3-bb8a-1b10d1348080"
|
||||
|
||||
static char buffer[2*1024] = {0}; // 用于存储不完整数据块的缓冲区
|
||||
static size_t buffer_len = 0;
|
||||
|
||||
char accumulated_content[10*1024] = {0};
|
||||
static size_t accumulated_length = 0;
|
||||
static size_t add_length = 0;
|
||||
|
||||
static OS_SEM csem;
|
||||
|
||||
OS_SEM tts_sem;
|
||||
|
||||
static void process_sse_chunk(const char *chunk)
|
||||
{
|
||||
int argv[4];
|
||||
const char *data_start = strstr(chunk, "data:");
|
||||
if (!data_start) {
|
||||
return;
|
||||
}
|
||||
data_start += 5;
|
||||
while (isspace(*data_start)) data_start++;
|
||||
|
||||
if (strcmp(data_start, "[DONE]") == 0) {
|
||||
printf("SSE stream ended\r\n");
|
||||
printf("answer: %s\r\n",accumulated_content);
|
||||
} else {
|
||||
cJSON *json = cJSON_Parse(data_start);
|
||||
if (json) {
|
||||
cJSON *choices = cJSON_GetObjectItem(json, "choices");
|
||||
if (cJSON_IsArray(choices)) {
|
||||
cJSON *choice = cJSON_GetArrayItem(choices, 0);
|
||||
if (cJSON_IsObject(choice)) {
|
||||
cJSON *delta = cJSON_GetObjectItem(choice, "delta");
|
||||
if (cJSON_IsObject(delta)) {
|
||||
cJSON *content = cJSON_GetObjectItem(delta, "content");
|
||||
if (cJSON_IsString(content) && content->valuestring) {
|
||||
|
||||
size_t new_content_length = strlen(content->valuestring);
|
||||
//printf("Chunk content: %s", content->valuestring);
|
||||
if (accumulated_length + new_content_length < 10*1024) {
|
||||
strcat(accumulated_content, content->valuestring);
|
||||
accumulated_length += new_content_length;
|
||||
}
|
||||
add_length+=new_content_length;
|
||||
if(strcmp(content->valuestring,",")==0||strcmp(content->valuestring,":")==0||strcmp(content->valuestring,":")==0||
|
||||
strcmp(content->valuestring,"。")==0||strcmp(content->valuestring,"!")==0||strcmp(content->valuestring,"?")==0||
|
||||
strcmp(content->valuestring,";")==0){
|
||||
printf("sentence:%s\r\n",accumulated_content+(accumulated_length-add_length));
|
||||
argv[0]=accumulated_length;
|
||||
argv[1]=add_length;
|
||||
os_sem_set(&tts_sem,0);
|
||||
os_sem_post(&tts_sem);
|
||||
os_taskq_post_type("websockets_tts_client_main_thread", Q_USER, 20, argv);
|
||||
add_length = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static err_t http_rev_func(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
//LWIP_PLATFORM_DIAG(("this is http_rev_func\r\n"));
|
||||
|
||||
if (p == NULL) {
|
||||
// 连接关闭
|
||||
LWIP_PLATFORM_DIAG(("this is http_rev_func close\r\n"));
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
// 将新数据追加到缓冲区
|
||||
if (buffer_len + p->len < sizeof(buffer)) {
|
||||
memcpy(buffer + buffer_len, p->payload, p->len);
|
||||
buffer_len += p->len;
|
||||
buffer[buffer_len] = '\0';
|
||||
} else {
|
||||
printf("缓冲区溢出,丢弃数据\r\n");
|
||||
}
|
||||
// printf("buffer: %.*s\r\n", buffer_len, buffer);
|
||||
//
|
||||
// printf("buffer_len: %d\r\n",buffer_len);
|
||||
|
||||
// 处理完整的数据块
|
||||
char *start = buffer;
|
||||
char *end;
|
||||
while ((end = strstr(start, "\n\n")) != NULL) {
|
||||
*end = '\0';
|
||||
process_sse_chunk(start);
|
||||
start = end + 2;
|
||||
}
|
||||
|
||||
// 将剩余的不完整数据块移到缓冲区开头
|
||||
if (start != buffer) {
|
||||
buffer_len = strlen(start);
|
||||
memmove(buffer, start, buffer_len + 1);
|
||||
}
|
||||
tcp_recved(tpcb, p->len);//通知 lwIP 应用程序已处理了 len 字节的数据,增加接收窗口
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void http_chat_task(void *priv)
|
||||
{
|
||||
const char *server_name = "ark.cn-beijing.volces.com"; // 服务器名称
|
||||
u16_t port = 80; // 端口
|
||||
const char *uri = "/api/v3/chat/completions"; // 请求的 URI
|
||||
|
||||
httpc_connection_t setting = { .use_proxy = 0,
|
||||
.result_fn = NULL,
|
||||
.headers_done_fn = NULL,
|
||||
.use_post = 1,
|
||||
.authorization =AUTHORIZATION,
|
||||
.arglen = strlen(HTTP_DEMO_DATA),
|
||||
.args = HTTP_DEMO_DATA
|
||||
};
|
||||
|
||||
while(1){
|
||||
os_sem_pend(&csem,0);
|
||||
add_length = 0;
|
||||
httpc_get_file_dns(server_name, port, uri, &setting,http_rev_func, NULL, NULL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void http_chat_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
os_sem_create(&csem,1);
|
||||
os_sem_create(&tts_sem,0);
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(1000);
|
||||
}
|
||||
|
||||
if (thread_fork("http_chat_task", 10, 1024 * 10, 0, NULL, http_chat_task, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
static int chat_lwip_http(void)
|
||||
{
|
||||
if (thread_fork("http_chat_start", 15, 512, 0, NULL, http_chat_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(chat_lwip_http);
|
||||
#endif //CHAT
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,595 +0,0 @@
|
|||
#include "web_socket/websocket_api.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "system/includes.h"
|
||||
#include "fs/fs.h"
|
||||
#include "asm/sfc_norflash_api.h"
|
||||
#include "cJSON.h"
|
||||
#include "aduio_record.h"
|
||||
#ifdef WEBSOCKET_MEMHEAP_DEBUG
|
||||
#include "mem_leak_test.h"
|
||||
#endif
|
||||
|
||||
#include "app_config.h"
|
||||
|
||||
//#define stt
|
||||
#ifdef stt
|
||||
|
||||
// 定义消息类型常量
|
||||
#define SERVER_FULL_RESPONSE 0b1001
|
||||
#define SERVER_ACK 0b1011
|
||||
#define SERVER_ERROR_RESPONSE 0b1111
|
||||
|
||||
// 定义压缩和序列化方法常量
|
||||
#define GZIP 1
|
||||
#define JSON 1
|
||||
#define NO_SERIALIZATION 0
|
||||
|
||||
// 定义响应结构体,用于存储原始响应数据
|
||||
typedef struct {
|
||||
uint8_t protocol_version;
|
||||
uint8_t header_size;
|
||||
uint8_t message_type;
|
||||
uint8_t message_type_specific_flags;
|
||||
uint8_t serialization_method;
|
||||
uint8_t message_compression;
|
||||
uint8_t reserved;
|
||||
uint8_t* header_extensions;
|
||||
uint8_t* payload;
|
||||
size_t payload_size;
|
||||
} Response;
|
||||
|
||||
// 定义解析后的响应结构体,用于存储处理后的数据
|
||||
typedef struct {
|
||||
unsigned char* payload_msg;
|
||||
size_t payload_size;
|
||||
} ParsedResponse;
|
||||
|
||||
typedef struct {
|
||||
char* appid;
|
||||
char* cluster;
|
||||
char* token;
|
||||
char* uid;
|
||||
int nbest;
|
||||
char* workflow;
|
||||
int show_language;
|
||||
int show_utterances;
|
||||
char* result_type;
|
||||
char* format;
|
||||
int rate;
|
||||
char* language;
|
||||
int bits;
|
||||
int channel;
|
||||
char* codec;
|
||||
} AsrWsClient;
|
||||
|
||||
|
||||
#define STT_DEFAULT_VERSION 0b0001
|
||||
#define STT_DEFAULT_MESSAGE_TYPE 0b0001
|
||||
#define STT_DEFAULT_MESSAGE_TYPE_SPECIFIC_FLAGS 0b0000
|
||||
#define STT_DEFAULT_SERIAL_METHOD 0b0001
|
||||
#define STT_DEFAULT_COMPRESSION_TYPE 0b0000
|
||||
#define STT_DEFAULT_RESERVED_DATA 0
|
||||
#define STT_DEFAULT_EXTENSION_HEADER NULL
|
||||
#define STT_DEFAULT_EXTENSION_HEADER_LENGTH 0
|
||||
|
||||
#define DEFAULT_HEADR STT_DEFAULT_VERSION,STT_DEFAULT_MESSAGE_TYPE,STT_DEFAULT_MESSAGE_TYPE_SPECIFIC_FLAGS,\
|
||||
STT_DEFAULT_SERIAL_METHOD,STT_DEFAULT_COMPRESSION_TYPE,STT_DEFAULT_RESERVED_DATA,\
|
||||
STT_DEFAULT_EXTENSION_HEADER,STT_DEFAULT_EXTENSION_HEADER_LENGTH
|
||||
|
||||
#define OBJ_URL "wss://openspeech.bytedance.com/api/v2/asr"
|
||||
|
||||
extern OS_SEM voice_msg_sem;
|
||||
|
||||
extern OS_SEM voice_start_sem;
|
||||
|
||||
OS_SEM chat_sem;
|
||||
|
||||
unsigned char chat_msg[4 * 1024];
|
||||
|
||||
extern struct record_message record_msg;
|
||||
|
||||
static void websockets_callback(u8 *buf, u32 len, u8 type)
|
||||
{
|
||||
if(buf!=NULL&&type!=0x89){
|
||||
|
||||
ParsedResponse *req=NULL;
|
||||
|
||||
printf(" . len: %d, . type: %x \r\n",len,type);
|
||||
|
||||
extern ParsedResponse* parse_response(const uint8_t* res, size_t res_size);
|
||||
|
||||
req=parse_response(buf, len);
|
||||
|
||||
printf(" . msg: %s\r\n",req->payload_msg);
|
||||
|
||||
cJSON* json = cJSON_Parse(req->payload_msg);
|
||||
|
||||
if(json!=NULL){
|
||||
cJSON *sequence = cJSON_GetObjectItem(json, "sequence");
|
||||
if(cJSON_IsNumber(sequence)){
|
||||
if(sequence->valueint<0){
|
||||
cJSON *results = cJSON_GetObjectItem(json, "result");
|
||||
if (cJSON_IsArray(results)) {
|
||||
int array_size = cJSON_GetArraySize(results);
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON *result = cJSON_GetArrayItem(results, i);
|
||||
cJSON *text = cJSON_GetObjectItem(result,"text");
|
||||
if (cJSON_IsString(text) && (text->valuestring != NULL)) {
|
||||
printf("text: %s\r\n", (unsigned char*)text->valuestring);
|
||||
sprintf(chat_msg,"%s",text->valuestring);
|
||||
os_sem_post(&chat_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Websocket Client api
|
||||
*******************************************************************************/
|
||||
static void websockets_client_reg(struct websocket_struct *websockets_info, char mode)
|
||||
{
|
||||
memset(websockets_info, 0, sizeof(struct websocket_struct));
|
||||
websockets_info->_init = websockets_client_socket_init;
|
||||
websockets_info->_exit = websockets_client_socket_exit;
|
||||
websockets_info->_handshack = webcockets_client_socket_handshack;
|
||||
websockets_info->_send = websockets_client_socket_send;
|
||||
websockets_info->_recv_thread = websockets_client_socket_recv_thread;
|
||||
websockets_info->_heart_thread = websockets_client_socket_heart_thread;
|
||||
websockets_info->_recv_cb = websockets_callback;
|
||||
websockets_info->_recv = NULL;
|
||||
|
||||
websockets_info->websocket_mode = mode;
|
||||
|
||||
#if 0
|
||||
//应用层指定申请接收内存大小(申请即可,不用释放,内部会释放),默认不使用
|
||||
websockets_info->recv_buf_size = 30 * 1024;
|
||||
websockets_info->recv_buf = malloc(websockets_info->recv_buf_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int websockets_client_init(struct websocket_struct *websockets_info, u8 *url, const char *origin_str, const char *user_agent_str)
|
||||
{
|
||||
websockets_info->ip_or_url = url;
|
||||
websockets_info->origin_str = origin_str;
|
||||
websockets_info->user_agent_str = user_agent_str;
|
||||
websockets_info->recv_time_out = 5000;
|
||||
|
||||
//应用层和库的版本检测,结构体不一样则返回出错
|
||||
int err = websockets_struct_check(sizeof(struct websocket_struct));
|
||||
if (err == FALSE) {
|
||||
return err;
|
||||
}
|
||||
return websockets_info->_init(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_handshack(struct websocket_struct *websockets_info)
|
||||
{
|
||||
return websockets_info->_handshack(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_send(struct websocket_struct *websockets_info, u8 *buf, int len, char type)
|
||||
{
|
||||
//SSL加密时一次发送数据不能超过16K,用户需要自己分包
|
||||
return websockets_info->_send(websockets_info, buf, len, type);
|
||||
}
|
||||
|
||||
static void websockets_client_exit(struct websocket_struct *websockets_info)
|
||||
{
|
||||
websockets_info->_exit(websockets_info);
|
||||
}
|
||||
|
||||
static uint8_t* generate_header(
|
||||
uint8_t version,
|
||||
uint8_t message_type,
|
||||
uint8_t message_type_specific_flags,
|
||||
uint8_t serial_method,
|
||||
uint8_t compression_type,
|
||||
uint8_t reserved_data,
|
||||
const uint8_t* extension_header,
|
||||
size_t extension_header_length
|
||||
) {
|
||||
size_t header_size = (extension_header_length / 4) + 1;
|
||||
size_t total_size = header_size * 4;
|
||||
|
||||
uint8_t* header = (uint8_t*)malloc(total_size);
|
||||
if (header == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 设置基本头部(4字节)
|
||||
header[0] = (version << 4) | (header_size & 0x0F);
|
||||
printf(" .header[0]: %x \r\n",header[0]);
|
||||
header[1] = (message_type << 4) | (message_type_specific_flags & 0x0F);
|
||||
printf(" .header[1]: %x \r\n",header[1]);
|
||||
header[2] = (serial_method << 4) | (compression_type & 0x0F);
|
||||
printf(" .header[2]: %x \r\n",header[2]);
|
||||
header[3] = reserved_data;
|
||||
printf(" .header[3]: %x \r\n",header[3]);
|
||||
|
||||
// 复制扩展头部(如果有的话)
|
||||
if (extension_header_length > 0 && extension_header != NULL) {
|
||||
memcpy(header + 4, extension_header, extension_header_length);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
static char* construct_request(AsrWsClient* client, const char* reqid) {
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// App object
|
||||
cJSON* app = cJSON_AddObjectToObject(root, "app");
|
||||
if (app == NULL ||
|
||||
!cJSON_AddStringToObject(app, "appid", client->appid) ||
|
||||
!cJSON_AddStringToObject(app, "cluster", client->cluster) ||
|
||||
!cJSON_AddStringToObject(app, "token", client->token)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// User object
|
||||
cJSON* user = cJSON_AddObjectToObject(root, "user");
|
||||
if (user == NULL ||
|
||||
!cJSON_AddStringToObject(user, "uid", client->uid)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Audio object
|
||||
cJSON* audio = cJSON_AddObjectToObject(root, "audio");
|
||||
if (audio == NULL ||
|
||||
!cJSON_AddStringToObject(audio, "format", client->format)||
|
||||
!cJSON_AddNumberToObject(audio, "rate", client->rate) ||
|
||||
!cJSON_AddStringToObject(audio, "language", client->language) ||
|
||||
!cJSON_AddNumberToObject(audio, "bits", client->bits) ||
|
||||
!cJSON_AddNumberToObject(audio, "channel", client->channel) ||
|
||||
!cJSON_AddStringToObject(audio, "codec", client->codec)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
// Request object
|
||||
cJSON* request = cJSON_AddObjectToObject(root, "request");
|
||||
if (request == NULL ||
|
||||
!cJSON_AddStringToObject(request, "reqid", reqid) ||
|
||||
!cJSON_AddNumberToObject(request, "nbest", client->nbest) ||
|
||||
!cJSON_AddStringToObject(request, "workflow", client->workflow) ||
|
||||
!cJSON_AddBoolToObject(request, "show_utterances", client->show_utterances) ||
|
||||
!cJSON_AddStringToObject(request, "result_type", client->result_type) ||
|
||||
!cJSON_AddNumberToObject(request, "sequence", 1)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
char* json_string = cJSON_Print(root);
|
||||
cJSON_Delete(root);
|
||||
return json_string;
|
||||
|
||||
fail:
|
||||
if (root != NULL) {
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ParsedResponse* parse_response(const uint8_t* res, size_t res_size) {
|
||||
// 检查响应大小是否足够
|
||||
if (res_size < 4) return NULL;
|
||||
|
||||
// 解析响应头部
|
||||
Response response = {
|
||||
.protocol_version = res[0] >> 4,
|
||||
.header_size = res[0] & 0x0f,
|
||||
.message_type = res[1] >> 4,
|
||||
.message_type_specific_flags = res[1] & 0x0f,
|
||||
.serialization_method = res[2] >> 4,
|
||||
.message_compression = res[2] & 0x0f,
|
||||
.reserved = res[3]
|
||||
};
|
||||
|
||||
// 提取头部扩展
|
||||
size_t header_extensions_size = 4 * (response.header_size - 1);
|
||||
response.header_extensions = (uint8_t*)malloc(header_extensions_size);
|
||||
memcpy(response.header_extensions, res + 4, header_extensions_size);
|
||||
|
||||
// 设置payload指针和大小
|
||||
response.payload = (uint8_t*)res + response.header_size * 4;
|
||||
response.payload_size = res_size - response.header_size * 4;
|
||||
|
||||
// 分配ParsedResponse结构体内存
|
||||
ParsedResponse* result = (ParsedResponse*)malloc(sizeof(ParsedResponse));
|
||||
memset(result, 0, sizeof(ParsedResponse));
|
||||
|
||||
// 根据消息类型处理payload
|
||||
switch (response.message_type) {
|
||||
case SERVER_FULL_RESPONSE:
|
||||
// 解析payload大小(前4字节)
|
||||
result->payload_size = ntohl(*(int32_t*)response.payload);
|
||||
response.payload += 4;
|
||||
response.payload_size -= 4;
|
||||
break;
|
||||
case SERVER_ACK:
|
||||
if (response.payload_size >= 8) {
|
||||
// 如果有额外的payload,解析其大小(接下来的4字节)
|
||||
result->payload_size = ntohl(*(uint32_t*)(response.payload + 4));
|
||||
response.payload += 8;
|
||||
response.payload_size -= 8;
|
||||
}
|
||||
break;
|
||||
case SERVER_ERROR_RESPONSE:
|
||||
result->payload_size = ntohl(*(uint32_t*)(response.payload + 4));
|
||||
response.payload += 8;
|
||||
response.payload_size -= 8;
|
||||
break;
|
||||
}
|
||||
|
||||
// 处理payload(如果存在)
|
||||
if (response.payload_size > 0) {
|
||||
|
||||
// 根据序列化方法处理payload
|
||||
if (response.serialization_method == JSON) {
|
||||
// 解析JSON
|
||||
cJSON* json = cJSON_Parse((char*)response.payload);
|
||||
if (json) {
|
||||
|
||||
result->payload_msg = cJSON_Print(json);
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
} else if (response.serialization_method != NO_SERIALIZATION) {
|
||||
// 如果不是JSON也不是NO_SERIALIZATION,则直接复制字符串
|
||||
result->payload_msg = strdup((char*)response.payload);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理临时分配的内存
|
||||
free(response.header_extensions);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void generate_uuid(char *uuid_out)
|
||||
{
|
||||
const char *c = "89ab";
|
||||
char *p = uuid_out;
|
||||
for (int n = 0; n < 16; ++n) {
|
||||
int b = rand() % 255;
|
||||
switch (n) {
|
||||
case 6:
|
||||
sprintf(p, "4%x", b % 15);
|
||||
break;
|
||||
case 8:
|
||||
sprintf(p, "%c%x", c[rand() % strlen(c)], b % 15);
|
||||
break;
|
||||
default:
|
||||
sprintf(p, "%02x", b);
|
||||
break;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
|
||||
switch (n) {
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
*p++='-';
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Websocket Client.c
|
||||
*******************************************************************************/
|
||||
static void websockets_stt_client_main_thread(void *priv)
|
||||
{
|
||||
int err;
|
||||
char mode = WEBSOCKET_MODE;
|
||||
u8 url[256] = OBJ_URL;
|
||||
const char *origin_str = "wss://openspeech.bytedance.com/api/v2/asr";
|
||||
const char *user_agent_str = "Authorization: Bearer; BNA-XBIrEisvMczcmc-Sm4B3H-PmfqlL";
|
||||
char *websocket_header = NULL;
|
||||
char *payload = NULL;
|
||||
uint32_t payloadsize;
|
||||
size_t total_length;
|
||||
size_t header_length;
|
||||
size_t payload_length;
|
||||
char *full_client_request=NULL;
|
||||
u8 uuid[16] = {0};
|
||||
|
||||
char *aduio_client_request=NULL;
|
||||
|
||||
AsrWsClient STTPayload={
|
||||
.appid="8571950639",
|
||||
.cluster="volcengine_streaming_common",
|
||||
.token="BNA-XBIrEisvMczcmc-Sm4B3H-PmfqlL",
|
||||
.uid="388808088185088",
|
||||
.nbest=1,
|
||||
.workflow="audio_in,resample,partition,vad,fe,decode,itn,nlu_punctuate",
|
||||
.show_language=false,
|
||||
.show_utterances=false,
|
||||
.result_type="full",
|
||||
.format="mp3",
|
||||
.rate=16000,
|
||||
.language="zh-CN",
|
||||
.bits=16,
|
||||
.channel=1,
|
||||
.codec="raw",
|
||||
|
||||
};
|
||||
|
||||
os_sem_create(&chat_sem,0);
|
||||
|
||||
printf(" . ----------------- stt Client Websocket ------------------\r\n");
|
||||
|
||||
/* 0 . malloc buffer */
|
||||
struct websocket_struct *websockets_info = malloc(sizeof(struct websocket_struct));
|
||||
if (!websockets_info) {
|
||||
return;
|
||||
}
|
||||
/* 1 . register */
|
||||
websockets_client_reg(websockets_info, mode);
|
||||
|
||||
/* 2 . init */
|
||||
err = websockets_client_init(websockets_info, url, origin_str, user_agent_str);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! Cilent websocket init error !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
/* 3 . hanshack */
|
||||
err = websockets_client_handshack(websockets_info);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! Handshake error !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
printf(" . Handshake success \r\n");
|
||||
|
||||
/* 4 . CreateThread */
|
||||
thread_fork("websocket_client_heart", 19, 512, 0,
|
||||
&websockets_info->ping_thread_id,
|
||||
websockets_info->_heart_thread,
|
||||
websockets_info);
|
||||
thread_fork("websocket_client_recv", 18, 512, 0,
|
||||
&websockets_info->recv_thread_id,
|
||||
websockets_info->_recv_thread,
|
||||
websockets_info);
|
||||
|
||||
websockets_sleep(1000);
|
||||
|
||||
/* 5 . recv or send data */
|
||||
while (1) {
|
||||
//接收信号量0
|
||||
os_sem_pend(&voice_start_sem, 0); //获取信号量
|
||||
|
||||
websocket_header=generate_header(DEFAULT_HEADR);
|
||||
|
||||
header_length=4+STT_DEFAULT_EXTENSION_HEADER_LENGTH;
|
||||
|
||||
generate_uuid(uuid);
|
||||
|
||||
payload=construct_request(&STTPayload, uuid);
|
||||
|
||||
printf(" . Payload :%s \r\n",payload);
|
||||
|
||||
payload_length=strlen(payload);
|
||||
|
||||
payloadsize=htonl(payload_length);
|
||||
|
||||
total_length=header_length+4+payload_length;
|
||||
|
||||
full_client_request=(char *)malloc(total_length+1);
|
||||
if (full_client_request == NULL) {
|
||||
printf(" . ! malloc err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
memcpy(full_client_request, websocket_header, header_length);
|
||||
|
||||
memcpy(full_client_request + header_length, &payloadsize, 4);
|
||||
|
||||
memcpy(full_client_request + 4 + header_length,payload,payload_length+1);
|
||||
|
||||
err = websockets_client_send(websockets_info, (u8 *)full_client_request, total_length, WCT_TXTDATA);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! send err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
free(full_client_request);
|
||||
|
||||
//websockets_sleep(1000);
|
||||
|
||||
while(1){
|
||||
os_sem_pend(&voice_msg_sem, 0);
|
||||
|
||||
if(record_msg.end){
|
||||
websocket_header=generate_header(STT_DEFAULT_VERSION,0b0010,0b0010,0b0000,\
|
||||
STT_DEFAULT_COMPRESSION_TYPE,STT_DEFAULT_RESERVED_DATA,\
|
||||
STT_DEFAULT_EXTENSION_HEADER,STT_DEFAULT_EXTENSION_HEADER_LENGTH);
|
||||
}else{
|
||||
websocket_header=generate_header(STT_DEFAULT_VERSION,0b0010,0b0000,0b0000,\
|
||||
STT_DEFAULT_COMPRESSION_TYPE,STT_DEFAULT_RESERVED_DATA,\
|
||||
STT_DEFAULT_EXTENSION_HEADER,STT_DEFAULT_EXTENSION_HEADER_LENGTH);
|
||||
}
|
||||
|
||||
payloadsize=htonl(record_msg.msg_size);
|
||||
|
||||
total_length=header_length+4+record_msg.msg_size;
|
||||
|
||||
aduio_client_request=(char *)malloc(total_length+1);
|
||||
|
||||
if (aduio_client_request == NULL) {
|
||||
printf(" . ! malloc err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
memcpy(aduio_client_request, websocket_header, header_length);
|
||||
|
||||
memcpy(aduio_client_request + header_length, &payloadsize, 4);
|
||||
|
||||
memcpy(aduio_client_request + 4 + header_length,record_msg.msg,record_msg.msg_size+1);
|
||||
|
||||
err = websockets_client_send(websockets_info, (u8 *)aduio_client_request, total_length, WCT_TXTDATA);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! send err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
free(aduio_client_request);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exit_ws:
|
||||
/* 6 . exit */
|
||||
if (websockets_info->ping_thread_id) {
|
||||
thread_kill(&websockets_info->ping_thread_id, KILL_REQ);
|
||||
}
|
||||
if (websockets_info->recv_thread_id) {
|
||||
thread_kill(&websockets_info->recv_thread_id, KILL_REQ);
|
||||
}
|
||||
|
||||
websockets_client_exit(websockets_info);
|
||||
free(websockets_info);
|
||||
}
|
||||
|
||||
static void websocket_client_thread_create(void)
|
||||
{
|
||||
thread_fork("websockets_stt_client_main_thread", 15, 1024 * 250, 0, 0, websockets_stt_client_main_thread, NULL);
|
||||
}
|
||||
|
||||
static void http_websocket_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(500);
|
||||
}
|
||||
|
||||
websocket_client_thread_create();
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
static int stt_websocket_init(void)
|
||||
{
|
||||
if (thread_fork("http_websocket_start", 10, 512, 0, NULL, http_websocket_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(stt_websocket_init);
|
||||
#endif // stt
|
||||
|
|
@ -1,664 +0,0 @@
|
|||
#include "web_socket/websocket_api.h"
|
||||
#include "wifi/wifi_connect.h"
|
||||
#include "system/includes.h"
|
||||
#include "fs/fs.h"
|
||||
#include "asm/sfc_norflash_api.h"
|
||||
#include "cJSON.h"
|
||||
#include "aduio_record.h"
|
||||
#ifdef WEBSOCKET_MEMHEAP_DEBUG
|
||||
#include "mem_leak_test.h"
|
||||
#endif
|
||||
|
||||
#include "app_config.h"
|
||||
|
||||
//#define stt_local
|
||||
#ifdef stt_local
|
||||
|
||||
// 定义消息类型常量
|
||||
#define SERVER_FULL_RESPONSE 0b1001
|
||||
#define SERVER_ACK 0b1011
|
||||
#define SERVER_ERROR_RESPONSE 0b1111
|
||||
|
||||
// 定义压缩和序列化方法常量
|
||||
#define GZIP 1
|
||||
#define JSON 1
|
||||
#define NO_SERIALIZATION 0
|
||||
|
||||
// 定义响应结构体,用于存储原始响应数据
|
||||
typedef struct {
|
||||
uint8_t protocol_version;
|
||||
uint8_t header_size;
|
||||
uint8_t message_type;
|
||||
uint8_t message_type_specific_flags;
|
||||
uint8_t serialization_method;
|
||||
uint8_t message_compression;
|
||||
uint8_t reserved;
|
||||
uint8_t* header_extensions;
|
||||
uint8_t* payload;
|
||||
size_t payload_size;
|
||||
} Response;
|
||||
|
||||
// 定义解析后的响应结构体,用于存储处理后的数据
|
||||
typedef struct {
|
||||
unsigned char* payload_msg;
|
||||
size_t payload_size;
|
||||
} ParsedResponse;
|
||||
|
||||
typedef struct {
|
||||
char* appid;
|
||||
char* cluster;
|
||||
char* token;
|
||||
char* uid;
|
||||
int nbest;
|
||||
char* workflow;
|
||||
int show_language;
|
||||
int show_utterances;
|
||||
char* result_type;
|
||||
char* format;
|
||||
int rate;
|
||||
char* language;
|
||||
int bits;
|
||||
int channel;
|
||||
char* codec;
|
||||
} AsrWsClient;
|
||||
|
||||
typedef struct {
|
||||
unsigned char* data;
|
||||
size_t size;
|
||||
bool is_last;
|
||||
} DataSegment;
|
||||
|
||||
typedef struct {
|
||||
unsigned char* data;
|
||||
size_t data_len;
|
||||
size_t chunk_size;
|
||||
size_t current_offset;
|
||||
} DataSlicer;
|
||||
|
||||
|
||||
#define STT_DEFAULT_VERSION 0b0001
|
||||
#define STT_DEFAULT_MESSAGE_TYPE 0b0001
|
||||
#define STT_DEFAULT_MESSAGE_TYPE_SPECIFIC_FLAGS 0b0000
|
||||
#define STT_DEFAULT_SERIAL_METHOD 0b0001
|
||||
#define STT_DEFAULT_COMPRESSION_TYPE 0b0000
|
||||
#define STT_DEFAULT_RESERVED_DATA 0
|
||||
#define STT_DEFAULT_EXTENSION_HEADER NULL
|
||||
#define STT_DEFAULT_EXTENSION_HEADER_LENGTH 0
|
||||
|
||||
#define DEFAULT_HEADR STT_DEFAULT_VERSION,STT_DEFAULT_MESSAGE_TYPE,STT_DEFAULT_MESSAGE_TYPE_SPECIFIC_FLAGS,\
|
||||
STT_DEFAULT_SERIAL_METHOD,STT_DEFAULT_COMPRESSION_TYPE,STT_DEFAULT_RESERVED_DATA,\
|
||||
STT_DEFAULT_EXTENSION_HEADER,STT_DEFAULT_EXTENSION_HEADER_LENGTH
|
||||
|
||||
#define OBJ_URL "wss://openspeech.bytedance.com/api/v2/asr"
|
||||
|
||||
static OS_SEM x_sem;
|
||||
|
||||
OS_SEM chat_sem;
|
||||
|
||||
unsigned char chat_msg[4 * 1024];
|
||||
|
||||
static void websockets_callback(u8 *buf, u32 len, u8 type)
|
||||
{
|
||||
if(buf!=NULL&&type!=0x89){
|
||||
|
||||
ParsedResponse *req=NULL;
|
||||
|
||||
printf(" . len: %d, . type: %x \r\n",len,type);
|
||||
|
||||
extern ParsedResponse* parse_response(const uint8_t* res, size_t res_size);
|
||||
|
||||
req=parse_response(buf, len);
|
||||
|
||||
printf(" . msg: %s\r\n",req->payload_msg);
|
||||
|
||||
cJSON* json = cJSON_Parse(req->payload_msg);
|
||||
|
||||
free(req);
|
||||
|
||||
if(json!=NULL){
|
||||
cJSON *sequence = cJSON_GetObjectItem(json, "sequence");
|
||||
if(cJSON_IsNumber(sequence)){
|
||||
if(sequence->valueint<0){
|
||||
cJSON *results = cJSON_GetObjectItem(json, "result");
|
||||
if (cJSON_IsArray(results)) {
|
||||
int array_size = cJSON_GetArraySize(results);
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON *result = cJSON_GetArrayItem(results, i);
|
||||
cJSON *text = cJSON_GetObjectItem(result,"text");
|
||||
if (cJSON_IsString(text) && (text->valuestring != NULL)) {
|
||||
printf("text: %s\r\n", (unsigned char*)text->valuestring);
|
||||
sprintf(chat_msg,"%s",text->valuestring);
|
||||
os_sem_post(&chat_sem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Websocket Client api
|
||||
*******************************************************************************/
|
||||
static void websockets_client_reg(struct websocket_struct *websockets_info, char mode)
|
||||
{
|
||||
memset(websockets_info, 0, sizeof(struct websocket_struct));
|
||||
websockets_info->_init = websockets_client_socket_init;
|
||||
websockets_info->_exit = websockets_client_socket_exit;
|
||||
websockets_info->_handshack = webcockets_client_socket_handshack;
|
||||
websockets_info->_send = websockets_client_socket_send;
|
||||
websockets_info->_recv_thread = websockets_client_socket_recv_thread;
|
||||
websockets_info->_heart_thread = websockets_client_socket_heart_thread;
|
||||
websockets_info->_recv_cb = websockets_callback;
|
||||
websockets_info->_recv = NULL;
|
||||
|
||||
websockets_info->websocket_mode = mode;
|
||||
|
||||
#if 0
|
||||
//应用层指定申请接收内存大小(申请即可,不用释放,内部会释放),默认不使用
|
||||
websockets_info->recv_buf_size = 30 * 1024;
|
||||
websockets_info->recv_buf = malloc(websockets_info->recv_buf_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int websockets_client_init(struct websocket_struct *websockets_info, u8 *url, const char *origin_str, const char *user_agent_str)
|
||||
{
|
||||
websockets_info->ip_or_url = url;
|
||||
websockets_info->origin_str = origin_str;
|
||||
websockets_info->user_agent_str = user_agent_str;
|
||||
websockets_info->recv_time_out = 5000;
|
||||
|
||||
//应用层和库的版本检测,结构体不一样则返回出错
|
||||
int err = websockets_struct_check(sizeof(struct websocket_struct));
|
||||
if (err == FALSE) {
|
||||
return err;
|
||||
}
|
||||
return websockets_info->_init(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_handshack(struct websocket_struct *websockets_info)
|
||||
{
|
||||
return websockets_info->_handshack(websockets_info);
|
||||
}
|
||||
|
||||
static int websockets_client_send(struct websocket_struct *websockets_info, u8 *buf, int len, char type)
|
||||
{
|
||||
//SSL加密时一次发送数据不能超过16K,用户需要自己分包
|
||||
return websockets_info->_send(websockets_info, buf, len, type);
|
||||
}
|
||||
|
||||
static void websockets_client_exit(struct websocket_struct *websockets_info)
|
||||
{
|
||||
websockets_info->_exit(websockets_info);
|
||||
}
|
||||
|
||||
static uint8_t* generate_header(
|
||||
uint8_t version,
|
||||
uint8_t message_type,
|
||||
uint8_t message_type_specific_flags,
|
||||
uint8_t serial_method,
|
||||
uint8_t compression_type,
|
||||
uint8_t reserved_data,
|
||||
const uint8_t* extension_header,
|
||||
size_t extension_header_length
|
||||
) {
|
||||
size_t header_size = (extension_header_length / 4) + 1;
|
||||
size_t total_size = header_size * 4;
|
||||
|
||||
uint8_t* header = (uint8_t*)malloc(total_size);
|
||||
if (header == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 设置基本头部(4字节)
|
||||
header[0] = (version << 4) | (header_size & 0x0F);
|
||||
printf(" .header[0]: %x \r\n",header[0]);
|
||||
header[1] = (message_type << 4) | (message_type_specific_flags & 0x0F);
|
||||
printf(" .header[1]: %x \r\n",header[1]);
|
||||
header[2] = (serial_method << 4) | (compression_type & 0x0F);
|
||||
printf(" .header[2]: %x \r\n",header[2]);
|
||||
header[3] = reserved_data;
|
||||
printf(" .header[3]: %x \r\n",header[3]);
|
||||
|
||||
// 复制扩展头部(如果有的话)
|
||||
if (extension_header_length > 0 && extension_header != NULL) {
|
||||
memcpy(header + 4, extension_header, extension_header_length);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
static char* construct_request(AsrWsClient* client, const char* reqid) {
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (root == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// App object
|
||||
cJSON* app = cJSON_AddObjectToObject(root, "app");
|
||||
if (app == NULL ||
|
||||
!cJSON_AddStringToObject(app, "appid", client->appid) ||
|
||||
!cJSON_AddStringToObject(app, "cluster", client->cluster) ||
|
||||
!cJSON_AddStringToObject(app, "token", client->token)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// User object
|
||||
cJSON* user = cJSON_AddObjectToObject(root, "user");
|
||||
if (user == NULL ||
|
||||
!cJSON_AddStringToObject(user, "uid", client->uid)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Audio object
|
||||
cJSON* audio = cJSON_AddObjectToObject(root, "audio");
|
||||
if (audio == NULL ||
|
||||
!cJSON_AddStringToObject(audio, "format", client->format)||
|
||||
!cJSON_AddNumberToObject(audio, "rate", client->rate) ||
|
||||
!cJSON_AddStringToObject(audio, "language", client->language) ||
|
||||
!cJSON_AddNumberToObject(audio, "bits", client->bits) ||
|
||||
!cJSON_AddNumberToObject(audio, "channel", client->channel) ||
|
||||
!cJSON_AddStringToObject(audio, "codec", client->codec)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
// Request object
|
||||
cJSON* request = cJSON_AddObjectToObject(root, "request");
|
||||
if (request == NULL ||
|
||||
!cJSON_AddStringToObject(request, "reqid", reqid) ||
|
||||
!cJSON_AddNumberToObject(request, "nbest", client->nbest) ||
|
||||
!cJSON_AddStringToObject(request, "workflow", client->workflow) ||
|
||||
!cJSON_AddBoolToObject(request, "show_utterances", client->show_utterances) ||
|
||||
!cJSON_AddStringToObject(request, "result_type", client->result_type) ||
|
||||
!cJSON_AddNumberToObject(request, "sequence", 1)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
char* json_string = cJSON_Print(root);
|
||||
cJSON_Delete(root);
|
||||
return json_string;
|
||||
|
||||
fail:
|
||||
if (root != NULL) {
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ParsedResponse* parse_response(const uint8_t* res, size_t res_size) {
|
||||
// 检查响应大小是否足够
|
||||
if (res_size < 4) return NULL;
|
||||
|
||||
// 解析响应头部
|
||||
Response response = {
|
||||
.protocol_version = res[0] >> 4,
|
||||
.header_size = res[0] & 0x0f,
|
||||
.message_type = res[1] >> 4,
|
||||
.message_type_specific_flags = res[1] & 0x0f,
|
||||
.serialization_method = res[2] >> 4,
|
||||
.message_compression = res[2] & 0x0f,
|
||||
.reserved = res[3]
|
||||
};
|
||||
|
||||
// 提取头部扩展
|
||||
size_t header_extensions_size = 4 * (response.header_size - 1);
|
||||
response.header_extensions = (uint8_t*)malloc(header_extensions_size);
|
||||
memcpy(response.header_extensions, res + 4, header_extensions_size);
|
||||
|
||||
// 设置payload指针和大小
|
||||
response.payload = (uint8_t*)res + response.header_size * 4;
|
||||
response.payload_size = res_size - response.header_size * 4;
|
||||
|
||||
// 分配ParsedResponse结构体内存
|
||||
ParsedResponse* result = (ParsedResponse*)malloc(sizeof(ParsedResponse));
|
||||
memset(result, 0, sizeof(ParsedResponse));
|
||||
|
||||
// 根据消息类型处理payload
|
||||
switch (response.message_type) {
|
||||
case SERVER_FULL_RESPONSE:
|
||||
// 解析payload大小(前4字节)
|
||||
result->payload_size = ntohl(*(int32_t*)response.payload);
|
||||
response.payload += 4;
|
||||
response.payload_size -= 4;
|
||||
break;
|
||||
case SERVER_ACK:
|
||||
if (response.payload_size >= 8) {
|
||||
// 如果有额外的payload,解析其大小(接下来的4字节)
|
||||
result->payload_size = ntohl(*(uint32_t*)(response.payload + 4));
|
||||
response.payload += 8;
|
||||
response.payload_size -= 8;
|
||||
}
|
||||
break;
|
||||
case SERVER_ERROR_RESPONSE:
|
||||
result->payload_size = ntohl(*(uint32_t*)(response.payload + 4));
|
||||
response.payload += 8;
|
||||
response.payload_size -= 8;
|
||||
break;
|
||||
}
|
||||
|
||||
// 处理payload(如果存在)
|
||||
if (response.payload_size > 0) {
|
||||
|
||||
// 根据序列化方法处理payload
|
||||
if (response.serialization_method == JSON) {
|
||||
// 解析JSON
|
||||
cJSON* json = cJSON_Parse((char*)response.payload);
|
||||
if (json) {
|
||||
|
||||
result->payload_msg = cJSON_Print(json);
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
} else if (response.serialization_method != NO_SERIALIZATION) {
|
||||
// 如果不是JSON也不是NO_SERIALIZATION,则直接复制字符串
|
||||
result->payload_msg = strdup((char*)response.payload);
|
||||
}
|
||||
}
|
||||
|
||||
// 清理临时分配的内存
|
||||
free(response.header_extensions);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void generate_uuid(char *uuid_out)
|
||||
{
|
||||
const char *c = "89ab";
|
||||
char *p = uuid_out;
|
||||
for (int n = 0; n < 16; ++n) {
|
||||
int b = rand() % 255;
|
||||
switch (n) {
|
||||
case 6:
|
||||
sprintf(p, "4%x", b % 15);
|
||||
break;
|
||||
case 8:
|
||||
sprintf(p, "%c%x", c[rand() % strlen(c)], b % 15);
|
||||
break;
|
||||
default:
|
||||
sprintf(p, "%02x", b);
|
||||
break;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
|
||||
switch (n) {
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
case 9:
|
||||
*p++='-';
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
DataSlicer* create_data_slicer(unsigned char* data, size_t data_len, size_t chunk_size) {
|
||||
DataSlicer* slicer = (DataSlicer*)malloc(sizeof(DataSlicer));
|
||||
slicer->data = data;
|
||||
slicer->data_len = data_len;
|
||||
slicer->chunk_size = chunk_size;
|
||||
slicer->current_offset = 0;
|
||||
return slicer;
|
||||
}
|
||||
|
||||
DataSegment* slice_data(DataSlicer* slicer) {
|
||||
if (slicer->current_offset >= slicer->data_len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DataSegment* segment = (DataSegment*)malloc(sizeof(DataSegment));
|
||||
size_t remaining = slicer->data_len - slicer->current_offset;
|
||||
size_t segment_size = (remaining < slicer->chunk_size) ? remaining : slicer->chunk_size;
|
||||
|
||||
segment->data = (unsigned char*)malloc(segment_size);
|
||||
memcpy(segment->data, slicer->data + slicer->current_offset, segment_size);
|
||||
segment->size = segment_size;
|
||||
segment->is_last = (slicer->current_offset + segment_size >= slicer->data_len);
|
||||
|
||||
slicer->current_offset += segment_size;
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Websocket Client.c
|
||||
*******************************************************************************/
|
||||
static void websockets_stt_client_main_thread(void *priv)
|
||||
{
|
||||
int err;
|
||||
char mode = WEBSOCKET_MODE;
|
||||
u8 url[256] = OBJ_URL;
|
||||
const char *origin_str = "wss://openspeech.bytedance.com/api/v2/asr";
|
||||
const char *user_agent_str = "Authorization: Bearer; BNA-XBIrEisvMczcmc-Sm4B3H-PmfqlL";
|
||||
char *websocket_header = NULL;
|
||||
char *payload = NULL;
|
||||
uint32_t payloadsize;
|
||||
size_t total_length;
|
||||
size_t header_length;
|
||||
size_t payload_length;
|
||||
char *full_client_request=NULL;
|
||||
u8 uuid[16] = {0};
|
||||
|
||||
char *aduio_client_request=NULL;
|
||||
|
||||
AsrWsClient STTPayload={
|
||||
.appid="8571950639",
|
||||
.cluster="volcengine_streaming_common",
|
||||
.token="BNA-XBIrEisvMczcmc-Sm4B3H-PmfqlL",
|
||||
.uid="388808088185088",
|
||||
.nbest=1,
|
||||
.workflow="audio_in,resample,partition,vad,fe,decode,itn,nlu_punctuate",
|
||||
.show_language=false,
|
||||
.show_utterances=false,
|
||||
.result_type="full",
|
||||
.format="mp3",
|
||||
.rate=16000,
|
||||
.language="zh-CN",
|
||||
.bits=16,
|
||||
.channel=1,
|
||||
.codec="raw",
|
||||
|
||||
};
|
||||
|
||||
char name[128];
|
||||
char aduio[100*1024]={0};
|
||||
int aduio_len;
|
||||
FILE *f = fopen("mnt/sdfile/res/demo/story.mp3", "r");
|
||||
if (!f) {
|
||||
printf("fopen err!\n");
|
||||
}else{
|
||||
fget_name(f, name, sizeof(name));
|
||||
printf("file_name: %s\n", name);
|
||||
|
||||
aduio_len = fread(aduio, 1, sizeof(aduio), f);
|
||||
if(aduio_len > 0)
|
||||
{
|
||||
printf("aduio_len:%d\r\n",aduio_len);
|
||||
}
|
||||
}
|
||||
|
||||
DataSlicer* slicer = create_data_slicer((unsigned char*)aduio, aduio_len, 10*1024);
|
||||
|
||||
DataSegment* segment;
|
||||
|
||||
os_sem_create(&chat_sem,0);
|
||||
|
||||
printf(" . ----------------- stt Client Websocket ------------------\r\n");
|
||||
|
||||
/* 0 . malloc buffer */
|
||||
struct websocket_struct *websockets_info = malloc(sizeof(struct websocket_struct));
|
||||
if (!websockets_info) {
|
||||
return;
|
||||
}
|
||||
/* 1 . register */
|
||||
websockets_client_reg(websockets_info, mode);
|
||||
|
||||
/* 2 . init */
|
||||
err = websockets_client_init(websockets_info, url, origin_str, user_agent_str);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! Cilent websocket init error !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
/* 3 . hanshack */
|
||||
err = websockets_client_handshack(websockets_info);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! Handshake error !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
printf(" . Handshake success \r\n");
|
||||
|
||||
/* 4 . CreateThread */
|
||||
thread_fork("websocket_client_heart", 19, 512, 0,
|
||||
&websockets_info->ping_thread_id,
|
||||
websockets_info->_heart_thread,
|
||||
websockets_info);
|
||||
thread_fork("websocket_client_recv", 18, 512, 0,
|
||||
&websockets_info->recv_thread_id,
|
||||
websockets_info->_recv_thread,
|
||||
websockets_info);
|
||||
|
||||
websockets_sleep(1000);
|
||||
|
||||
/* 5 . recv or send data */
|
||||
while (1) {
|
||||
//接收信号量0
|
||||
os_sem_pend(&x_sem, 0); //获取信号量
|
||||
|
||||
websocket_header=generate_header(DEFAULT_HEADR);
|
||||
|
||||
header_length=4+STT_DEFAULT_EXTENSION_HEADER_LENGTH;
|
||||
|
||||
generate_uuid(uuid);
|
||||
|
||||
payload=construct_request(&STTPayload, uuid);
|
||||
|
||||
printf(" . Payload :%s \r\n",payload);
|
||||
|
||||
payload_length=strlen(payload);
|
||||
|
||||
payloadsize=htonl(payload_length);
|
||||
|
||||
total_length=header_length+4+payload_length;
|
||||
|
||||
full_client_request=(char *)malloc(total_length+1);
|
||||
if (full_client_request == NULL) {
|
||||
printf(" . ! malloc err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
memcpy(full_client_request, websocket_header, header_length);
|
||||
|
||||
memcpy(full_client_request + header_length, &payloadsize, 4);
|
||||
|
||||
memcpy(full_client_request + 4 + header_length,payload,payload_length+1);
|
||||
|
||||
err = websockets_client_send(websockets_info, (u8 *)full_client_request, total_length, WCT_TXTDATA);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! send err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
free(payload);
|
||||
free(full_client_request);
|
||||
|
||||
websockets_sleep(1000);
|
||||
|
||||
while(1){
|
||||
segment = slice_data(slicer);
|
||||
|
||||
if(segment!=NULL){
|
||||
|
||||
if(segment->is_last){
|
||||
websocket_header=generate_header(STT_DEFAULT_VERSION,0b0010,0b0010,0b0000,\
|
||||
STT_DEFAULT_COMPRESSION_TYPE,STT_DEFAULT_RESERVED_DATA,\
|
||||
STT_DEFAULT_EXTENSION_HEADER,STT_DEFAULT_EXTENSION_HEADER_LENGTH);
|
||||
}else{
|
||||
websocket_header=generate_header(STT_DEFAULT_VERSION,0b0010,0b0000,0b0000,\
|
||||
STT_DEFAULT_COMPRESSION_TYPE,STT_DEFAULT_RESERVED_DATA,\
|
||||
STT_DEFAULT_EXTENSION_HEADER,STT_DEFAULT_EXTENSION_HEADER_LENGTH);
|
||||
}
|
||||
|
||||
payloadsize=htonl(segment->size);
|
||||
|
||||
total_length=header_length+4+segment->size;
|
||||
|
||||
aduio_client_request=(char *)malloc(total_length+1);
|
||||
|
||||
if (aduio_client_request == NULL) {
|
||||
printf(" . ! malloc err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
|
||||
memcpy(aduio_client_request, websocket_header, header_length);
|
||||
|
||||
memcpy(aduio_client_request + header_length, &payloadsize, 4);
|
||||
|
||||
memcpy(aduio_client_request + 4 + header_length,segment->data,segment->size+1);
|
||||
|
||||
err = websockets_client_send(websockets_info, (u8 *)aduio_client_request, total_length, WCT_TXTDATA);
|
||||
if (FALSE == err) {
|
||||
printf(" . ! send err !!!\r\n");
|
||||
goto exit_ws;
|
||||
}
|
||||
free(aduio_client_request);
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
free(segment);
|
||||
websockets_sleep(1000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exit_ws:
|
||||
/* 6 . exit */
|
||||
if (websockets_info->ping_thread_id) {
|
||||
thread_kill(&websockets_info->ping_thread_id, KILL_REQ);
|
||||
}
|
||||
if (websockets_info->recv_thread_id) {
|
||||
thread_kill(&websockets_info->recv_thread_id, KILL_REQ);
|
||||
}
|
||||
|
||||
websockets_client_exit(websockets_info);
|
||||
|
||||
free(slicer);
|
||||
free(websockets_info);
|
||||
}
|
||||
|
||||
static void websocket_client_thread_create(void)
|
||||
{
|
||||
thread_fork("websockets_stt_client_main_thread", 15, 1024 * 250, 0, 0, websockets_stt_client_main_thread, NULL);
|
||||
}
|
||||
|
||||
static void http_websocket_start(void *priv)
|
||||
{
|
||||
enum wifi_sta_connect_state state;
|
||||
os_sem_create(&x_sem,1);
|
||||
while (1) {
|
||||
printf("Connecting to the network...\n");
|
||||
state = wifi_get_sta_connect_state();
|
||||
if (WIFI_STA_NETWORK_STACK_DHCP_SUCC == state) {
|
||||
printf("Network connection is successful!\n");
|
||||
break;
|
||||
}
|
||||
os_time_dly(500);
|
||||
}
|
||||
|
||||
websocket_client_thread_create();
|
||||
}
|
||||
|
||||
//应用程序入口,需要运行在STA模式下
|
||||
static int stt_websocket_init(void)
|
||||
{
|
||||
if (thread_fork("http_websocket_start", 10, 512, 0, NULL, http_websocket_start, NULL) != OS_NO_ERR) {
|
||||
printf("thread fork fail\n");
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(stt_websocket_init);
|
||||
#endif // stt
|
||||
Loading…
Reference in New Issue