say_job(
s,opts) ->job,err
Speak incrementally: a sliced render job
The cooperative sibling of say for callers living inside one event loop: a
long line renders tens of ms of CPU, and a monolithic say blocks everything
sharing the thread (an audio writer task starves, a frame stalls). The job
slices that work: each job.step() renders opts.yield_every parameter
frames (default 40 ≈ 200 ms of speech, ~1 ms of work) and returns
nil: still rendering — call again laterbuf, info: done (the final step resamples to opts.rate, one C call)false, err: failed — the job is deadThe stepped output is byte-identical to say(s, opts): the render state
lives in locals a yield cannot touch. job.done() reports completion
without stepping.