@@ -182,91 +182,11 @@ def _emit_chunk(
182182 user : Optional [Dict [str , Any ]],
183183 ) -> tuple [int , float ]:
184184 """Resample, serialize, emit TTSAudioEvent; return (bytes_len, duration_ms)."""
185-
186- if (
187- pcm .sample_rate == self ._desired_sample_rate
188- and pcm .channels == self ._desired_channels
189- ):
190- # No resampling needed
191- pcm_out = pcm
192- else :
193- resampler = self ._get_resampler (pcm .sample_rate , pcm .channels )
194-
195- # Prepare input frame in planar format
196- samples = pcm .samples
197- if isinstance (samples , np .ndarray ):
198- if samples .ndim == 1 :
199- if pcm .channels > 1 :
200- cmaj = np .tile (samples , (pcm .channels , 1 ))
201- else :
202- cmaj = samples .reshape (1 , - 1 )
203- elif samples .ndim == 2 :
204- ch = pcm .channels if pcm .channels else 1
205- if samples .shape [0 ] == ch :
206- cmaj = samples
207- elif samples .shape [1 ] == ch :
208- cmaj = samples .T
209- else :
210- if samples .shape [1 ] > samples .shape [0 ]:
211- cmaj = samples
212- else :
213- cmaj = samples .T
214- cmaj = np .ascontiguousarray (cmaj )
215- else :
216- # Shouldn't happen, but handle it
217- cmaj = (
218- samples .reshape (1 , - 1 )
219- if isinstance (samples , np .ndarray )
220- else samples
221- )
222-
223- in_layout = "mono" if pcm .channels == 1 else "stereo"
224- frame = av .AudioFrame .from_ndarray (cmaj , format = "s16p" , layout = in_layout )
225- frame .sample_rate = pcm .sample_rate
226-
227- # Resample using persistent resampler
228- resampled_frames = resampler .resample (frame )
229-
230- if resampled_frames :
231- resampled_frame = resampled_frames [0 ]
232- raw_array = resampled_frame .to_ndarray ()
233- num_frames = resampled_frame .samples
234-
235- # Handle PyAV's packed format quirk
236- ch = self ._desired_channels
237- if raw_array .ndim == 2 and raw_array .shape [0 ] == 1 and ch > 1 :
238- flat = raw_array .reshape (- 1 )
239- if len (flat ) == num_frames * ch :
240- resampled_samples = flat .reshape (- 1 , ch ).T
241- else :
242- resampled_samples = flat .reshape (ch , - 1 )
243- elif raw_array .ndim == 2 :
244- if raw_array .shape [1 ] == ch :
245- resampled_samples = raw_array .T
246- elif raw_array .shape [0 ] == ch :
247- resampled_samples = raw_array
248- else :
249- resampled_samples = raw_array .T
250- elif raw_array .ndim == 1 :
251- if ch == 1 :
252- resampled_samples = raw_array
253- else :
254- resampled_samples = np .tile (raw_array , (ch , 1 ))
255- else :
256- resampled_samples = raw_array .reshape (ch , - 1 )
257-
258- if resampled_samples .dtype != np .int16 :
259- resampled_samples = resampled_samples .astype (np .int16 )
260-
261- pcm_out = PcmData (
262- samples = resampled_samples ,
263- sample_rate = self ._desired_sample_rate ,
264- format = "s16" ,
265- channels = self ._desired_channels ,
266- )
267- else :
268- # Resampling failed, use original
269- pcm_out = pcm
185+ # Resample using persistent resampler to avoid discontinuities between chunks
186+ resampler = self ._get_resampler (pcm .sample_rate , pcm .channels )
187+ pcm_out = pcm .resample (
188+ self ._desired_sample_rate , self ._desired_channels , resampler = resampler
189+ )
270190
271191 payload = pcm_out .to_bytes ()
272192 # Metrics: counters per chunk
0 commit comments