stem_splitter_core/
pipeline.rs1use crate::utils::tmp_dir;
2use crate::{
3 audio::read_audio,
4 model::{PythonModel, StemModel},
5 types::{SplitConfig, StemResult},
6};
7use anyhow::{Context, Result};
8use std::path::{Path, PathBuf};
9
10pub fn split_file(path: &str, config: SplitConfig) -> Result<StemResult> {
11 std::fs::metadata(path).with_context(|| format!("File does not exist: {}", path))?;
12
13 let model = PythonModel::new();
14
15 let audio = read_audio(path)?;
16 let tmp_output_dir = tmp_dir();
17 let result = model
18 .separate(&audio.samples, audio.channels, &tmp_output_dir)
19 .with_context(|| format!("Failed to separate stems for file: {}", path))?;
20
21 let file_stem = Path::new(path)
22 .file_stem()
23 .and_then(|s| s.to_str())
24 .unwrap_or("output");
25
26 let base_path = PathBuf::from(&config.output_dir).join(file_stem);
27 save_stems(base_path.to_str().unwrap())?;
28
29 std::fs::remove_dir_all(&tmp_output_dir).with_context(|| {
30 format!(
31 "Failed to remove tmp directory: {}",
32 tmp_output_dir.display()
33 )
34 })?;
35
36 Ok(result)
37}
38
39fn save_stems(base_path: &str) -> Result<()> {
40 let stem_names = ["vocals", "drums", "bass", "other"];
41
42 if let Some(parent) = Path::new(base_path).parent() {
44 std::fs::create_dir_all(parent)
45 .with_context(|| format!("Failed to create output directory: {}", parent.display()))?;
46 }
47
48 for name in stem_names {
49 let src = Path::new("tmp").join(format!("{name}.wav")); let dst = format!("{base_path}_{name}.wav");
51
52 std::fs::copy(&src, &dst)
53 .with_context(|| format!("Failed to copy {name} from tmp to output"))?;
54 }
55
56 Ok(())
57}