// Emit a standalone host TU containing the actual production state machine, // SDK event/status callbacks and conversation task. No device or cloud access. const fs = require('node:fs'), path = require('node:path'); const root = path.resolve(__dirname, '..'); const inc = fs.readFileSync(path.join(root, 'components/application/service/conv_service/inc/call_display_state.h'), 'utf8'); const state = fs.readFileSync(path.join(root, 'components/application/service/conv_service/src/call_display_state.c'), 'utf8').replace('#include "call_display_state.h"', ''); const task = fs.readFileSync(path.join(root, 'components/application/service/conv_service/src/volc_conv.c'), 'utf8'); const voiceHeader = fs.readFileSync(path.join(root, 'components/application/service/conv_service/inc/call_voice_end.h'), 'utf8').replace('#pragma once', ''); const voiceState = fs.readFileSync(path.join(root, 'components/application/service/conv_service/src/call_voice_end.c'), 'utf8').replace('#include "call_voice_end.h"', ''); function section(first, next) { const start = task.indexOf(first), end = task.indexOf(next, start + first.length); if (start < 0 || end < 0) throw new Error(`production section missing: ${first}`); return task.slice(start, end); } process.stdout.write(inc + '\n' + state + '\n' + voiceHeader + '\n' + voiceState + '\n' + String.raw` #include #include #include #include #include typedef void *volc_engine_t; typedef void *volc_hal_display_t; typedef void *volc_hal_capture_t; typedef struct {int code;} volc_event_t; typedef enum {VOLC_CONV_STATUS_LISTENING,VOLC_CONV_STATUS_THINKING,VOLC_CONV_STATUS_ANSWERING,VOLC_CONV_STATUS_INTERRUPTED,VOLC_CONV_STATUS_ANSWER_FINISH} volc_conv_status_e; typedef struct {void *display_handle,*capture_handle[1],*player_handle[1];} volc_hal_context_t; typedef struct {int media_type;void (*data_cb)(void);void (*audio_wakeup_cb)(void*,void*);void *user_data;} volc_hal_capture_config_t; typedef struct {int type;} esp_gmf_afe_evt_t; #define ESP_GMF_AFE_EVT_VAD_START 1 #define ESP_GMF_AFE_EVT_VAD_END 2 typedef struct {int media_type;} volc_hal_player_config_t; typedef struct {int mode;char *bot_id,*params;} volc_opt_t; typedef struct {bool is_binary;} volc_message_info_t; typedef struct {void *engine;int wait_time;} volc_conv_service_t; #define VOLC_EV_CONNECTED 1 #define VOLC_EV_DISCONNECTED 2 #define VOLC_DISPLAY_OBJ_STATUS 0 #define VOLC_DISPLAY_OBJ_MAIN 1 #define VOLC_DISPLAY_OBJ_SUBTITLE 2 #define VOLC_DISPLAY_TEXT 0 #define VOLC_DISPLAY_IMAGE 1 #define MMAP_EAF_HAPPY_EAF 0 #define MMAP_EAF_EMOTION_BLINK_FAST_284_126_EAF 1 #define MMAP_EAF_EMOTION_DIZZY_284_126_EAF 2 #define VOLC_HAL_PLAYER_AUDIO 0 #define VOLC_HAL_CAPTURE_AUDIO 0 #define VOLC_MEDIA_TYPE_AUDIO 0 #define VOLC_AUDIO_MODE_CAPTURE 0 #define VOLC_AUDIO_MODE_WAKEUP 1 #define VOLC_MODE_RTC 1 #define VOLC_MODE_WS 2 #define VOLC_AGENT_TYPE_LLM 0 #define VOLC_SERVICE_AI_CONVERSATION_OVER 1 #define CONV_SERVICE_TIMEOUT 60 #define LOGI(...) do { if (0) printf(__VA_ARGS__); } while (0) #define LOGE(...) do { if (0) printf(__VA_ARGS__); } while (0) #define pdMS_TO_TICKS(value) (value) enum {NORMAL,DELAYED_CONNECTION,START_FAILURE,BEGIN_FAILURE,REMOTE_DROP,SLOW_END_ACK,AUDIO_STOP_FAILURE,RESPONSE_FINISHED,TIMEOUT_WITHOUT_CONNECTION,IDLE_CONNECTED,ACTIVE_LONG_CALL,VOICE_GOODBYE,VOICE_RETRACT,VOICE_MISSING_REPLY}; static int scenario,polls,frames,ended_frames,interrupted_frames,failed_frames,ending_frames,talking_frames,over_count; static uint64_t now_ms; static char text[64]; static bool audio_stopped,output_stopped,sdk_stopped; static atomic_bool is_stop=true,stop_requested=false,provider_disconnected=false,rtc_connected=false,display_connected=false,user_stop_requested=false; static call_display_state_t call_display; static volc_hal_display_t global_display; static bool session_started_reported,diagnostic_say_pending; static call_voice_end_t voice_end; static atomic_uint voice_finish_serial,last_voice_received_ms; typedef struct {double valuedouble;} cJSON; static cJSON *cJSON_GetObjectItemCaseSensitive(const cJSON *j,const char *k){(void)j;(void)k;return NULL;} static const char *cJSON_GetStringValue(const cJSON *j){(void)j;return NULL;} static bool cJSON_IsNumber(const cJSON *j){return j!=NULL;} static bool cJSON_IsTrue(const cJSON *j){(void)j;return false;} static void *platform_report_queue; static int xQueueReceive(void *q,cJSON **out,unsigned ticks){(void)q;(void)out;(void)ticks;return 0;} static void cJSON_Delete(cJSON *root){(void)root;} #define pdTRUE 1 enum { DIAGNOSTIC_NONE, DIAGNOSTIC_SAY, DIAGNOSTIC_TTS, DIAGNOSTIC_PENDING_MASK=3 }; static atomic_uint diagnostic_state; static void diagnostic_prepare(void){} static void diagnostic_close(void){} static unsigned diagnostic_take_pending(void){return 0;} static void diagnostic_send_tts(void){} static unsigned uxTaskPriorityGet(void *task){(void)task;return 5;} static int xTaskGetCoreID(void *task){(void)task;return 0;} static volc_conv_service_t conv_service; static volc_hal_context_t hal={(void*)1,{(void*)2},{(void*)3}}; static void __on_volc_event(volc_engine_t,volc_event_t*,void*); static void __on_volc_conversation_status(volc_engine_t,volc_conv_status_e,void*); void conv_ai_service_task_stop(void); static uint64_t volc_osal_get_time_ms(void){return now_ms;} ` + section('/* CALL_IDLE_BEGIN:', '/* CALL_IDLE_END */') + String.raw` static void vTaskDelay(unsigned ms){now_ms+=ms;} static void sleep(unsigned seconds){now_ms+=seconds*1000;} static void volc_osal_thread_exit(void *unused){(void)unused;} static void *volc_osal_malloc(size_t size){return malloc(size);} static void volc_osal_free(void *p){free(p);} static volc_hal_context_t *volc_get_global_hal_context(void){return &hal;} static int volc_hal_display_set_content(void *display,int obj,int type,const void *content){ (void)display;(void)type; if(obj!=VOLC_DISPLAY_OBJ_STATUS)return 0; snprintf(text,sizeof(text),"%s",(const char*)content);frames++; if(!strcmp(text,"通话中")){assert(atomic_load(&display_connected));talking_frames++;} if(!strcmp(text,"正在结束"))ending_frames++; if(!strcmp(text,"通话结束")||!strcmp(text,"连接失败")||!strcmp(text,"连接中断")){ assert(audio_stopped&&output_stopped); // never claim ended before local shutdown if(!strcmp(text,"通话结束"))ended_frames++; if(!strcmp(text,"连接失败"))failed_frames++; if(!strcmp(text,"连接中断"))interrupted_frames++; } return 0; } static void platform_conversation_observe(const char *type){(void)type;} static bool platform_runtime_rtc(void){return true;} static bool platform_conversation_audio_authorized(void){return true;} static int platform_conversation_begin(void){return scenario==BEGIN_FAILURE?-1:0;} static const char *platform_last_error_code(void){return "";} static const char *call_client_start_error(void){return "";} static const char *platform_conversation_update_json(void){return "{}";} static const char *platform_conversation_bot_id(void){return "test";} static const char *volc_current_user_id(void *engine,bool remote){(void)engine;(void)remote;return "test";} static void process_platform_reports(void){} static void platform_conversation_stop_result(bool started,int result){(void)started;(void)result;} static void platform_conversation_finish(bool failed,const char *error){ (void)failed;(void)error; if(scenario==AUDIO_STOP_FAILURE){assert(!strcmp(text,"正在结束"));return;} assert(ended_frames+failed_frames+interrupted_frames>0); // visible before HTTP ACK assert(sdk_stopped); if(scenario==SLOW_END_ACK)now_ms+=5000; } static bool conversation_output_allowed(void){return !stop_requested;} static bool volc_hal_player_voice_quiet(void *player,unsigned ms){(void)player;(void)ms;return true;} ` + section('/* Called only by the conversation task,', '/* HTTPS reporting') + String.raw` static void __audio_capture_cb(void){} static void *volc_hal_capture_create(const volc_hal_capture_config_t *cfg){(void)cfg;return (void*)2;} static int volc_hal_capture_stop(void *capture){(void)capture;if(stop_requested&&scenario==AUDIO_STOP_FAILURE)return -1;audio_stopped=true;return 0;} static int volc_hal_capture_update_config(void *capture,const volc_hal_capture_config_t *cfg){(void)capture;(void)cfg;return 0;} static int volc_hal_capture_start(void *capture,int mode){(void)capture;if(mode==VOLC_AUDIO_MODE_CAPTURE)audio_stopped=false;return 0;} static void *volc_hal_player_create(const volc_hal_player_config_t *cfg){(void)cfg;return (void*)3;} static void volc_hal_player_set_authorization_gate(void *player,bool(*gate)(void)){(void)player;(void)gate;} static void volc_hal_player_start(void *player){(void)player;output_stopped=false;} static int volc_hal_player_stop(void *player){(void)player;output_stopped=true;return 0;} static void volc_hal_player_discard_pending(void *player){(void)player;} static void volc_hal_player_destroy(void *player){(void)player;} static int volc_start(void *engine,const volc_opt_t *opt){ (void)engine;(void)opt; if(scenario==START_FAILURE)return -1; if(scenario!=DELAYED_CONNECTION&&scenario!=TIMEOUT_WITHOUT_CONNECTION){volc_event_t event={VOLC_EV_CONNECTED};__on_volc_event(NULL,&event,NULL);} return 0; } static int volc_stop(void *engine){(void)engine;sdk_stopped=true;return 0;} static int platform_conversation_poll(void){ polls++; if(scenario==VOICE_GOODBYE||scenario==VOICE_RETRACT||scenario==VOICE_MISSING_REPLY){ if(polls==1)process_voice_end_transcript("subject","就聊到这吧",true); if(scenario==VOICE_GOODBYE&&polls==2)__on_volc_conversation_status(NULL,VOLC_CONV_STATUS_ANSWER_FINISH,NULL); if(scenario==VOICE_RETRACT&&polls==2)process_voice_end_transcript("subject","等等,我还想聊",true); if(scenario==VOICE_RETRACT&&polls==3){assert(!voice_end.pending);conv_ai_service_task_stop();} assert(polls<15);return 0; } if(scenario==IDLE_CONNECTED){ __on_volc_conversation_status(NULL,VOLC_CONV_STATUS_LISTENING,NULL); return 0; // Persistent listening + cloud renewals must still time out. } if(scenario==ACTIVE_LONG_CALL){ if(polls<=150&&polls%30==1)call_idle_activity(); return 0; // Activity keeps a call alive beyond 60s, then silence ends it. } if(scenario==TIMEOUT_WITHOUT_CONNECTION){assert(!talking_frames);return 0;} if(scenario==DELAYED_CONNECTION){assert(!talking_frames);assert(!strcmp(text,"正在连接"));} if(scenario==REMOTE_DROP){volc_event_t event={VOLC_EV_DISCONNECTED};__on_volc_event(NULL,&event,NULL);} else if(scenario==RESPONSE_FINISHED&&polls==1){ __on_volc_conversation_status(NULL,VOLC_CONV_STATUS_ANSWER_FINISH,NULL); assert(!stop_requested);assert(!strcmp(text,"通话中"));assert(!ended_frames); }else conv_ai_service_task_stop(); return 0; } static int volc_send_text_to_agent(void *engine,const char *text,int type,int mode){(void)engine;(void)text;(void)type;(void)mode;return 0;} static int volc_send_message(void *engine,const char *data,size_t len,const volc_message_info_t *info){(void)engine;(void)data;(void)len;(void)info;return 0;} static void aios_event_pub(int type,void *a,void *b){ (void)a;(void)b;assert(type==VOLC_SERVICE_AI_CONVERSATION_OVER);over_count++; if(scenario!=AUDIO_STOP_FAILURE)assert(!strcmp(text,"待机")); } ` + section('static void publish_call_display(', '\nstatic bool conversation_output_allowed(') + section('static void __on_volc_event(', '\nstatic void __on_volc_audio_data(') + section('void conv_ai_service_prepare_task(', '\n#if __cplusplus') + String.raw` static void reset(int next){ scenario=next;polls=frames=ended_frames=interrupted_frames=failed_frames=ending_frames=talking_frames=over_count=0; now_ms=10000;text[0]=0;audio_stopped=output_stopped=true;sdk_stopped=false; memset(&call_display,0,sizeof(call_display));memset(&conv_service,0,sizeof(conv_service)); hal.player_handle[0]=(void*)3;global_display=hal.display_handle;diagnostic_say_pending=false; conv_ai_service_prepare_task(); } int main(void){ // State transitions are fenced by generation, even if a former terminal // deadline or delayed CONNECTED observation is delivered after a new begin. call_display_state_t state={0};unsigned first=call_display_begin(&state); call_display_connected(&state,first);call_display_stopping(&state,first); call_display_finished(&state,first,CALL_DISPLAY_STOP_NORMAL,false,100); assert(state.phase==CALL_DISPLAY_ENDING); call_display_finished(&state,first,CALL_DISPLAY_STOP_NORMAL,true,100); assert(!call_display_idle_if_due(&state,first,2099)); unsigned next=call_display_begin(&state); assert(!call_display_idle_if_due(&state,first,9999));call_display_connected(&state,first); assert(state.phase==CALL_DISPLAY_CONNECTING);call_display_connected(&state,next);assert(state.phase==CALL_DISPLAY_TALKING); for(int test=NORMAL;test<=VOICE_MISSING_REPLY;test++){ reset(test);conv_ai_service_task(NULL);assert(over_count==1);assert(ending_frames); if(test==START_FAILURE||test==BEGIN_FAILURE||test==TIMEOUT_WITHOUT_CONNECTION){assert(failed_frames&& !talking_frames && !ended_frames);} else if(test==REMOTE_DROP){assert(interrupted_frames&&!ended_frames);} else if(test==AUDIO_STOP_FAILURE){assert(!ended_frames&&!failed_frames&&!interrupted_frames);} else{assert(ended_frames&&!failed_frames&&!interrupted_frames);} if(test!=AUDIO_STOP_FAILURE){assert(call_display.phase==CALL_DISPLAY_IDLE);assert(now_ms>=12000);} if(test==IDLE_CONNECTED||test==TIMEOUT_WITHOUT_CONNECTION)assert(polls==60); if(test==ACTIVE_LONG_CALL)assert(polls==180); if(test==VOICE_GOODBYE)assert(polls==2&&atomic_load(&user_stop_requested)); if(test==VOICE_RETRACT)assert(polls==3); if(test==VOICE_MISSING_REPLY)assert(polls==12&&atomic_load(&user_stop_requested)); // Late supplier turn callbacks after teardown cannot overwrite idle/end. char before[sizeof(text)];strcpy(before,text); __on_volc_conversation_status(NULL,VOLC_CONV_STATUS_ANSWER_FINISH,NULL); __on_volc_conversation_status(NULL,VOLC_CONV_STATUS_LISTENING,NULL); assert(!strcmp(before,text)); } // Actual VAD callback records both edges, and starts a new quiet window. int16_t quiet[320]={0}, comfort[320], speech[320]; for(int i=0;i<320;i++){comfort[i]=(i&1)?-20:20;speech[i]=(i&1)?-500:500;} assert(!call_idle_pcm_active(quiet,sizeof(quiet))); assert(!call_idle_pcm_active(comfort,sizeof(comfort))); assert(call_idle_pcm_active(speech,sizeof(speech))); assert(!call_idle_pcm_active(NULL,640)&&!call_idle_pcm_active(speech,639)); reset(NORMAL); esp_gmf_afe_evt_t vad={ESP_GMF_AFE_EVT_VAD_START}; now_ms+=59000;call_idle_capture_event(&vad,NULL); assert(atomic_load(&local_speech_active)&&!call_idle_expired()); vad.type=ESP_GMF_AFE_EVT_VAD_END;now_ms+=90000;call_idle_capture_event(&vad,NULL); assert(!atomic_load(&local_speech_active)&&!call_idle_expired()); now_ms+=59999;assert(!call_idle_expired());now_ms++;assert(call_idle_expired()); now_ms=UINT32_MAX-1000ull;call_idle_activity(); now_ms+=59999;assert(!call_idle_expired());now_ms++;assert(call_idle_expired()); puts("PASS call display + idle: actual task/callbacks, connected-only talking, local shutdown before terminal, start failure, provider drop, answer-finish continuity, slow end ACK, failed local stop, 60s quiet timeout, 180s activity call, VAD edges, silence/comfort PCM rejection, clock wrap, generation-fenced hold"); return 0; } `);