{ "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 json\n", "import os\n", "import platform\n", "import re\n", "import hashlib\n", "import shutil\n", "import socket\n", "import subprocess\n", "import sys\n", "import tarfile\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/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", " 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", "CLOUDFLARED = Path(\"/content/cloudflared\")\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", "# The prebuilt is linked against CUDA 12. If the Colab image doesn't provide\n", "# those libraries (e.g. it ships CUDA 13), they are fetched from the official\n", "# NVIDIA pip wheels into this isolated folder (the Colab Python env is untouched).\n", "CUDA12_LIBS_DIR = Path(\"/content/cuda12-libs\")\n", "CUDA12_LIB_PACKAGES = {\n", " \"libcublas\": \"nvidia-cublas-cu12\", # also covers libcublasLt\n", " \"libcudart\": \"nvidia-cuda-runtime-cu12\",\n", " \"libcufft\": \"nvidia-cufft-cu12\",\n", " \"libcurand\": \"nvidia-curand-cu12\",\n", " \"libnvrtc\": \"nvidia-cuda-nvrtc-cu12\",\n", "}\n", "\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", "\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", "\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", "\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(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", " 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", "\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", "\n", "def missing_shared_libs(binary, env):\n", " \"\"\"Return the shared libraries `ldd` cannot resolve for this binary.\"\"\"\n", " result = subprocess.run(\n", " [\"ldd\", str(binary)], env=env, capture_output=True, text=True\n", " )\n", " return sorted(\n", " {\n", " line.split(\"=>\")[0].strip()\n", " for line in result.stdout.splitlines()\n", " if \"not found\" in line\n", " }\n", " )\n", "\n", "\n", "def ensure_runtime_libs(binary):\n", " \"\"\"Make sure every shared library the binary needs can be resolved.\n", "\n", " Returns an environment dict (with LD_LIBRARY_PATH extended if needed) that\n", " must be used when launching the binary.\n", " \"\"\"\n", " env = os.environ.copy()\n", " missing = missing_shared_libs(binary, env)\n", " if not missing:\n", " return env\n", "\n", " print(f\"Missing CUDA libraries: {', '.join(missing)}\", flush=True)\n", "\n", " packages = set()\n", " unknown = []\n", " for lib in missing:\n", " stem = lib.split(\".so\")[0]\n", " package = next(\n", " (pkg for prefix, pkg in CUDA12_LIB_PACKAGES.items() if stem.startswith(prefix)),\n", " None,\n", " )\n", " if package is None:\n", " unknown.append(lib)\n", " else:\n", " packages.add(package)\n", " if unknown:\n", " raise RuntimeError(f\"Cannot provide these libraries automatically: {', '.join(unknown)}\")\n", "\n", " print(f\"Installing CUDA 12 runtime libraries: {', '.join(sorted(packages))}...\", flush=True)\n", " run(\n", " [\n", " sys.executable, \"-m\", \"pip\", \"install\", \"--quiet\", \"--no-deps\",\n", " \"--target\", str(CUDA12_LIBS_DIR), *sorted(packages),\n", " ],\n", " log=BUILD_LOG,\n", " )\n", "\n", " lib_dirs = sorted({str(path.parent) for path in CUDA12_LIBS_DIR.rglob(\"lib*.so*\")})\n", " existing = env.get(\"LD_LIBRARY_PATH\", \"\")\n", " env[\"LD_LIBRARY_PATH\"] = \":\".join(lib_dirs + ([existing] if existing else []))\n", "\n", " still_missing = missing_shared_libs(binary, env)\n", " if still_missing:\n", " raise RuntimeError(f\"Still missing after install: {', '.join(still_missing)}\")\n", " return env\n", "\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", " \"ca-certificates\", \"curl\", \"ffmpeg\", \"libgomp1\", \"libssl3\",\n", " ],\n", " log=BUILD_LOG,\n", ")\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", "server_binary = None\n", "server_env = os.environ.copy()\n", "if cuda_arch in PREBUILT_CUDA_ARCHS:\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", " 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, filter=\"data\")\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", " server_binary.chmod(0o755)\n", "\n", " # NEW: resolve missing CUDA 12 libs (libcublas.so.12, etc.) before launch.\n", " # If this fails, the except below falls back to building from source.\n", " server_env = ensure_runtime_libs(server_binary)\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", " server_env = os.environ.copy()\n", " finally:\n", " if archive_path is not None:\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", " 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", " \"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", " server_env = os.environ.copy()\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=server_binary.parent,\n", " env=server_env, # NEW: carries LD_LIBRARY_PATH for the CUDA 12 libs\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" ] } ] }