Download python/cutetts_ax/tts.py from HY-2012/CuteTTS.AXERA: direct link, hf CLI and curl.
- Browser
- Download file 23.6 kB
-
https://huggingface.co/HY-2012/CuteTTS.AXERA/resolve/main/python/cutetts_ax/tts.py
- Command line
-
hf download hf://HY-2012/CuteTTS.AXERA/python/cutetts_ax/tts.py
-
curl -L -o tts.py https://huggingface.co/HY-2012/CuteTTS.AXERA/resolve/main/python/cutetts_ax/tts.py
23.6 kB
| """CuteTTS-distill AX650 runtime (numpy + axengine, no torch/onnxruntime). | |
| Pipeline (all heavy parts run on the NPU): | |
| reference wav --vae_enc--> latents --locenc--> speech embeddings \ | |
| reference wav --logmel-> speaker_enc --> speaker embedding ---------> prompt embeds | |
| prompt embeds --lm_prefill--> hidden / kv cache | |
| loop: | |
| hidden --dit_head (4 Euler steps)--> latent patch | |
| latent patch --locenc_step--> next token embedding --lm_decode--\ | |
| latents --vae_dec--> waveform | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import time | |
| import numpy as np | |
| from . import audio as A | |
| ASSETS = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets") | |
| # --------------------------------------------------------------------- engines | |
| class OnnxEngine: | |
| """Host-side verification engine (onnxruntime).""" | |
| def __init__(self, path): | |
| import onnxruntime as ort | |
| self.sess = ort.InferenceSession(path, providers=["CPUExecutionProvider"]) | |
| self.output_names = [o.name for o in self.sess.get_outputs()] | |
| def run(self, feed): | |
| return self.sess.run(self.output_names, feed) | |
| def close(self): | |
| pass | |
| class AxEngine: | |
| """AX650 NPU engine (pyaxengine).""" | |
| def __init__(self, path): | |
| import axengine as axe | |
| self.sess = axe.InferenceSession(path, providers=["AxEngineExecutionProvider"]) | |
| self.input_names = [i.name for i in self.sess.get_inputs()] | |
| self.output_names = [o.name for o in self.sess.get_outputs()] | |
| def run(self, feed): | |
| return self.sess.run(self.output_names, {k: np.ascontiguousarray(feed[k]) for k in self.input_names}) | |
| def close(self): | |
| self.sess = None | |
| # ------------------------------------------------------------------ tokenizer | |
| class Tokenizer: | |
| """SentencePiece with HuggingFace-compatible added-token splitting.""" | |
| def __init__(self, model_path): | |
| import sentencepiece as spm | |
| self.sp = spm.SentencePieceProcessor() | |
| self.sp.Load(model_path) | |
| folder = os.path.dirname(model_path) | |
| self.specials = {"<unk>": 0, "<s>": 1, "</s>": 2, "<pad>": 3} | |
| cfg_path = os.path.join(folder, "tokenizer_config.json") | |
| if os.path.exists(cfg_path): | |
| cfg = json.load(open(cfg_path)) | |
| for tid, entry in cfg.get("added_tokens_decoder", {}).items(): | |
| self.specials[entry["content"]] = int(tid) | |
| added = os.path.join(folder, "added_tokens.json") | |
| if os.path.exists(added): | |
| for k, v in json.load(open(added)).items(): | |
| self.specials[k] = int(v) | |
| self._keys = sorted(self.specials, key=len, reverse=True) | |
| def encode(self, text): | |
| out, buf, i = [], [], 0 | |
| while i < len(text): | |
| hit = None | |
| for k in self._keys: | |
| if text.startswith(k, i): | |
| hit = k | |
| break | |
| if hit is None: | |
| buf.append(text[i]) | |
| i += 1 | |
| continue | |
| chunk = "".join(buf).strip() | |
| if chunk: | |
| out += list(self.sp.encode(chunk, out_type=int)) | |
| out.append(self.specials[hit]) | |
| buf = [] | |
| i += len(hit) | |
| chunk = "".join(buf).strip() | |
| if chunk: | |
| out += list(self.sp.encode(chunk, out_type=int)) | |
| return out | |
| class CuteTTSAX: | |
| def __init__(self, model_dir=None, assets_dir=ASSETS, engine="ax", verbose=True): | |
| t_init = time.time() | |
| self.root = os.path.abspath(model_dir or os.environ.get("CUTETTS_MODEL_DIR", ".")) | |
| self.assets = os.path.abspath(assets_dir) | |
| self.cfg = json.load(open(os.path.join(self.assets, "config.json"))) | |
| self.verbose = verbose | |
| base_mk = AxEngine if engine == "ax" else OnnxEngine | |
| def mk(path_): | |
| # AX CMM is a shared pool; retry briefly on transient load failures | |
| import gc | |
| last = None | |
| for _ in range(4): | |
| try: | |
| return base_mk(path_) | |
| except Exception as exc: # noqa: BLE001 | |
| last = exc | |
| gc.collect() | |
| time.sleep(0.4) | |
| raise last | |
| onnx_dir = os.path.join(self.root, "onnx") | |
| def path(name): | |
| for cand in (os.path.join(self.root, name + ".axmodel"), | |
| os.path.join(self.root, "models", name + ".axmodel"), | |
| os.path.join(onnx_dir, name + ".onnx")): | |
| if os.path.exists(cand): | |
| return cand | |
| raise FileNotFoundError(name) | |
| self._find = path | |
| self._vae_enc = mk(path("vae_enc")) | |
| self.m_vae_stream = None | |
| self.vae_state_shapes = None | |
| self._stream_path = None | |
| sp = os.path.join(self.assets, "vae_stream.json") | |
| if os.path.exists(sp): | |
| try: | |
| self._stream_path = path("vae_dec_stream") | |
| self.vae_state_shapes = [tuple(int(v) for v in sh) | |
| for sh in json.load(open(sp))["states"]] | |
| except FileNotFoundError: | |
| self._stream_path = None | |
| self.m_spk = mk(path("spk_body")) | |
| self.m_loc_step = mk(path("locenc_step")) | |
| self.m_dit = mk(path("dit_step")) | |
| self.m_pref = mk(path("lm_prefill")) | |
| self.m_dec = mk(path("lm_decode")) | |
| self.m_vae_dec = {} | |
| self._dec_paths = {} | |
| for lb in (64, 128, 256): | |
| try: | |
| self._dec_paths[lb] = path(f"vae_dec_l{lb}") | |
| except FileNotFoundError: | |
| pass | |
| self.embed = np.load(os.path.join(self.assets, "embed_tokens.npy")).astype(np.float32) | |
| self.spk_proj = np.load(os.path.join(self.assets, "lm_speaker_proj.npy")) | |
| self.mel_filter = np.load(os.path.join(self.assets, "mel_filter.npy")) | |
| self.spk_bn = (np.load(os.path.join(self.assets, "spk_bn_mean.npy")), | |
| np.load(os.path.join(self.assets, "spk_bn_var.npy")), | |
| np.load(os.path.join(self.assets, "spk_bn_w.npy")), | |
| np.load(os.path.join(self.assets, "spk_bn_b.npy")), | |
| np.load(os.path.join(self.assets, "spk_linear_w.npy")), | |
| np.load(os.path.join(self.assets, "spk_linear_b.npy"))) | |
| self.mel_window = np.load(os.path.join(self.assets, "mel_window.npy")) | |
| self.tok = Tokenizer(self._find_tokenizer()) | |
| self.scale = float(self.cfg["speech_scaling_factor"]) | |
| self.bias = float(self.cfg["speech_bias_factor"]) | |
| self.L = int(self.cfg["lm_prefill_len"]) | |
| self.B = int(self.cfg["lm_kv_capacity"]) | |
| self.sr = int(self.cfg["sample_rate"]) | |
| self.rng = np.random.default_rng(42) | |
| self.last_timing = {} | |
| self.init_seconds = time.time() - t_init | |
| def _find_tokenizer(self): | |
| for p in (os.path.join(self.assets, "tokenizer", "tokenizer.model"), | |
| os.path.join(self.root, "tokenizer", "tokenizer.model"), | |
| os.path.join(self.root, "tokenizer.model")): | |
| if os.path.exists(p): | |
| return p | |
| raise FileNotFoundError("tokenizer.model") | |
| # ------------------------------------------------------------------ helpers | |
| def _rms(x): | |
| return np.sqrt(np.mean(x * x) + 1e-12) | |
| def _ref_latents(self, wav24): | |
| cap = int(self.cfg["vae_enc_samples"]) | |
| hop = int(self.cfg["vae_hop"]) | |
| x = np.asarray(wav24[:cap], dtype=np.float32) | |
| nreal = len(x) // hop | |
| if len(x) < cap: | |
| x = np.pad(x, (0, cap - len(x))) | |
| lat = self._vae_enc.run({"wav": x.reshape(1, 1, -1)})[0].reshape(-1, 64) | |
| return lat[:max(nreal, 1)].astype(np.float32) | |
| def _speaker_embedding(self, wav16): | |
| mel = A.logmel(wav16, self.mel_filter, self.mel_window, | |
| n_fft=self.cfg["mel"]["n_fft"], hop=self.cfg["mel"]["hop"], | |
| win_length=self.cfg["mel"]["win_length"], eps=self.cfg["mel"]["eps"]) | |
| T = int(self.cfg["speaker_mel_frames"]) | |
| if mel.shape[1] < T: | |
| mel = np.pad(mel, ((0, 0), (0, T - mel.shape[1]))) | |
| else: | |
| mel = mel[:, :T] | |
| stats = self.m_spk.run({"logmel": mel[None]})[0] | |
| # BN + linear + L2-normalize on host (tiny, keeps the NPU graph conv-only) | |
| mean, var, w, b, lw, lb = self.spk_bn | |
| y = (stats - mean) / np.sqrt(var + 1e-5) * w + b | |
| emb = y @ lw.T + lb | |
| return (emb / np.linalg.norm(emb, axis=1, keepdims=True)).astype(np.float32) | |
| def _prompt_embeds(self, text, ref_lat_scaled, spk_emb): | |
| p = self.cfg["prompt"] | |
| ids_prefix = self.tok.encode(p["voice_prefix"]) | |
| ids_mid = self.tok.encode(p["voice_mid"]) | |
| ids_suffix = self.tok.encode(p["voice_suffix_fmt"].format(text)) | |
| # speech latents -> patches (pad to even) | |
| T = ref_lat_scaled.shape[0] | |
| if T % 2: | |
| ref_lat_scaled = np.concatenate([ref_lat_scaled, np.zeros((1, 64), np.float32)], 0) | |
| patches = ref_lat_scaled.reshape(-1, 2, 64) | |
| n_patch = patches.shape[0] | |
| if n_patch > int(self.cfg["locenc_prompt_patches"]): | |
| n_patch = int(self.cfg["locenc_prompt_patches"]) | |
| patches = patches[:n_patch] | |
| # LocEnc is per-patch: reuse the single-patch graph (cheap, 23..63 calls) | |
| speech_emb = np.concatenate( | |
| [self.m_loc_step.run({"patches": patches[i:i + 1][None].astype(np.float32)})[0][0] | |
| for i in range(n_patch)], axis=0) | |
| seq_ids = ids_prefix + [0] + ids_mid + [0] * n_patch + ids_suffix | |
| n = len(seq_ids) | |
| if n > self.L: | |
| raise ValueError(f"prompt too long ({n} > {self.L}); use a shorter reference/text") | |
| emb = self.embed[np.asarray(seq_ids, dtype=np.int64)] | |
| spk_pos = len(ids_prefix) | |
| emb[spk_pos] = (spk_emb @ self.spk_proj.T)[0] | |
| s0 = spk_pos + 1 + len(ids_mid) | |
| emb[s0:s0 + n_patch] = speech_emb | |
| out = np.zeros((1, self.L, 1024), np.float32) | |
| out[0, :n] = emb | |
| return out, n | |
| def _rope(pos): | |
| inv = 1.0 / (1e6 ** (np.arange(0, 128, 2, dtype=np.float32) / 128.0)) | |
| freqs = np.float32(pos) * inv | |
| emb = np.concatenate([freqs, freqs]) | |
| return (np.cos(emb).astype(np.float32).reshape(1, 1, 1, 128), | |
| np.sin(emb).astype(np.float32).reshape(1, 1, 1, 128)) | |
| def _get_dec(self, bucket): | |
| if bucket not in self.m_vae_dec: | |
| if bucket not in self._dec_paths: | |
| raise ValueError(f"no VAE decoder for {bucket} latents") | |
| self.m_vae_dec[bucket] = self._find_engine(self._dec_paths[bucket]) | |
| return self.m_vae_dec[bucket] | |
| def _find_engine(self, path_): | |
| mk = AxEngine if path_.endswith(".axmodel") else OnnxEngine | |
| import gc | |
| last = None | |
| for _ in range(4): | |
| try: | |
| return mk(path_) | |
| except Exception as exc: # noqa: BLE001 | |
| last = exc | |
| gc.collect() | |
| time.sleep(0.4) | |
| raise last | |
| def _get_stream_dec(self): | |
| if self.m_vae_stream is None and self._stream_path is not None: | |
| self.m_vae_stream = self._find_engine(self._stream_path) | |
| return self.m_vae_stream | |
| def _prepare_reference(self, reference_audio, ref_max_seconds=5.0): | |
| """Reference latents + speaker embedding, cached per (path, size, seconds).""" | |
| try: | |
| key = (os.path.abspath(reference_audio), os.path.getsize(reference_audio), | |
| float(ref_max_seconds)) | |
| except OSError: | |
| key = None | |
| cache = getattr(self, "_ref_cache", None) | |
| if cache is None: | |
| cache = self._ref_cache = {} | |
| if key is not None and key in cache: | |
| self.ref_cache_hit = True | |
| return cache[key] | |
| self.ref_cache_hit = False | |
| wav, sr = A.read_wav(reference_audio) | |
| wav24 = A.resample_to(wav, sr, self.sr) | |
| cap = int(self.cfg["vae_enc_samples"]) / float(self.sr) | |
| wav24 = wav24[: int(min(ref_max_seconds, cap) * self.sr)] | |
| if len(wav24) < 2 * self.sr: | |
| wav24 = np.tile(wav24, int(np.ceil(2 * self.sr / len(wav24)))) | |
| wav16 = A.resample_to(wav, sr, 16000)[: 8 * 16000] | |
| if len(wav16) < 32000: | |
| wav16 = np.tile(wav16, int(np.ceil(32000 / len(wav16)))) | |
| lat = self._ref_latents(wav24) | |
| spk = self._speaker_embedding(wav16) | |
| if key is not None: | |
| cache[key] = (lat, spk) | |
| return lat, spk | |
| # ------------------------------------------------------------------ streaming | |
| def _decode_window(self, vae_lat, start, end): | |
| """Exact VAE decode of latents [start, end) using a 128-latent sliding window. | |
| The DAC decoder is causal with a 64-latent receptive field (verified: zeroing | |
| the first 64 latents leaves all later samples bit-identical), so a window of | |
| ``[end-128, end)`` reproduces the offline decode exactly. | |
| """ | |
| w0 = max(0, end - 128) | |
| win = np.zeros((1, 64, 128), np.float32) | |
| n = end - w0 | |
| win[0, :, :n] = np.stack(vae_lat[w0:end], axis=1) | |
| out = self._get_dec(128).run({"latent": win})[0][0, 0] | |
| return out[(start - w0) * 1920:(end - w0) * 1920] | |
| # --------------------------------------------------------------------- api | |
| def synthesize(self, text, reference_audio, max_new=None, cfg_strength=None, seed=42, | |
| ref_max_seconds=5.0, rng=None): | |
| t0 = time.time() | |
| cfg_strength = self.cfg["cfg_strength"] if cfg_strength is None else float(cfg_strength) | |
| max_new = int(self.cfg["max_new_patches"] if max_new is None else max_new) | |
| self.rng = rng if rng is not None else np.random.default_rng(seed) | |
| # ---- reference features ------------------------------------------------- | |
| t1 = time.time() | |
| lat, spk = self._prepare_reference(reference_audio, ref_max_seconds) | |
| t2 = t3 = time.time() | |
| lat_scaled = (lat + self.bias) * self.scale | |
| embeds, plen = self._prompt_embeds(text, lat_scaled, spk) | |
| t4 = time.time() | |
| # ---- prefill ------------------------------------------------------------ | |
| pre = self.m_pref.run({"embeds": embeds}) | |
| hidden = pre[0][0, plen - 1].reshape(1, 1024) | |
| stop = pre[1].reshape(2) | |
| kvs = [] | |
| for i in range(7): | |
| kk = np.zeros((1, 8, self.B, 128), np.float32) | |
| vv = np.zeros((1, 8, self.B, 128), np.float32) | |
| kk[0, :, :plen] = pre[2 + 2 * i][0, :, :plen] | |
| vv[0, :, :plen] = pre[3 + 2 * i][0, :, :plen] | |
| kvs += [kk, vv] | |
| t5 = time.time() | |
| # ---- autoregressive loop ------------------------------------------------ | |
| mask = np.full((1, 1, 1, self.B + 1), -30.0, np.float32) | |
| mask[..., :plen] = 0.0 | |
| cond = np.zeros((1, 2, 64), np.float32) | |
| cfg_t = np.array([cfg_strength], np.float32) | |
| latents = [] | |
| pos = plen | |
| n_steps = 0 | |
| t_dit = t_dec = 0.0 | |
| for step in range(max_new): | |
| stop_now = int(np.argmax(stop)) == 1 | |
| x0 = self.rng.standard_normal((1, 2, 64)).astype(np.float32) | |
| t_a = time.time() | |
| x = x0 | |
| for k in range(int(self.cfg["diffusion_steps"])): | |
| v = self.m_dit.run({"x": x, "z": hidden, "cond": cond, "speaker": spk, | |
| "cfg_strength": cfg_t, "t": np.array([k * 0.25], np.float32), | |
| "dt": np.array([0.25], np.float32)})[0] | |
| x = x + v * 0.25 | |
| pred = x | |
| t_dit += time.time() - t_a | |
| latents.append(pred.copy()) | |
| n_steps += 1 | |
| if stop_now: | |
| break | |
| cond = pred | |
| emb = self.m_loc_step.run({"patches": pred[None].astype(np.float32)})[0] | |
| if pos >= self.B - 1: | |
| break | |
| mask[..., pos] = 0.0 | |
| mask[..., self.B] = 0.0 | |
| cos, sin = self._rope(pos) | |
| feed = {"embed": emb, "mask_add": mask, "cos": cos, "sin": sin} | |
| for i in range(7): | |
| feed[f"pk{i}"] = kvs[2 * i] | |
| feed[f"pv{i}"] = kvs[2 * i + 1] | |
| t_a = time.time() | |
| d = self.m_dec.run(feed) | |
| t_dec += time.time() - t_a | |
| hidden = d[0].reshape(1, 1024) | |
| stop = d[1].reshape(2) | |
| for i in range(7): | |
| kvs[2 * i][0, :, pos] = d[2 + 2 * i][0, :, 0] | |
| kvs[2 * i + 1][0, :, pos] = d[3 + 2 * i][0, :, 0] | |
| pos += 1 | |
| t6 = time.time() | |
| # ---- decode ------------------------------------------------------------- | |
| lat = np.concatenate(latents, axis=0) # [N,2,64] | |
| self.last_patches = lat.copy() | |
| vae_lat = (lat / self.scale - self.bias).reshape(-1, 64).T[None] # [1,64,2N] | |
| n = vae_lat.shape[-1] | |
| bucket = next((b for b in sorted(self._dec_paths) if n <= b), max(self._dec_paths)) | |
| if n > bucket: | |
| raise ValueError(f"audio too long for available decoders ({n} > {bucket} latents)") | |
| padded = np.zeros((1, 64, bucket), np.float32) | |
| padded[:, :, :n] = vae_lat | |
| self.last_latents = vae_lat | |
| self.last_bucket = bucket | |
| wav_out = self._get_dec(bucket).run({"latent": padded})[0][0, 0, : n * int(self.cfg["vae_hop"])] | |
| t7 = time.time() | |
| self.last_timing = { | |
| "vae_enc": t2 - t1, "speaker_enc": t3 - t2, "prompt": t4 - t3, | |
| "prefill": t5 - t4, "ar_loop": t6 - t5, "dit": t_dit, "lm_decode": t_dec, | |
| "vae_dec": t7 - t6, "total": t7 - t0, "steps": n_steps, | |
| "audio_seconds": len(wav_out) / self.sr, | |
| } | |
| return wav_out.astype(np.float32) | |
| def synthesize_stream(self, text, reference_audio, max_new=None, cfg_strength=None, seed=42, | |
| ref_max_seconds=5.0, rng=None, first_chunk=16, chunk=32, windowed=False): | |
| """True streaming synthesis: yields float32 PCM chunks while the AR loop runs. | |
| Nothing is sentence-split: the AR loop keeps generating latents and audio is | |
| decoded/emitted every ``chunk`` latents (2.56 s) after an initial | |
| ``first_chunk`` latents (1.28 s), using an exact 128-latent sliding window. | |
| """ | |
| t0 = time.time() | |
| cfg_strength = self.cfg["cfg_strength"] if cfg_strength is None else float(cfg_strength) | |
| max_new = int(self.cfg["max_new_patches"] if max_new is None else max_new) | |
| self.rng = rng if rng is not None else np.random.default_rng(seed) | |
| lat, spk = self._prepare_reference(reference_audio, ref_max_seconds) | |
| lat_scaled = (lat + self.bias) * self.scale | |
| embeds, plen = self._prompt_embeds(text, lat_scaled, spk) | |
| pre = self.m_pref.run({"embeds": embeds}) | |
| hidden = pre[0][0, plen - 1].reshape(1, 1024) | |
| stop = pre[1].reshape(2) | |
| kvs = [] | |
| for i in range(7): | |
| kk = np.zeros((1, 8, self.B, 128), np.float32) | |
| vv = np.zeros((1, 8, self.B, 128), np.float32) | |
| kk[0, :, :plen] = pre[2 + 2 * i][0, :, :plen] | |
| vv[0, :, :plen] = pre[3 + 2 * i][0, :, :plen] | |
| kvs += [kk, vv] | |
| mask = np.full((1, 1, 1, self.B + 1), -30.0, np.float32) | |
| mask[..., :plen] = 0.0 | |
| cond = np.zeros((1, 2, 64), np.float32) | |
| cfg_t = np.array([cfg_strength], np.float32) | |
| vae_lat = [] # VAE-space latents, 64 floats each | |
| emitted = 0 | |
| if not windowed and self._get_stream_dec() is not None: | |
| states = [np.zeros(sh, np.float32) for sh in self.vae_state_shapes] | |
| else: | |
| states = None | |
| pos = plen | |
| n_steps = 0 | |
| ttft = None | |
| n_chunks = 0 | |
| t_dit = t_dec = 0.0 | |
| def emit_patch(pred): | |
| """stateful per-patch decode (160 ms per call)""" | |
| nonlocal states, ttft, n_chunks | |
| vae = pred[0] / self.scale - self.bias # scaled (LM) space -> VAE space | |
| lat2 = np.ascontiguousarray(vae.T[None].astype(np.float32)) # [1,64,2] | |
| feed = {"latent": lat2} | |
| for i, st in enumerate(states): | |
| feed[f"s{i}"] = st | |
| out = self.m_vae_stream.run(feed) | |
| # copy: the engine reuses its output buffers, and we feed them back next step | |
| states = [np.array(o, copy=True) for o in out[1:]] | |
| n_chunks += 1 | |
| if ttft is None: | |
| ttft = time.time() - t0 | |
| return np.array(out[0][0, 0], copy=True) | |
| def emit(upto): | |
| nonlocal emitted, ttft, n_chunks | |
| audio = self._decode_window(vae_lat, emitted, upto) | |
| emitted = upto | |
| n_chunks += 1 | |
| if ttft is None: | |
| ttft = time.time() - t0 | |
| return audio | |
| for step in range(max_new): | |
| stop_now = int(np.argmax(stop)) == 1 | |
| x0 = self.rng.standard_normal((1, 2, 64)).astype(np.float32) | |
| t_a = time.time() | |
| x = x0 | |
| for k in range(int(self.cfg["diffusion_steps"])): | |
| v = self.m_dit.run({"x": x, "z": hidden, "cond": cond, "speaker": spk, | |
| "cfg_strength": cfg_t, "t": np.array([k * 0.25], np.float32), | |
| "dt": np.array([0.25], np.float32)})[0] | |
| x = x + v * 0.25 | |
| t_dit += time.time() - t_a | |
| cond = x | |
| n_steps += 1 | |
| for c in range(2): | |
| vae_lat.append((x[0, c] / self.scale - self.bias).astype(np.float32)) | |
| if states is not None: | |
| yield emit_patch(x) | |
| if stop_now: | |
| if states is None and emitted < len(vae_lat): | |
| yield emit(len(vae_lat)) | |
| break | |
| emb = self.m_loc_step.run({"patches": x[None].astype(np.float32)})[0] | |
| if pos >= self.B - 1: | |
| if emitted < len(vae_lat): | |
| yield emit(len(vae_lat)) | |
| break | |
| mask[..., pos] = 0.0 | |
| mask[..., self.B] = 0.0 | |
| cos, sin = self._rope(pos) | |
| feed = {"embed": emb, "mask_add": mask, "cos": cos, "sin": sin} | |
| for i in range(7): | |
| feed[f"pk{i}"] = kvs[2 * i] | |
| feed[f"pv{i}"] = kvs[2 * i + 1] | |
| t_a = time.time() | |
| d = self.m_dec.run(feed) | |
| t_dec += time.time() - t_a | |
| hidden = d[0].reshape(1, 1024) | |
| stop = d[1].reshape(2) | |
| for i in range(7): | |
| kvs[2 * i][0, :, pos] = d[2 + 2 * i][0, :, 0] | |
| kvs[2 * i + 1][0, :, pos] = d[3 + 2 * i][0, :, 0] | |
| pos += 1 | |
| if states is None: | |
| need = first_chunk if emitted == 0 else chunk | |
| if len(vae_lat) - emitted >= need: | |
| yield emit(len(vae_lat)) | |
| if states is None and emitted < len(vae_lat): | |
| yield emit(len(vae_lat)) | |
| self.last_timing = { | |
| "ttft": ttft or (time.time() - t0), "total": time.time() - t0, | |
| "audio_seconds": len(vae_lat) / 12.5, "steps": n_steps, | |
| "chunks": n_chunks, "dit": t_dit, "lm_decode": t_dec, | |
| "streaming": True, | |
| } | |