167 lines
11 KiB
Plaintext
167 lines
11 KiB
Plaintext
/* Synthetic-only SDK/RTOS boundaries. No device, network or captured speech. */
|
|||
|
|
#include <assert.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdatomic.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdarg.h>
|
||
|
|
static void host_log(const char *fmt, ...) {(void)fmt;}
|
||
|
|
#define LOGI(...) host_log(__VA_ARGS__)
|
||
|
|
#define ESP_LOGI(tag,...) host_log(__VA_ARGS__)
|
||
|
|
#define pdFALSE 0
|
||
|
|
#define eInvalid 0
|
||
|
|
#define eDeleted 1
|
||
|
|
#define ESP_PLATFORM 1
|
||
|
|
typedef unsigned UBaseType_t;
|
||
|
|
typedef struct {unsigned number, priority, base; int core;} host_task_t;
|
||
|
|
typedef host_task_t *TaskHandle_t;
|
||
|
|
typedef struct {unsigned xTaskNumber,uxBasePriority;int eCurrentState;} TaskStatus_t;
|
||
|
|
static host_task_t rtc_task={42,4,4,1}, conv_task={43,5,5,1};
|
||
|
|
static bool rtc_present=true, conv_present;
|
||
|
|
static bool engine_ready=true, publish_fail;
|
||
|
|
static const char *current_name="VolcRTCMain";
|
||
|
|
static unsigned priority_writes, start_events, tts_sends, audio_sends, creates;
|
||
|
|
static bool authorized=true, rtc_mode=true, create_fail;
|
||
|
|
static int tts_result;
|
||
|
|
static TaskHandle_t xTaskGetHandle(const char *name) {
|
||
|
|
if(!strcmp(name,"VolcRTCMain")) return rtc_present?&rtc_task:NULL;
|
||
|
|
if(!strcmp(name,"conv_ai")) return conv_present?&conv_task:NULL;
|
||
|
|
assert(0);return NULL;
|
||
|
|
}
|
||
|
|
static void vTaskGetInfo(TaskHandle_t task,TaskStatus_t *s,int a,int b) {assert(!task);(void)a;(void)b;s->xTaskNumber=rtc_task.number;s->uxBasePriority=rtc_task.base;s->eCurrentState=eInvalid;}
|
||
|
|
static unsigned uxTaskPriorityGet(TaskHandle_t task) {assert(!task);return rtc_task.priority;}
|
||
|
|
static void vTaskPrioritySet(TaskHandle_t task,unsigned priority) {
|
||
|
|
assert(!task);
|
||
|
|
bool inherited=rtc_task.priority>rtc_task.base;
|
||
|
|
rtc_task.base=priority;
|
||
|
|
if(!inherited || rtc_task.priority<priority) rtc_task.priority=priority;
|
||
|
|
priority_writes++;
|
||
|
|
}
|
||
|
|
static int xTaskGetCoreID(TaskHandle_t task) {assert(!task);return rtc_task.core;}
|
||
|
|
static TaskHandle_t xTaskGetCurrentTaskHandle(void) {return &rtc_task;}
|
||
|
|
static const char *pcTaskGetName(TaskHandle_t task) {assert(!task);return current_name;}
|
||
|
|
static uint64_t esp_timer_get_time(void) {return 987654321;}
|
||
|
|
enum {VOLC_SERVICE_AI_CONVERSATION=1,VOLC_SERVICE_AI_CONVERSATION_START,VOLC_SERVICE_AI_CONVERSATION_INTERRUPT,
|
||
|
|
VOLC_SERVICE_AI_CONVERSATION_QUIT,VOLC_SERVICE_AI_CONVERSATION_OVER,VOLC_SERVICE_AI_CONVERSATION_TOGGLE};
|
||
|
|
static int aios_event_pub(int event,void *a,void *b) {assert(event==VOLC_SERVICE_AI_CONVERSATION);assert(!a&&!b);start_events++;return publish_fail?-1:0;}
|
||
|
|
static bool call_client_is_engine_ready(void) {return engine_ready;}
|
||
|
|
bool conv_ai_service_diagnostic_active(void);
|
||
|
|
typedef void *volc_hal_capture_t;
|
||
|
|
typedef struct {int data_type;} volc_hal_frame_info_t;
|
||
|
|
typedef struct {bool commit;int data_type;} volc_audio_frame_info_t;
|
||
|
|
enum {VOLC_AUDIO_DATA_TYPE_PCM=11,VOLC_AGENT_TYPE_TTS=12};
|
||
|
|
static struct {void *engine;int wait_time;} conv_service={(void*)1,0};
|
||
|
|
static atomic_bool is_stop,stop_requested;
|
||
|
|
static bool platform_conversation_audio_authorized(void) {return authorized;}
|
||
|
|
bool platform_runtime_rtc(void) {return rtc_mode;}
|
||
|
|
static uint64_t host_now_ms=1234;
|
||
|
|
static uint64_t volc_osal_get_time_ms(void) {return host_now_ms;}
|
||
|
|
typedef struct {int type;} esp_gmf_afe_evt_t;
|
||
|
|
enum {ESP_GMF_AFE_EVT_VAD_START=1,ESP_GMF_AFE_EVT_VAD_END};
|
||
|
|
static int get_rssi(void) {return -10;}
|
||
|
|
static int volc_send_audio_data(void *engine,const void *data,int len,volc_audio_frame_info_t *info) {
|
||
|
|
assert(engine==conv_service.engine&&data&&len>0&&!info->commit);audio_sends++;return 0;
|
||
|
|
}
|
||
|
|
static int volc_send_text_to_agent(void *engine,const char *text,int type,int interrupt) {
|
||
|
|
assert(engine==conv_service.engine&&type==VOLC_AGENT_TYPE_TTS&&interrupt==1);
|
||
|
|
assert(strstr(text,"这是连续语音播放测试。")&&strstr(text,"平稳地读完。"));tts_sends++;return tts_result;
|
||
|
|
}
|
||
|
|
typedef int aios_ret_t;
|
||
|
|
typedef struct {int id;} aios_event_t;
|
||
|
|
typedef struct {void *conv_thread_id;} volc_conv_service_manager_t;
|
||
|
|
typedef struct {void *display_handle;} volc_hal_context_t;
|
||
|
|
typedef void *volc_hal_display_t;
|
||
|
|
static volc_hal_context_t hal_context={NULL};
|
||
|
|
static volc_hal_context_t *volc_get_global_hal_context(void) {return &hal_context;}
|
||
|
|
enum {AIOS_Ret_NotHandled,AIOS_Ret_Handled,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT};
|
||
|
|
typedef struct {char name[32];int stack_size,priority,stack_in_ext;} volc_osal_thread_param_t;
|
||
|
|
static void volc_hal_display_set_content(void *h,int obj,int kind,const char *text) {(void)h;(void)obj;(void)kind;(void)text;}
|
||
|
|
static void diagnostic_prepare(void);
|
||
|
|
static void conv_ai_service_prepare_task(void) {diagnostic_prepare();}
|
||
|
|
static void conv_ai_service_task(void *arg) {(void)arg;}
|
||
|
|
void conv_ai_service_diagnostic_reset(void);
|
||
|
|
static void volc_osal_thread_destroy(void *handle) {(void)handle;}
|
||
|
|
static void vTaskDeleteWithCaps(TaskHandle_t handle) {assert(handle==&conv_task);conv_present=false;}
|
||
|
|
static void conv_ai_service_task_stop(void) {conv_ai_service_diagnostic_reset();}
|
||
|
|
static int volc_osal_thread_create(void **handle,volc_osal_thread_param_t *p,void (*f)(void*),void *arg) {
|
||
|
|
(void)p;(void)f;(void)arg;creates++;if(create_fail)return -1;*handle=&conv_task;conv_present=true;return 0;
|
||
|
|
}
|
||
|
|
/* @PRODUCTION@ */
|
||
|
|
static void capture(void) {
|
||
|
|
int16_t pcm[8]={1,2,3,4,5,6,7,8};volc_hal_frame_info_t info={VOLC_AUDIO_DATA_TYPE_PCM};
|
||
|
|
__audio_capture_cb(NULL,pcm,sizeof(pcm),&info);
|
||
|
|
}
|
||
|
|
int main(void) {
|
||
|
|
// Execute the real VAD + PCM callback: silence does not postpone idle,
|
||
|
|
// continuous detected speech does, and its end starts a fresh minute.
|
||
|
|
is_stop=false;stop_requested=false;call_idle_activity();
|
||
|
|
host_now_ms+=59000;capture();assert(!call_idle_expired());
|
||
|
|
host_now_ms+=1000;capture();assert(call_idle_expired());
|
||
|
|
esp_gmf_afe_evt_t vad={ESP_GMF_AFE_EVT_VAD_START};
|
||
|
|
call_idle_capture_event(&vad,NULL);
|
||
|
|
for(int tick=0;tick<180;tick++){host_now_ms+=1000;capture();assert(!call_idle_expired());}
|
||
|
|
vad.type=ESP_GMF_AFE_EVT_VAD_END;call_idle_capture_event(&vad,NULL);
|
||
|
|
host_now_ms+=60000;capture();assert(call_idle_expired());
|
||
|
|
audio_sends=0;
|
||
|
|
rtc_task.priority=rtc_task.base=3;
|
||
|
|
current_name="worker";__rtc_diagnostic_priority_apply();assert(priority_writes==0&&rtc_task.base==3);
|
||
|
|
diagnostic_rtc_priority(false);diagnostic_rtc_priority(true);assert(priority_writes==0);
|
||
|
|
current_name="VolcRTCMain";__rtc_diagnostic_priority_apply();
|
||
|
|
assert(rtc_task.priority==6&&rtc_task.base==6&&priority_writes==1&&!conv_ai_service_diagnostic_active());
|
||
|
|
unsigned writes=priority_writes;__rtc_diagnostic_priority_apply();assert(priority_writes==writes);
|
||
|
|
rtc_task.priority=rtc_task.base=7;__rtc_diagnostic_priority_apply();assert(rtc_task.base==7&&priority_writes==writes);
|
||
|
|
rtc_task.priority=8;rtc_task.base=3; // inherited effective priority is not the configured base
|
||
|
|
__rtc_diagnostic_priority_apply();assert(rtc_task.base==6&&rtc_task.priority==8);
|
||
|
|
rtc_task.priority=rtc_task.base;__rtc_diagnostic_priority_apply();assert(rtc_task.priority==6);
|
||
|
|
rtc_task.number++;rtc_task.priority=rtc_task.base=3; // a new same-name task must receive the floor too
|
||
|
|
__rtc_diagnostic_priority_apply();assert(rtc_task.base==6);
|
||
|
|
rtc_task.priority=rtc_task.base=4;__rtc_diagnostic_priority_apply();assert(rtc_task.base==6);
|
||
|
|
writes=priority_writes;diagnostic_rtc_priority(false);diagnostic_rtc_priority(true);
|
||
|
|
conv_ai_service_diagnostic_reset();diagnostic_close();diagnostic_prepare();
|
||
|
|
__rtc_diagnostic_priority_apply();assert(rtc_task.base==6&&priority_writes==writes);
|
||
|
|
volc_rtc_diagnostic_snapshot();assert(atomic_load(&diagnostic_rtc_seen));
|
||
|
|
assert(atomic_load(&diagnostic_rtc_base)==6&&priority_writes==writes);
|
||
|
|
puts("PASS normal-call self-only floor, higher base, inherited effective priority, new task, no diagnostic override");
|
||
|
|
|
||
|
|
capture();assert(audio_sends==1);
|
||
|
|
conv_ai_service_diagnostic_tts();assert(start_events==1&&diagnostic_uplink_suppressed());
|
||
|
|
capture();assert(audio_sends==1);
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_TTS&&diagnostic_uplink_suppressed());
|
||
|
|
diagnostic_send_tts();assert(tts_sends==1&&diagnostic_uplink_suppressed());
|
||
|
|
conv_present=true;conv_ai_service_diagnostic_tts();assert(start_events==1);
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_TTS);diagnostic_send_tts();assert(tts_sends==2);
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE);capture();assert(audio_sends==1);
|
||
|
|
conv_ai_service_diagnostic_say();assert(start_events==1&&!diagnostic_uplink_suppressed());
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_SAY);capture();assert(audio_sends==2);
|
||
|
|
conv_ai_service_diagnostic_tts();diagnostic_rtc_priority(true);conv_ai_service_diagnostic_reset();__rtc_diagnostic_priority_apply();
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE&&!diagnostic_uplink_suppressed()&&rtc_task.priority==6);
|
||
|
|
capture();assert(audio_sends==3);
|
||
|
|
puts("PASS fixed TTS bypass, same-room repetition, mic-only gate, say/reset restores uplink");
|
||
|
|
|
||
|
|
conv_ai_service_diagnostic_tts();tts_result=-1;diagnostic_send_tts();assert(!diagnostic_uplink_suppressed());
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE);
|
||
|
|
rtc_mode=false;conv_ai_service_diagnostic_tts();unsigned sends=tts_sends;diagnostic_send_tts();
|
||
|
|
assert(tts_sends==sends&&!diagnostic_uplink_suppressed());rtc_mode=true;tts_result=0;
|
||
|
|
conv_present=false;create_fail=true;volc_conv_service_manager_t manager={0};
|
||
|
|
aios_event_t event={VOLC_SERVICE_AI_CONVERSATION_START};
|
||
|
|
conv_ai_service_diagnostic_tts();diagnostic_rtc_priority(true);
|
||
|
|
assert(__state_conversation(&manager,&event)==AIOS_Ret_Handled&&creates==1&&!manager.conv_thread_id);
|
||
|
|
assert(!diagnostic_uplink_suppressed()&&diagnostic_take_pending()==DIAGNOSTIC_NONE&&rtc_task.priority==6);
|
||
|
|
authorized=false;capture();assert(audio_sends==3);
|
||
|
|
puts("PASS send failure, unsupported transport and actual task-create failure restore diagnostics");
|
||
|
|
conv_present=true;conv_ai_service_diagnostic_tts();diagnostic_close();
|
||
|
|
conv_ai_service_diagnostic_tts();assert(!diagnostic_uplink_suppressed()&&!conv_ai_service_diagnostic_active());
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE);
|
||
|
|
diagnostic_rtc_priority(true);assert(rtc_task.priority==6);
|
||
|
|
conv_ai_service_diagnostic_reset();conv_ai_service_diagnostic_tts(); // reset must preserve closing gate
|
||
|
|
assert(!diagnostic_uplink_suppressed());
|
||
|
|
conv_present=false;publish_fail=true;conv_ai_service_diagnostic_tts();
|
||
|
|
assert(!diagnostic_uplink_suppressed()&&!conv_ai_service_diagnostic_active());publish_fail=false;
|
||
|
|
conv_ai_service_diagnostic_tts();assert(diagnostic_uplink_suppressed());diagnostic_prepare();
|
||
|
|
assert(diagnostic_take_pending()==DIAGNOSTIC_TTS);diagnostic_close();diagnostic_prepare();
|
||
|
|
assert(!diagnostic_uplink_suppressed()&&!conv_ai_service_diagnostic_active());
|
||
|
|
puts("PASS closing request rejection, no priority pollution, event failure and next-call reset");
|
||
|
|
puts("voice diagnostic host PASS");return 0;
|
||
|
|
}
|