FlowHigh.AXERA / python /demo_board.py
HY-2012's picture
FlowHigh AX650 U16/S8 quantized models, Python runtime and samples
c760209 verified
Raw History Blame Contribute Delete
3.95 kB
import argparse
import time
from pathlib import Path
import numpy as np
import soundfile as sf
from flowhigh_sdk import audio
def decode_mel(voc, mel, chunk, overlap):
hop = chunk - overlap
wav = None
for s in range(0, mel.shape[-1], hop):
m = mel[..., s:s + chunk]
if m.shape[-1] < chunk:
m = np.pad(m, ((0, 0), (0, 0), (0, chunk - m.shape[-1])), mode="edge")
c = voc.run(None, {"mel": np.ascontiguousarray(m)})[0][0, 0]
if wav is None:
wav = c
else:
n = min(overlap * 480, len(wav), len(c))
if n > 0:
fade = np.linspace(0.0, 1.0, n, dtype=np.float32)
wav[-n:] = wav[-n:] * (1.0 - fade) + c[:n] * fade
wav = np.concatenate([wav, c[n:]])
return wav.astype(np.float32)
def main():
p = argparse.ArgumentParser()
p.add_argument("--input", required=True)
p.add_argument("--output", required=True)
p.add_argument("--models", default="models")
p.add_argument("--steps", type=int, default=1)
p.add_argument("--std", type=float, default=1e-4)
p.add_argument("--seed", type=int, default=0)
p.add_argument("--chunk", type=int, default=100)
p.add_argument("--overlap", type=int, default=20)
p.add_argument("--flow-overlap", type=int, default=50)
p.add_argument("--time-emb", default="assets/time_emb_4.npy")
args = p.parse_args()
x, sr = sf.read(args.input, always_2d=False)
if x.ndim > 1:
x = x.mean(axis=1)
x = np.asarray(x, dtype=np.float32)
target = int(round(audio.SR / sr * x.size))
x = audio.resample(x, sr)
peak = float(np.max(np.abs(x)))
if peak > 0:
x = x / peak
cond_wave = x.astype(np.float32)
mel = audio.log_mel(cond_wave)[0].T[None]
frames = 500
if mel.shape[-1] < frames:
mel = np.pad(mel, ((0, 0), (0, 0), (0, frames - mel.shape[-1])), mode="edge")
total = mel.shape[-1]
if total > frames:
n_chunks = (total - frames + (frames - args.flow_overlap) - 1) // (frames - args.flow_overlap) + 1
padded = frames + (n_chunks - 1) * (frames - args.flow_overlap)
if padded > total:
mel = np.pad(mel, ((0, 0), (0, 0), (0, padded - total)), mode="edge")
padded_total = mel.shape[-1]
import axengine
flow = axengine.InferenceSession(str(Path(args.models) / "flowhigh.axmodel"))
voc = axengine.InferenceSession(str(Path(args.models) / "bigvgan.axmodel"))
emb = np.load(args.time_emb).astype(np.float32)
rng = np.random.RandomState(args.seed)
hop = frames - args.flow_overlap
starts = list(range(0, max(1, padded_total - frames + 1), hop))
t0 = time.time()
flow_ms = []
out = None
for s in starts:
c = mel[..., s:s + frames]
y = c + args.std * rng.randn(*c.shape).astype(np.float32)
for i in range(args.steps):
t1 = time.time()
v = flow.run(None, {
"x": np.ascontiguousarray(y),
"cond": np.ascontiguousarray(c),
"time_emb": np.ascontiguousarray(emb[i]),
})[0]
flow_ms.append((time.time() - t1) * 1000)
y = y + (1.0 / args.steps) * v
wav = decode_mel(voc, y, args.chunk, args.overlap)
if out is None:
out = wav
else:
n = min(args.flow_overlap * 480, len(out), len(wav))
if n > 0:
fade = np.linspace(0.0, 1.0, n, dtype=np.float32)
out[-n:] = out[-n:] * (1.0 - fade) + wav[:n] * fade
out = np.concatenate([out, wav[n:]])
out = out[:int(total * 480)]
out = audio.postprocess(out, cond_wave[:len(out)], len(out))[:target]
sf.write(args.output, out, audio.SR, subtype="PCM_16")
dt = time.time() - t0
print(f"chunks={len(starts)} flow_ms={flow_ms} total_ms={dt*1000:.1f} rtf={dt/(x.size/audio.SR):.4f}")
if __name__ == "__main__":
main()