Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# ffmpeg (with arnndn/afftdn filters) + curl for model download
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ffmpeg ca-certificates curl && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Download a standard RNNoise model for ffmpeg's arnndn filter.
|
||||
# Source: GregorR/rnnoise-models (beguiling-drafts, a widely used general model).
|
||||
# Non-fatal: if the download fails, handler.py falls back to afftdn.
|
||||
RUN mkdir -p /app/models && \
|
||||
( curl -fsSL -o /app/models/rnnoise.rnnn \
|
||||
https://raw.githubusercontent.com/GregorR/rnnoise-models/master/beguiling-drafts-2018-08-30/bd.rnnn \
|
||||
|| echo "WARNING: RNNoise model download failed; will fall back to afftdn" )
|
||||
|
||||
COPY workers/audio-denoise/requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY workers/audio-denoise/handler.py .
|
||||
|
||||
CMD ["python3", "-u", "handler.py"]
|
||||
@@ -0,0 +1,26 @@
|
||||
# Audio Denoise Worker (RNNoise / arnndn)
|
||||
|
||||
RunPod Serverless CPU worker that removes background noise from audio using
|
||||
ffmpeg's RNNoise filter (`arnndn`), with a built-in FFT denoiser (`afftdn`)
|
||||
as an automatic fallback.
|
||||
|
||||
## Contract
|
||||
- Input: `{"audio": "<base64>"}` — any format/sample rate. A `data:...;base64,`
|
||||
prefix is stripped automatically.
|
||||
- Output: `{"audio": "<base64 WAV>"}` — denoised, 48 kHz mono WAV.
|
||||
- Error: `{"error": "<message>"}`.
|
||||
|
||||
## Pipeline
|
||||
decode base64 -> temp input file -> `ffmpeg -i in -af arnndn=m=/app/models/rnnoise.rnnn -ar 48000 -ac 1 out.wav` -> read -> base64.
|
||||
|
||||
## Deploy
|
||||
- Dockerfile path: `/workers/audio-denoise/Dockerfile`
|
||||
- Build context: repo root (`docker build -f workers/audio-denoise/Dockerfile .`)
|
||||
- GPU: none (CPU-only worker)
|
||||
- App env var to set: `RUNPOD_AUDIO_DENOISE_URL` (your deployed endpoint URL)
|
||||
|
||||
## Model
|
||||
The Dockerfile downloads the `beguiling-drafts` RNNoise model from
|
||||
`GregorR/rnnoise-models` to `/app/models/rnnoise.rnnn`. If that download
|
||||
fails at build time, the handler transparently falls back to ffmpeg's
|
||||
built-in `afftdn` FFT denoiser (no model file needed).
|
||||
@@ -0,0 +1,70 @@
|
||||
_V = "/runpod-volume"
|
||||
import os
|
||||
if os.path.isdir(_V):
|
||||
os.environ.setdefault("HF_HOME", os.path.join(_V, "huggingface"))
|
||||
|
||||
import base64
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import runpod
|
||||
|
||||
# Path where the Dockerfile places the RNNoise model file.
|
||||
RNNN_MODEL = "/app/models/rnnoise.rnnn"
|
||||
|
||||
|
||||
def _decode_b64(s):
|
||||
if isinstance(s, str) and "," in s and s.strip().lower().startswith("data:"):
|
||||
s = s.split(",", 1)[1]
|
||||
return base64.b64decode(s)
|
||||
|
||||
|
||||
def _denoise(in_path, out_path):
|
||||
"""Run ffmpeg. Prefer RNNoise (arnndn) when a model file is present,
|
||||
otherwise fall back to the built-in FFT denoiser (afftdn)."""
|
||||
if os.path.isfile(RNNN_MODEL):
|
||||
af = "arnndn=m=" + RNNN_MODEL
|
||||
else:
|
||||
af = "afftdn=nf=-25"
|
||||
|
||||
cmd = [
|
||||
"ffmpeg", "-y", "-hide_banner", "-nostdin",
|
||||
"-i", in_path,
|
||||
"-af", af,
|
||||
"-ar", "48000", "-ac", "1",
|
||||
"-f", "wav",
|
||||
out_path,
|
||||
]
|
||||
proc = subprocess.run(cmd, capture_output=True)
|
||||
if proc.returncode != 0:
|
||||
raise RuntimeError(
|
||||
"ffmpeg failed: " + proc.stderr.decode("utf-8", "replace")[-800:]
|
||||
)
|
||||
|
||||
|
||||
def handler(event):
|
||||
try:
|
||||
inp = (event or {}).get("input") or {}
|
||||
audio_b64 = inp.get("audio")
|
||||
if not audio_b64:
|
||||
return {"error": "Missing 'audio' in input"}
|
||||
|
||||
raw = _decode_b64(audio_b64)
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
in_path = os.path.join(td, "in")
|
||||
out_path = os.path.join(td, "out.wav")
|
||||
with open(in_path, "wb") as f:
|
||||
f.write(raw)
|
||||
|
||||
_denoise(in_path, out_path)
|
||||
|
||||
with open(out_path, "rb") as f:
|
||||
out_bytes = f.read()
|
||||
|
||||
return {"audio": base64.b64encode(out_bytes).decode("ascii")}
|
||||
except Exception as exc:
|
||||
return {"error": f"{type(exc).__name__}: {exc}"}
|
||||
|
||||
|
||||
runpod.serverless.start({"handler": handler})
|
||||
@@ -0,0 +1 @@
|
||||
runpod==1.7.9
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user