Skip to main content

rustriff_lib/infrastructure/file_loader/
file_loader_trait.rs

1use std::path::Path;
2
3/// Abstraction over filesystem operations required by the IR profile pipeline.
4///
5/// The trait is `Send + Sync` so it can be injected into services that may be
6/// shared across threads (e.g. Tauri's managed state).
7///
8/// A real implementation based on [`std::fs`] and the [`hound`] WAV crate is
9/// provided by [`FileLoader`].
10///
11/// [`FileLoader`]: super::FileLoader
12pub trait FileLoaderTrait: Send + Sync {
13    /// Reads the sample rate declared in the WAV header at `path`.
14    /// Returns `None` if the file cannot be opened or parsed (missing, corrupt,
15    /// unsupported format). The caller should treat `None` as "assume DSP rate
16    /// and skip resampling".
17    fn read_wav_sample_rate(&self, path: &Path) -> Option<u32>;
18
19    /// Decodes a WAV file into a flat mono `f32` sample buffer.
20    ///
21    /// **Multi-channel handling**: Stereo and multi-channel WAV files are
22    /// automatically downmixed to mono by averaging all channels in each frame.
23    /// For example, a stereo file with interleaved `[L0, R0, L1, R1]` samples
24    /// produces `[(L0+R0)/2, (L1+R1)/2]`.
25    ///
26    /// **Normalization**: Integer samples are normalized to `[-1.0, 1.0]` using
27    /// the full integer range determined by `bits_per_sample`.
28    ///
29    /// Returns an empty `Vec` and logs a warning on any I/O or parse error so
30    /// that the caller can fall back to passthrough rather than panicking.
31    fn read_wav_to_buffer(&self, path: &Path) -> Vec<f32>;
32
33    /// Returns a sorted list of `.wav` filenames (not full paths) found in
34    /// `directory`.
35    ///
36    /// Only regular files with a `.wav` extension (case-insensitive) are
37    /// included; subdirectories and other file types are silently skipped.
38    ///
39    /// Returns `Err` when the directory itself cannot be read.
40    fn list_ir_profile_file_names(&self, directory: &Path) -> Result<Vec<String>, String>;
41
42    /// Creates `directory` and all missing parent components if they do not
43    /// already exist (`mkdir -p` semantics).
44    fn ensure_directory(&self, directory: &Path) -> Result<(), String>;
45
46    /// Writes `bytes` to `path`, truncating any existing file.
47    fn write_file_bytes(&self, path: &Path, bytes: &[u8]) -> Result<(), String>;
48
49    /// Removes the file at `path`.
50    ///
51    /// Returns `Err` when the file does not exist or cannot be removed.
52    fn remove_file(&self, path: &Path) -> Result<(), String>;
53
54    /// Validates that raw bytes represent a usable cabinet IR WAV file.
55    ///
56    /// Checks performed in order:
57    ///
58    /// 1. **Extension** – `file_name` must end with `.wav` (case-insensitive).
59    /// 2. **Parse** – bytes must form a valid WAV container readable by [`hound`].
60    ///    Proprietary extensions that produce unexpected fmt-chunk sizes are
61    ///    detected and surface a user-friendly re-export suggestion.
62    /// 3. **Impulse presence** – at least one of the first `256` samples must
63    ///    exceed `impulse_threshold` in absolute value.  A completely silent
64    ///    header region usually indicates the wrong file was selected (e.g. a
65    ///    music recording rather than an IR capture).
66    ///
67    /// Returns `Ok(())` when all checks pass, or `Err(description)` on the first
68    /// failing check.
69    fn validate_ir_wav_bytes(
70        &self,
71        file_name: &str,
72        file_bytes: &[u8],
73        impulse_threshold: f32,
74    ) -> Result<(), String>;
75}