From ff4418f547ebd2bb7a6c9c051696ae3838ef64d6 Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Wed, 9 Sep 2026 17:38:53 -0400 Subject: [PATCH 1/6] Add Google Colab WebUI notebook --- Notebooks/colab_audio_cpp.ipynb | 258 ++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 260 insertions(+) create mode 100644 Notebooks/colab_audio_cpp.ipynb diff --git a/Notebooks/colab_audio_cpp.ipynb b/Notebooks/colab_audio_cpp.ipynb new file mode 100644 index 00000000..d8d945d3 --- /dev/null +++ b/Notebooks/colab_audio_cpp.ipynb @@ -0,0 +1,258 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open\n", + "\n", + "# audio.cpp WebUI\n", + "Run the cell below to build audio.cpp with CUDA and open its WebUI. The public link remains active while the cell is running. Anyone with the link can use the WebUI, so do not share it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "start-audio-cpp", + "cellView": "form", + "collapsed": true + }, + "outputs": [], + "source": [ + "# @title Start audio.cpp\n", + "\n", + "import os\n", + "import platform\n", + "import re\n", + "import subprocess\n", + "import time\n", + "import urllib.request\n", + "from pathlib import Path\n", + "\n", + "from IPython.display import HTML, clear_output, display\n", + "\n", + "REPO_DIR = Path(\"/content/audio.cpp\")\n", + "REPO_URL = \"https://github.com/DrewThomasson/audio.cpp.git\"\n", + "REPO_BRANCH = \"main\"\n", + "BUILD_DIR = REPO_DIR / \"build/colab-cuda\"\n", + "SERVER_PORT = 8080\n", + "BUILD_LOG = Path(\"/content/audio_cpp_build.log\")\n", + "SERVER_LOG = Path(\"/content/audio_cpp_server.log\")\n", + "TUNNEL_LOG = Path(\"/content/audio_cpp_tunnel.log\")\n", + "CLOUDFLARED = Path(\"/content/cloudflared\")\n", + "\n", + "def run(command, *, cwd=None, env=None, log=None):\n", + " if log is None:\n", + " subprocess.run(command, cwd=cwd, env=env, check=True)\n", + " return\n", + " with log.open(\"a\", encoding=\"utf-8\") as output:\n", + " try:\n", + " subprocess.run(\n", + " command, cwd=cwd, env=env, check=True, stdout=output, stderr=subprocess.STDOUT\n", + " )\n", + " except subprocess.CalledProcessError as error:\n", + " command_text = \" \".join(map(str, command))\n", + " raise RuntimeError(f\"Command failed: {command_text}\\n\\n{tail(log)}\") from error\n", + "\n", + "def tail(path, line_count=40):\n", + " if not path.exists():\n", + " return \"\"\n", + " return \"\".join(path.read_text(encoding=\"utf-8\", errors=\"replace\").splitlines(True)[-line_count:])\n", + "\n", + "def stop(process):\n", + " if process is None or process.poll() is not None:\n", + " return\n", + " process.terminate()\n", + " try:\n", + " process.wait(timeout=10)\n", + " except subprocess.TimeoutExpired:\n", + " process.kill()\n", + " process.wait()\n", + "\n", + "if subprocess.run(\n", + " [\"nvidia-smi\"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL\n", + ").returncode != 0:\n", + " raise RuntimeError(\n", + " \"No NVIDIA GPU was detected. In Colab, select Runtime > Change runtime type > T4 GPU, \"\n", + " \"restart the session, and run this cell again.\"\n", + " )\n", + "\n", + "if platform.machine() != \"x86_64\":\n", + " raise RuntimeError(f\"This notebook currently supports x86_64 Colab runtimes, not {platform.machine()}.\")\n", + "\n", + "BUILD_LOG.write_text(\"\", encoding=\"utf-8\")\n", + "SERVER_LOG.write_text(\"\", encoding=\"utf-8\")\n", + "TUNNEL_LOG.write_text(\"\", encoding=\"utf-8\")\n", + "\n", + "print(\"Preparing the Colab runtime...\", flush=True)\n", + "run([\"apt-get\", \"update\", \"-qq\"], log=BUILD_LOG)\n", + "run(\n", + " [\n", + " \"apt-get\", \"install\", \"-y\", \"-qq\",\n", + " \"build-essential\", \"ca-certificates\", \"cmake\", \"curl\", \"ffmpeg\",\n", + " \"libssl-dev\", \"ninja-build\", \"software-properties-common\",\n", + " ],\n", + " log=BUILD_LOG,\n", + ")\n", + "if subprocess.run([\"bash\", \"-lc\", \"command -v gcc-13 && command -v g++-13\"], stdout=subprocess.DEVNULL).returncode != 0:\n", + " run([\"add-apt-repository\", \"-y\", \"ppa:ubuntu-toolchain-r/test\"], log=BUILD_LOG)\n", + " run([\"apt-get\", \"update\", \"-qq\"], log=BUILD_LOG)\n", + "run([\"apt-get\", \"install\", \"-y\", \"-qq\", \"gcc-13\", \"g++-13\"], log=BUILD_LOG)\n", + "\n", + "if not (REPO_DIR / \".git\").exists():\n", + " if REPO_DIR.exists():\n", + " raise RuntimeError(f\"{REPO_DIR} exists but is not a Git checkout. Restart the runtime and try again.\")\n", + " print(\"Downloading audio.cpp...\", flush=True)\n", + " run([\"git\", \"clone\", \"--depth=1\", \"--branch\", REPO_BRANCH, REPO_URL, str(REPO_DIR)], log=BUILD_LOG)\n", + "else:\n", + " print(\"Using the existing audio.cpp checkout.\", flush=True)\n", + "\n", + "compute_capability = subprocess.check_output(\n", + " [\"nvidia-smi\", \"--query-gpu=compute_cap\", \"--format=csv,noheader\"], text=True\n", + ").splitlines()[0].strip()\n", + "cuda_arch = compute_capability.replace(\".\", \"\")\n", + "if not cuda_arch.isdigit():\n", + " raise RuntimeError(f\"Could not determine the CUDA architecture from: {compute_capability!r}\")\n", + "\n", + "build_env = os.environ.copy()\n", + "build_env.update({\"CC\": \"gcc-13\", \"CXX\": \"g++-13\", \"CUDAHOSTCXX\": \"g++-13\"})\n", + "jobs = max(1, min(os.cpu_count() or 2, 4))\n", + "server_binary = BUILD_DIR / \"bin/audiocpp_server\"\n", + "\n", + "if not server_binary.exists():\n", + " print(\"Building audio.cpp with CUDA. This can take several minutes...\", flush=True)\n", + " run(\n", + " [\n", + " \"bash\", \"scripts/build_linux.sh\",\n", + " \"--backend\", \"cuda\",\n", + " \"--build-dir\", str(BUILD_DIR),\n", + " \"--build-type\", \"Release\",\n", + " \"--cuda-arch\", cuda_arch,\n", + " \"--deployment-build\",\n", + " \"--native-model-manager\",\n", + " \"--system-openssl\",\n", + " \"--target\", \"audiocpp_server\",\n", + " \"--jobs\", str(jobs),\n", + " ],\n", + " cwd=REPO_DIR,\n", + " env=build_env,\n", + " log=BUILD_LOG,\n", + " )\n", + "else:\n", + " print(\"Using the existing audio.cpp build.\", flush=True)\n", + "\n", + "if not CLOUDFLARED.exists():\n", + " print(\"Installing the public tunnel...\", flush=True)\n", + " run(\n", + " [\n", + " \"curl\", \"-L\", \"--fail\", \"--silent\", \"--show-error\",\n", + " \"https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64\",\n", + " \"-o\", str(CLOUDFLARED),\n", + " ],\n", + " log=BUILD_LOG,\n", + " )\n", + " CLOUDFLARED.chmod(0o755)\n", + "\n", + "server_process = None\n", + "tunnel_process = None\n", + "server_output = None\n", + "tunnel_output = None\n", + "\n", + "try:\n", + " print(\"Starting the audio.cpp WebUI...\", flush=True)\n", + " server_output = SERVER_LOG.open(\"w\", encoding=\"utf-8\")\n", + " server_process = subprocess.Popen(\n", + " [\n", + " str(server_binary), \"--ui\", \"--ui-management\",\n", + " \"--backend\", \"cuda\", \"--host\", \"127.0.0.1\",\n", + " \"--port\", str(SERVER_PORT),\n", + " ],\n", + " cwd=REPO_DIR,\n", + " stdout=server_output,\n", + " stderr=subprocess.STDOUT,\n", + " text=True,\n", + " )\n", + "\n", + " health_url = f\"http://127.0.0.1:{SERVER_PORT}/health\"\n", + " deadline = time.monotonic() + 120\n", + " while time.monotonic() < deadline:\n", + " if server_process.poll() is not None:\n", + " raise RuntimeError(\"audio.cpp stopped during startup.\\n\\n\" + tail(SERVER_LOG))\n", + " try:\n", + " with urllib.request.urlopen(health_url, timeout=2) as response:\n", + " if response.status == 200:\n", + " break\n", + " except Exception:\n", + " time.sleep(1)\n", + " else:\n", + " raise RuntimeError(\"audio.cpp did not become ready.\\n\\n\" + tail(SERVER_LOG))\n", + "\n", + " tunnel_output = TUNNEL_LOG.open(\"w\", encoding=\"utf-8\")\n", + " tunnel_process = subprocess.Popen(\n", + " [str(CLOUDFLARED), \"tunnel\", \"--url\", f\"http://127.0.0.1:{SERVER_PORT}\", \"--no-autoupdate\"],\n", + " stdout=tunnel_output,\n", + " stderr=subprocess.STDOUT,\n", + " text=True,\n", + " )\n", + "\n", + " public_url = None\n", + " deadline = time.monotonic() + 90\n", + " while time.monotonic() < deadline:\n", + " if tunnel_process.poll() is not None:\n", + " raise RuntimeError(\"The public tunnel stopped during startup.\\n\\n\" + tail(TUNNEL_LOG))\n", + " match = re.search(r\"https://[a-z0-9-]+\\.trycloudflare\\.com\", TUNNEL_LOG.read_text(encoding=\"utf-8\", errors=\"replace\"))\n", + " if match:\n", + " public_url = match.group(0)\n", + " break\n", + " time.sleep(1)\n", + " if public_url is None:\n", + " raise RuntimeError(\"The public tunnel did not provide a URL.\\n\\n\" + tail(TUNNEL_LOG))\n", + "\n", + " clear_output(wait=True)\n", + " display(HTML(\n", + " f'

