refactor(pyt): replace all inline float32 conversion with to_float32()

PyQt had 7 inline float32 conversion patterns:
  hasattr(x, 'numpy') ? x.numpy().astype('float32') : x.astype('float32')
spread across TTS loop, subtitle processing, and streaming.

All replaced with domain.audio_helpers.to_float32() which handles:
- None → zeros
- PyTorch tensors → .detach().cpu().numpy()
- Plain numpy → asarray(dtype=float32)
- reshape(-1) for consistent 1D output

The old inline code missed .detach() and .cpu() on GPU tensors,
causing potential crashes. Now both UIs use the same robust conversion.

1053 tests pass.
This commit is contained in:
Artem Akymenko
2026-07-18 06:58:21 +00:00
parent e77c8b3372
commit 85b5851786
+9 -27
View File
@@ -329,11 +329,7 @@ class ConversionThread(QThread):
# Stream each segment individually # Stream each segment individually
for i, segment in enumerate(segments): for i, segment in enumerate(segments):
try: try:
# Handle both NumPy arrays and PyTorch tensors segment_bytes = to_float32(segment).tobytes()
if hasattr(segment, "astype"):
segment_bytes = segment.astype("float32").tobytes()
else:
segment_bytes = segment.cpu().numpy().astype("float32").tobytes()
is_last = i == len(segments) - 1 is_last = i == len(segments) - 1
# Update progress periodically - skip if there's only one segment # Update progress periodically - skip if there's only one segment
@@ -1149,27 +1145,16 @@ class ConversionThread(QThread):
chunk_dur = len(result.audio) / rate chunk_dur = len(result.audio) / rate
chunk_start = current_time chunk_start = current_time
audio_np = to_float32(result.audio)
# Write audio directly to merged file ONLY if merging # Write audio directly to merged file ONLY if merging
if merge_chapters_at_end and merged_out_file: if merge_chapters_at_end and merged_out_file:
merged_out_file.write(result.audio) merged_out_file.write(audio_np)
elif merge_chapters_at_end and ffmpeg_proc: elif merge_chapters_at_end and ffmpeg_proc:
if hasattr(result.audio, "numpy"): ffmpeg_proc.stdin.write(audio_np.tobytes())
audio_bytes = (
result.audio.numpy().astype("float32").tobytes()
)
else:
audio_bytes = result.audio.astype("float32").tobytes()
ffmpeg_proc.stdin.write(audio_bytes)
if chapter_out_file: if chapter_out_file:
chapter_out_file.write(result.audio) chapter_out_file.write(audio_np)
elif chapter_ffmpeg_proc: elif chapter_ffmpeg_proc:
if hasattr(result.audio, "numpy"): chapter_ffmpeg_proc.stdin.write(audio_np.tobytes())
audio_bytes = (
result.audio.numpy().astype("float32").tobytes()
)
else:
audio_bytes = result.audio.astype("float32").tobytes()
chapter_ffmpeg_proc.stdin.write(audio_bytes)
# Subtitle logic # Subtitle logic
if self.subtitle_mode != "Disabled": if self.subtitle_mode != "Disabled":
tokens_list = getattr(result, "tokens", []) tokens_list = getattr(result, "tokens", [])
@@ -1700,7 +1685,7 @@ class ConversionThread(QThread):
# Concatenate audio and determine duration # Concatenate audio and determine duration
full_audio = ( full_audio = (
np.concatenate( np.concatenate(
[a.numpy() if hasattr(a, "numpy") else a for a in audio_chunks] [to_float32(a) for a in audio_chunks]
) )
if audio_chunks if audio_chunks
else np.zeros( else np.zeros(
@@ -1797,10 +1782,7 @@ class ConversionThread(QThread):
full_audio = ( full_audio = (
np.concatenate( np.concatenate(
[ [to_float32(a) for a in audio_chunks]
a.numpy() if hasattr(a, "numpy") else a
for a in audio_chunks
]
) )
if audio_chunks if audio_chunks
else np.zeros( else np.zeros(
@@ -1873,7 +1855,7 @@ class ConversionThread(QThread):
merged_out_file.write(audio_buffer) merged_out_file.write(audio_buffer)
merged_out_file.close() merged_out_file.close()
elif ffmpeg_proc: elif ffmpeg_proc:
ffmpeg_proc.stdin.write(audio_buffer.astype("float32").tobytes()) ffmpeg_proc.stdin.write(to_float32(audio_buffer).tobytes())
ffmpeg_proc.stdin.close() ffmpeg_proc.stdin.close()
ffmpeg_proc.wait() ffmpeg_proc.wait()