nam_rs/models/convnet/
model.rs1use crate::math::common::{AlignedVec, SimdMath};
8use crate::models::wavenet::PostStackHead;
9use crate::models::wavenet::common::WAVENET_MAX_NUM_FRAMES;
10
11use super::block::ConvNetBlock;
12
13#[repr(align(64))]
22pub struct ConvNetModel {
23 pub blocks: Vec<ConvNetBlock>,
25 pub head_scale: f32,
27 pub receptive_field_size: usize,
29 pub post_stack_head: Option<PostStackHead>,
31 pub head_output_scratch: AlignedVec<f32>,
33 pub(crate) scratch_a: AlignedVec<f32>,
35 pub(crate) scratch_b: AlignedVec<f32>,
36 pub prewarm_on_reset: bool,
38 pub linear_head: Option<LinearHead>,
42}
43
44#[derive(Clone)]
46#[repr(align(64))]
47pub struct LinearHead {
48 pub weight: AlignedVec<f32>,
50 pub bias: AlignedVec<f32>,
52 pub in_ch: usize,
54 pub out_ch: usize,
56}
57
58impl ConvNetModel {
59 pub fn in_channels(&self) -> usize {
61 self.blocks.first().map(|b| b.conv.in_ch).unwrap_or(1)
62 }
63
64 pub fn out_channels(&self) -> usize {
66 if let Some(ref head) = self.post_stack_head {
67 head.out_channels()
68 } else if let Some(ref linear) = self.linear_head {
69 linear.out_ch
70 } else {
71 self.blocks.last().map(|b| b.conv.out_ch).unwrap_or(1)
72 }
73 }
74
75 pub fn process(&mut self, input: &[f32], output: &mut [f32]) {
77 unsafe { crate::math::common::dispatch_simd!(self, process_internal, input, output) };
78 }
79
80 #[inline(always)]
87 unsafe fn process_internal<M: SimdMath>(&mut self, input: &[f32], output: &mut [f32]) {
88 let total_frames = input.len();
89 if total_frames == 0 || self.blocks.is_empty() {
90 output[..total_frames].fill(0.0);
91 return;
92 }
93
94 let out_ch = self.out_channels();
95 let mut pos = 0;
96
97 while pos < total_frames {
98 let num_frames = (total_frames - pos).min(WAVENET_MAX_NUM_FRAMES);
99 let in_slice = &input[pos..pos + num_frames];
100
101 let num_blocks = self.blocks.len();
102 let blocks_ptr = self.blocks.as_mut_ptr();
108
109 let first_out_ch = unsafe { (*blocks_ptr).conv.out_ch };
113 let dst_a = &mut self.scratch_a[..num_frames * first_out_ch];
114 unsafe {
116 (*blocks_ptr).process_block_internal::<M>(in_slice, dst_a, num_frames);
117 }
118
119 let mut src_is_a = true;
120
121 for i in 1..num_blocks {
122 let curr = unsafe { &mut *blocks_ptr.add(i) };
125 let curr_out_ch = curr.conv.out_ch;
126
127 if src_is_a {
128 let src = &self.scratch_a
129 [..num_frames * unsafe { (*blocks_ptr.add(i - 1)).conv.out_ch }];
130 let dst = &mut self.scratch_b[..num_frames * curr_out_ch];
131 unsafe {
135 curr.process_block_internal::<M>(src, dst, num_frames);
136 }
137 } else {
138 let src = &self.scratch_b
139 [..num_frames * unsafe { (*blocks_ptr.add(i - 1)).conv.out_ch }];
140 let dst = &mut self.scratch_a[..num_frames * curr_out_ch];
141 unsafe {
144 curr.process_block_internal::<M>(src, dst, num_frames);
145 }
146 }
147
148 src_is_a = !src_is_a;
149 }
150
151 let last_result_in_a = (num_blocks - 1).is_multiple_of(2);
152 let last_out_ch = unsafe { (*blocks_ptr.add(num_blocks - 1)).conv.out_ch };
155 let last_slice = if last_result_in_a {
156 &self.scratch_a[..num_frames * last_out_ch]
157 } else {
158 &self.scratch_b[..num_frames * last_out_ch]
159 };
160
161 if let Some(ref mut head_proc) = self.post_stack_head {
162 let head_out_ch = head_proc.out_channels();
163 let head_scratch = &mut self.head_output_scratch[..num_frames * head_out_ch];
164 unsafe {
165 head_proc.process_block(last_slice, head_scratch, num_frames);
166 }
167 let out_start = pos * out_ch;
168 let out_slice = &mut output[out_start..out_start + num_frames * out_ch];
169 out_slice.copy_from_slice(head_scratch);
170 unsafe {
171 M::apply_gain(out_slice, self.head_scale);
172 }
173 } else if let Some(ref linear) = self.linear_head {
174 let lh_out_ch = linear.out_ch;
175 let out_start = pos * out_ch;
176 let out_slice = &mut output[out_start..out_start + num_frames * lh_out_ch];
177 out_slice.fill(linear.bias[0]);
178 for f in 0..num_frames {
179 let src = &last_slice[f * linear.in_ch..(f + 1) * linear.in_ch];
180 let dst = &mut out_slice[f * lh_out_ch..(f + 1) * lh_out_ch];
181 for (o, dst_val) in dst.iter_mut().enumerate().take(lh_out_ch) {
182 let mut acc = linear.bias[o];
183 let row_start = o * linear.in_ch;
184 for (i, &src_val) in src.iter().enumerate().take(linear.in_ch) {
185 acc += src_val * linear.weight[row_start + i];
186 }
187 *dst_val = acc;
188 }
189 }
190 unsafe {
191 M::apply_gain(out_slice, self.head_scale);
192 }
193 } else {
194 let out_start = pos * out_ch;
195 let out_slice = &mut output[out_start..out_start + num_frames * out_ch];
196 out_slice.copy_from_slice(last_slice);
197 unsafe {
198 M::apply_gain(out_slice, self.head_scale);
199 }
200 }
201
202 pos += num_frames;
203 }
204 }
205
206 #[cold]
208 pub fn prewarm(&mut self) {
209 unsafe {
210 crate::math::common::dispatch_simd!(self, prewarm_internal);
211 }
212 }
213
214 #[inline(always)]
215 #[cold]
216 unsafe fn prewarm_internal<M: SimdMath>(&mut self) {
217 let num_blocks = self.blocks.len();
218 if num_blocks == 0 {
219 return;
220 }
221
222 let blocks_ptr = self.blocks.as_mut_ptr();
223
224 for i in 0..num_blocks {
225 unsafe {
226 (*blocks_ptr.add(i)).prewarm_internal::<M>();
227 }
228 }
229
230 if let Some(ref mut head_proc) = self.post_stack_head {
231 head_proc.prewarm();
232 }
233 }
235}
236
237#[cfg(test)]
238#[path = "convnet_model_test.rs"]
239mod tests;