A session is not a grab bag of helpers. It is a small machine with a start and a stop. You construct it with a device id or a preset, you prepare hardware or a dummy source, you start the stream, you read blocks, and you stop. Skip a step and the next one has nothing to hold.
That shape is deliberate. Application code should not care whether the source is a synthetic board, a playback file, or a headset on the desk. The session hides that. Filters and models take tables. They never open devices.
Four calls, one owner
Prepare claims the device and checks that the preset still maps. Start opens the stream. Read returns a numeric block with a stable column order. Stop releases the device even if the rest of your script has already thrown.
import ability as ab
session = ab.Session(device_id=0)
session.prepare()
session.start()
block = session.read(n_samples=256)
session.stop()Why this survives a rewrite
When you move from a notebook to a game engine, you keep the same lifecycle. The language changes. The owner does not. That is the contract Reference documents and Docs walks through in samples.
If you need markers, insert them on the live session, not on a copy of the table after the fact. File round-trips keep extra columns so playback can still find them.