---
title: sa_audio
---

<div class="python-reference">
<a id="fastvideo.models.vaes.sa_audio-classes"></a>
<a id="fastvideo.models.vaes.sa_audio.SAAudioVAEModel-functions"></a>
<a id="fastvideo.models.vaes.sa_audio"></a>
<p>Lazy-loading pipeline wrapper around <code>OobleckVAE</code>.</p>
<p>Two reasons this exists rather than using <code>OobleckVAE</code> directly:</p>
<ol>
<li>The underlying VAE is fetched on first <code>encode</code>/<code>decode</code> call, not
     at construction — lets pipelines build the module tree on CPU
     before knowing the target device.</li>
<li>The lazy VAE's params are hidden from <code>named_parameters()</code> so the
     FastVideo pipeline-component loader doesn't try to match Oobleck's
     safetensors against the host pipeline's converted-repo state dict.</li>
</ol>
<p>For standalone use prefer <code>OobleckVAE.from_pretrained(...)</code> directly.</p>
<h2 id="fastvideo.models.vaes.sa_audio.OobleckVAEConfig"><code>fastvideo.models.vaes.sa_audio.OobleckVAEConfig</code></h2>
<pre class="doc-signature"><code>OobleckVAEConfig(arch_config: VAEArchConfig = OobleckVAEArchConfig(), load_encoder: bool = True, load_decoder: bool = True, tile_sample_min_height: int = 256, tile_sample_min_width: int = 256, tile_sample_min_num_frames: int = 16, tile_sample_stride_height: int = 192, tile_sample_stride_width: int = 192, tile_sample_stride_num_frames: int = 12, blend_num_frames: int = 0, use_tiling: bool = False, use_temporal_tiling: bool = False, use_parallel_tiling: bool = False, use_temporal_scaling_frames: bool = True, pretrained_path: str = &#x27;stabilityai/stable-audio-open-1.0&#x27;, pretrained_subfolder: str = &#x27;vae&#x27;, pretrained_dtype: str = &#x27;float16&#x27;, *, _resolved_attention_backend: AttentionBackendEnum | None = None)</code></pre>
<p>FastVideo VAE config wrapping the Oobleck arch.</p>
<p>Audio VAEs don't use the temporal/spatial tiling defaults that the
base VAEConfig is shaped for (those exist for video VAEs); they are
retained but irrelevant for audio.</p>

<h2 id="fastvideo.models.vaes.sa_audio.SAAudioVAEModel"><code>fastvideo.models.vaes.sa_audio.SAAudioVAEModel</code></h2>
<pre class="doc-signature"><code>SAAudioVAEModel(config: OobleckVAEConfig)</code></pre>
<p>Pipeline-glue lazy loader around <code>OobleckVAE</code>.</p>

<details><summary>Source</summary><pre><code>class SAAudioVAEModel(nn.Module):
    &quot;&quot;&quot;Pipeline-glue lazy loader around `OobleckVAE`.&quot;&quot;&quot;

    def __init__(self, config: OobleckVAEConfig) -&gt; None:
        super().__init__()
        self.config = config
        arch = config.arch_config
        self.pretrained_path: str = config.pretrained_path
        self.pretrained_subfolder: str | None = config.pretrained_subfolder
        self.pretrained_dtype: str = config.pretrained_dtype
        self.sampling_rate: int = arch.sampling_rate
        self.audio_channels: int = arch.audio_channels
        self.decoder_input_channels: int = arch.decoder_input_channels
        self._oobleck_vae = None

    def named_parameters(self, prefix: str = &quot;&quot;, recurse: bool = True):
        # Hide the lazy-loaded VAE — its weights are fetched separately
        # and shouldn&#x27;t appear in the host pipeline&#x27;s loader sweep.
        for name, param in super().named_parameters(prefix=prefix, recurse=recurse):
            if name.startswith(&quot;_oobleck_vae.&quot;) or name == &quot;_oobleck_vae&quot;:
                continue
            yield name, param

    def _build(self, device: torch.device | None = None):
        from fastvideo.models.vaes.oobleck import OobleckVAE

        path = self.pretrained_path
        if not path:
            raise ValueError(&quot;OobleckVAEConfig.pretrained_path must be set; expected &quot;
                             &quot;`stabilityai/stable-audio-open-1.0` or a local path.&quot;)
        dtype = getattr(torch, self.pretrained_dtype, torch.float32)
        # If the caller already pointed us at the VAE dir directly, drop
        # the subfolder. Otherwise pass through (default &quot;vae&quot;).
        subfolder: str | None = self.pretrained_subfolder
        if subfolder and os.path.isdir(path) and os.path.isfile(os.path.join(path, &quot;config.json&quot;)):
            subfolder = None
        model = OobleckVAE.from_pretrained(path, subfolder=subfolder, torch_dtype=dtype)
        if device is not None:
            model = model.to(device=device)
        model.eval()
        return model

    @property
    def oobleck_vae(self):
        if self._oobleck_vae is None:
            self._oobleck_vae = self._build()
        return self._oobleck_vae

    # Back-compat alias: callers that imported this earlier referred to
    # the underlying VAE as `sa_audio_vae_model`. Both names point at the
    # same object.
    @property
    def sa_audio_vae_model(self):
        return self.oobleck_vae

    @property
    def hop_length(self) -&gt; int:
        return int(self.oobleck_vae.hop_length)

    def _move_to_input_device(self, model, ref: torch.Tensor):
        if ref is None:
            return model
        first_param = next(model.parameters(), None)
        if first_param is not None and first_param.device != ref.device:
            model = model.to(device=ref.device)
            self._oobleck_vae = model
        return model

    def decode(self, latent: torch.Tensor) -&gt; torch.Tensor:
        &quot;&quot;&quot;Decode an audio latent (`&#91;B, C_latent, L&#93;`) -&gt; waveform
        (`&#91;B, audio_channels, samples&#93;`).
        &quot;&quot;&quot;
        model = self.oobleck_vae
        model = self._move_to_input_device(model, latent)
        with torch.no_grad():
            out = model.decode(latent.to(next(model.parameters()).dtype))
        if hasattr(out, &quot;sample&quot;):
            return out.sample
        return out

    def encode(self, waveform: torch.Tensor, sample_posterior: bool = False) -&gt; torch.Tensor:
        &quot;&quot;&quot;Encode `&#91;B, C_audio, samples&#93;` -&gt; latent `&#91;B, C_latent, L&#93;`.

        `sample_posterior=False` (default): deterministic mean.
        `sample_posterior=True`: stochastic sample (`mean + softplus(scale) * randn`).
        &quot;&quot;&quot;
        model = self.oobleck_vae
        model = self._move_to_input_device(model, waveform)
        with torch.no_grad():
            out = model.encode(waveform.to(next(model.parameters()).dtype))
        if hasattr(out, &quot;latent_dist&quot;):
            out = out.latent_dist
        return out.sample() if sample_posterior else out.mode()</code></pre></details>
<h3 id="fastvideo.models.vaes.sa_audio.SAAudioVAEModel.named_parameters"><code>fastvideo.models.vaes.sa_audio.SAAudioVAEModel.named_parameters</code></h3>
<pre class="doc-signature"><code>named_parameters(prefix: str = &#x27;&#x27;, recurse: bool = True)</code></pre>

<details><summary>Source</summary><pre><code>def named_parameters(self, prefix: str = &quot;&quot;, recurse: bool = True):
    # Hide the lazy-loaded VAE — its weights are fetched separately
    # and shouldn&#x27;t appear in the host pipeline&#x27;s loader sweep.
    for name, param in super().named_parameters(prefix=prefix, recurse=recurse):
        if name.startswith(&quot;_oobleck_vae.&quot;) or name == &quot;_oobleck_vae&quot;:
            continue
        yield name, param</code></pre></details>
<h3 id="fastvideo.models.vaes.sa_audio.SAAudioVAEModel.decode"><code>fastvideo.models.vaes.sa_audio.SAAudioVAEModel.decode</code></h3>
<pre class="doc-signature"><code>decode(latent: torch.Tensor) -&gt; torch.Tensor</code></pre>
<p>Decode an audio latent (<code>[B, C_latent, L]</code>) -&gt; waveform
(<code>[B, audio_channels, samples]</code>).</p>
<details><summary>Source</summary><pre><code>def decode(self, latent: torch.Tensor) -&gt; torch.Tensor:
    &quot;&quot;&quot;Decode an audio latent (`&#91;B, C_latent, L&#93;`) -&gt; waveform
    (`&#91;B, audio_channels, samples&#93;`).
    &quot;&quot;&quot;
    model = self.oobleck_vae
    model = self._move_to_input_device(model, latent)
    with torch.no_grad():
        out = model.decode(latent.to(next(model.parameters()).dtype))
    if hasattr(out, &quot;sample&quot;):
        return out.sample
    return out</code></pre></details>
<h3 id="fastvideo.models.vaes.sa_audio.SAAudioVAEModel.encode"><code>fastvideo.models.vaes.sa_audio.SAAudioVAEModel.encode</code></h3>
<pre class="doc-signature"><code>encode(waveform: torch.Tensor, sample_posterior: bool = False) -&gt; torch.Tensor</code></pre>
<p>Encode <code>[B, C_audio, samples]</code> -&gt; latent <code>[B, C_latent, L]</code>.</p>
<p><code>sample_posterior=False</code> (default): deterministic mean.
<code>sample_posterior=True</code>: stochastic sample (<code>mean + softplus(scale) * randn</code>).</p>
<details><summary>Source</summary><pre><code>def encode(self, waveform: torch.Tensor, sample_posterior: bool = False) -&gt; torch.Tensor:
    &quot;&quot;&quot;Encode `&#91;B, C_audio, samples&#93;` -&gt; latent `&#91;B, C_latent, L&#93;`.

    `sample_posterior=False` (default): deterministic mean.
    `sample_posterior=True`: stochastic sample (`mean + softplus(scale) * randn`).
    &quot;&quot;&quot;
    model = self.oobleck_vae
    model = self._move_to_input_device(model, waveform)
    with torch.no_grad():
        out = model.encode(waveform.to(next(model.parameters()).dtype))
    if hasattr(out, &quot;latent_dist&quot;):
        out = out.latent_dist
    return out.sample() if sample_posterior else out.mode()</code></pre></details>
</div>
