From 152fc6196d12a126fd21de5b37657c391b17124c Mon Sep 17 00:00:00 2001 From: "j.foucher" Date: Sun, 22 Feb 2026 08:46:15 +0100 Subject: [PATCH] v1.7.1: Fix mic not heard + latency optimizations + thread safety Fix regression from v1.7.0 where agent couldn't hear user speech: - Restore AsyncTask game-thread dispatch for delegate broadcast (AddUObject weak pointer checks are not thread-safe from WASAPI thread) - Keep early echo suppression in WASAPI callback (before resampling) - Keep MicChunkMinBytes at 3200 (100ms) for lower latency - Add thread safety: std::atomic for bIsListening/bAgentSpeaking/bCapturing, FCriticalSection for MicSendLock and WebSocketSendLock - Add EchoSuppressFlag pointer from agent to mic component Co-Authored-By: Claude Opus 4.6 --- .../PS_AI_Agent/Content/test_AI_Actor.uasset | Bin 157437 -> 156705 bytes ...ElevenLabsConversationalAgentComponent.cpp | 39 ++++++++++++------ .../ElevenLabsMicrophoneCaptureComponent.cpp | 25 +++++++---- .../Private/ElevenLabsWebSocketProxy.cpp | 12 ++++-- .../ElevenLabsConversationalAgentComponent.h | 11 +++-- .../ElevenLabsMicrophoneCaptureComponent.h | 13 ++++-- .../Public/ElevenLabsWebSocketProxy.h | 4 ++ 7 files changed, 75 insertions(+), 29 deletions(-) diff --git a/Unreal/PS_AI_Agent/Content/test_AI_Actor.uasset b/Unreal/PS_AI_Agent/Content/test_AI_Actor.uasset index a4affbacdfdb7f48067bd653f8e7bc23f093c758..e58641efe931094387554336ccc4b3711c880c3c 100644 GIT binary patch literal 156705 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 z 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 F{|iDHc{l(7 literal 157437 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

_nWv_%Pf1v^r|O7+OPp=Y%FsS@*Qy)GFm9<@My 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

