This guide provides a complete step-by-step installation and integration setup for connecting a 3ds Max plugin with NVIDIA ACE and Audio2Face (A2F) using Docker and gRPC.
.fxa format.wsl --install
wsl --set-default-version 2
Then, open Ubuntu and update packages:
sudo apt update && sudo apt upgrade -y
Check NVIDIA Driver Version:
nvidia-smi
If missing or outdated, install:
sudo apt install nvidia-driver-535
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
The following MaxScript launches Python to send audio and face mesh data to A2F:
global A2F_Client = "C:\\path\\to\\a2f_client.py"
fn runA2F audioFile configYml ipPort = (
local cmd = "python \"" + A2F_Client + "\" \"" + audioFile + "\" \"" + configYml + "\" -u " + ipPort
shellLaunch "cmd.exe" ("/c " + cmd)
format "A2F Process Started... \n"
)
runA2F "C:\\path\\to\\audio.wav" "C:\\path\\to\\config.yml" "127.0.0.1:52000"
Install dependencies:
pip install grpcio grpcio-tools numpy pandas
Python gRPC Client:
import grpc
import csv
import sys
import os
import a2f_pb2
import a2f_pb2_grpc
def send_audio(audio_file, config_file, ip_port):
# Validate input files
if not os.path.exists(audio_file):
print(f"Error: Audio file '{audio_file}' not found.")
return
if not os.path.exists(config_file):
print(f"Error: Config file '{config_file}' not found.")
return
# Establish gRPC channel with timeout and error handling
try:
channel = grpc.insecure_channel(ip_port)
stub = a2f_pb2_grpc.A2FServiceStub(channel)
except grpc.RpcError as e:
print(f"gRPC Error: {e}")
return
# Read audio file
try:
with open(audio_file, "rb") as f:
audio_data = f.read()
except Exception as e:
print(f"Error reading audio file: {e}")
return
# Create request and send to A2F Controller
request = a2f_pb2.A2FRequest(audio=audio_data, config=config_file)
try:
response = stub.ProcessAudio(request, timeout=10) # 10s timeout
except grpc.RpcError as e:
print(f"gRPC Processing Error: {e}")
return
# Ensure output directory exists
output_csv = "C:\\path\\to\\output_keyframes.csv"
os.makedirs(os.path.dirname(output_csv), exist_ok=True)
# Save keyframes to CSV
try:
with open(output_csv, "w", newline="") as csvfile:
writer = csv.writer(csvfile)
writer.writerow(["frame", "blendshape", "value"])
for frame in response.keyframes:
writer.writerow([frame.time, frame.name, frame.value])
print(f"Keyframes saved to {output_csv}")
except Exception as e:
print(f"Error writing CSV: {e}")
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python client.py <audio_file> <config_file> <ip:port>")
else:
send_audio(sys.argv[1], sys.argv[2], sys.argv[3])
Apply received keyframes to the 3D model:
fn applyKeyframes csvFile = (
local f = openFile csvFile
while not eof f do (
local line = readDelimitedString f ","
local frameNum = line[1] as integer
local blendshapeName = line[2] as string
local value = line[3] as float
local target = $FaceMesh.modifiers[#Morpher]
if target != undefined do (
local channelIndex = findItem target.channelNames blendshapeName
if channelIndex > 0 do (
at time frameNum target[blendshapeName].value = value
)
)
)
close f
format "Keyframes Applied! \n"
)
applyKeyframes "C:\\path\\to\\output_keyframes.csv"
This setup automates facial animation in 3ds Max using NVIDIA ACE & Audio2Face! 🎭