@@ -185,52 +185,17 @@ class TTSService {
185185 }
186186
187187 /**
188- * Stop any currently playing audio and cancel queued sentences
188+ * Stop any currently playing audio and cancel queued playback
189189 */
190190 stop ( ) {
191191 this . cancelled = true ;
192192 this . stopAudio ( ) ;
193193 }
194194
195195 /**
196- * Split text into speakable sentence chunks.
197- * Splits on sentence-ending punctuation while keeping the punctuation attached.
198- */
199- private splitIntoSentences ( text : string ) : string [ ] {
200- // Split on sentence boundaries (.!?) followed by space or end of string
201- // Keep short fragments together to avoid tiny TTS calls
202- const raw = text . match ( / [ ^ . ! ? ] + [ . ! ? ] + [ \s ] * / g) || [ text ] ;
203- const sentences : string [ ] = [ ] ;
204- let buffer = '' ;
205-
206- for ( const chunk of raw ) {
207- buffer += chunk ;
208- // Only split if the buffer is long enough (>20 chars) to avoid tiny fragments
209- if ( buffer . trim ( ) . length >= 20 ) {
210- sentences . push ( buffer . trim ( ) ) ;
211- buffer = '' ;
212- }
213- }
214- // Push any remaining buffer
215- if ( buffer . trim ( ) ) {
216- if ( sentences . length > 0 ) {
217- // Attach short trailing fragment to last sentence
218- sentences [ sentences . length - 1 ] += ' ' + buffer . trim ( ) ;
219- } else {
220- sentences . push ( buffer . trim ( ) ) ;
221- }
222- }
223-
224- return sentences . filter ( s => s . length > 0 ) ;
225- }
226-
227- /**
228- * Speak text using sentence-level streaming for low latency.
229- * Splits the text into sentences, speaks the first one immediately,
230- * and queues the rest to play sequentially.
231- *
232- * onStart fires when the first sentence starts playing.
233- * onEnd fires when the last sentence finishes.
196+ * Speak text using native streaming for lowest latency.
197+ * Passes the full text natively to the underlying TTS provider's streaming API
198+ * (e.g. ElevenLabs chunked REST API).
234199 */
235200 async speakStreaming (
236201 text : string ,
@@ -245,60 +210,15 @@ class TTSService {
245210 onError ?: ( error : Error ) => void ;
246211 }
247212 ) : Promise < void > {
248- const sentences = this . splitIntoSentences ( text ) ;
249-
250- if ( sentences . length === 0 ) {
213+ if ( ! text || text . trim ( ) . length === 0 ) {
251214 options ?. onEnd ?.( ) ;
252- return ;
215+ return Promise . resolve ( ) ;
253216 }
254217
255- // If only one sentence, just use normal speak (no overhead)
256- if ( sentences . length === 1 ) {
257- return this . speak ( text , options ) ;
258- }
259-
260- // Reset cancellation
261218 this . cancelled = false ;
262219
263- log . debug ( `📝 Sentence streaming: ${ sentences . length } chunks` , sentences . map ( s => s . substring ( 0 , 40 ) + '...' ) ) ;
264-
265- let started = false ;
266-
267- for ( let i = 0 ; i < sentences . length ; i ++ ) {
268- if ( this . cancelled ) {
269- log . debug ( '⏹️ Sentence streaming cancelled' ) ;
270- break ;
271- }
272-
273- const isFirst = i === 0 ;
274- const isLast = i === sentences . length - 1 ;
275- const sentence = sentences [ i ] ;
276-
277- try {
278- await this . speak ( sentence , {
279- ...options ,
280- onStart : ( ) => {
281- if ( ! started ) {
282- started = true ;
283- options ?. onStart ?.( ) ;
284- }
285- } ,
286- onEnd : isLast ? options ?. onEnd : undefined ,
287- onError : undefined , // Handle errors below
288- } ) ;
289- } catch ( error ) {
290- log . error ( `❌ Sentence ${ i + 1 } /${ sentences . length } failed:` , error ) ;
291- // Skip this sentence and continue with the next
292- if ( isLast ) {
293- options ?. onEnd ?.( ) ;
294- }
295- }
296- }
297-
298- // If cancelled before any sentence played, still fire onEnd
299- if ( this . cancelled && ! started ) {
300- options ?. onEnd ?.( ) ;
301- }
220+ // Directly use native TTS provider streaming instead of custom sentence splitting
221+ return this . speak ( text , options ) ;
302222 }
303223
304224 /**
0 commit comments