e_78q(Z z(mqB6b!iR}tziGw5D~I4hlp0N{VPNS2j&pb3f2-LlAuH$b}ilDVAH%vKKlQL79LyS z<8j(E3YkQ_o2tN>;@D4;i0yi>=)3Ib_z^d(>-<2`q)j)n0Kh^Ue9#d|r#VElf;DG| z7)LbT`mHR^A20^(w8nlA_Tcbh9!W<1+!S=6c5FD7JK`YpP5Dj9h1IIr99!ONUFfpY* z?}5xY`{+ZDFB~=es>kNH-#sefBV(Y(?}5N)o8JSmf;Cbgu=n2s0W;?JK&)WR`5s6T z(O94NK){E2!nJ}m=X)S<8|EN}K;eUe8K^gR?VC>a&y^nKka#@?*J=Ub1b5m88dSX=PT%q|LEn{*C*bX^Pdi1$6VJl zrLPriUtTPM-iiyiz1HrOnQ80$U$ea3b>*9_V2$J>8~>{_!-#=?7gv>*-JhPl{I~S~ z^#AcIF9yiY;Efv*d)?SKH{{;7`@JE*9@ldl7&pV7LTBm-Ol&@`phorPn&KL=z)%a{ zPZi1LlOwX-b0VA`SHwJbO$4~o4}q5-!!p)*&ZTi)GK*>(#<|?J|K*3)zB_*ICkd<5 z&cDua*XBd#-C-Q!Hfn@=($$B)H1j^0%AuCufh(;ZOlPW@d97e|~A?h<^fuU9-?G}P^bAHGQ_HWG( zK^ErxkQFT5Ff@Uqy-t2pwz~A-DLyr-W3<{19FjsB?bk+}S1ZOS^O#x$^TT z-ukG0yW5!rki)i#2*LawpcSk+7f}&Jqj@nzpzy;><=~_LvNG?yUq8=zY?tHM_18`8 zZw1?z7g3;hN9$*v%wBxih^vOQe(2AY+E-SvM)IAFpEt@P1{QpF%Kvq_;FFY9xBS`t z`pegg#c|jb@Lsab$A~r@Y#8;k&AdbFVVfD#f1J5`$3to7#jlG#IktEclK^tqHnA2l ze~Zfs)||H4jcBZoZGw+as?Yhe?6zjLf$|6bcXpCqK6gAY0u%8U4<>5P8oKmpEH*C*5!uA#(mFZ_ji78@TY9NF>f z*si6y_b+QbphxyIDScF74|U~>|L>+=q#!|m4>OAT4ms)8@AuEn-_rK#jy+RX9@n;T z$IW-1Ze`e@H>Sgeu1|HmVbqG*ryVu(yN*#-u;w)Ec%pH{J$Igz{M*#5+kI2+_~+WG zmqLnR{Wg_@5A*M)S;3mauvwAtn`P$TpCgbh-Y#{XKDOK0Ph>y(@tc=*z3r`q;LQwc zpuux_69mF22(d7QrTs?rGY=6(ad1_j2<8yi-z-c*kdF(>Sk zUmODE=8X+27?!|%?AzR!H#WeH8TJgbLf1fUwy6@HEB?2B01dJ*Z){k>(nHE;ohsRv zHa5V4d1J#0wjXS4>}w(6na54`2TLtQWI4-k~SfF_sqI+y*py|%BL2x0Kj4!#b-adngc;ASaSw~F+}6S zyA7IS>TB=0MO2*2IAztz*@1AL(}l5{FPaC>sv!k(yt> z0^E)=nxjGRX@>0|(I6-{M}tqM63ifY}1|bV`G-w6ezoJ2KV2%c@VEaKd zcnq*Er&il@JL}bV?aXLjN7YaTCza(6uU}hI9UOoBd)VRc3lQKu%ckivc zADVRfZ^o$NK-CaF+x)(>6|9j0fxZ8}Gng^I?`#FDRsz8#AX?L&RwYL9i8+m11Qalk zt~@UqK>pi+iE}W4>+`-d_%KhnRIkbX9e4rmvErh?ES1@`^pl|>aXfR|Lqu*dASBY&9MDr2?xr}OE@do z{;`Au<>n=v73|--go7;1OE@do{L9P!X*(aI2Ou__&fxk@d^++^&T#6 z`<_})P(1`C?cwn1yx@A|+JkmXtI$SXdDK}WhQ6CuX$-4)!8L$7{%Jzi>}URXG;@*b zt8GKukFU0Z-K;3XB{1|Drb>*x`zs$ncPth&IsQr(Hvus_{^s_M+q%x%{zjh>xAvcR zMb0cA{EqfjEL3Td0yDWXE! zW-_U2$Tii=WVm%UzhBDFAKSGke$<2AuD|B*?$7$aQ3Fa~7dWtD*pXX${dZ?A9(7Mu z^r9u1r}wmi-7IR$kHBFZrb;YF3uu^gGdYGHgKh;19_M4Jcvtn$@kgF^Oxis?KJb1Q z^}}yy5pH-XRbWMnB9c~|Gj%}9VcxWRe?NZR{PEZP*9z7EMPts*-vu968 zkM=YL0Co0g<}VLgDOxmod5 zrcj`_wDjP&pFQ6v()4TOrb@+nS;IB5` zLVGUJoO%eUPGKo8g+`8nPgu zR^UUxxM8Q`!1-rh`FnPC%6)IQ+jXOR`U+GJH*P3nnjc4HIzkc>vOu;IpXg8vZI-J< zaTv2)O`CBc{XBlavd(6i6J7T6X|Mb^`muIDoblO9YZn{2;D7?}t?ya6`^K(`% zaHqAsD>2pz)&R3)&duZ~fmmJwhPM+{`QY+btBy*E$z3-2jqK-RwyZ>}aKnpot9SCP zV9jZkClQ_XG0V`3wiK+>5z_y6I5do+ts+Hh-1LR(k*ChtdBQKlZ#-eUv&U7N`x&*6 z(--)W&{wOwytaLC@9fKt`@P4OTa!mw!5W~2EEY34LRKL@0<`cLs&s9a&fk4~<~t*9 z9D3K)yOw@*FWS|ng;qa+Y6WXfE$m5r)JF@!$CKNSU7Ii@GW+u4pI)2Napw6}uv;N0 zbqJyE#mNCb`!%fnSCfuN8<_gguPYs|&FF9yNC`J?C}Wx*M`b#UdMhF7Gh~6G7CHiSF$@pE^l%-rK1dHqK(-2Le5qbrPDv{vNA(e=GWt^T>-uC6&3 zbxK&gv)@mzTfrJ&mdv@C97TcUCBQ7ZP?ds`_kP|wY|5xbr~Oj;_fr==h*sf-7v(F1P3HKFkWXndVLQ|_uNu9%nWtaN3%J(W(M)|-mYprZ70Z*_%Jl$;fDMxMv*bDN+dZ+JLW4}@#2 z4j|YbP!0m}U8R-ug?X(bDhBsrs7;ZhR^XcJ^ZzN#y%2LFb7tjM2`t9XtbaTnC^sKg z<81{K%Ap$XZ(}4z6kl|6dG^LI8AS|H)^#|{W`;h@u=@>2_P61n7+A~kMr4*(IV<$> z=&m?ow`X8b-i9m)s;L8nXVo9;W*#t`&8l&uIAv*vlT?>as4Jig4EJzEd}QM0T=ld> zU2&nXL(_hsnc*DV(6LZn#HVnf8x(-W)>A$AFjT~C2Q2Y$1QpbI(c9(DS0`WE=icn= z`oDDEL;d<+Kma}`hb_s_pQVpqn5++|OH_S`>@eYMyMb~75OBXE8krl7{&xuy^a zbM6P)xCu}cjEEd7>z1I`{Oe>^u;z?k`VfugUwH#JwehCyCG}hnV(8#e45^+M%X-0o zmG)&S7^MowsVlT>jJgM>taSkOQ*c~_h2$$hCv^=nfX<-`E>Hzq)fJbC_$a_xR_=|c zau>TI=vrLvbw`4?lk1z5Oz7`mwnCiaxJe{xWs~5qnYBzw?fE zwaH$4@@efKePqWLD_8?ekw+P_v3u0X6hZIjIakk^{czinR~|Hc(oyZ4zgxkY!xWj} z+Ds1gp8WK416J-nBW2Bb!?MPo^YFD+u$#C;y82xrly^S9Yy(uA*`; zO;UX|$2!$PgMJFD6?GCdAW8(cxbjh_Vbg9?%{r(n{L(`1imq*O4l`7^J;v4HpB0H2>(0*|y!yk(bF6F!tNUfW9<7S}``1xdW)Giu&T*Zqtzd zOw}R@Q&L=FYJ6IH+MuY!)Yz2hgxI+F=#1FJ)YSCYLFqArQZ@VruHmO>3Ovp#Z;_|G z+7~hS|J%D3=qRc)JVd~dHU+Vg@MsyN3ML_s&BkmB)a)}MP>=+$K0r1wd7Nx^V|J62 zQYt79c@-$A#1>GaJQ5_-Dj)}J1e78ap^BvkOF8tw3D9E!Db#2m|9595nH}zK$R@CO zPX2Rd?(8@B_5b(#@7$TWcUGL4GOM|=XeMld1a{G{v*Ka(nhgAG)#awxiWQ;O`(7$r zzg`X1xe?v;PkEr59#d(IJpFVN8UT@WC=dK}!=nlP6F!s}RJV?EP&_>dcz=)`ih~y4 zQ@`+yh{nu!dzyOI?=)~fpp94FdI#lUPr?$4_&3Z=AzfP>)Shb0Fr-^DjA?ee!;q0| zOgC7}HnSx?B_r8vvN(hos<*~L?YX(*a@**eFp1tbd004oGlA+Dif=*>{j3Kb7Ea&f z%@m<(2lvf@*TUq8AlE`eD6ZBxV*iSPZ}rMq`O)5`Q5CClw7nD+#$-6{^{&RlZev$V zv73#lDUJt?siqWDvc+ahO|v-+CL1o0X-1n#=tnwgTuuH)Z}WHAWIMFdY7_}p(R&*Y z3#YeXEn1tst>dm1y`q9p_$Q%IIMrf(>Z+yDvdwg7QhiNyik8Catq|UU>nY5`Zet4D z9ceUzGg*uVhuvmK%}C8K+AW3*huLOGHzubUg@L4_hQfFsM$aC+WgfRzYU5P{rqT15 zhlP{J_l3e^N&`Lgqc~=~A32tX-OjiDLJ3pFzd?ScU{F-;oB&~7y)Y9y677w0B!GR`k*KB$ zI6!!rA_W|aH&9+}ZmluV2c35;xHf9lm%B`pr@Zg?ePUICA5p39tl=;9S^TZ+xf>Q7 z>9xACfQN0Qc&hk~#c$%ef0a=p6&%V`66kSh&EDNhCmbBNdDG#^?+z<@m4|In6{GN0 z>xP~`RNbPM58G#HOv-vOcK;vyzcP0!bkk!hX`!OOZhHWcsKh)J$^&imv|~MPi9HKq z2P{w8vBK0J`p!hfS@^N?OWO<0Q7|Obc77J%Pb(*{^R>~+iJ?k3SP9jjdHq{JC%j0V zXh0`KP1@{)37fxoETh4bz4hDaU&k-4o~W7|by78Zh>qi*a~BqEvt?Ibv+Wvo`GS{+ zsrn%;voAFn3$J@S(eeKT2wS@5@A)KpM2WR-*bCLstBcvY{h&=tX`sjDfk*n}FUreW z6O_DHszwT=AaLP!;_g+fvf@|?)+-Yxl+f15gFr`I4ZaEuyAl!t>XZy z;}+x0{ldmRj=IfFx&5a1auUR5r=2i=zAviOVqY>Zs&bh(BOZ95CdWBQCT;(G;#BqD ze)nbO_Jiy53*84!@UTu!lc7jL?~hN>urna1GpVy~GpGMOCZ_U}*f{g*Ox1*&goBj_7Fa zj()~)=J5lwY|9Rh`os9xs3Sb?TB*)@?((p3a<@O#@$A?>HT6wB96L-+|4O?5(LJzr zYutq%dhYVDPVT~*?cwh7U~9yaLm4wj$D9#K^+mQnpq{Vb!*&(9(wNb zuuks6n(g84#mFGsP3aoKy;@RF>!nje&)I8h$9=VW;@y{c+;vc$_1xuQ;ovR}qL^SZ z$jDLY-qJJfu`eI^jrHwASITe#Z?1D1`qkI};yv^Ht_!*hs(&LMcx#-7etJ&xuue|H zn(g8Aa%zfz{jop`vF6Jq_`SI+P%wQnvR7!E;x;6VOj&DNVtT3Is_FCtJWgj(26N7g zct3XD-_7f4n(qDe^13cOES#LapX&JM?g#c|FJ3l!)$r~+o7M;yp!X2kVm|N*jm3HP zy61(vH(&hEDByZy0MiQetM!fiAR*$2ZN>2?pNPCvb&7|5GdZnoW%LH<8DNn?v_ZPj z<}0s|*q17C0#i>Q%_q|72loV~BXdvSr7QOY_0#MGQmO)OBJv~^ppc$lFzj^=)?2~Y zvwsD@i$dz&**8l7;UkbqadD!(691W9RV2AZEGni|JB3)IQ{X8VeOTk;#Da@NF)m5K zjSwV?HVa}sO!0+h7$ykcr_kzhdcBf2DGO_s%V}5TDKAN4i!~C9ghNV9knhGyWeH&1 zflAV5L#HahTnb5xs2*aRXr+l5)I4lOktk2JO&}d4=rv8ksv~civ&t<_^OVw@4bqyF zqxDMDJ)(y#E%8dein2tORVJ-pl0+=M7Aw58uBq59TFWJ;PqMfwq_Xm2vY5Jd3GrKO zgQe7d^~hrrenYU5SD#bp8nr!$_=j^Cu2VcDs@hv`&-j?OhIz=VJ z5IB%YEp-zc=+-q(_!&xSWI#35+lCuc^K39RyWe^>>+_@)|J#1_$GawO`tHT8FZ>{&nq$ylO3cOh^PErA(+2Z1@X!BEJBk%PiECBpCOnAudioCaptured.RemoveAll(this); Mic->OnAudioCaptured.AddUObject(this, &UElevenLabsConversationalAgentComponent::OnMicrophoneDataCaptured); + // Echo suppression: point the mic at our atomic bAgentSpeaking flag so it skips + // capture entirely (before resampling) while the agent is speaking. + Mic->EchoSuppressFlag = &bAgentSpeaking; Mic->StartCapture(); const double T = TurnStartTime - SessionStartTime; @@ -275,20 +278,23 @@ void UElevenLabsConversationalAgentComponent::StopListening() // "user speaking" state and stall waiting for more audio that never arrives, // leaving both sides stuck — no audio for the collision response and no response // for subsequent turns. - if (bAgentGenerating) { - if (MicAccumulationBuffer.Num() > 0) + FScopeLock Lock(&MicSendLock); + if (bAgentGenerating) { - UE_LOG(LogElevenLabsAgent, Log, - TEXT("StopListening: discarding %d bytes of accumulated mic audio (collision — server is mid-generation)."), - MicAccumulationBuffer.Num()); + if (MicAccumulationBuffer.Num() > 0) + { + UE_LOG(LogElevenLabsAgent, Log, + TEXT("StopListening: discarding %d bytes of accumulated mic audio (collision — server is mid-generation)."), + MicAccumulationBuffer.Num()); + } } + else if (MicAccumulationBuffer.Num() > 0 && WebSocketProxy && IsConnected()) + { + WebSocketProxy->SendAudioChunk(MicAccumulationBuffer); + } + MicAccumulationBuffer.Reset(); } - else if (MicAccumulationBuffer.Num() > 0 && WebSocketProxy && IsConnected()) - { - WebSocketProxy->SendAudioChunk(MicAccumulationBuffer); - } - MicAccumulationBuffer.Reset(); if (WebSocketProxy && TurnMode == EElevenLabsTurnMode::Client) { @@ -394,7 +400,10 @@ void UElevenLabsConversationalAgentComponent::HandleDisconnected(int32 StatusCod GeneratingTickCount = 0; TurnIndex = 0; LastClosedTurnIndex = 0; - MicAccumulationBuffer.Reset(); + { + FScopeLock Lock(&MicSendLock); + MicAccumulationBuffer.Reset(); + } OnAgentDisconnected.Broadcast(StatusCode, Reason); } @@ -611,10 +620,14 @@ void UElevenLabsConversationalAgentComponent::OnMicrophoneDataCaptured(const TAr if (bAgentSpeaking) return; // Convert this callback's samples to int16 bytes and accumulate. - // WASAPI fires every ~5ms (158 bytes at 16kHz). ElevenLabs needs ≥250ms - // (8000 bytes) per chunk for reliable VAD and STT. We hold bytes here + // WASAPI fires every ~5ms (158 bytes at 16kHz). ElevenLabs needs ≥100ms + // (3200 bytes) per chunk for reliable VAD and STT. We hold bytes here // until we have enough, then send the whole batch in one WebSocket frame. TArray PCMBytes = FloatPCMToInt16Bytes(FloatPCM); + + // Lock: MicAccumulationBuffer is accessed from WASAPI thread (here) and + // game thread (StopListening flush). WebSocket send is also serialized. + FScopeLock Lock(&MicSendLock); MicAccumulationBuffer.Append(PCMBytes); if (MicAccumulationBuffer.Num() >= MicChunkMinBytes) diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsMicrophoneCaptureComponent.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsMicrophoneCaptureComponent.cpp index a0685cd..e9bea15 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsMicrophoneCaptureComponent.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsMicrophoneCaptureComponent.cpp @@ -89,6 +89,12 @@ void UElevenLabsMicrophoneCaptureComponent::OnAudioGenerate( UE_LOG(LogElevenLabsMic, Verbose, TEXT("Audio capture buffer overflow.")); } + // Echo suppression: skip resampling + broadcasting entirely when agent is speaking. + if (EchoSuppressFlag && EchoSuppressFlag->load(std::memory_order_relaxed)) + { + return; + } + // Device sends float32 interleaved samples; cast from the void* API. const float* FloatAudio = static_cast(InAudio); @@ -104,15 +110,20 @@ void UElevenLabsMicrophoneCaptureComponent::OnAudioGenerate( } } - // Fire the delegate on the game thread so subscribers don't need to be - // thread-safe (WebSocket Send is not thread-safe in UE's implementation). - AsyncTask(ENamedThreads::GameThread, [this, Data = MoveTemp(Resampled)]() + // Dispatch to game thread for delegate broadcast. + // UE's FMulticastDelegate with AddUObject uses weak object pointer checks that + // are not thread-safe — broadcasting from the WASAPI thread causes the invocation + // to be silently skipped. The game thread dispatch adds ~8ms latency but is required. + if (bCapturing) { - if (bCapturing) + AsyncTask(ENamedThreads::GameThread, [this, Captured = MoveTemp(Resampled)]() { - OnAudioCaptured.Broadcast(Data); - } - }); + if (bCapturing) + { + OnAudioCaptured.Broadcast(Captured); + } + }); + } } // ───────────────────────────────────────────────────────────────────────────── diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsWebSocketProxy.cpp b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsWebSocketProxy.cpp index 83f273d..0dd8219 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsWebSocketProxy.cpp +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Private/ElevenLabsWebSocketProxy.cpp @@ -120,9 +120,12 @@ void UElevenLabsWebSocketProxy::SendAudioChunk(const TArray& PCMData) // Per-chunk log at Verbose only — Log level is too spammy (10+ lines per second). UE_LOG(LogElevenLabsWS, Verbose, TEXT("SendAudioChunk: %d bytes"), PCMData.Num()); - if (WebSocket.IsValid() && WebSocket->IsConnected()) { - WebSocket->Send(AudioJson); + FScopeLock Lock(&WebSocketSendLock); + if (WebSocket.IsValid() && WebSocket->IsConnected()) + { + WebSocket->Send(AudioJson); + } } } @@ -658,7 +661,10 @@ void UElevenLabsWebSocketProxy::SendJsonMessage(const TSharedPtr& J UE_LOG(LogElevenLabsWS, Verbose, TEXT("<< %s"), *Out); } - WebSocket->Send(Out); + { + FScopeLock Lock(&WebSocketSendLock); + WebSocket->Send(Out); + } } FString UElevenLabsWebSocketProxy::BuildWebSocketURL(const FString& AgentIDOverride, const FString& APIKeyOverride) const diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsConversationalAgentComponent.h b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsConversationalAgentComponent.h index 27f93b7..bd1334c 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsConversationalAgentComponent.h +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsConversationalAgentComponent.h @@ -7,6 +7,7 @@ #include "ElevenLabsDefinitions.h" #include "ElevenLabsWebSocketProxy.h" #include "Sound/SoundWaveProcedural.h" +#include #include "ElevenLabsConversationalAgentComponent.generated.h" class UAudioComponent; @@ -337,8 +338,9 @@ private: USoundWaveProcedural* ProceduralSoundWave = nullptr; // ── State ───────────────────────────────────────────────────────────────── - bool bIsListening = false; - bool bAgentSpeaking = false; + // Atomic: read from WASAPI background thread (OnMicrophoneDataCaptured), written from game thread. + std::atomic bIsListening{false}; + std::atomic bAgentSpeaking{false}; // True from the first agent_chat_response_part until the first audio chunk arrives. // Used to block StartListening() while the server is processing the previous turn. bool bAgentGenerating = false; @@ -399,6 +401,9 @@ private: // WASAPI fires callbacks every ~5ms (158 bytes at 16kHz 16-bit mono). // ElevenLabs needs at least ~100ms (3200 bytes) per chunk for reliable VAD/STT. // We accumulate here and only call SendAudioChunk once enough bytes are ready. + // MicSendLock protects MicAccumulationBuffer + WebSocket send (accessed from WASAPI thread + // in OnMicrophoneDataCaptured and from game thread in StopListening flush). TArray MicAccumulationBuffer; - static constexpr int32 MicChunkMinBytes = 8000; // 250ms @ 16kHz 16-bit mono (4000 samples, matches ElevenLabs SDK recommendation) + FCriticalSection MicSendLock; + static constexpr int32 MicChunkMinBytes = 3200; // 100ms @ 16kHz 16-bit mono (1600 samples) }; diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsMicrophoneCaptureComponent.h b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsMicrophoneCaptureComponent.h index 175c57d..de58348 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsMicrophoneCaptureComponent.h +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsMicrophoneCaptureComponent.h @@ -5,6 +5,7 @@ #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "AudioCapture.h" +#include #include "ElevenLabsMicrophoneCaptureComponent.generated.h" // Delivers captured float PCM samples (16000 Hz mono, resampled from device rate). @@ -33,11 +34,17 @@ public: float VolumeMultiplier = 1.0f; /** - * Delegate fired on the game thread each time a new chunk of PCM audio - * is captured. Samples are float32, resampled to 16000 Hz mono. + * Delegate fired on the game thread each time a new chunk of PCM audio is + * captured. Samples are float32, resampled to 16000 Hz mono. + * Audio is captured on a WASAPI background thread, resampled there (with + * echo suppression), then dispatched to the game thread for this broadcast. */ FOnElevenLabsAudioCaptured OnAudioCaptured; + /** Optional pointer to an atomic bool that suppresses capture when true. + * Set by the agent component for echo suppression (skip mic while agent speaks). */ + std::atomic* EchoSuppressFlag = nullptr; + /** Open the default capture device and begin streaming audio. */ UFUNCTION(BlueprintCallable, Category = "ElevenLabs") void StartCapture(); @@ -65,7 +72,7 @@ private: Audio::FAudioCapture AudioCapture; Audio::FAudioCaptureDeviceParams DeviceParams; - bool bCapturing = false; + std::atomic bCapturing{false}; // Device sample rate discovered on StartCapture int32 DeviceSampleRate = 44100; diff --git a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsWebSocketProxy.h b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsWebSocketProxy.h index fa1ee67..c1a3cb7 100644 --- a/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsWebSocketProxy.h +++ b/Unreal/PS_AI_Agent/Plugins/PS_AI_Agent_ElevenLabs/Source/PS_AI_Agent_ElevenLabs/Public/ElevenLabsWebSocketProxy.h @@ -205,6 +205,10 @@ private: EElevenLabsConnectionState ConnectionState = EElevenLabsConnectionState::Disconnected; FElevenLabsConversationInfo ConversationInfo; + // Serializes WebSocket->Send() calls — needed because SendAudioChunk can now be + // called from the WASAPI background thread while SendJsonMessage runs on game thread. + FCriticalSection WebSocketSendLock; + // Accumulation buffer for multi-fragment binary WebSocket frames. // ElevenLabs sends JSON as binary frames; large messages arrive in fragments. TArray BinaryFrameBuffer;