pub struct OobleckDecoder<B: Backend> {
pub conv1: WnConv1d<B>,
pub block: Vec<OobleckDecoderBlock<B>>,
pub snake1: Snake1d<B>,
pub conv2: WnConv1d<B>,
pub hop_length: usize,
/* private fields */
}Expand description
Oobleck VAE decoder. See the module-level docs for the canonical burnpack tensor names.
Fields§
§conv1: WnConv1d<B>§block: Vec<OobleckDecoderBlock<B>>§snake1: Snake1d<B>§conv2: WnConv1d<B>§hop_length: usizeAudio samples per latent frame (product of downsampling_ratios).
Implementations§
Source§impl<B: Backend> OobleckDecoder<B>
impl<B: Backend> OobleckDecoder<B>
pub fn new(config: &OobleckVaeConfig, device: &B::Device) -> Self
Sourcepub fn from_burnpack(
config: &OobleckVaeConfig,
path: &Path,
device: &B::Device,
) -> Result<Self>
pub fn from_burnpack( config: &OobleckVaeConfig, path: &Path, device: &B::Device, ) -> Result<Self>
Load decoder weights from a burnpack file using the canonical tensor names documented at the top of this module.
Examples found in repository?
13fn main() -> anyhow::Result<()> {
14 let model_dir = std::env::args()
15 .nth(1)
16 .map(std::path::PathBuf::from)
17 .unwrap_or_else(|| std::path::PathBuf::from("/home/meka/repos/ace"));
18 let device = Default::default();
19
20 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
21 let vae = OobleckDecoder::<B>::from_burnpack(
22 &vae_config,
23 &model_dir.join("acestep-vae.bpk"),
24 &device,
25 )?;
26 let silence =
27 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
28 let [_, frames, _] = silence.silence_latent.dims();
29 println!("silence latent: {frames} frames");
30
31 let latents = silence.slice(750);
32 let audio = vae.decode(latents);
33 let [_, channels, samples] = audio.dims();
34 let values: Vec<f32> = audio
35 .into_data()
36 .convert::<f32>()
37 .to_vec()
38 .map_err(|e| anyhow::anyhow!("{e}"))?;
39 let peak = values.iter().fold(0.0_f32, |a, v| a.max(v.abs()));
40 let rms = (values.iter().map(|v| v * v).sum::<f32>() / values.len() as f32).sqrt();
41 println!("decoded: {channels} ch x {samples} samples");
42 println!("peak {peak:.6} rms {rms:.6}");
43 println!(
44 "{}",
45 if peak < 1e-3 {
46 "OK: VAE decodes silence correctly"
47 } else {
48 "SUSPECT: VAE decoder output is far from silence"
49 }
50 );
51
52 // Also dump the latent stats so we can eyeball them.
53 let latents = SilenceLatent::<B>::from_burnpack(
54 Path::new(&model_dir).join("silence_latent.bpk").as_path(),
55 &device,
56 )?
57 .slice(4);
58 let v: Vec<f32> = latents
59 .into_data()
60 .convert::<f32>()
61 .to_vec()
62 .map_err(|e| anyhow::anyhow!("{e}"))?;
63 let lpeak = v.iter().fold(0.0_f32, |a, v| a.max(v.abs()));
64 let lrms = (v.iter().map(|v| v * v).sum::<f32>() / v.len() as f32).sqrt();
65 println!("latent peak {lpeak:.6} rms {lrms:.6}");
66 Ok(())
67}More examples
61fn main() -> anyhow::Result<()> {
62 let model_dir = std::env::args()
63 .nth(1)
64 .unwrap_or_else(|| "/home/meka/repos/ace".to_string());
65 let model_dir = Path::new(&model_dir);
66 let device = Default::default();
67
68 let dit_config = AceStepConfig::load(&model_dir.join("dit_config.json"))?;
69 let condition = AceStepCondition::<B>::from_burnpack(
70 &dit_config,
71 &model_dir.join("acestep-condition.bpk"),
72 &device,
73 )?;
74 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
75 let vae = OobleckDecoder::<B>::from_burnpack(
76 &vae_config,
77 &model_dir.join("acestep-vae.bpk"),
78 &device,
79 )?;
80 let silence =
81 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
82
83 // Splitmix64 random codes.
84 let mut state = 42_u64;
85 let mut rand_codes = Vec::new();
86 for _ in 0..20 {
87 state = state
88 .wrapping_mul(6364136223846793005)
89 .wrapping_add(1442695040888963407);
90 rand_codes.push(((state >> 33) % 64000) as u32);
91 }
92
93 let lm_codes: Vec<u32> = vec![
94 61890, 51649, 53753, 53753, 56314, 53754, 55802, 4538, 4538, 4538, 5050, 5050, 5050, 5050,
95 5050, 5050, 5050, 5050, 5050, 43513,
96 ];
97
98 // Structure check: for a few distinct codes, dump the detokenizer output
99 // frames and measure how much outputs differ between codes.
100 for code in [0u32, 12345, 53754] {
101 let tensor = Tensor::<B, 2, Int>::from_data(TensorData::new(vec![code], [1, 1]), &device);
102 let hints = condition.codes_to_hints(tensor); // [1, 5, 64]
103 let values: Vec<f32> = hints
104 .into_data()
105 .convert::<f32>()
106 .to_vec()
107 .map_err(|e| anyhow::anyhow!("{e}"))?;
108 let frame_means: Vec<f32> = values
109 .as_chunks::<64>()
110 .0
111 .iter()
112 .map(|f| f.iter().sum::<f32>() / 64.0)
113 .collect();
114 let frame_rms: Vec<f32> = values.as_chunks::<64>().0.iter().map(|f| rms(f)).collect();
115 println!(
116 "code {code:>6}: frame means {:?}",
117 frame_means
118 .iter()
119 .map(|v| format!("{v:.4}"))
120 .collect::<Vec<_>>()
121 );
122 println!(
123 " frame rms {:?}",
124 frame_rms
125 .iter()
126 .map(|v| format!("{v:.4}"))
127 .collect::<Vec<_>>()
128 );
129 }
130
131 // Time-variance vs channel-variance of hint latents for random codes.
132 let tensor =
133 Tensor::<B, 2, Int>::from_data(TensorData::new(rand_codes.clone(), [1, 20]), &device);
134 let hints = condition.codes_to_hints(tensor); // [1, 100, 64]
135 let values: Vec<f32> = hints
136 .into_data()
137 .convert::<f32>()
138 .to_vec()
139 .map_err(|e| anyhow::anyhow!("{e}"))?;
140 let (frames, _) = values.as_chunks::<64>();
141 // variance of per-frame means (time structure) vs mean per-frame variance (channel structure)
142 let frame_means: Vec<f32> = frames
143 .iter()
144 .map(|f| f.iter().sum::<f32>() / 64.0)
145 .collect();
146 let tm = frame_means.iter().sum::<f32>() / frame_means.len() as f32;
147 let time_var =
148 frame_means.iter().map(|v| (v - tm) * (v - tm)).sum::<f32>() / frame_means.len() as f32;
149 let chan_var = frames
150 .iter()
151 .map(|f| {
152 let m = f.iter().sum::<f32>() / 64.0;
153 f.iter().map(|v| (v - m) * (v - m)).sum::<f32>() / 64.0
154 })
155 .sum::<f32>()
156 / frames.len() as f32;
157 println!("random-code hints: time var {time_var:.6} channel var {chan_var:.6}");
158
159 run_case(
160 "random codes ",
161 &rand_codes,
162 &condition,
163 &vae,
164 &silence,
165 &device,
166 )?;
167 run_case(
168 "LM codes ",
169 &lm_codes,
170 &condition,
171 &vae,
172 &silence,
173 &device,
174 )?;
175 run_case(
176 "constant 0 ",
177 &[0u32; 20],
178 &condition,
179 &vae,
180 &silence,
181 &device,
182 )?;
183 Ok(())
184}63fn main() -> anyhow::Result<()> {
64 let mut args = std::env::args().skip(1);
65 let model_dir = PathBuf::from(args.next().unwrap_or_else(|| "/home/meka/repos/ace".into()));
66 let out_dir = PathBuf::from(args.next().unwrap_or_else(|| "/var/tmp/dump_ours".into()));
67 let noise_path = args.next().map(PathBuf::from);
68 std::fs::create_dir_all(&out_dir)?;
69
70 let device = Default::default();
71
72 let dit_config = AceStepConfig::load(&model_dir.join("dit_config.json"))?;
73 let text_config = Qwen3Config::load(&model_dir.join("qwen3_config.json"))?;
74 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
75 eprintln!("loading components...");
76 let text_encoder = Qwen3Model::<B>::from_burnpack(
77 &text_config,
78 &model_dir.join("qwen3-encoder.bpk"),
79 &device,
80 )?;
81 let condition = AceStepCondition::<B>::from_burnpack(
82 &dit_config,
83 &model_dir.join("acestep-condition.bpk"),
84 &device,
85 )?;
86 let dit =
87 AceStepDiT::<B>::from_burnpack(&dit_config, &model_dir.join("acestep-dit.bpk"), &device)?;
88 let vae = OobleckDecoder::<B>::from_burnpack(
89 &vae_config,
90 &model_dir.join("acestep-vae.bpk"),
91 &device,
92 )?;
93 let silence =
94 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
95 let tokenizer = tokie::Tokenizer::from_json(model_dir.join("tokenizer.json"))
96 .map_err(|e| anyhow::anyhow!("tokenizer: {e}"))?;
97
98 // ---- text + lyric encoding (causal, official prompts) ----
99 let metas = build_metas_block(Some(120.0), Some("A minor"), Some("4/4"), 4);
100 let text_prompt = build_dit_text_prompt("Metal guitar with a lot of distortion", &metas);
101 let ids = tokenizer.encode(&text_prompt, false).ids;
102 let mut ids: Vec<i64> = ids.into_iter().map(i64::from).collect();
103 ids.push(151643); // explicit EOS (official add_eos=true)
104 let n_text = ids.len();
105 let ids_t = Tensor::<B, 2, Int>::from_data(TensorData::new(ids, [1, n_text]), &device);
106 let text_hidden = text_encoder.forward(ids_t, true);
107 let text_hidden_vec = vec_of(text_hidden.clone());
108 dump_tensor(
109 &out_dir,
110 "text_hidden",
111 &text_hidden_vec,
112 &[n_text as i32, 1024],
113 );
114
115 let lyric_ids = tokenizer
116 .encode(
117 maolan_generate::acestep::pipeline::INSTRUMENTAL_LYRIC_PROMPT,
118 false,
119 )
120 .ids;
121 let mut lyric_ids: Vec<i64> = lyric_ids.into_iter().map(i64::from).collect();
122 lyric_ids.push(151643); // explicit EOS (official add_eos=true)
123 let n_lyric = lyric_ids.len();
124 let lyric_ids_t =
125 Tensor::<B, 2, Int>::from_data(TensorData::new(lyric_ids, [1, n_lyric]), &device);
126 // Official: lyric branch is a raw embed_tokens lookup (no transformer).
127 let lyric_hidden = text_encoder.embed_tokens.forward(lyric_ids_t);
128 let lyric_embed_vec = vec_of(lyric_hidden.clone());
129 dump_tensor(
130 &out_dir,
131 "lyric_embed",
132 &lyric_embed_vec,
133 &[n_lyric as i32, 1024],
134 );
135 let lyric_mask = Tensor::<B, 2, Int>::ones([1, n_lyric], &device);
136
137 // ---- conditioning: use the oracle's enc_hidden/context when provided ----
138 let mut oracle_context: Option<Tensor<B, 3>> = None;
139 let enc = if let Some(dir) = std::env::var_os("MAOLAN_ORACLE_DUMP_DIR") {
140 let dir = PathBuf::from(dir);
141 let (enc_shape, enc_data) = load_bin(&dir.join("enc_hidden.bin"));
142 eprintln!("oracle enc_hidden: {enc_shape:?}");
143 let (ctx_shape, ctx_data) = load_bin(&dir.join("context.bin"));
144 eprintln!("oracle context: {ctx_shape:?}");
145 let enc = Tensor::<B, 3>::from_data(
146 TensorData::new(enc_data, [1, enc_shape[0] as usize, enc_shape[1] as usize]),
147 &device,
148 );
149 let ctx = Tensor::<B, 3>::from_data(TensorData::new(ctx_data, [1, 96, 128]), &device);
150 oracle_context = Some(ctx);
151 enc
152 } else {
153 condition.encode(
154 text_hidden,
155 lyric_hidden,
156 lyric_mask,
157 silence.timbre_reference(),
158 )
159 };
160 let [_, enc_len, enc_dim] = enc.dims();
161 let enc_vec = vec_of(enc.clone());
162 dump_tensor(
163 &out_dir,
164 "enc_hidden",
165 &enc_vec,
166 &[enc_len as i32, enc_dim as i32],
167 );
168
169 // ---- FSQ hints from the given codes (env MAOLAN_ACESTEP_CODES or the
170 // built-in oracle sequence) ----
171 let codes: Vec<u32> = std::env::var_os("MAOLAN_ACESTEP_CODES")
172 .map(|raw| {
173 raw.to_string_lossy()
174 .split(',')
175 .filter_map(|part| part.trim().parse::<u32>().ok())
176 .collect()
177 })
178 .unwrap_or_else(|| ORACLE_CODES.to_vec());
179 let n_codes = codes.len();
180 let codes_tensor =
181 Tensor::<B, 2, Int>::from_data(TensorData::new(codes, [1, n_codes]), &device);
182 let hints = condition.codes_to_hints(codes_tensor);
183 let [_, hint_len, _] = hints.dims();
184 let hints_vec = vec_of(hints.clone());
185 dump_tensor(&out_dir, "detok_output", &hints_vec, &[hint_len as i32, 64]);
186
187 // ---- noise ----
188 let noise = if let Some(path) = noise_path {
189 let (shape, data) = load_bin(&path);
190 let frames = shape[0] as usize;
191 Tensor::<B, 3>::from_data(TensorData::new(data, [1, frames, 64]), &device)
192 } else {
193 maolan_generate::acestep::pipeline::seeded_latent_noise(0, hint_len, 64, &device)
194 };
195 let [_, frames, _] = noise.dims();
196
197 // src latents: hints cropped or silence-padded to the noise's frame count
198 let src = if hint_len >= frames {
199 hints.narrow(1, 0, frames)
200 } else {
201 let padding = silence.slice(frames - hint_len);
202 Tensor::cat(vec![hints, padding], 1)
203 };
204 let chunk_mask = Tensor::ones([1, frames, 64], &device);
205 let context = Tensor::cat(vec![src.clone(), chunk_mask], 2);
206 let context = oracle_context.unwrap_or(context);
207 let context_vec = vec_of(context.clone());
208 dump_tensor(&out_dir, "context", &context_vec, &[frames as i32, 128]);
209
210 let noise_vec = vec_of(noise.clone());
211 dump_tensor(&out_dir, "noise", &noise_vec, &[frames as i32, 64]);
212
213 // ---- DiT loop with per-step dumps ----
214 let kv = dit.prepare_cross_kv(enc);
215 let mut xt = noise;
216 let total = TURBO_TIMESTEPS.len();
217 for (index, &t_cur) in TURBO_TIMESTEPS.iter().enumerate() {
218 let xt_vec = vec_of(xt.clone());
219 dump_tensor(
220 &out_dir,
221 &format!("dit_step{index}_xt"),
222 &xt_vec,
223 &[frames as i32, 64],
224 );
225 let v = dit.forward_with_kv(xt.clone(), t_cur, context.clone(), &kv);
226 let v_vec = vec_of(v.clone());
227 dump_tensor(
228 &out_dir,
229 &format!("dit_step{index}_vt"),
230 &v_vec,
231 &[frames as i32, 64],
232 );
233 let dt = if index + 1 == total {
234 t_cur
235 } else {
236 t_cur - TURBO_TIMESTEPS[index + 1]
237 };
238 xt = xt - v * dt;
239 }
240 let x0_vec = vec_of(xt.clone());
241 dump_tensor(&out_dir, "dit_x0", &x0_vec, &[frames as i32, 64]);
242
243 // ---- VAE decode ----
244 let audio = vae.decode(xt);
245 let [_, channels, samples] = audio.dims();
246 let audio_vec = vec_of(audio);
247 dump_tensor(
248 &out_dir,
249 "vae_audio",
250 &audio_vec,
251 &[channels as i32, samples as i32],
252 );
253 println!("dumps written to {}", out_dir.display());
254 Ok(())
255}63fn main() -> anyhow::Result<()> {
64 let mut args = std::env::args().skip(1);
65 let model_dir = PathBuf::from(args.next().unwrap_or_else(|| "/home/meka/repos/ace".into()));
66 let out_dir = PathBuf::from(args.next().unwrap_or_else(|| "/var/tmp/dump_ours".into()));
67 let noise_path = args.next().map(PathBuf::from);
68 std::fs::create_dir_all(&out_dir)?;
69
70 let device = burn::backend::wgpu::WgpuDevice::default();
71 burn::backend::wgpu::init_setup::<burn::backend::wgpu::graphics::Vulkan>(
72 &device,
73 burn::backend::wgpu::RuntimeOptions {
74 memory_config: burn::backend::wgpu::MemoryConfiguration::ExclusivePages,
75 ..Default::default()
76 },
77 );
78
79 let variant_sft = std::env::var("MAOLAN_ACESTEP_VARIANT")
80 .map(|v| v == "sft")
81 .unwrap_or(false);
82 let prefix = if variant_sft { "sft-" } else { "" };
83
84 let dit_config = AceStepConfig::load(&model_dir.join(format!("{prefix}dit_config.json")))?;
85 let text_config = Qwen3Config::load(&model_dir.join("qwen3_config.json"))?;
86 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
87 eprintln!("loading components (prefix '{prefix}')...");
88 let text_encoder = Qwen3Model::<B>::from_burnpack(
89 &text_config,
90 &model_dir.join("qwen3-encoder.bpk"),
91 &device,
92 )?;
93 let condition = AceStepCondition::<B>::from_burnpack(
94 &dit_config,
95 &model_dir.join(format!("{prefix}acestep-condition.bpk")),
96 &device,
97 )?;
98 let dit = AceStepDiT::<B>::from_burnpack(
99 &dit_config,
100 &model_dir.join(format!("{prefix}acestep-dit.bpk")),
101 &device,
102 )?;
103 let vae = OobleckDecoder::<B>::from_burnpack(
104 &vae_config,
105 &model_dir.join("acestep-vae.bpk"),
106 &device,
107 )?;
108 let silence =
109 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
110 let tokenizer = tokie::Tokenizer::from_json(model_dir.join("tokenizer.json"))
111 .map_err(|e| anyhow::anyhow!("tokenizer: {e}"))?;
112
113 // ---- text + lyric encoding (causal, official prompts) ----
114 let metas = build_metas_block(Some(120.0), Some("A minor"), Some("4/4"), 4);
115 let text_prompt = build_dit_text_prompt("Metal guitar with a lot of distortion", &metas);
116 let ids = tokenizer.encode(&text_prompt, false).ids;
117 let mut ids: Vec<i64> = ids.into_iter().map(i64::from).collect();
118 ids.push(151643); // explicit EOS (official add_eos=true)
119 let n_text = ids.len();
120 let ids_t = Tensor::<B, 2, Int>::from_data(TensorData::new(ids, [1, n_text]), &device);
121 let text_hidden = text_encoder.forward(ids_t, true);
122 let text_hidden_vec = vec_of(text_hidden.clone());
123 dump_tensor(
124 &out_dir,
125 "text_hidden",
126 &text_hidden_vec,
127 &[n_text as i32, 1024],
128 );
129
130 let lyric_ids = tokenizer
131 .encode(
132 maolan_generate::acestep::pipeline::INSTRUMENTAL_LYRIC_PROMPT,
133 false,
134 )
135 .ids;
136 let mut lyric_ids: Vec<i64> = lyric_ids.into_iter().map(i64::from).collect();
137 lyric_ids.push(151643); // explicit EOS (official add_eos=true)
138 let n_lyric = lyric_ids.len();
139 let lyric_ids_t =
140 Tensor::<B, 2, Int>::from_data(TensorData::new(lyric_ids, [1, n_lyric]), &device);
141 // Official: lyric branch is a raw embed_tokens lookup (no transformer).
142 let lyric_hidden = text_encoder.embed_tokens.forward(lyric_ids_t);
143 let lyric_embed_vec = vec_of(lyric_hidden.clone());
144 dump_tensor(
145 &out_dir,
146 "lyric_embed",
147 &lyric_embed_vec,
148 &[n_lyric as i32, 1024],
149 );
150 let lyric_mask = Tensor::<B, 2, Int>::ones([1, n_lyric], &device);
151
152 // ---- conditioning: use the oracle's enc_hidden/context when provided ----
153 let mut oracle_context: Option<Tensor<B, 3>> = None;
154 let enc = if let Some(dir) = std::env::var_os("MAOLAN_ORACLE_DUMP_DIR") {
155 let dir = PathBuf::from(dir);
156 let (enc_shape, enc_data) = load_bin(&dir.join("enc_hidden.bin"));
157 eprintln!("oracle enc_hidden: {enc_shape:?}");
158 let (ctx_shape, ctx_data) = load_bin(&dir.join("context.bin"));
159 eprintln!("oracle context: {ctx_shape:?}");
160 let enc = Tensor::<B, 3>::from_data(
161 TensorData::new(enc_data, [1, enc_shape[0] as usize, enc_shape[1] as usize]),
162 &device,
163 );
164 let ctx = Tensor::<B, 3>::from_data(TensorData::new(ctx_data, [1, 96, 128]), &device);
165 oracle_context = Some(ctx);
166 enc
167 } else {
168 condition.encode(
169 text_hidden,
170 lyric_hidden,
171 lyric_mask,
172 silence.timbre_reference(),
173 )
174 };
175 let [_, enc_len, enc_dim] = enc.dims();
176 let enc_vec = vec_of(enc.clone());
177 dump_tensor(
178 &out_dir,
179 "enc_hidden",
180 &enc_vec,
181 &[enc_len as i32, enc_dim as i32],
182 );
183
184 // ---- FSQ hints from the given codes (env MAOLAN_ACESTEP_CODES or the
185 // built-in oracle sequence) ----
186 let codes: Vec<u32> = std::env::var_os("MAOLAN_ACESTEP_CODES")
187 .map(|raw| {
188 raw.to_string_lossy()
189 .split(',')
190 .filter_map(|part| part.trim().parse::<u32>().ok())
191 .collect()
192 })
193 .unwrap_or_else(|| ORACLE_CODES.to_vec());
194 let n_codes = codes.len();
195 let codes_tensor =
196 Tensor::<B, 2, Int>::from_data(TensorData::new(codes, [1, n_codes]), &device);
197 let hints = condition.codes_to_hints(codes_tensor);
198 let [_, hint_len, _] = hints.dims();
199 let hints_vec = vec_of(hints.clone());
200 dump_tensor(&out_dir, "detok_output", &hints_vec, &[hint_len as i32, 64]);
201
202 // ---- noise ----
203 let noise = if let Some(path) = noise_path {
204 let (shape, data) = load_bin(&path);
205 let frames = shape[0] as usize;
206 Tensor::<B, 3>::from_data(TensorData::new(data, [1, frames, 64]), &device)
207 } else {
208 maolan_generate::acestep::pipeline::seeded_latent_noise(0, hint_len, 64, &device)
209 };
210 let [_, frames, _] = noise.dims();
211
212 // src latents: hints cropped or silence-padded to the noise's frame count
213 let src = if hint_len >= frames {
214 hints.narrow(1, 0, frames)
215 } else {
216 let padding = silence.slice(frames - hint_len);
217 Tensor::cat(vec![hints, padding], 1)
218 };
219 let chunk_mask = Tensor::ones([1, frames, 64], &device);
220 let context = Tensor::cat(vec![src.clone(), chunk_mask], 2);
221 let context = oracle_context.unwrap_or(context);
222 let context_vec = vec_of(context.clone());
223 dump_tensor(&out_dir, "context", &context_vec, &[frames as i32, 128]);
224
225 let noise_vec = vec_of(noise.clone());
226 dump_tensor(&out_dir, "noise", &noise_vec, &[frames as i32, 64]);
227
228 // ---- DiT loop with per-step dumps (schedule from is_turbo) ----
229 let timesteps: &[f32] = if dit_config.is_turbo {
230 &TURBO_TIMESTEPS
231 } else {
232 &SFT_TIMESTEPS
233 };
234 let kv = dit.prepare_cross_kv(enc);
235 let mut xt = noise;
236 let total = timesteps.len();
237 for (index, &t_cur) in timesteps.iter().enumerate() {
238 let xt_vec = vec_of(xt.clone());
239 dump_tensor(
240 &out_dir,
241 &format!("dit_step{index}_xt"),
242 &xt_vec,
243 &[frames as i32, 64],
244 );
245 let v = dit.forward_with_kv(xt.clone(), t_cur, context.clone(), &kv);
246 let v_vec = vec_of(v.clone());
247 dump_tensor(
248 &out_dir,
249 &format!("dit_step{index}_vt"),
250 &v_vec,
251 &[frames as i32, 64],
252 );
253 let dt = if index + 1 == total {
254 t_cur
255 } else {
256 t_cur - timesteps[index + 1]
257 };
258 xt = xt - v * dt;
259 }
260 let x0_vec = vec_of(xt.clone());
261 dump_tensor(&out_dir, "dit_x0", &x0_vec, &[frames as i32, 64]);
262
263 // ---- VAE decode ----
264 let audio = vae.decode(xt);
265 let [_, channels, samples] = audio.dims();
266 let audio_vec = vec_of(audio);
267 dump_tensor(
268 &out_dir,
269 "vae_audio",
270 &audio_vec,
271 &[channels as i32, samples as i32],
272 );
273 println!("dumps written to {}", out_dir.display());
274 Ok(())
275}Sourcepub fn forward(&self, latents: Tensor<B, 3>) -> Tensor<B, 3>
pub fn forward(&self, latents: Tensor<B, 3>) -> Tensor<B, 3>
Forward pass on channel-major latents [B, decoder_input_channels, T]
returning audio [B, audio_channels, T * hop_length].
Sourcepub fn decode(&self, latents: Tensor<B, 3>) -> Tensor<B, 3>
pub fn decode(&self, latents: Tensor<B, 3>) -> Tensor<B, 3>
Decode time-major DiT latents [B, T, decoder_input_channels] into
audio [B, audio_channels, T * hop_length].
Every released ratio is even, in which case each transpose conv
upsamples by exactly its stride and the output length is already
T * hop_length; the final trim/pad only matters for hypothetical
odd ratios, where ConvTranspose1d falls one sample short per stage.
Examples found in repository?
19fn run_case(
20 name: &str,
21 codes: &[u32],
22 condition: &AceStepCondition<B>,
23 vae: &OobleckDecoder<B>,
24 silence: &SilenceLatent<B>,
25 device: &burn::tensor::Device<B>,
26) -> anyhow::Result<()> {
27 let n = codes.len();
28 let tensor = Tensor::<B, 2, Int>::from_data(TensorData::new(codes.to_vec(), [1, n]), device);
29 let hints = condition.codes_to_hints(tensor);
30 let silence_frames = silence.slice(n * 5);
31 let diff = (hints.clone() - silence_frames)
32 .abs()
33 .into_data()
34 .convert::<f32>()
35 .to_vec::<f32>()
36 .map_err(|e| anyhow::anyhow!("{e}"))?;
37 let hint_values: Vec<f32> = hints
38 .clone()
39 .into_data()
40 .convert::<f32>()
41 .to_vec()
42 .map_err(|e| anyhow::anyhow!("{e}"))?;
43 let audio = vae.decode(hints);
44 let audio_values: Vec<f32> = audio
45 .into_data()
46 .convert::<f32>()
47 .to_vec()
48 .map_err(|e| anyhow::anyhow!("{e}"))?;
49 let peak = audio_values.iter().fold(0.0_f32, |a, v| a.max(v.abs()));
50 let mean_abs_diff = diff.iter().sum::<f32>() / diff.len() as f32;
51 println!(
52 "{name}: hints rms {:.4} mean|hints-silence| {:.4} audio rms {:.4} audio peak {:.4}",
53 rms(&hint_values),
54 mean_abs_diff,
55 rms(&audio_values),
56 peak
57 );
58 Ok(())
59}More examples
13fn main() -> anyhow::Result<()> {
14 let model_dir = std::env::args()
15 .nth(1)
16 .map(std::path::PathBuf::from)
17 .unwrap_or_else(|| std::path::PathBuf::from("/home/meka/repos/ace"));
18 let device = Default::default();
19
20 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
21 let vae = OobleckDecoder::<B>::from_burnpack(
22 &vae_config,
23 &model_dir.join("acestep-vae.bpk"),
24 &device,
25 )?;
26 let silence =
27 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
28 let [_, frames, _] = silence.silence_latent.dims();
29 println!("silence latent: {frames} frames");
30
31 let latents = silence.slice(750);
32 let audio = vae.decode(latents);
33 let [_, channels, samples] = audio.dims();
34 let values: Vec<f32> = audio
35 .into_data()
36 .convert::<f32>()
37 .to_vec()
38 .map_err(|e| anyhow::anyhow!("{e}"))?;
39 let peak = values.iter().fold(0.0_f32, |a, v| a.max(v.abs()));
40 let rms = (values.iter().map(|v| v * v).sum::<f32>() / values.len() as f32).sqrt();
41 println!("decoded: {channels} ch x {samples} samples");
42 println!("peak {peak:.6} rms {rms:.6}");
43 println!(
44 "{}",
45 if peak < 1e-3 {
46 "OK: VAE decodes silence correctly"
47 } else {
48 "SUSPECT: VAE decoder output is far from silence"
49 }
50 );
51
52 // Also dump the latent stats so we can eyeball them.
53 let latents = SilenceLatent::<B>::from_burnpack(
54 Path::new(&model_dir).join("silence_latent.bpk").as_path(),
55 &device,
56 )?
57 .slice(4);
58 let v: Vec<f32> = latents
59 .into_data()
60 .convert::<f32>()
61 .to_vec()
62 .map_err(|e| anyhow::anyhow!("{e}"))?;
63 let lpeak = v.iter().fold(0.0_f32, |a, v| a.max(v.abs()));
64 let lrms = (v.iter().map(|v| v * v).sum::<f32>() / v.len() as f32).sqrt();
65 println!("latent peak {lpeak:.6} rms {lrms:.6}");
66 Ok(())
67}23fn main() -> anyhow::Result<()> {
24 let mut args = std::env::args().skip(1);
25 let model_dir = args
26 .next()
27 .unwrap_or_else(|| "/home/meka/repos/ace".to_string());
28 let out_prefix = args
29 .next()
30 .unwrap_or_else(|| "/tmp/acestep_phase".to_string());
31 let caption = args
32 .next()
33 .unwrap_or_else(|| "Metal guitar with a lot of distortion".to_string());
34 let bpm: Option<f32> = args.next().and_then(|v| v.parse().ok()).or(Some(120.0));
35 let key_scale = args.next().or_else(|| Some("A minor".to_string()));
36 let time_signature = args.next().or_else(|| Some("4/4".to_string()));
37 let length_ms: usize = args.next().and_then(|v| v.parse().ok()).unwrap_or(4000);
38
39 let device = burn::backend::wgpu::WgpuDevice::default();
40 burn::backend::wgpu::init_setup::<burn::backend::wgpu::graphics::Vulkan>(
41 &device,
42 burn::backend::wgpu::RuntimeOptions {
43 memory_config: burn::backend::wgpu::MemoryConfiguration::ExclusivePages,
44 ..Default::default()
45 },
46 );
47
48 let variant = if std::env::var("MAOLAN_ACESTEP_VARIANT")
49 .map(|v| v == "sft")
50 .unwrap_or(false)
51 {
52 maolan_generate::acestep::AceStepVariant::Sft
53 } else {
54 maolan_generate::acestep::AceStepVariant::Turbo
55 };
56 let paths = AceStepModelPaths::resolve(Path::new(&model_dir), variant)?;
57 let mut progress = |phase: &str, p: f32, op: &str| {
58 eprintln!("[{phase}] {:.0}% {op}", p * 100.0);
59 };
60 let pipeline = AceStepPipeline::<B>::load(&paths, &device, &mut progress)?;
61
62 let metadata = GenerateMetadata {
63 bpm,
64 key_scale: key_scale.as_deref(),
65 time_signature: time_signature.as_deref(),
66 };
67 let mut trace = AceStepTrace::default();
68 let (audio, meta) =
69 pipeline.generate_traced(&caption, &metadata, length_ms, 0, &mut progress, &mut trace)?;
70
71 // ---- Phase dumps ----
72 println!(
73 "\n===== TEXT PROMPT (caption branch) =====\n{}",
74 trace.text_prompt
75 );
76 println!("===== LYRIC PROMPT =====\n{}", trace.lyric_prompt);
77 println!("===== LM CoT BLOCK =====\n{}", trace.cot_block);
78 println!("===== LM CODES ({} total) =====", trace.codes.len());
79 println!("all codes: {:?}", trace.codes);
80 let mut sorted = trace.codes.clone();
81 sorted.sort_unstable();
82 sorted.dedup();
83 println!(
84 "unique: {}, min: {}, max: {}",
85 sorted.len(),
86 sorted.first().unwrap_or(&0),
87 sorted.last().unwrap_or(&0)
88 );
89 println!(
90 "\n===== CONDITIONING =====\nenc mean {:.6} enc std {:.6}",
91 trace.enc_mean, trace.enc_std
92 );
93 println!("hints latent rms: {:.6}", trace.hints_latent_rms);
94 println!("final latent rms: {:.6}", trace.final_latent_rms);
95 println!("\n===== DIT STEPS =====\nstep t xt_rms v_rms");
96 for (i, step) in trace.dit_steps.iter().enumerate() {
97 println!(
98 "{i:>4} {:.4} {:.4} {:.4}",
99 step.t, step.xt_rms, step.v_rms
100 );
101 }
102
103 // ---- WAVs ----
104 if let Some((interleaved, channels, frames)) = &trace.hints_audio {
105 let path = format!("{out_prefix}_hints.wav");
106 write_wav_from_f32_interleaved(
107 interleaved,
108 *channels,
109 *frames,
110 meta.sample_rate_hz,
111 Path::new(&path),
112 )?;
113 println!("\nwrote {path} (VAE decode of LM hints, DiT bypassed)");
114 }
115
116 let [_, channels, frames] = audio.dims();
117 let channel_major: Vec<f32> = audio
118 .into_data()
119 .convert::<f32>()
120 .to_vec()
121 .map_err(|e| anyhow::anyhow!("{e}"))?;
122 let mut interleaved = vec![0.0_f32; channel_major.len()];
123 for (ch, samples) in channel_major.chunks_exact(frames).enumerate() {
124 for (frame, sample) in samples.iter().enumerate() {
125 interleaved[frame * channels + ch] = *sample;
126 }
127 }
128 let path = format!("{out_prefix}_final.wav");
129 write_wav_from_f32_interleaved(
130 &interleaved,
131 channels,
132 frames,
133 meta.sample_rate_hz,
134 Path::new(&path),
135 )?;
136 println!("wrote {path} (full pipeline)");
137
138 // Silence decode reference.
139 let silence = SilenceLatent::<B>::from_burnpack(&paths.silence_latent_bpk, &device)?;
140 let silence_audio = pipeline.vae.decode(silence.slice(frames / 1920));
141 let [_, sch, sframes] = silence_audio.dims();
142 let smaj: Vec<f32> = silence_audio
143 .into_data()
144 .convert::<f32>()
145 .to_vec()
146 .map_err(|e| anyhow::anyhow!("{e}"))?;
147 let mut sinter = vec![0.0_f32; smaj.len()];
148 for (ch, samples) in smaj.chunks_exact(sframes).enumerate() {
149 for (frame, sample) in samples.iter().enumerate() {
150 sinter[frame * sch + ch] = *sample;
151 }
152 }
153 let path = format!("{out_prefix}_silence.wav");
154 write_wav_from_f32_interleaved(&sinter, sch, sframes, meta.sample_rate_hz, Path::new(&path))?;
155 println!("wrote {path} (VAE decode of silence latent)");
156
157 Ok(())
158}63fn main() -> anyhow::Result<()> {
64 let mut args = std::env::args().skip(1);
65 let model_dir = PathBuf::from(args.next().unwrap_or_else(|| "/home/meka/repos/ace".into()));
66 let out_dir = PathBuf::from(args.next().unwrap_or_else(|| "/var/tmp/dump_ours".into()));
67 let noise_path = args.next().map(PathBuf::from);
68 std::fs::create_dir_all(&out_dir)?;
69
70 let device = Default::default();
71
72 let dit_config = AceStepConfig::load(&model_dir.join("dit_config.json"))?;
73 let text_config = Qwen3Config::load(&model_dir.join("qwen3_config.json"))?;
74 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
75 eprintln!("loading components...");
76 let text_encoder = Qwen3Model::<B>::from_burnpack(
77 &text_config,
78 &model_dir.join("qwen3-encoder.bpk"),
79 &device,
80 )?;
81 let condition = AceStepCondition::<B>::from_burnpack(
82 &dit_config,
83 &model_dir.join("acestep-condition.bpk"),
84 &device,
85 )?;
86 let dit =
87 AceStepDiT::<B>::from_burnpack(&dit_config, &model_dir.join("acestep-dit.bpk"), &device)?;
88 let vae = OobleckDecoder::<B>::from_burnpack(
89 &vae_config,
90 &model_dir.join("acestep-vae.bpk"),
91 &device,
92 )?;
93 let silence =
94 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
95 let tokenizer = tokie::Tokenizer::from_json(model_dir.join("tokenizer.json"))
96 .map_err(|e| anyhow::anyhow!("tokenizer: {e}"))?;
97
98 // ---- text + lyric encoding (causal, official prompts) ----
99 let metas = build_metas_block(Some(120.0), Some("A minor"), Some("4/4"), 4);
100 let text_prompt = build_dit_text_prompt("Metal guitar with a lot of distortion", &metas);
101 let ids = tokenizer.encode(&text_prompt, false).ids;
102 let mut ids: Vec<i64> = ids.into_iter().map(i64::from).collect();
103 ids.push(151643); // explicit EOS (official add_eos=true)
104 let n_text = ids.len();
105 let ids_t = Tensor::<B, 2, Int>::from_data(TensorData::new(ids, [1, n_text]), &device);
106 let text_hidden = text_encoder.forward(ids_t, true);
107 let text_hidden_vec = vec_of(text_hidden.clone());
108 dump_tensor(
109 &out_dir,
110 "text_hidden",
111 &text_hidden_vec,
112 &[n_text as i32, 1024],
113 );
114
115 let lyric_ids = tokenizer
116 .encode(
117 maolan_generate::acestep::pipeline::INSTRUMENTAL_LYRIC_PROMPT,
118 false,
119 )
120 .ids;
121 let mut lyric_ids: Vec<i64> = lyric_ids.into_iter().map(i64::from).collect();
122 lyric_ids.push(151643); // explicit EOS (official add_eos=true)
123 let n_lyric = lyric_ids.len();
124 let lyric_ids_t =
125 Tensor::<B, 2, Int>::from_data(TensorData::new(lyric_ids, [1, n_lyric]), &device);
126 // Official: lyric branch is a raw embed_tokens lookup (no transformer).
127 let lyric_hidden = text_encoder.embed_tokens.forward(lyric_ids_t);
128 let lyric_embed_vec = vec_of(lyric_hidden.clone());
129 dump_tensor(
130 &out_dir,
131 "lyric_embed",
132 &lyric_embed_vec,
133 &[n_lyric as i32, 1024],
134 );
135 let lyric_mask = Tensor::<B, 2, Int>::ones([1, n_lyric], &device);
136
137 // ---- conditioning: use the oracle's enc_hidden/context when provided ----
138 let mut oracle_context: Option<Tensor<B, 3>> = None;
139 let enc = if let Some(dir) = std::env::var_os("MAOLAN_ORACLE_DUMP_DIR") {
140 let dir = PathBuf::from(dir);
141 let (enc_shape, enc_data) = load_bin(&dir.join("enc_hidden.bin"));
142 eprintln!("oracle enc_hidden: {enc_shape:?}");
143 let (ctx_shape, ctx_data) = load_bin(&dir.join("context.bin"));
144 eprintln!("oracle context: {ctx_shape:?}");
145 let enc = Tensor::<B, 3>::from_data(
146 TensorData::new(enc_data, [1, enc_shape[0] as usize, enc_shape[1] as usize]),
147 &device,
148 );
149 let ctx = Tensor::<B, 3>::from_data(TensorData::new(ctx_data, [1, 96, 128]), &device);
150 oracle_context = Some(ctx);
151 enc
152 } else {
153 condition.encode(
154 text_hidden,
155 lyric_hidden,
156 lyric_mask,
157 silence.timbre_reference(),
158 )
159 };
160 let [_, enc_len, enc_dim] = enc.dims();
161 let enc_vec = vec_of(enc.clone());
162 dump_tensor(
163 &out_dir,
164 "enc_hidden",
165 &enc_vec,
166 &[enc_len as i32, enc_dim as i32],
167 );
168
169 // ---- FSQ hints from the given codes (env MAOLAN_ACESTEP_CODES or the
170 // built-in oracle sequence) ----
171 let codes: Vec<u32> = std::env::var_os("MAOLAN_ACESTEP_CODES")
172 .map(|raw| {
173 raw.to_string_lossy()
174 .split(',')
175 .filter_map(|part| part.trim().parse::<u32>().ok())
176 .collect()
177 })
178 .unwrap_or_else(|| ORACLE_CODES.to_vec());
179 let n_codes = codes.len();
180 let codes_tensor =
181 Tensor::<B, 2, Int>::from_data(TensorData::new(codes, [1, n_codes]), &device);
182 let hints = condition.codes_to_hints(codes_tensor);
183 let [_, hint_len, _] = hints.dims();
184 let hints_vec = vec_of(hints.clone());
185 dump_tensor(&out_dir, "detok_output", &hints_vec, &[hint_len as i32, 64]);
186
187 // ---- noise ----
188 let noise = if let Some(path) = noise_path {
189 let (shape, data) = load_bin(&path);
190 let frames = shape[0] as usize;
191 Tensor::<B, 3>::from_data(TensorData::new(data, [1, frames, 64]), &device)
192 } else {
193 maolan_generate::acestep::pipeline::seeded_latent_noise(0, hint_len, 64, &device)
194 };
195 let [_, frames, _] = noise.dims();
196
197 // src latents: hints cropped or silence-padded to the noise's frame count
198 let src = if hint_len >= frames {
199 hints.narrow(1, 0, frames)
200 } else {
201 let padding = silence.slice(frames - hint_len);
202 Tensor::cat(vec![hints, padding], 1)
203 };
204 let chunk_mask = Tensor::ones([1, frames, 64], &device);
205 let context = Tensor::cat(vec![src.clone(), chunk_mask], 2);
206 let context = oracle_context.unwrap_or(context);
207 let context_vec = vec_of(context.clone());
208 dump_tensor(&out_dir, "context", &context_vec, &[frames as i32, 128]);
209
210 let noise_vec = vec_of(noise.clone());
211 dump_tensor(&out_dir, "noise", &noise_vec, &[frames as i32, 64]);
212
213 // ---- DiT loop with per-step dumps ----
214 let kv = dit.prepare_cross_kv(enc);
215 let mut xt = noise;
216 let total = TURBO_TIMESTEPS.len();
217 for (index, &t_cur) in TURBO_TIMESTEPS.iter().enumerate() {
218 let xt_vec = vec_of(xt.clone());
219 dump_tensor(
220 &out_dir,
221 &format!("dit_step{index}_xt"),
222 &xt_vec,
223 &[frames as i32, 64],
224 );
225 let v = dit.forward_with_kv(xt.clone(), t_cur, context.clone(), &kv);
226 let v_vec = vec_of(v.clone());
227 dump_tensor(
228 &out_dir,
229 &format!("dit_step{index}_vt"),
230 &v_vec,
231 &[frames as i32, 64],
232 );
233 let dt = if index + 1 == total {
234 t_cur
235 } else {
236 t_cur - TURBO_TIMESTEPS[index + 1]
237 };
238 xt = xt - v * dt;
239 }
240 let x0_vec = vec_of(xt.clone());
241 dump_tensor(&out_dir, "dit_x0", &x0_vec, &[frames as i32, 64]);
242
243 // ---- VAE decode ----
244 let audio = vae.decode(xt);
245 let [_, channels, samples] = audio.dims();
246 let audio_vec = vec_of(audio);
247 dump_tensor(
248 &out_dir,
249 "vae_audio",
250 &audio_vec,
251 &[channels as i32, samples as i32],
252 );
253 println!("dumps written to {}", out_dir.display());
254 Ok(())
255}63fn main() -> anyhow::Result<()> {
64 let mut args = std::env::args().skip(1);
65 let model_dir = PathBuf::from(args.next().unwrap_or_else(|| "/home/meka/repos/ace".into()));
66 let out_dir = PathBuf::from(args.next().unwrap_or_else(|| "/var/tmp/dump_ours".into()));
67 let noise_path = args.next().map(PathBuf::from);
68 std::fs::create_dir_all(&out_dir)?;
69
70 let device = burn::backend::wgpu::WgpuDevice::default();
71 burn::backend::wgpu::init_setup::<burn::backend::wgpu::graphics::Vulkan>(
72 &device,
73 burn::backend::wgpu::RuntimeOptions {
74 memory_config: burn::backend::wgpu::MemoryConfiguration::ExclusivePages,
75 ..Default::default()
76 },
77 );
78
79 let variant_sft = std::env::var("MAOLAN_ACESTEP_VARIANT")
80 .map(|v| v == "sft")
81 .unwrap_or(false);
82 let prefix = if variant_sft { "sft-" } else { "" };
83
84 let dit_config = AceStepConfig::load(&model_dir.join(format!("{prefix}dit_config.json")))?;
85 let text_config = Qwen3Config::load(&model_dir.join("qwen3_config.json"))?;
86 let vae_config = OobleckVaeConfig::load(&model_dir.join("vae_config.json"))?;
87 eprintln!("loading components (prefix '{prefix}')...");
88 let text_encoder = Qwen3Model::<B>::from_burnpack(
89 &text_config,
90 &model_dir.join("qwen3-encoder.bpk"),
91 &device,
92 )?;
93 let condition = AceStepCondition::<B>::from_burnpack(
94 &dit_config,
95 &model_dir.join(format!("{prefix}acestep-condition.bpk")),
96 &device,
97 )?;
98 let dit = AceStepDiT::<B>::from_burnpack(
99 &dit_config,
100 &model_dir.join(format!("{prefix}acestep-dit.bpk")),
101 &device,
102 )?;
103 let vae = OobleckDecoder::<B>::from_burnpack(
104 &vae_config,
105 &model_dir.join("acestep-vae.bpk"),
106 &device,
107 )?;
108 let silence =
109 SilenceLatent::<B>::from_burnpack(&model_dir.join("silence_latent.bpk"), &device)?;
110 let tokenizer = tokie::Tokenizer::from_json(model_dir.join("tokenizer.json"))
111 .map_err(|e| anyhow::anyhow!("tokenizer: {e}"))?;
112
113 // ---- text + lyric encoding (causal, official prompts) ----
114 let metas = build_metas_block(Some(120.0), Some("A minor"), Some("4/4"), 4);
115 let text_prompt = build_dit_text_prompt("Metal guitar with a lot of distortion", &metas);
116 let ids = tokenizer.encode(&text_prompt, false).ids;
117 let mut ids: Vec<i64> = ids.into_iter().map(i64::from).collect();
118 ids.push(151643); // explicit EOS (official add_eos=true)
119 let n_text = ids.len();
120 let ids_t = Tensor::<B, 2, Int>::from_data(TensorData::new(ids, [1, n_text]), &device);
121 let text_hidden = text_encoder.forward(ids_t, true);
122 let text_hidden_vec = vec_of(text_hidden.clone());
123 dump_tensor(
124 &out_dir,
125 "text_hidden",
126 &text_hidden_vec,
127 &[n_text as i32, 1024],
128 );
129
130 let lyric_ids = tokenizer
131 .encode(
132 maolan_generate::acestep::pipeline::INSTRUMENTAL_LYRIC_PROMPT,
133 false,
134 )
135 .ids;
136 let mut lyric_ids: Vec<i64> = lyric_ids.into_iter().map(i64::from).collect();
137 lyric_ids.push(151643); // explicit EOS (official add_eos=true)
138 let n_lyric = lyric_ids.len();
139 let lyric_ids_t =
140 Tensor::<B, 2, Int>::from_data(TensorData::new(lyric_ids, [1, n_lyric]), &device);
141 // Official: lyric branch is a raw embed_tokens lookup (no transformer).
142 let lyric_hidden = text_encoder.embed_tokens.forward(lyric_ids_t);
143 let lyric_embed_vec = vec_of(lyric_hidden.clone());
144 dump_tensor(
145 &out_dir,
146 "lyric_embed",
147 &lyric_embed_vec,
148 &[n_lyric as i32, 1024],
149 );
150 let lyric_mask = Tensor::<B, 2, Int>::ones([1, n_lyric], &device);
151
152 // ---- conditioning: use the oracle's enc_hidden/context when provided ----
153 let mut oracle_context: Option<Tensor<B, 3>> = None;
154 let enc = if let Some(dir) = std::env::var_os("MAOLAN_ORACLE_DUMP_DIR") {
155 let dir = PathBuf::from(dir);
156 let (enc_shape, enc_data) = load_bin(&dir.join("enc_hidden.bin"));
157 eprintln!("oracle enc_hidden: {enc_shape:?}");
158 let (ctx_shape, ctx_data) = load_bin(&dir.join("context.bin"));
159 eprintln!("oracle context: {ctx_shape:?}");
160 let enc = Tensor::<B, 3>::from_data(
161 TensorData::new(enc_data, [1, enc_shape[0] as usize, enc_shape[1] as usize]),
162 &device,
163 );
164 let ctx = Tensor::<B, 3>::from_data(TensorData::new(ctx_data, [1, 96, 128]), &device);
165 oracle_context = Some(ctx);
166 enc
167 } else {
168 condition.encode(
169 text_hidden,
170 lyric_hidden,
171 lyric_mask,
172 silence.timbre_reference(),
173 )
174 };
175 let [_, enc_len, enc_dim] = enc.dims();
176 let enc_vec = vec_of(enc.clone());
177 dump_tensor(
178 &out_dir,
179 "enc_hidden",
180 &enc_vec,
181 &[enc_len as i32, enc_dim as i32],
182 );
183
184 // ---- FSQ hints from the given codes (env MAOLAN_ACESTEP_CODES or the
185 // built-in oracle sequence) ----
186 let codes: Vec<u32> = std::env::var_os("MAOLAN_ACESTEP_CODES")
187 .map(|raw| {
188 raw.to_string_lossy()
189 .split(',')
190 .filter_map(|part| part.trim().parse::<u32>().ok())
191 .collect()
192 })
193 .unwrap_or_else(|| ORACLE_CODES.to_vec());
194 let n_codes = codes.len();
195 let codes_tensor =
196 Tensor::<B, 2, Int>::from_data(TensorData::new(codes, [1, n_codes]), &device);
197 let hints = condition.codes_to_hints(codes_tensor);
198 let [_, hint_len, _] = hints.dims();
199 let hints_vec = vec_of(hints.clone());
200 dump_tensor(&out_dir, "detok_output", &hints_vec, &[hint_len as i32, 64]);
201
202 // ---- noise ----
203 let noise = if let Some(path) = noise_path {
204 let (shape, data) = load_bin(&path);
205 let frames = shape[0] as usize;
206 Tensor::<B, 3>::from_data(TensorData::new(data, [1, frames, 64]), &device)
207 } else {
208 maolan_generate::acestep::pipeline::seeded_latent_noise(0, hint_len, 64, &device)
209 };
210 let [_, frames, _] = noise.dims();
211
212 // src latents: hints cropped or silence-padded to the noise's frame count
213 let src = if hint_len >= frames {
214 hints.narrow(1, 0, frames)
215 } else {
216 let padding = silence.slice(frames - hint_len);
217 Tensor::cat(vec![hints, padding], 1)
218 };
219 let chunk_mask = Tensor::ones([1, frames, 64], &device);
220 let context = Tensor::cat(vec![src.clone(), chunk_mask], 2);
221 let context = oracle_context.unwrap_or(context);
222 let context_vec = vec_of(context.clone());
223 dump_tensor(&out_dir, "context", &context_vec, &[frames as i32, 128]);
224
225 let noise_vec = vec_of(noise.clone());
226 dump_tensor(&out_dir, "noise", &noise_vec, &[frames as i32, 64]);
227
228 // ---- DiT loop with per-step dumps (schedule from is_turbo) ----
229 let timesteps: &[f32] = if dit_config.is_turbo {
230 &TURBO_TIMESTEPS
231 } else {
232 &SFT_TIMESTEPS
233 };
234 let kv = dit.prepare_cross_kv(enc);
235 let mut xt = noise;
236 let total = timesteps.len();
237 for (index, &t_cur) in timesteps.iter().enumerate() {
238 let xt_vec = vec_of(xt.clone());
239 dump_tensor(
240 &out_dir,
241 &format!("dit_step{index}_xt"),
242 &xt_vec,
243 &[frames as i32, 64],
244 );
245 let v = dit.forward_with_kv(xt.clone(), t_cur, context.clone(), &kv);
246 let v_vec = vec_of(v.clone());
247 dump_tensor(
248 &out_dir,
249 &format!("dit_step{index}_vt"),
250 &v_vec,
251 &[frames as i32, 64],
252 );
253 let dt = if index + 1 == total {
254 t_cur
255 } else {
256 t_cur - timesteps[index + 1]
257 };
258 xt = xt - v * dt;
259 }
260 let x0_vec = vec_of(xt.clone());
261 dump_tensor(&out_dir, "dit_x0", &x0_vec, &[frames as i32, 64]);
262
263 // ---- VAE decode ----
264 let audio = vae.decode(xt);
265 let [_, channels, samples] = audio.dims();
266 let audio_vec = vec_of(audio);
267 dump_tensor(
268 &out_dir,
269 "vae_audio",
270 &audio_vec,
271 &[channels as i32, samples as i32],
272 );
273 println!("dumps written to {}", out_dir.display());
274 Ok(())
275}Trait Implementations§
Source§impl<B> AutodiffModule<B> for OobleckDecoder<B>
impl<B> AutodiffModule<B> for OobleckDecoder<B>
Source§type InnerModule = OobleckDecoder<<B as AutodiffBackend>::InnerBackend>
type InnerModule = OobleckDecoder<<B as AutodiffBackend>::InnerBackend>
Source§fn valid(&self) -> Self::InnerModule
fn valid(&self) -> Self::InnerModule
Source§fn from_inner(module: Self::InnerModule) -> Self
fn from_inner(module: Self::InnerModule) -> Self
Source§impl<B: Backend> Clone for OobleckDecoder<B>
impl<B: Backend> Clone for OobleckDecoder<B>
Source§impl<B: Backend> Display for OobleckDecoder<B>
impl<B: Backend> Display for OobleckDecoder<B>
Source§impl<B> HasAutodiffModule<B> for OobleckDecoder<B::InnerBackend>
impl<B> HasAutodiffModule<B> for OobleckDecoder<B::InnerBackend>
Source§type TrainModule = OobleckDecoder<B>
type TrainModule = OobleckDecoder<B>
Source§impl<B: Backend> Module<B> for OobleckDecoder<B>
impl<B: Backend> Module<B> for OobleckDecoder<B>
Source§type Record = OobleckDecoderRecord<B>
type Record = OobleckDecoderRecord<B>
Source§fn load_record(self, record: Self::Record) -> Self
fn load_record(self, record: Self::Record) -> Self
Source§fn into_record(self) -> Self::Record
fn into_record(self) -> Self::Record
Source§fn num_params(&self) -> usize
fn num_params(&self) -> usize
Source§fn visit<Visitor: ModuleVisitor<B>>(&self, visitor: &mut Visitor)
fn visit<Visitor: ModuleVisitor<B>>(&self, visitor: &mut Visitor)
Source§fn map<Mapper: ModuleMapper<B>>(self, mapper: &mut Mapper) -> Self
fn map<Mapper: ModuleMapper<B>>(self, mapper: &mut Mapper) -> Self
Source§fn collect_devices(&self, devices: Devices<B>) -> Devices<B>
fn collect_devices(&self, devices: Devices<B>) -> Devices<B>
Source§fn to_device(self, device: &B::Device) -> Self
fn to_device(self, device: &B::Device) -> Self
Source§fn fork(self, device: &B::Device) -> Self
fn fork(self, device: &B::Device) -> Self
Source§fn devices(&self) -> Vec<<B as BackendTypes>::Device>
fn devices(&self) -> Vec<<B as BackendTypes>::Device>
Source§fn train<AB>(self) -> Self::TrainModulewhere
AB: AutodiffBackend<InnerBackend = B>,
Self: HasAutodiffModule<AB>,
fn train<AB>(self) -> Self::TrainModulewhere
AB: AutodiffBackend<InnerBackend = B>,
Self: HasAutodiffModule<AB>,
Source§fn save_file<FR, PB>(
self,
file_path: PB,
recorder: &FR,
) -> Result<(), RecorderError>
fn save_file<FR, PB>( self, file_path: PB, recorder: &FR, ) -> Result<(), RecorderError>
Source§fn load_file<FR, PB>(
self,
file_path: PB,
recorder: &FR,
device: &<B as BackendTypes>::Device,
) -> Result<Self, RecorderError>
fn load_file<FR, PB>( self, file_path: PB, recorder: &FR, device: &<B as BackendTypes>::Device, ) -> Result<Self, RecorderError>
Source§fn quantize_weights(self, quantizer: &mut Quantizer) -> Self
fn quantize_weights(self, quantizer: &mut Quantizer) -> Self
Source§impl<B: Backend> ModuleDisplay for OobleckDecoder<B>
impl<B: Backend> ModuleDisplay for OobleckDecoder<B>
Source§fn format(&self, passed_settings: DisplaySettings) -> String
fn format(&self, passed_settings: DisplaySettings) -> String
Source§fn custom_settings(&self) -> Option<DisplaySettings>
fn custom_settings(&self) -> Option<DisplaySettings>
Auto Trait Implementations§
impl<B> !Freeze for OobleckDecoder<B>
impl<B> !RefUnwindSafe for OobleckDecoder<B>
impl<B> !UnwindSafe for OobleckDecoder<B>
impl<B> Send for OobleckDecoder<B>
impl<B> Sync for OobleckDecoder<B>
impl<B> Unpin for OobleckDecoder<B>where
<B as BackendTypes>::FloatTensorPrimitive: Unpin,
<B as BackendTypes>::QuantizedTensorPrimitive: Unpin,
<B as BackendTypes>::Device: Unpin,
impl<B> UnsafeUnpin for OobleckDecoder<B>where
<B as BackendTypes>::Device: UnsafeUnpin,
<B as BackendTypes>::FloatTensorPrimitive: UnsafeUnpin,
<B as BackendTypes>::QuantizedTensorPrimitive: UnsafeUnpin,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<C> CloneExpand for Cwhere
C: Clone,
impl<C> CloneExpand for Cwhere
C: Clone,
fn __expand_clone_method(&self, _scope: &mut Scope) -> C
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreimpl<T> MaybeSendSync for T
Source§impl<B, M> ModuleSnapshot<B> for M
impl<B, M> ModuleSnapshot<B> for M
Source§fn collect(
&self,
filter: Option<PathFilter>,
adapter: Option<Box<dyn ModuleAdapter>>,
skip_enum_variants: bool,
) -> Vec<TensorSnapshot>
fn collect( &self, filter: Option<PathFilter>, adapter: Option<Box<dyn ModuleAdapter>>, skip_enum_variants: bool, ) -> Vec<TensorSnapshot>
Source§fn apply(
&mut self,
snapshots: Vec<TensorSnapshot>,
filter: Option<PathFilter>,
adapter: Option<Box<dyn ModuleAdapter>>,
skip_enum_variants: bool,
) -> ApplyResultwhere
Self: Sized,
fn apply(
&mut self,
snapshots: Vec<TensorSnapshot>,
filter: Option<PathFilter>,
adapter: Option<Box<dyn ModuleAdapter>>,
skip_enum_variants: bool,
) -> ApplyResultwhere
Self: Sized,
Source§fn save_into<P>(&self, store: &mut P) -> Result<(), <P as ModuleStore>::Error>where
P: ModuleStore,
fn save_into<P>(&self, store: &mut P) -> Result<(), <P as ModuleStore>::Error>where
P: ModuleStore,
ModuleStore. Read moreSource§fn load_from<P>(
&mut self,
store: &mut P,
) -> Result<ApplyResult, <P as ModuleStore>::Error>where
P: ModuleStore,
fn load_from<P>(
&mut self,
store: &mut P,
) -> Result<ApplyResult, <P as ModuleStore>::Error>where
P: ModuleStore,
ModuleStore. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> ToCompactString for Twhere
T: Display,
impl<T> ToCompactString for Twhere
T: Display,
Source§fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
ToCompactString::to_compact_string() Read moreSource§fn to_compact_string(&self) -> CompactString
fn to_compact_string(&self) -> CompactString
CompactString. Read more