1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! [`AudioPerceptualAnalysis`] trait definition.
use WindowType;
use crate::;
use ;
/// Psychoacoustic analysis operations on [`AudioSamples`].
///
/// This trait exposes the [`analyse_psychoacoustic`](AudioPerceptualAnalysis::analyse_psychoacoustic)
/// method, which drives the full psychoacoustic model pipeline:
///
/// 1. Compute MDCT coefficients of the signal.
/// 2. Derive per-bin energies from the MDCT power spectrum.
/// 3. Map bins to perceptual bands via [`BandLayout`].
/// 4. Compute masking thresholds and signal-to-mask ratios using the
/// psychoacoustic model in [`PsychoacousticConfig`].
///
/// The result contains the raw MDCT coefficients (useful for downstream
/// quantization), the per-bin energies, and the per-band [`BandMetrics`] that
/// summarise audibility, masking, and allowed quantization noise.
///
/// ## Usage
///
/// Bring the trait into scope and call the method on any mono [`AudioSamples`]:
///
/// ```rust
/// # #[cfg(feature = "psychoacoustic")] {
/// use audio_samples::{
/// AudioPerceptualAnalysis, BandLayout, PsychoacousticConfig,
/// sine_wave, sample_rate,
/// };
/// use spectrograms::WindowType;
/// use non_empty_slice::NonEmptySlice;
/// use std::num::NonZeroUsize;
/// use std::time::Duration;
///
/// let signal = sine_wave::<f32>(440.0, Duration::from_millis(100), sample_rate!(44100), 0.5);
///
/// let n_bins = NonZeroUsize::new(1024).unwrap();
/// let layout = BandLayout::bark(NonZeroUsize::new(24).unwrap(), 44100.0, n_bins);
///
/// // Build a minimal config (24 uniform weights, standard masking parameters).
/// let weights: Vec<f32> = vec![1.0; 24];
/// let weights_slice = NonEmptySlice::from_slice(&weights).unwrap();
/// let config = PsychoacousticConfig::new(
/// -60.0, // noise_floor dB
/// 14.5, // masking_gain dB (tonal)
/// 5.5, // noise_masking_gain dB
/// 25.0, // upward_spread dB/Bark
/// 6.0, // downward_spread dB/Bark
/// weights_slice,
/// 1e-10, // epsilon
/// );
///
/// let result = signal.analyse_psychoacoustic(WindowType::Hanning, &layout, &config).unwrap();
/// assert_eq!(result.band_metrics.len().get(), 24);
/// # }
/// ```
///
/// [`BandMetrics`]: super::BandMetrics