audio.cpp is running

'\n", + " f'

Open the audio.cpp WebUI

'\n", + " '

Keep this cell running while you use the WebUI. Anyone with the link can access this session.

'\n", + " ))\n", + "\n", + " while True:\n", + " if server_process.poll() is not None:\n", + " raise RuntimeError(\"audio.cpp stopped unexpectedly.\\n\\n\" + tail(SERVER_LOG))\n", + " if tunnel_process.poll() is not None:\n", + " raise RuntimeError(\"The public tunnel stopped unexpectedly.\\n\\n\" + tail(TUNNEL_LOG))\n", + " time.sleep(5)\n", + "except KeyboardInterrupt:\n", + " clear_output(wait=True)\n", + " print(\"audio.cpp stopped.\")\n", + "finally:\n", + " stop(tunnel_process)\n", + " stop(server_process)\n", + " if tunnel_output is not None:\n", + " tunnel_output.close()\n", + " if server_output is not None:\n", + " server_output.close()\n" + ] + } + ] +} diff --git a/README.md b/README.md index 3f019d74..2279a762 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,8 @@ package notes. ## WebUI ![Maintained by contributors](https://img.shields.io/badge/maintained%20by-contributors-brightgreen) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DrewThomasson/audio.cpp/blob/main/Notebooks/colab_audio_cpp.ipynb) + `audiocpp_server` includes an embedded SvelteKit/TypeScript WebUI for running local TTS, cloning, ASR, generation, conversion, separation, VAD, diarization, and alignment workflows. The production UI is compiled into the server binary, so using it requires neither Python nor separate frontend files: From 24feb17ae1494e9dd98344ee242d4fad16bb58ce Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Wed, 9 Sep 2026 18:14:57 -0400 Subject: [PATCH 2/6] Show live Colab build progress --- Notebooks/colab_audio_cpp.ipynb | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Notebooks/colab_audio_cpp.ipynb b/Notebooks/colab_audio_cpp.ipynb index d8d945d3..4278242a 100644 --- a/Notebooks/colab_audio_cpp.ipynb +++ b/Notebooks/colab_audio_cpp.ipynb @@ -80,6 +80,34 @@ " return \"\"\n", " return \"\".join(path.read_text(encoding=\"utf-8\", errors=\"replace\").splitlines(True)[-line_count:])\n", "\n", + "def run_build(command, *, cwd, env, log):\n", + " progress_pattern = re.compile(r\"\\[(\\d+)/(\\d+)\\]\")\n", + " with log.open(\"a\", encoding=\"utf-8\") as output:\n", + " process = subprocess.Popen(\n", + " command, cwd=cwd, env=env, stdout=output, stderr=subprocess.STDOUT\n", + " )\n", + " last_progress = None\n", + " while process.poll() is None:\n", + " matches = progress_pattern.findall(\n", + " log.read_text(encoding=\"utf-8\", errors=\"replace\")\n", + " )\n", + " if matches and matches[-1] != last_progress:\n", + " completed, total = map(int, matches[-1])\n", + " width = 30\n", + " filled = min(width, completed * width // total)\n", + " bar = \"#\" * filled + \"-\" * (width - filled)\n", + " percent = completed * 100 // total\n", + " clear_output(wait=True)\n", + " print(\"Building audio.cpp with CUDA\")\n", + " print(f\"[{bar}] {percent}% ({completed}/{total})\")\n", + " print(f\"Detailed build log: {log}\")\n", + " last_progress = matches[-1]\n", + " time.sleep(1)\n", + " return_code = process.wait()\n", + " if return_code != 0:\n", + " command_text = \" \".join(map(str, command))\n", + " raise RuntimeError(f\"Command failed: {command_text}\\n\\n{tail(log)}\")\n", + "\n", "def stop(process):\n", " if process is None or process.poll() is not None:\n", " return\n", @@ -142,7 +170,7 @@ "\n", "if not server_binary.exists():\n", " print(\"Building audio.cpp with CUDA. This can take several minutes...\", flush=True)\n", - " run(\n", + " run_build(\n", " [\n", " \"bash\", \"scripts/build_linux.sh\",\n", " \"--backend\", \"cuda\",\n", From dd97b0d15873cdcd932b2da0448cde570efe2aad Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Wed, 9 Sep 2026 18:15:41 -0400 Subject: [PATCH 3/6] Keep Colab build progress on one line --- Notebooks/colab_audio_cpp.ipynb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Notebooks/colab_audio_cpp.ipynb b/Notebooks/colab_audio_cpp.ipynb index 4278242a..c57f61d6 100644 --- a/Notebooks/colab_audio_cpp.ipynb +++ b/Notebooks/colab_audio_cpp.ipynb @@ -98,9 +98,7 @@ " bar = \"#\" * filled + \"-\" * (width - filled)\n", " percent = completed * 100 // total\n", " clear_output(wait=True)\n", - " print(\"Building audio.cpp with CUDA\")\n", - " print(f\"[{bar}] {percent}% ({completed}/{total})\")\n", - " print(f\"Detailed build log: {log}\")\n", + " print(f\"Building audio.cpp with CUDA [{bar}] {percent}% ({completed}/{total})\")\n", " last_progress = matches[-1]\n", " time.sleep(1)\n", " return_code = process.wait()\n", From b48cd534648b4708f9633d08a3a967db2e067720 Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Thu, 10 Sep 2026 00:01:51 -0400 Subject: [PATCH 4/6] Use tested T4 prebuilt in Colab --- Notebooks/colab_audio_cpp.ipynb | 86 ++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/Notebooks/colab_audio_cpp.ipynb b/Notebooks/colab_audio_cpp.ipynb index c57f61d6..ec976200 100644 --- a/Notebooks/colab_audio_cpp.ipynb +++ b/Notebooks/colab_audio_cpp.ipynb @@ -45,7 +45,10 @@ "import os\n", "import platform\n", "import re\n", + "import hashlib\n", + "import shutil\n", "import subprocess\n", + "import tarfile\n", "import time\n", "import urllib.request\n", "from pathlib import Path\n", @@ -61,6 +64,10 @@ "SERVER_LOG = Path(\"/content/audio_cpp_server.log\")\n", "TUNNEL_LOG = Path(\"/content/audio_cpp_tunnel.log\")\n", "CLOUDFLARED = Path(\"/content/cloudflared\")\n", + "PREBUILT_URL = \"https://github.com/DrewThomasson/audio.cpp/releases/download/colab-test-20260909/audio-cpp-colab-t4-cuda12.8-ubuntu22-4ec12ac38cdb.tar.gz\"\n", + "PREBUILT_SHA256 = \"5ba1c7266d267e9c15e5b05fc77aa099e7ddd68c3638e50652c4749708507dc7\"\n", + "PREBUILT_CUDA_ARCHS = {\"75\"}\n", + "PREBUILT_ROOT = Path(\"/content/audio-cpp-prebuilt\")\n", "\n", "def run(command, *, cwd=None, env=None, log=None):\n", " if log is None:\n", @@ -136,23 +143,10 @@ "run(\n", " [\n", " \"apt-get\", \"install\", \"-y\", \"-qq\",\n", - " \"build-essential\", \"ca-certificates\", \"cmake\", \"curl\", \"ffmpeg\",\n", - " \"libssl-dev\", \"ninja-build\", \"software-properties-common\",\n", + " \"ca-certificates\", \"curl\", \"ffmpeg\", \"libgomp1\", \"libssl3\",\n", " ],\n", " log=BUILD_LOG,\n", ")\n", - "if subprocess.run([\"bash\", \"-lc\", \"command -v gcc-13 && command -v g++-13\"], stdout=subprocess.DEVNULL).returncode != 0:\n", - " run([\"add-apt-repository\", \"-y\", \"ppa:ubuntu-toolchain-r/test\"], log=BUILD_LOG)\n", - " run([\"apt-get\", \"update\", \"-qq\"], log=BUILD_LOG)\n", - "run([\"apt-get\", \"install\", \"-y\", \"-qq\", \"gcc-13\", \"g++-13\"], log=BUILD_LOG)\n", - "\n", - "if not (REPO_DIR / \".git\").exists():\n", - " if REPO_DIR.exists():\n", - " raise RuntimeError(f\"{REPO_DIR} exists but is not a Git checkout. Restart the runtime and try again.\")\n", - " print(\"Downloading audio.cpp...\", flush=True)\n", - " run([\"git\", \"clone\", \"--depth=1\", \"--branch\", REPO_BRANCH, REPO_URL, str(REPO_DIR)], log=BUILD_LOG)\n", - "else:\n", - " print(\"Using the existing audio.cpp checkout.\", flush=True)\n", "\n", "compute_capability = subprocess.check_output(\n", " [\"nvidia-smi\", \"--query-gpu=compute_cap\", \"--format=csv,noheader\"], text=True\n", @@ -161,12 +155,61 @@ "if not cuda_arch.isdigit():\n", " raise RuntimeError(f\"Could not determine the CUDA architecture from: {compute_capability!r}\")\n", "\n", - "build_env = os.environ.copy()\n", - "build_env.update({\"CC\": \"gcc-13\", \"CXX\": \"g++-13\", \"CUDAHOSTCXX\": \"g++-13\"})\n", - "jobs = max(1, min(os.cpu_count() or 2, 4))\n", - "server_binary = BUILD_DIR / \"bin/audiocpp_server\"\n", + "server_binary = None\n", + "if cuda_arch in PREBUILT_CUDA_ARCHS:\n", + " archive_path = Path(\"/content\") / PREBUILT_URL.rsplit(\"/\", 1)[-1]\n", + " try:\n", + " print(\"Downloading the CUDA prebuilt...\", flush=True)\n", + " run([\"curl\", \"-L\", \"--fail\", \"--silent\", \"--show-error\", PREBUILT_URL, \"-o\", str(archive_path)], log=BUILD_LOG)\n", + " actual_sha256 = hashlib.sha256(archive_path.read_bytes()).hexdigest()\n", + " if actual_sha256 != PREBUILT_SHA256:\n", + " raise RuntimeError(\"The downloaded CUDA prebuilt failed SHA-256 verification.\")\n", + " if PREBUILT_ROOT.exists():\n", + " shutil.rmtree(PREBUILT_ROOT)\n", + " PREBUILT_ROOT.mkdir(parents=True)\n", + " with tarfile.open(archive_path, \"r:gz\") as archive:\n", + " destination = PREBUILT_ROOT.resolve()\n", + " for member in archive.getmembers():\n", + " member_path = (PREBUILT_ROOT / member.name).resolve()\n", + " if destination not in member_path.parents and member_path != destination:\n", + " raise RuntimeError(\"The CUDA prebuilt contains an unsafe path.\")\n", + " archive.extractall(PREBUILT_ROOT)\n", + " matches = list(PREBUILT_ROOT.glob(\"*/audiocpp_server\"))\n", + " if len(matches) != 1:\n", + " raise RuntimeError(\"The CUDA prebuilt does not contain one audiocpp_server binary.\")\n", + " server_binary = matches[0]\n", + " server_binary.chmod(0o755)\n", + " except Exception as error:\n", + " print(f\"The CUDA prebuilt could not be used; building from source instead. ({error})\", flush=True)\n", + " server_binary = None\n", + " finally:\n", + " archive_path.unlink(missing_ok=True)\n", + "\n", + "if server_binary is None:\n", + " run(\n", + " [\n", + " \"apt-get\", \"install\", \"-y\", \"-qq\", \"build-essential\", \"cmake\",\n", + " \"libssl-dev\", \"ninja-build\", \"software-properties-common\",\n", + " ],\n", + " log=BUILD_LOG,\n", + " )\n", + " if subprocess.run([\"bash\", \"-lc\", \"command -v gcc-13 && command -v g++-13\"], stdout=subprocess.DEVNULL).returncode != 0:\n", + " run([\"add-apt-repository\", \"-y\", \"ppa:ubuntu-toolchain-r/test\"], log=BUILD_LOG)\n", + " run([\"apt-get\", \"update\", \"-qq\"], log=BUILD_LOG)\n", + " run([\"apt-get\", \"install\", \"-y\", \"-qq\", \"gcc-13\", \"g++-13\"], log=BUILD_LOG)\n", + "\n", + " if not (REPO_DIR / \".git\").exists():\n", + " if REPO_DIR.exists():\n", + " raise RuntimeError(f\"{REPO_DIR} exists but is not a Git checkout. Restart the runtime and try again.\")\n", + " print(\"Downloading audio.cpp source...\", flush=True)\n", + " run([\"git\", \"clone\", \"--depth=1\", \"--branch\", REPO_BRANCH, REPO_URL, str(REPO_DIR)], log=BUILD_LOG)\n", + " else:\n", + " print(\"Using the existing audio.cpp checkout.\", flush=True)\n", "\n", - "if not server_binary.exists():\n", + " build_env = os.environ.copy()\n", + " build_env.update({\"CC\": \"gcc-13\", \"CXX\": \"g++-13\", \"CUDAHOSTCXX\": \"g++-13\"})\n", + " jobs = max(1, min(os.cpu_count() or 2, 4))\n", + " server_binary = BUILD_DIR / \"bin/audiocpp_server\"\n", " print(\"Building audio.cpp with CUDA. This can take several minutes...\", flush=True)\n", " run_build(\n", " [\n", @@ -185,9 +228,6 @@ " env=build_env,\n", " log=BUILD_LOG,\n", " )\n", - "else:\n", - " print(\"Using the existing audio.cpp build.\", flush=True)\n", - "\n", "if not CLOUDFLARED.exists():\n", " print(\"Installing the public tunnel...\", flush=True)\n", " run(\n", @@ -214,7 +254,7 @@ " \"--backend\", \"cuda\", \"--host\", \"127.0.0.1\",\n", " \"--port\", str(SERVER_PORT),\n", " ],\n", - " cwd=REPO_DIR,\n", + " cwd=server_binary.parent,\n", " stdout=server_output,\n", " stderr=subprocess.STDOUT,\n", " text=True,\n", From f3601b569d253f6195859c280c350d6e2ba82e9a Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Thu, 10 Sep 2026 00:05:41 -0400 Subject: [PATCH 5/6] Avoid Colab WebUI port collisions --- Notebooks/colab_audio_cpp.ipynb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Notebooks/colab_audio_cpp.ipynb b/Notebooks/colab_audio_cpp.ipynb index ec976200..4c39cf0f 100644 --- a/Notebooks/colab_audio_cpp.ipynb +++ b/Notebooks/colab_audio_cpp.ipynb @@ -47,6 +47,7 @@ "import re\n", "import hashlib\n", "import shutil\n", + "import socket\n", "import subprocess\n", "import tarfile\n", "import time\n", @@ -59,7 +60,9 @@ "REPO_URL = \"https://github.com/DrewThomasson/audio.cpp.git\"\n", "REPO_BRANCH = \"main\"\n", "BUILD_DIR = REPO_DIR / \"build/colab-cuda\"\n", - "SERVER_PORT = 8080\n", + "with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as port_socket:\n", + " port_socket.bind((\"127.0.0.1\", 0))\n", + " SERVER_PORT = port_socket.getsockname()[1]\n", "BUILD_LOG = Path(\"/content/audio_cpp_build.log\")\n", "SERVER_LOG = Path(\"/content/audio_cpp_server.log\")\n", "TUNNEL_LOG = Path(\"/content/audio_cpp_tunnel.log\")\n", @@ -173,7 +176,7 @@ " member_path = (PREBUILT_ROOT / member.name).resolve()\n", " if destination not in member_path.parents and member_path != destination:\n", " raise RuntimeError(\"The CUDA prebuilt contains an unsafe path.\")\n", - " archive.extractall(PREBUILT_ROOT)\n", + " archive.extractall(PREBUILT_ROOT, filter=\"data\")\n", " matches = list(PREBUILT_ROOT.glob(\"*/audiocpp_server\"))\n", " if len(matches) != 1:\n", " raise RuntimeError(\"The CUDA prebuilt does not contain one audiocpp_server binary.\")\n", From 5ac71f5a06c1d49ff5c39841af109d0f972f0939 Mon Sep 17 00:00:00 2001 From: DrewThomasson Date: Thu, 10 Sep 2026 00:22:04 -0400 Subject: [PATCH 6/6] Build and use a CUDA prebuilt for Colab --- .github/workflows/release.yml | 61 +++++++++++++++++++++++++++++++++ Notebooks/colab_audio_cpp.ipynb | 38 +++++++++++++++----- README.md | 2 +- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fe078799..2a6f55a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -180,6 +180,66 @@ jobs: name: audio-${{ needs.get-version.outputs.tag }}-bin-ubuntu-x64-vulkan path: build/bin/* + linux-cuda-colab: + name: Ubuntu x64 (CUDA 12.8, Colab T4) + needs: [check-release, get-version] + if: ${{ needs.check-release.outputs.should_release == 'true' }} + runs-on: ubuntu-latest + container: nvidia/cuda:12.8.1-devel-ubuntu22.04 + env: + CC: gcc-13 + CXX: g++-13 + CUDAHOSTCXX: g++-13 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install dependencies + run: | + apt-get update -qq + DEBIAN_FRONTEND=noninteractive apt-get install -y -qq software-properties-common + add-apt-repository -y ppa:ubuntu-toolchain-r/test + apt-get update -qq + DEBIAN_FRONTEND=noninteractive apt-get install -y -qq gcc-13 g++-13 cmake ninja-build libssl-dev + - name: Configure + run: | + cmake -S . -B build/colab-cuda -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CUDA_ARCHITECTURES=75 \ + -DCMAKE_EXE_LINKER_FLAGS='-static-libstdc++ -static-libgcc' \ + -DENGINE_ENABLE_CUDA=ON \ + -DENGINE_ENABLE_VULKAN=OFF \ + -DENGINE_ENABLE_NATIVE_CPU=OFF \ + -DENGINE_BUILD_TESTS=OFF \ + -DAUDIOCPP_VERSION=${{ needs.get-version.outputs.version }} \ + -DAUDIOCPP_DEPLOYMENT_BUILD=ON \ + -DAUDIOCPP_BUILD_NATIVE_MODEL_MANAGER=ON \ + -DAUDIOCPP_USE_SYSTEM_OPENSSL=ON \ + -DENGINE_ENABLE_LLAMAFILE=ON \ + -DENGINE_ENABLE_CUDA_GRAPHS=ON + - name: Build server + run: cmake --build build/colab-cuda --parallel "$(nproc)" --target audiocpp_server + - name: Verify portable executable + run: | + build/colab-cuda/bin/audiocpp_server --version + if ldd build/colab-cuda/bin/audiocpp_server | grep -Eq 'not found|libstdc\+\+|libgcc_s'; then + echo "::error::The Colab executable has missing or non-portable runtime dependencies." + ldd build/colab-cuda/bin/audiocpp_server + exit 1 + fi + - name: Stage bundle + run: | + mkdir -p dist/models + install -m 0755 build/colab-cuda/bin/audiocpp_server dist/audiocpp_server + install -m 0644 LICENSE dist/LICENSE + cp -r tools dist/tools + cp -r model_specs dist/model_specs + sha256sum dist/audiocpp_server > dist/SHA256SUMS + - uses: actions/upload-artifact@v4 + with: + name: audio-${{ needs.get-version.outputs.tag }}-bin-ubuntu-x64-cuda12.8-colab + path: dist/* + windows-cpu: name: Windows x64 (CPU) needs: [check-release, get-version] @@ -459,6 +519,7 @@ jobs: - macos-metal - linux-cpu - linux-vulkan + - linux-cuda-colab - windows-cpu - windows-cpu-portable - windows-vulkan diff --git a/Notebooks/colab_audio_cpp.ipynb b/Notebooks/colab_audio_cpp.ipynb index 4c39cf0f..0a54594a 100644 --- a/Notebooks/colab_audio_cpp.ipynb +++ b/Notebooks/colab_audio_cpp.ipynb @@ -24,7 +24,7 @@ "colab_type": "text" }, "source": [ - "\"Open\n", + "\"Open\n", "\n", "# audio.cpp WebUI\n", "Run the cell below to build audio.cpp with CUDA and open its WebUI. The public link remains active while the cell is running. Anyone with the link can use the WebUI, so do not share it." @@ -42,6 +42,7 @@ "source": [ "# @title Start audio.cpp\n", "\n", + "import json\n", "import os\n", "import platform\n", "import re\n", @@ -57,7 +58,7 @@ "from IPython.display import HTML, clear_output, display\n", "\n", "REPO_DIR = Path(\"/content/audio.cpp\")\n", - "REPO_URL = \"https://github.com/DrewThomasson/audio.cpp.git\"\n", + "REPO_URL = \"https://github.com/0xShug0/audio.cpp.git\"\n", "REPO_BRANCH = \"main\"\n", "BUILD_DIR = REPO_DIR / \"build/colab-cuda\"\n", "with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as port_socket:\n", @@ -67,11 +68,27 @@ "SERVER_LOG = Path(\"/content/audio_cpp_server.log\")\n", "TUNNEL_LOG = Path(\"/content/audio_cpp_tunnel.log\")\n", "CLOUDFLARED = Path(\"/content/cloudflared\")\n", - "PREBUILT_URL = \"https://github.com/DrewThomasson/audio.cpp/releases/download/colab-test-20260909/audio-cpp-colab-t4-cuda12.8-ubuntu22-4ec12ac38cdb.tar.gz\"\n", - "PREBUILT_SHA256 = \"5ba1c7266d267e9c15e5b05fc77aa099e7ddd68c3638e50652c4749708507dc7\"\n", + "RELEASE_API_URL = \"https://api.github.com/repos/0xShug0/audio.cpp/releases/latest\"\n", + "PREBUILT_ASSET_SUFFIX = \"-bin-ubuntu-x64-cuda12.8-colab.tar.gz\"\n", "PREBUILT_CUDA_ARCHS = {\"75\"}\n", "PREBUILT_ROOT = Path(\"/content/audio-cpp-prebuilt\")\n", "\n", + "def latest_colab_prebuilt():\n", + " request = urllib.request.Request(\n", + " RELEASE_API_URL,\n", + " headers={\"Accept\": \"application/vnd.github+json\", \"User-Agent\": \"audio.cpp-colab\"},\n", + " )\n", + " with urllib.request.urlopen(request, timeout=30) as response:\n", + " release = json.load(response)\n", + " matches = [asset for asset in release.get(\"assets\", []) if asset[\"name\"].endswith(PREBUILT_ASSET_SUFFIX)]\n", + " if len(matches) != 1:\n", + " raise RuntimeError(\"The latest release does not contain a Colab CUDA prebuilt.\")\n", + " asset = matches[0]\n", + " digest = asset.get(\"digest\", \"\")\n", + " if not digest.startswith(\"sha256:\"):\n", + " raise RuntimeError(\"The Colab CUDA prebuilt does not have a SHA-256 digest.\")\n", + " return asset[\"browser_download_url\"], digest.removeprefix(\"sha256:\")\n", + "\n", "def run(command, *, cwd=None, env=None, log=None):\n", " if log is None:\n", " subprocess.run(command, cwd=cwd, env=env, check=True)\n", @@ -160,12 +177,14 @@ "\n", "server_binary = None\n", "if cuda_arch in PREBUILT_CUDA_ARCHS:\n", - " archive_path = Path(\"/content\") / PREBUILT_URL.rsplit(\"/\", 1)[-1]\n", + " archive_path = None\n", " try:\n", + " prebuilt_url, prebuilt_sha256 = latest_colab_prebuilt()\n", + " archive_path = Path(\"/content\") / prebuilt_url.rsplit(\"/\", 1)[-1]\n", " print(\"Downloading the CUDA prebuilt...\", flush=True)\n", - " run([\"curl\", \"-L\", \"--fail\", \"--silent\", \"--show-error\", PREBUILT_URL, \"-o\", str(archive_path)], log=BUILD_LOG)\n", + " run([\"curl\", \"-L\", \"--fail\", \"--silent\", \"--show-error\", prebuilt_url, \"-o\", str(archive_path)], log=BUILD_LOG)\n", " actual_sha256 = hashlib.sha256(archive_path.read_bytes()).hexdigest()\n", - " if actual_sha256 != PREBUILT_SHA256:\n", + " if actual_sha256 != prebuilt_sha256:\n", " raise RuntimeError(\"The downloaded CUDA prebuilt failed SHA-256 verification.\")\n", " if PREBUILT_ROOT.exists():\n", " shutil.rmtree(PREBUILT_ROOT)\n", @@ -177,7 +196,7 @@ " if destination not in member_path.parents and member_path != destination:\n", " raise RuntimeError(\"The CUDA prebuilt contains an unsafe path.\")\n", " archive.extractall(PREBUILT_ROOT, filter=\"data\")\n", - " matches = list(PREBUILT_ROOT.glob(\"*/audiocpp_server\"))\n", + " matches = list(PREBUILT_ROOT.rglob(\"audiocpp_server\"))\n", " if len(matches) != 1:\n", " raise RuntimeError(\"The CUDA prebuilt does not contain one audiocpp_server binary.\")\n", " server_binary = matches[0]\n", @@ -186,7 +205,8 @@ " print(f\"The CUDA prebuilt could not be used; building from source instead. ({error})\", flush=True)\n", " server_binary = None\n", " finally:\n", - " archive_path.unlink(missing_ok=True)\n", + " if archive_path is not None:\n", + " archive_path.unlink(missing_ok=True)\n", "\n", "if server_binary is None:\n", " run(\n", diff --git a/README.md b/README.md index 2279a762..15fb7f9c 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ package notes. ## WebUI ![Maintained by contributors](https://img.shields.io/badge/maintained%20by-contributors-brightgreen) -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/DrewThomasson/audio.cpp/blob/main/Notebooks/colab_audio_cpp.ipynb) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/0xShug0/audio.cpp/blob/main/Notebooks/colab_audio_cpp.ipynb) `audiocpp_server` includes an embedded SvelteKit/TypeScript WebUI for running local TTS, cloning, ASR, generation, conversion, separation, VAD, diarization, and alignment workflows. The production UI is compiled