GenServer that manages a Python VoxCPM2 bridge via Erlang Port.
Protocol (v2.1 — MessagePack binary framing, single-ref streaming)
Frame format: [4-byte BE total_length][msgpack-encoded payload]
Audio is raw WAV bytes inside msgpack — no base64.
Streaming: unlike v2.0 which used a dual stream_id/ref mapping, v2.1 eliminates stream IDs entirely. The Elixir ref (sent in the request) is the sole identifier — Python just echoes stream_start/chunk/end in order.
Summary
Functions
Waits for the model to finish loading.
Returns a specification to start this module under a supervisor.
Starts async streaming. Returns {:ok, ref} immediately.
Returns runtime model info: device, sample_rate, status.
Gracefully stops the GenServer and the Python bridge process.
Types
@type generate_opt() :: {:audio_prompt, String.t()} | {:prompt_wav_path, String.t()} | {:prompt_text, String.t()} | {:cfg_value, float()} | {:inference_timesteps, pos_integer()} | {:min_len, pos_integer()} | {:max_len, pos_integer()} | {:normalize, boolean()} | {:denoise, boolean()}
@type start_opts() :: [model_option()]
Functions
@spec await_ready(GenServer.server(), timeout()) :: :ok | {:error, term()}
Waits for the model to finish loading.
Returns :ok when ready, {:error, :loading} if still initializing,
or {:error, reason} if initialization failed.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec collect_stream(GenServer.server(), reference()) :: {:ok, binary()} | {:error, term()}
@spec generate(GenServer.server(), String.t(), [generate_opt()]) :: {:ok, binary()} | {:error, term()}
@spec generate(GenServer.server(), String.t(), [generate_opt()], timeout()) :: {:ok, binary()} | {:error, term()}
@spec generate_streaming_async(GenServer.server(), String.t(), [generate_opt()]) :: {:ok, reference()} | {:error, term()}
Starts async streaming. Returns {:ok, ref} immediately.
Poll with next_chunk/2: {:ok, chunk} (raw float32 PCM) | :eos | {:error, reason}.
Collect everything with collect_stream/2: {:ok, wav_binary}.
@spec info(GenServer.server()) :: map()
Returns runtime model info: device, sample_rate, status.
@spec load_lora(GenServer.server(), String.t()) :: {:ok, non_neg_integer(), non_neg_integer()} | {:error, term()}
@spec next_chunk(GenServer.server(), reference()) :: {:ok, binary()} | :eos | {:error, term()}
@spec start_link(start_opts()) :: GenServer.on_start()
@spec stop(GenServer.server()) :: :ok
Gracefully stops the GenServer and the Python bridge process.
@spec unload_lora(GenServer.server()) :: :ok | {:error, term()}