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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! Gaborator is a C++ library for converting audio samples to a special spectral representation
//! that uses different FTT sizes based on whether it is bass or treble (oversimplifying here).
//! The transformation is reversible.
//! See [the website](https://www.gaborator.com/) for more info.
//!
//! This crate is a [cxx](https://cxx.rs/)-based wrapper of this library, allowing Rust code to use Gaborator (although with reduced efficiency).
//!
//! Limitations:
//!
//! * `f32` only
//! * Not performance-minded
//! * Some overridable or low-level details not exposed
//! * No visualisation
//! * Crate soundness may be iffy - I was just followed the path of least resistance.
//! * Arithmentic overflows in buffer length calculations are not checked.
//! * Not really tested, apart from included examples. For example, streaming should be supported, but I haven't tried it myself.
//!
//! Currently based on Gaborator version 1.6. Source code of the Gaborator is included into the crate.
//! 
//! Availble examples:
//! 
//! * Phase information randomizer, creating sort-of-reverberation audio effect.
//! * Converts the analyzed sound to (sample,band,magnitude,phase) CSV file and back.
//!
//! License of Gaborator is Affero GPL 3.0.
//!
//! Glue code (sans doccomments copied from Gaborator) in this crate may be considered
//! to be licensed as either MIT or AGPL-3.0, at your option.

#![deny(missing_docs)]

pub use gaborator_sys::{Coef, CoefMeta, Params as GaboratorParams};


/// Reprepresents C++'s `gaborator::coefs<float>`
/// Can be memory-hungry.
/// 
/// (I'm not sure whether this can be dropped after `Analyzer`.
/// I see some mention of reference counting withing Gaborator library,
/// but have not checked in detail.)
pub struct Coefs(gaborator_sys::cxx::UniquePtr<gaborator_sys::Coefs>);

impl Coefs {
    /// Create new instance of Gaborator analyzer/synthesizer based on supplied parameters
    pub fn new(gab: &Gaborator) -> Self {
        Coefs(
            gaborator_sys::create_coefs(&*gab.0)
        )
    }

    /// Allow the coefficients for points in time before limit 
    /// (a time in units of samples) to be forgotten.
    /// Streaming applications can use this to free memory used by coefficients
    /// that are no longer needed. Coefficients that have been forgotten will read as zero.
    /// This does not guarantee that all coefficients before limit are forgotten, only that
    /// ones for limit or later are not, and that the amount of memory consumed by
    /// any remaining coefficients before limit is bounded.
    pub fn forget_before(&mut self, g:&Gaborator, limit: i64, clean_cut: bool)
    {
        gaborator_sys::forget_before(
            &*g.0,
            self.0.pin_mut(),
            limit,
            clean_cut,
        )
    }

    /// Read or write values within `Coefs`, skipping over non-existent entries.
    /// Corresponds to `process` function of Gaborator.
    /// `from_band` and `to_band` may be given INT_MIN / INT_MAX values, that would mean all bands.
    /// `from_sample_time` and `to_sample_time` can also be given INT64_MIN / INT64_MAX value to mean all available data.
    pub fn process(
        &mut self,
        from_band: i32,
        to_band: i32,
        from_sample_time: i64,
        to_sample_time: i64,
        callback: impl FnMut(CoefMeta, &mut Coef),
    ) {
        gaborator_sys::process(
            self.0.pin_mut(),
            from_band,
            to_band,
            from_sample_time,
            to_sample_time,
            &mut gaborator_sys::ProcessOrFillCallback(Box::new(callback)),
        )
    }

    /// Write values to `Coefs`, creating non-existent entries as needed.
    /// Corresponds to `fill` function of Gaborator.
    /// `from_band` and `to_band` may be given INT_MIN / INT_MAX values, that would mean all bands.
    /// `from_sample_time` / `to_sample_time` should not be set to overtly large range, lest memory will be exhausted.
    pub fn fill(
        &mut self,
        from_band: i32,
        to_band: i32,
        from_sample_time: i64,
        to_sample_time: i64,
        callback: impl FnMut(CoefMeta, &mut Coef),
    ) {
        gaborator_sys::fill(
            self.0.pin_mut(),
            from_band,
            to_band,
            from_sample_time,
            to_sample_time,
            &mut gaborator_sys::ProcessOrFillCallback(Box::new(callback)),
        )
    }
}


