v4
This commit is contained in:
@@ -91,6 +91,58 @@ def stop_playback():
|
||||
pass
|
||||
|
||||
|
||||
# DONA_WAVES_V1 — volume réel du WAV Wyoming Piper (50 ms, hors micro).
|
||||
from array import array as _dona_array
|
||||
import sys as _dona_sys
|
||||
_dona_level_callback = None
|
||||
|
||||
def dona_set_level_callback(callback):
|
||||
global _dona_level_callback
|
||||
_dona_level_callback = callback
|
||||
|
||||
def _dona_emit_level(value):
|
||||
if _dona_level_callback is not None:
|
||||
try:
|
||||
_dona_level_callback(max(0.0, min(1.0, float(value))))
|
||||
except Exception:
|
||||
pass # Une erreur côté interface ne doit jamais interrompre Piper.
|
||||
|
||||
def _dona_waves_stream_envelope(wav_path, proc):
|
||||
"""Échantillonne le PCM pendant aplay, sans enregistrer ni exposer le son."""
|
||||
try:
|
||||
with wave.open(wav_path, 'rb') as stream:
|
||||
if stream.getsampwidth() != 2:
|
||||
return # Autre format : état « speaking » en secours côté navigateur.
|
||||
rate=stream.getframerate()
|
||||
if rate <= 0:
|
||||
return
|
||||
frames=max(1,int(rate * .05))
|
||||
started=time.monotonic()
|
||||
index=0
|
||||
while proc.poll() is None:
|
||||
raw=stream.readframes(frames)
|
||||
if not raw:
|
||||
break
|
||||
samples=_dona_array('h')
|
||||
samples.frombytes(raw)
|
||||
if _dona_sys.byteorder=='big':
|
||||
samples.byteswap()
|
||||
stride=max(1,len(samples)//420)
|
||||
window=samples[::stride]
|
||||
rms=(sum(s*s for s in window)/max(1,len(window)))**.5
|
||||
desired=started+index*frames/rate
|
||||
remaining=desired-time.monotonic()
|
||||
if remaining>0:
|
||||
time.sleep(min(remaining,.07))
|
||||
if proc.poll() is not None:
|
||||
break
|
||||
_dona_emit_level(min(1.0,rms/6500))
|
||||
index+=1
|
||||
except Exception as exc:
|
||||
print('[DONA WAVES] analyse PCM impossible :',type(exc).__name__,flush=True)
|
||||
finally:
|
||||
_dona_emit_level(0)
|
||||
|
||||
def speak(text):
|
||||
if not text or not text.strip():
|
||||
return
|
||||
@@ -123,6 +175,9 @@ def speak(text):
|
||||
_playback_proc = proc
|
||||
if _playback_cancel.is_set() and proc.poll() is None:
|
||||
proc.terminate()
|
||||
# DONA_WAVES_V1 : animation par l'amplitude du vrai son joué.
|
||||
threading.Thread(target=_dona_waves_stream_envelope,
|
||||
args=(wav_path,proc),daemon=True).start()
|
||||
try:
|
||||
result = proc.wait(timeout=120)
|
||||
except subprocess.TimeoutExpired:
|
||||
|
||||
Reference in New Issue
Block a user