Model lifecycle

This page covers loading, unloading, and swapping Piper instances. For what to do with a loaded Piper, see Usage.

Loading

Piper::new loads a model and its config from disk:

let piper = Piper::new(Path::new(&onnx_path), Path::new(&config_path))?;

This opens and parses the .onnx.json config into a ModelConfig, then builds an ONNX Runtime session from the .onnx model file. Both steps can fail; see Usage for the resulting PiperError variants.

Piper::from_session is the alternative entry point for callers who’ve already built their own ort::Session (e.g. with non-default execution providers) and just need it wrapped:

let piper = Piper::from_session(session, config);

Unloading

Piper has no unload() method, because it doesn’t need one: dropping a Piper value releases its ONNX Runtime session, and the native memory backing it, immediately. There’s nothing to explicitly tear down.

To unload a model on demand — e.g. in a long-running app where a model shouldn’t stay resident forever — hold it behind Option<Piper> and assign None to unload it:

struct TtsState {
    piper: Option<Piper>,
}

impl TtsState {
    fn load(&mut self, onnx_path: &Path, config_path: &Path) {
        self.piper = Some(Piper::new(onnx_path, config_path).unwrap());
    }

    fn unload(&mut self) {
        self.piper = None;
    }
}

Assigning None drops the previous Some(Piper) in place, which releases that model’s session and memory before the assignment returns. See examples/unload_model.rs, walked through in Examples, for the full program.

Swapping voices

Swapping to a different voice is the same operation as unloading, just without the intermediate None: assigning a new Some(Piper) to the Option<Piper> drops the old value first, releasing its session, then stores the new one:

impl TtsState {
    fn swap(&mut self, onnx_path: &Path, config_path: &Path) {
        self.piper = Some(Piper::new(onnx_path, config_path).unwrap());
    }
}

Because load and swap are the same assignment, one method covers both: calling load again on an already-loaded TtsState swaps the voice. Note the order of evaluation: Piper::new for the new model runs first, while self.piper still holds the old one, so both sessions are briefly resident together; only once the new Piper is constructed does the assignment drop the old value and store the new one.