Instructions to use Sky-Kim/com.sky.sentis.audio2lipsync with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- unity-sentis
How to use Sky-Kim/com.sky.sentis.audio2lipsync with unity-sentis:
string modelName = "[Your model name here].sentis"; Model model = ModelLoader.Load(Application.streamingAssetsPath + "/" + modelName); IWorker engine = WorkerFactory.CreateWorker(BackendType.GPUCompute, model); // Please see provided C# file for more details
- Notebooks
- Google Colab
- Kaggle
Download Runtime/LipsyncPostProcess.cs from Sky-Kim/com.sky.sentis.audio2lipsync: direct link, hf CLI and curl.
- Browser
- Download file 6.2 kB
-
https://huggingface.co/Sky-Kim/com.sky.sentis.audio2lipsync/resolve/main/Runtime/LipsyncPostProcess.cs
- Command line
-
hf download hf://Sky-Kim/com.sky.sentis.audio2lipsync/Runtime/LipsyncPostProcess.cs
-
curl -L -o LipsyncPostProcess.cs https://huggingface.co/Sky-Kim/com.sky.sentis.audio2lipsync/resolve/main/Runtime/LipsyncPostProcess.cs
6.2 kB
| using System; | |
| using UnityEngine; | |
| namespace Audio2Lipsync | |
| { | |
| /// <summary>Post-processing knobs — same names/defaults as server.py LipSyncRequest.</summary> | |
| [] | |
| public struct LipsyncSettings | |
| { | |
| [] public int smoothWindow; // moving-average window (1 = off) | |
| [] public float gain; // centered amplification around the rest pose | |
| [] public float silenceGate; // RMS mouth gate strength (0 = off) | |
| [] public float silenceThresholdDb; | |
| [] public float cfgScale; // classifier-free guidance (1 = off) | |
| public static LipsyncSettings Default => new LipsyncSettings | |
| { | |
| smoothWindow = 3, gain = 1.5f, silenceGate = 0f, silenceThresholdDb = -40f, cfgScale = 1f, | |
| }; | |
| } | |
| /// <summary> | |
| /// Pure-C# port of server.py run_inference() post-processing. Frames are row-major [n, 52]. | |
| /// </summary> | |
| public static class LipsyncPostProcess | |
| { | |
| const int N = A2LConstants.NumBlendshapes; | |
| const int FadeFrames = 6; | |
| /// <param name="cond">denormalized model output [n*52]</param> | |
| /// <param name="uncond">silent-audio output [n*52] (only read when cfgScale != 1)</param> | |
| public static float[] Apply(float[] cond, float[] uncond, int nFrames, float[] audio16k, LipsyncSettings s) | |
| { | |
| var bs = new float[nFrames * N]; | |
| Array.Copy(cond, bs, bs.Length); | |
| if (s.cfgScale != 1f && uncond != null) | |
| for (int i = 0; i < bs.Length; i++) | |
| bs[i] = uncond[i] + s.cfgScale * (bs[i] - uncond[i]); | |
| Clip01(bs); | |
| // Zero non-mouth channels (brows/eyes/nose belong to the upper-face layer). | |
| for (int f = 0; f < nFrames; f++) | |
| for (int c = 0; c < N; c++) | |
| if (!A2LConstants.KeepChannel[c]) bs[f * N + c] = 0f; | |
| if (s.gain != 1f) | |
| { | |
| for (int f = 0; f < nFrames; f++) | |
| for (int c = 0; c < N; c++) | |
| { | |
| if (!A2LConstants.KeepChannel[c]) continue; | |
| float rest = A2LConstants.Mean[c]; | |
| bs[f * N + c] = rest + s.gain * (bs[f * N + c] - rest); | |
| } | |
| Clip01(bs); | |
| } | |
| if (s.smoothWindow > 1 && nFrames > s.smoothWindow) | |
| { | |
| bs = UniformFilterFrames(bs, nFrames, s.smoothWindow); | |
| Clip01(bs); | |
| } | |
| if (s.silenceGate > 0f) | |
| { | |
| var gate = SilenceGate(audio16k, nFrames, s.silenceThresholdDb, s.silenceGate); | |
| for (int f = 0; f < nFrames; f++) | |
| for (int c = 0; c < N; c++) | |
| if (A2LConstants.KeepChannel[c]) bs[f * N + c] *= gate[f]; | |
| } | |
| // ~0.1 s fade in/out so the mouth opens from and closes to neutral. | |
| for (int f = 0; f < Mathf.Min(FadeFrames, nFrames); f++) | |
| { | |
| float k = (f + 1) / (float)FadeFrames; | |
| for (int c = 0; c < N; c++) | |
| { | |
| bs[f * N + c] *= k; | |
| bs[(nFrames - 1 - f) * N + c] *= k; | |
| } | |
| } | |
| return bs; | |
| } | |
| static void Clip01(float[] a) | |
| { | |
| for (int i = 0; i < a.Length; i++) a[i] = Mathf.Clamp01(a[i]); | |
| } | |
| // scipy.ndimage.uniform_filter1d(axis=0, mode="nearest"): window [i - w/2, i - w/2 + w - 1]. | |
| static float[] UniformFilterFrames(float[] src, int nFrames, int w) | |
| { | |
| var dst = new float[src.Length]; | |
| int left = w / 2; | |
| for (int f = 0; f < nFrames; f++) | |
| for (int c = 0; c < N; c++) | |
| { | |
| float sum = 0f; | |
| for (int k = 0; k < w; k++) | |
| { | |
| int j = Mathf.Clamp(f - left + k, 0, nFrames - 1); | |
| sum += src[j * N + c]; | |
| } | |
| dst[f * N + c] = sum / w; | |
| } | |
| return dst; | |
| } | |
| // server.py compute_silence_gate() | |
| static float[] SilenceGate(float[] audio, int nFrames, float thresholdDb, float strength) | |
| { | |
| var gate = new float[nFrames]; | |
| int spf = Mathf.Max(1, A2LConstants.AudioSampleRate / A2LConstants.FaceFps); | |
| int nAudio = audio.Length / spf; | |
| if (nAudio < 1) { for (int i = 0; i < nFrames; i++) gate[i] = 1f; return gate; } | |
| var rms = new float[nAudio]; | |
| for (int i = 0; i < nAudio; i++) | |
| { | |
| double acc = 0; | |
| for (int k = 0; k < spf; k++) { float v = audio[i * spf + k]; acc += v * v; } | |
| rms[i] = (float)Math.Sqrt(acc / spf + 1e-12); | |
| } | |
| int smoothN = Mathf.Max(1, (int)(0.25f * A2LConstants.FaceFps)); | |
| if (smoothN > 1 && nAudio > smoothN) | |
| { | |
| var sm = new float[nAudio]; | |
| int left = smoothN / 2; | |
| for (int i = 0; i < nAudio; i++) | |
| { | |
| float sum = 0f; | |
| for (int k = 0; k < smoothN; k++) sum += rms[Mathf.Clamp(i - left + k, 0, nAudio - 1)]; | |
| sm[i] = sum / smoothN; | |
| } | |
| rms = sm; | |
| } | |
| var raw = new float[nAudio]; | |
| for (int i = 0; i < nAudio; i++) | |
| { | |
| float db = 20f * Mathf.Log10(Mathf.Max(rms[i], 1e-8f)); | |
| raw[i] = 1f / (1f + Mathf.Exp(-(db - thresholdDb) / 4f)); | |
| } | |
| for (int f = 0; f < nFrames; f++) | |
| { | |
| float x = nFrames == 1 ? 0f : f / (float)(nFrames - 1) * (nAudio - 1); | |
| int i0 = Mathf.Clamp((int)x, 0, nAudio - 1), i1 = Mathf.Min(i0 + 1, nAudio - 1); | |
| float g = Mathf.Lerp(raw[i0], raw[i1], x - i0); | |
| gate[f] = (1f - strength) + strength * g; | |
| } | |
| return gate; | |
| } | |
| } | |
| } | |