/// Main type of the crate. Represents C++'s `gaborator::analyzer<float>`.
pub struct Gaborator(gaborator_sys::cxx::UniquePtr<gaborator_sys::Analyzer>);


impl Gaborator {
    /// Create new instance of Gaborator analyzer/synthesizer based on supplied parameters
    pub fn new(params: &GaboratorParams) -> Self {
        Gaborator(
            gaborator_sys::new_analyzer(params)
        )
    }

    /// Returns the one-sided worst-case time domain support of any of the analysis filters.
    /// When calling `analyze()` with a sample at time t, only spectrogram coefficients within
    /// the time range t ± support will be significantly changed. Coefficients outside the range
    /// may change, but the changes will sufficiently small that they may be ignored without significantly reducing accuracy.
    pub fn analysis_support_len(&self) -> usize { gaborator_sys::get_analysis_support_len(&*self.0) }

    /// Returns the one-sided worst-case time domain support of any of the reconstruction filters.
    /// When calling synthesize() to synthesize a sample at time t, the sample will only be significantly
    /// affected by spectrogram coefficients in the time range t ± support. Coefficients outside the range
    /// may be used in the synthesis, but substituting zeroes for the actual coefficient values will not significantly reduce accuracy.
    pub fn synthesis_support_len(&self) -> usize { gaborator_sys::get_synthesis_support_len(&*self.0) }

    /// Return the smallest valid bandpass band number, corresponding to the highest-frequency bandpass filter.
    /// 
    /// The frequency bands of the analysis filter bank are numbered by nonnegative integers that
    /// increase towards lower (sic) frequencies. There is a number of bandpass bands corresponding
    /// to the logarithmically spaced bandpass analysis filters, from near 0.5 (half the sample rate)
    /// to near fmin, and a single lowpass band containing the residual signal from frequencies below fmin.
    pub fn  bandpass_bands_begin(&self) -> i32 { gaborator_sys::bandpass_bands_begin(&*self.0) }

    /// Return the bandpass band number one past the highest valid bandpass band number,
    /// corresponding to one past the lowest-frequency bandpass filter. 
    pub fn  bandpass_bands_end(&self) -> i32 { gaborator_sys::bandpass_bands_end(&*self.0) }

    /// Return the band number of the lowpass band. 
    pub fn  band_lowpass(&self)  -> i32 { gaborator_sys::band_lowpass(&*self.0) }

    /// Return the band number corresponding to the reference frequency `ff_ref`.
    /// If `ff_ref` falls within the frequency range of the bandpass filter bank, this will be a valid bandpass band number, otherwise it will not. 
    pub fn  band_ref(&self) -> i32 { gaborator_sys::band_ref(&*self.0) }

    /// Return the center frequency of band number `band`, in units of the sampling frequency. 
    pub fn  band_ff(&self, band: i32) -> f64 { gaborator_sys::band_ff(&*self.0, band) }

    /// Spectrum analyze the samples at `signal` and add the resulting coefficients to `coefs`.
    /// `t1` parameter from Gaborator's `analyze` method is caluclated based on supplied slice size.
    ///
    /// If the `coefs` object already contains some coefficients, the new coefficients are summed to those already present.
    pub fn analyze(
        &self,
        signal: &[f32],
        signal_begin_sample_number: i64,
        coefs: &mut Coefs,
    ) {
        gaborator_sys::analyze(
            &*self.0,
            signal,
            signal_begin_sample_number,
            coefs.0.pin_mut(),
        )
    }
        
    /// Synthesize signal samples from the coefficients `coef` and store them at `signal`. 
    /// `t1` parameter from Gaborator's `synthesize` method is caluclated based on supplied slice size.
    /// 
    /// The time range may extend outside the range analyzed using analyze(), in which case
    /// the signal is assumed to be zero in the un-analyzed range.
    pub fn synthesize(
        &self,
        coefs: &Coefs,
        signal_begin_sample_number: i64,
        signal: &mut [f32],
    ) {
        gaborator_sys::synthesize(
            &*self.0,
            &*coefs.0,
            signal_begin_sample_number,
            signal,
        )
    }
}