diff --git a/scripts/ableton-linux b/scripts/ableton-linux new file mode 100755 index 0000000..32780f1 --- /dev/null +++ b/scripts/ableton-linux @@ -0,0 +1,135 @@ +#! /usr/bin/env python3 +import argparse +import os +import sys +import logging +import subprocess +import shutil +import time +from pathlib import Path + +logger = logging.getLogger(__name__) + +WINEASIO_SRC_PATH = Path("~/src/wineasio").expanduser() + + +def build_wineasio(): + # If the wineasio source directory doesn't exist, clone it + if not WINEASIO_SRC_PATH.is_dir(): + logger.info("Cloning wineasio source") + subprocess.check_call( + [ + "git", + "clone", + "https://github.com/wineasio/wineasio", + str(WINEASIO_SRC_PATH), + ] + ) + subprocess.check_call( + ["git", "submodule", "update", "--init", "--recursive"], + cwd=str(WINEASIO_SRC_PATH), + ) + + # Make sure `pipewire-jack` is installed + logger.info("Installing pipewire-jack") + + # Call make to build 64-bit libs + logger.info("Building wineasio") + try: + subprocess.check_call(["make", "64"], cwd=str(WINEASIO_SRC_PATH)) + except subprocess.CalledProcessError: + logger.error("Failed to build wineasio") + logger.info( + "Make sure you have `pipewire-jack-audio-connection-kit-devel` installed" + ) + logger.info("Make sure you have `wine-devel` installed") + sys.exit(1) + + # We need to copy the libs for wine to find them + logger.info("Copying wineasio libs") + subprocess.check_call( + ["sudo", "cp", "build64/wineasio64.dll", "/usr/lib64/wine/x86_64-windows/"], + cwd=str(WINEASIO_SRC_PATH), + ) + subprocess.check_call( + ["sudo", "cp", "build64/wineasio64.dll.so", "/usr/lib64/wine/x86_64-unix/"], + cwd=str(WINEASIO_SRC_PATH), + ) + + +def bottles_winepfx_from_name(bottle_name: str) -> Path: + return Path("~/.local/share/bottles/bottles/").expanduser() / ( + bottle_name.replace(" ", "-") + ) + + +def main() -> int: + # Handle program arguments + ap = argparse.ArgumentParser( + prog="ableton-linux", description="Executes Ableton on Linux" + ) + ap.add_argument( + "--bottle", "-b", help="Use the specified bottle", default="Ableton 11 Standard" + ) + ap.add_argument( + "--program", "-p", help="Program to run", default="Ableton Live 11 Standard" + ) + ap.add_argument( + "-v", "--verbose", help="Enable verbose logging", action="store_true" + ) + args = ap.parse_args() + + # Configure logging + logging.basicConfig( + level=logging.DEBUG if args.verbose else logging.INFO, + format="%(levelname)s: %(message)s", + ) + + # Ensure we have wineasio + if not (WINEASIO_SRC_PATH / "build64").exists(): + build_wineasio() + + # Figure out the wineprefix + wineprefix = bottles_winepfx_from_name(args.bottle) + logger.info(f"Wine prefix is: {wineprefix}") + + # Ensure that the bottle has the wineasio dll + if not (wineprefix / ".wineasio-installed").is_file(): + logger.info("Registering wineasio") + subprocess.check_call( + [WINEASIO_SRC_PATH / "wineasio-register"], + env={"WINEPREFIX": str(wineprefix)}, + ) + shutil.copy( + WINEASIO_SRC_PATH / "build64" / "wineasio64.dll.so", + wineprefix / "drive_c" / "windows" / "system" / "wineasio64.dll", + ) + shutil.copy( + WINEASIO_SRC_PATH / "build64" / "wineasio64.dll.so", + wineprefix / "drive_c" / "windows" / "system32" / "wineasio64.dll", + ) + (wineprefix / ".wineasio-installed").touch() + + logger.info("Waiting 15 seconds to let wine do its thing") + time.sleep(15) + + # Build a modified environment for ableton + ableton_env = os.environ.copy() + ableton_env.update( + { + "WINEASIO_NUMBER_INPUTS": "16", + "WINEASIO_NUMBER_OUTPUTS": "16", + "WINEASIO_CONNECT_TO_HARDWARE": "1", + } + ) + + # Launch Ableton via bottles + logger.info("Launching Ableton") + return_code = subprocess.call( + ["bottles-cli", "run", "-b", args.bottle, "-p", args.program], env=ableton_env + ) + return return_code + + +if __name__ == "__main__": + sys.exit(main())