use crate::dsp::mirror_buf::MirroredBuffer;
use crate::math::common::AlignedVec;
use crate::models::StaticModel;
use crate::models::a2::activations::ActivationType;
use crate::models::a2::gating::{BlendingActivationConfig, GatingActivationConfig, GatingMode};
use crate::models::a2::head::A2HeadConv;
use crate::models::a2::layer::A2Layer;
use crate::models::wavenet::common::WAVENET_MAX_NUM_FRAMES;
use serde_json::Value;
pub mod build;
pub mod prewarm;
pub mod process;
pub mod process_cascade;
pub struct WaveNetA2Dyn {
pub input_channels: usize,
pub head_size: usize,
pub channels: usize,
pub bottleneck: usize,
pub num_layers: usize,
pub layers: Vec<A2Layer>,
pub rechannel_w_f32: AlignedVec<f32>,
pub head_conv: Option<A2HeadConv>,
pub head_rechannel_w: AlignedVec<f32>,
pub head_rechannel_b: AlignedVec<f32>,
pub head_rechannel_scale: AlignedVec<f32>,
pub head_accum: AlignedVec<f32>,
pub head_write_pos: usize,
pub head_ring_mask: usize,
pub layer_buffers: Vec<MirroredBuffer<f32>>,
pub layer_ring_sizes: Vec<usize>,
pub layer_lookbacks: Vec<usize>,
pub layer_buffer_starts: Vec<usize>,
pub layer_in: AlignedVec<f32>,
pub kernel_sizes: Vec<usize>,
pub dilations: Vec<usize>,
pub activations: Vec<ActivationType>,
pub gating_modes: Vec<GatingMode>,
pub secondary_activations: Vec<Option<ActivationType>>,
pub gating_configs: Vec<Option<GatingActivationConfig>>,
pub blending_configs: Vec<Option<BlendingActivationConfig>>,
pub head1x1_active: bool,
pub head_accum_size: usize,
pub h1_in_size: usize,
pub head1x1_w: AlignedVec<f32>,
pub head1x1_b: AlignedVec<f32>,
pub receptive_field_size: usize,
pub max_buffer_size: usize,
pub layer_raw: Option<Value>,
pub condition_size: usize,
pub z_scratch: AlignedVec<f32>,
pub mixin_scratch: AlignedVec<f32>,
pub head1x1_scratch: AlignedVec<f32>,
pub l1x1_scratch: AlignedVec<f32>,
pub cond_scratch: AlignedVec<f32>,
pub prewarm_on_reset: bool,
pub condition_dsp: Option<Box<StaticModel>>,
pub condition_dsp_output: AlignedVec<f32>,
}
impl WaveNetA2Dyn {
#[expect(
clippy::too_many_arguments,
reason = "A2 dynamic model constructor requiring many topology parameters for runtime-adaptive neural network initialization"
)]
pub fn new(
input_channels: usize,
channels: usize,
bottleneck: usize,
head_size: usize,
head_accum_size: usize,
h1_in_size: usize,
kernel_sizes: &[usize],
dilations: &[usize],
activations: Vec<ActivationType>,
gating_modes: Vec<GatingMode>,
secondary_activations: Vec<Option<ActivationType>>,
head1x1_active: bool,
) -> anyhow::Result<Self> {
let num_layers = kernel_sizes.len();
assert_eq!(dilations.len(), num_layers);
assert_eq!(activations.len(), num_layers);
assert_eq!(gating_modes.len(), num_layers);
assert_eq!(secondary_activations.len(), num_layers);
let max_buf = WAVENET_MAX_NUM_FRAMES;
let mut rf = 0usize;
for i in 0..num_layers {
rf += (kernel_sizes[i] - 1) * dilations[i];
}
rf += super::super::params::A2_HEAD_KERNEL_SIZE - 1;
let head_ring_size = (rf + max_buf + 1).next_power_of_two();
let head_ring_mask = head_ring_size - 1;
let mut layer_buffers = Vec::with_capacity(num_layers);
let mut layer_ring_sizes = Vec::with_capacity(num_layers);
let mut layer_lookbacks = Vec::with_capacity(num_layers);
let mut layer_buffer_starts = Vec::with_capacity(num_layers);
for i in 0..num_layers {
let max_lookback = (kernel_sizes[i] - 1) * dilations[i];
let cap = max_lookback + max_buf + 1;
let mb = MirroredBuffer::<f32>::new(cap * channels)?;
let ring_size = mb.size();
layer_buffers.push(mb);
layer_ring_sizes.push(ring_size);
layer_lookbacks.push(max_lookback * channels);
layer_buffer_starts.push(ring_size);
}
let mut gating_configs = Vec::with_capacity(num_layers);
let mut blending_configs = Vec::with_capacity(num_layers);
for i in 0..num_layers {
let gc = match gating_modes[i] {
GatingMode::Gated => {
let sec = secondary_activations[i]
.clone()
.unwrap_or(ActivationType::Sigmoid);
Some(GatingActivationConfig::new(activations[i].clone(), sec))
}
_ => None,
};
let bc = match gating_modes[i] {
GatingMode::Blended => {
let sec = secondary_activations[i]
.clone()
.unwrap_or(ActivationType::Sigmoid);
Some(BlendingActivationConfig::new(
activations[i].clone(),
sec,
bottleneck,
)?)
}
_ => None,
};
gating_configs.push(gc);
blending_configs.push(bc);
}
let head1x1_w = if head1x1_active {
AlignedVec::new(head_accum_size * h1_in_size, 0.0f32)
.expect("allocation should succeed for test-sized buffers")
} else {
AlignedVec::new(0, 0.0f32).expect("allocation should succeed for test-sized buffers")
};
let head1x1_b = if head1x1_active {
AlignedVec::new(head_accum_size, 0.0f32)
.expect("allocation should succeed for test-sized buffers")
} else {
AlignedVec::new(0, 0.0f32).expect("allocation should succeed for test-sized buffers")
};
let head1x1_scratch = if head1x1_active {
AlignedVec::new(head_accum_size, 0.0f32)
.expect("allocation should succeed for test-sized buffers")
} else {
AlignedVec::new(0, 0.0f32).expect("allocation should succeed for test-sized buffers")
};
Ok(Self {
input_channels,
head_size,
head_accum_size,
h1_in_size,
channels,
bottleneck,
num_layers,
layers: Vec::with_capacity(num_layers),
rechannel_w_f32: AlignedVec::new(input_channels * channels, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
head_conv: None,
head_rechannel_w: AlignedVec::new(
head_size.max(1) * super::super::params::A2_HEAD_KERNEL_SIZE * head_accum_size,
0.0f32,
)
.expect("allocation should succeed for test-sized buffers"),
head_rechannel_b: AlignedVec::new(head_size.max(1), 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
head_rechannel_scale: AlignedVec::new(head_size.max(1), 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
head_accum: AlignedVec::new(head_ring_size * head_accum_size, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
head_write_pos: rf,
head_ring_mask,
layer_buffers,
layer_ring_sizes,
layer_lookbacks,
layer_buffer_starts,
layer_in: AlignedVec::new(channels * max_buf, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
kernel_sizes: kernel_sizes.to_vec(),
dilations: dilations.to_vec(),
activations,
gating_modes,
secondary_activations,
gating_configs,
blending_configs,
head1x1_active,
head1x1_w,
head1x1_b,
receptive_field_size: rf,
max_buffer_size: max_buf,
layer_raw: None,
condition_size: 1,
z_scratch: AlignedVec::new(bottleneck * 2, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
mixin_scratch: AlignedVec::new(bottleneck * 2, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
l1x1_scratch: AlignedVec::new(channels, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
cond_scratch: AlignedVec::new(1, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
head1x1_scratch,
prewarm_on_reset: true,
condition_dsp: None,
condition_dsp_output: AlignedVec::new(0, 0.0f32)
.expect("allocation should succeed for test-sized buffers"),
})
}
#[inline(always)]
pub fn channels(&self) -> usize {
self.channels
}
#[inline(always)]
pub fn receptive_field(&self) -> usize {
self.receptive_field_size
}
pub fn set_layer_raw(&mut self, raw: Option<Value>) {
self.layer_raw = raw;
}
pub fn set_condition_dsp(&mut self, cond_dsp: Option<Box<StaticModel>>, max_buf: usize) {
let cond_size = if cond_dsp.is_some() {
self.condition_size
} else {
0
};
self.condition_dsp_output = AlignedVec::new(cond_size * max_buf, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
self.condition_dsp = cond_dsp;
}
#[inline(always)]
pub fn has_weights(&self) -> bool {
!self.layers.is_empty()
}
pub fn set_max_buffer_size(&mut self, max_buf: usize) -> anyhow::Result<()> {
if max_buf < self.max_buffer_size {
return Ok(());
}
if max_buf == self.max_buffer_size {
let rf = self.receptive_field_size;
let ha_len = self.head_accum.len();
self.head_accum[..ha_len].fill(0.0);
self.head_write_pos = rf;
for buf in self.layer_buffers.iter_mut() {
let len = buf.size();
buf[..len].fill(0.0);
}
self.layer_buffer_starts
.copy_from_slice(&self.layer_ring_sizes);
let li_len = self.layer_in.len();
self.layer_in[..li_len].fill(0.0);
let cd_len = self.condition_dsp_output.len();
self.condition_dsp_output[..cd_len].fill(0.0);
return Ok(());
}
self.max_buffer_size = max_buf;
let rf = self.receptive_field_size;
let channels = self.channels;
self.layer_buffers.clear();
self.layer_ring_sizes.clear();
self.layer_lookbacks.clear();
self.layer_buffer_starts.clear();
for i in 0..self.num_layers {
let max_lookback = (self.kernel_sizes[i] - 1) * self.dilations[i];
let cap = max_lookback + max_buf + 1;
let mb = MirroredBuffer::<f32>::new(cap * channels)?;
let ring_size = mb.size();
self.layer_buffers.push(mb);
self.layer_ring_sizes.push(ring_size);
self.layer_lookbacks.push(max_lookback * channels);
self.layer_buffer_starts.push(ring_size);
}
self.layer_in = AlignedVec::new(channels * max_buf, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
let head_ring_size = (rf + max_buf + 1).next_power_of_two();
self.head_ring_mask = head_ring_size - 1;
self.head_accum = AlignedVec::new(head_ring_size * channels, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
self.head_write_pos = rf;
let cond_output_size = self.condition_size * max_buf;
self.condition_dsp_output = AlignedVec::new(cond_output_size, 0.0f32)
.expect("allocation should succeed for test-sized buffers");
Ok(())
}
pub fn reset(&mut self, _sample_rate: u32, max_buffer_size: usize) -> anyhow::Result<()> {
self.set_max_buffer_size(max_buffer_size)?;
if self.prewarm_on_reset {
self.prewarm();
}
Ok(())
}
}
#[cfg(test)]
#[path = "../dynamic_test.rs"]
mod tests;