Skip to content

tap/Python-Max

Repository files navigation

tap.python

build Max 9 Python 3.13 platforms license: MIT

Write Max objects in Python.

tap.python~ embeds a CPython interpreter inside a Max external and runs a Python class as an audio object:

  • The class's process() method is called on the audio signal.
  • The class's type-annotated attributes become Max attributes (gain 0.5 in the object box, getgain, attrui — it all works).
  • The class's public methods become Max messages, with arguments converted according to their type hints.
  • The source file is watched and hot-reloaded every time you save it, so you can live-code DSP with Max running.
  • Python's print() output and tracebacks land in the Max console.
from attrs import define, field

@define
class default:
    gain: float = field(default = 1.0)

    def process(self, x: float) -> float:
        return x * self.gain
[tap.python~ default]   ← loads python/default.py, exposes a 'gain' attribute

Requirements

  • Max 9 or later (macOS 11.0+ on Intel or Apple Silicon, Windows 10 22H2+ / Windows 11, 64-bit).
  • An internet connection for the one-time runtime install below.

Installation

  1. Place this package in your Documents/Max 9/Packages folder.

  2. Install the embedded Python runtime (CPython 3.13, from python-build-standalone) into the package's support folder:

    macOS — in Terminal, from the package root:

    ./scripts/install-runtime.sh

    Windows — in PowerShell, from the package root:

    powershell -ExecutionPolicy Bypass -File scripts\install-runtime.ps1

    The script also installs the Python packages used by the examples (attrs, numpy). To add more packages later:

    ./support/bin/python3 -m pip install <package>      # macOS
    .\support\python.exe -m pip install <package>       # Windows
  3. Restart Max and open the tap.python~ help patcher.

Writing a class

Python sources live in the package's python folder. [tap.python~ name] imports python/name.py and instantiates the class name defined in it (the file, class, and argument must share the same name; with no argument, default is loaded).

  • Attributes — class-level annotated fields (e.g. via attrs) become Max attributes. int maps to a Max long, float to float64, anything else is treated as a symbol. Names starting with _ are private and skipped.
  • Messages — public methods become Max messages. Argument type hints (int, float, str) drive the conversion from Max atoms. Methods named int, float, symbol, and bang map to those standard Max messages.
  • Audio — a method process(self, x: float) -> float is called once per sample with the input sample and must return the output sample. One signal inlet and one signal outlet, single-channel; wrap the object in mc. for multichannel. (Returning a tuple for multiple outputs is not supported yet.)
  • Hot reload — saving the .py file reloads it in place: attributes and messages are rebuilt and audio resumes with the new code. If the file has an error, the object prints the traceback to the Max console and outputs silence until the next successful reload.

A note on performance

process() is called once per sample from the audio thread, holding Python's global interpreter lock. That is fantastic for sketching and live-coding an algorithm, and expensive for production use — expect meaningful CPU load at high sample rates, and audio dropouts while a reload is in progress. All tap.python~ instances share one interpreter, so heavy Python work in one instance can steal time from the others. A vectorized process(numpy array) path is on the roadmap.

Building from source

Requires CMake 3.19+ and a C++20 compiler (Xcode 12+ / Visual Studio 2019+). The build links against the embedded runtime, so install it first (see Installation).

git submodule update --init --recursive
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release    # externals land in externals/
ctest --test-dir build                  # unit tests (mock kernel, no Max needed)

On macOS the default build is universal (arm64 + x86_64), so it links against a universal libpython — install the runtime with ./scripts/install-runtime.sh --universal (as CI does). Building against a native-only runtime fails at link time with symbol(s) not found for architecture arm64 (or x86_64). For faster local iteration, build for just your machine's architecture, which pairs with the plain native install:

./scripts/install-runtime.sh                                                    # native libpython
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=arm64  # your arch

On Windows, configure with cmake -S . -B build -A x64.

License

MIT — see License.md, which also lists the third-party components (Min-API, CPython, attrs, NumPy) and where their licenses live.

About

Python Package for Max

Resources

License

Stars

0 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors