pitch_detection/utils/
buffer.rs

1use rustfft::num_complex::Complex;
2use rustfft::num_traits::Zero;
3use std::{cell::RefCell, rc::Rc};
4
5use crate::float::Float;
6
7pub enum ComplexComponent {
8    Re,
9    Im,
10}
11
12pub fn new_real_buffer<T: Float>(size: usize) -> Vec<T> {
13    vec![T::zero(); size]
14}
15
16pub fn new_complex_buffer<T: Float>(size: usize) -> Vec<Complex<T>> {
17    vec![Complex::zero(); size]
18}
19
20pub fn copy_real_to_complex<T: Float>(
21    input: &[T],
22    output: &mut [Complex<T>],
23    component: ComplexComponent,
24) {
25    assert!(input.len() <= output.len());
26    match component {
27        ComplexComponent::Re => input.iter().zip(output.iter_mut()).for_each(|(i, o)| {
28            o.re = *i;
29            o.im = T::zero();
30        }),
31        ComplexComponent::Im => input.iter().zip(output.iter_mut()).for_each(|(i, o)| {
32            o.im = *i;
33            o.re = T::zero();
34        }),
35    }
36    output[input.len()..]
37        .iter_mut()
38        .for_each(|o| *o = Complex::zero())
39}
40
41pub fn copy_complex_to_real<T: Float>(
42    input: &[Complex<T>],
43    output: &mut [T],
44    component: ComplexComponent,
45) {
46    assert!(input.len() <= output.len());
47    match component {
48        ComplexComponent::Re => input
49            .iter()
50            .map(|c| c.re)
51            .zip(output.iter_mut())
52            .for_each(|(i, o)| *o = i),
53        ComplexComponent::Im => input
54            .iter()
55            .map(|c| c.im)
56            .zip(output.iter_mut())
57            .for_each(|(i, o)| *o = i),
58    }
59
60    output[input.len()..]
61        .iter_mut()
62        .for_each(|o| *o = T::zero());
63}
64
65/// Computes |x|^2 for each complex value x in `arr`. This function
66/// modifies `arr` in place and leaves the complex component zero.
67pub fn modulus_squared<'a, T: Float>(arr: &'a mut [Complex<T>]) {
68    for mut s in arr {
69        s.re = s.re * s.re + s.im * s.im;
70        s.im = T::zero();
71    }
72}
73
74/// Compute the sum of the square of each element of `arr`.
75pub fn square_sum<T>(arr: &[T]) -> T
76where
77    T: Float + std::iter::Sum,
78{
79    arr.iter().map(|&s| s * s).sum::<T>()
80}
81
82#[derive(Debug)]
83/// A pool of real/complex buffer objects. Buffers are dynamically created as needed
84/// and reused if previously `Drop`ed. Buffers are never freed. Instead buffers are kept
85/// in reserve and reused when a new buffer is requested.
86///
87/// ```rust
88///  use pitch_detection::utils::buffer::BufferPool;
89///
90///  let mut buffers = BufferPool::new(3);
91///  let buf_cell1 = buffers.get_real_buffer();
92///  {
93///      // This buffer won't be dropped until the end of the function
94///      let mut buf1 = buf_cell1.borrow_mut();
95///      buf1[0] = 5.5;
96///  }
97///  {
98///      // This buffer will be dropped when the scope ends
99///      let buf_cell2 = buffers.get_real_buffer();
100///      let mut buf2 = buf_cell2.borrow_mut();
101///      buf2[1] = 6.6;
102///  }
103///  {
104///      // This buffer will be dropped when the scope ends
105///      // It is the same buffer that was just used (i.e., it's a reused buffer)
106///      let buf_cell3 = buffers.get_real_buffer();
107///      let mut buf3 = buf_cell3.borrow_mut();
108///      buf3[2] = 7.7;
109///  }
110///  // The first buffer we asked for should not have been reused.
111///  assert_eq!(&buf_cell1.borrow()[..], &[5.5, 0., 0.]);
112///  let buf_cell2 = buffers.get_real_buffer();
113///  // The second buffer was reused because it was dropped and then another buffer was requested.
114///  assert_eq!(&buf_cell2.borrow()[..], &[0.0, 6.6, 7.7]);
115/// ```
116pub struct BufferPool<T> {
117    real_buffers: Vec<Rc<RefCell<Vec<T>>>>,
118    complex_buffers: Vec<Rc<RefCell<Vec<Complex<T>>>>>,
119    pub buffer_size: usize,
120}
121
122impl<T: Float> BufferPool<T> {
123    pub fn new(buffer_size: usize) -> Self {
124        BufferPool {
125            real_buffers: vec![],
126            complex_buffers: vec![],
127            buffer_size,
128        }
129    }
130    fn add_real_buffer(&mut self) -> Rc<RefCell<Vec<T>>> {
131        self.real_buffers
132            .push(Rc::new(RefCell::new(new_real_buffer::<T>(
133                self.buffer_size,
134            ))));
135        Rc::clone(&self.real_buffers.last().unwrap())
136    }
137    fn add_complex_buffer(&mut self) -> Rc<RefCell<Vec<Complex<T>>>> {
138        self.complex_buffers
139            .push(Rc::new(RefCell::new(new_complex_buffer::<T>(
140                self.buffer_size,
141            ))));
142        Rc::clone(&self.complex_buffers.last().unwrap())
143    }
144    /// Get a reference to a buffer that can e used until it is `Drop`ed. Call
145    /// `.borrow_mut()` to get a reference to a mutable version of the buffer.
146    pub fn get_real_buffer(&mut self) -> Rc<RefCell<Vec<T>>> {
147        self.real_buffers
148            .iter()
149            // If the Rc count is 1, we haven't loaned the buffer out yet.
150            .find(|&buf| Rc::strong_count(buf) == 1)
151            .map(|buf| Rc::clone(buf))
152            // If we haven't found a buffer we can reuse, create one.
153            .unwrap_or_else(|| self.add_real_buffer())
154    }
155    /// Get a reference to a buffer that can e used until it is `Drop`ed. Call
156    /// `.borrow_mut()` to get a reference to a mutable version of the buffer.
157    pub fn get_complex_buffer(&mut self) -> Rc<RefCell<Vec<Complex<T>>>> {
158        self.complex_buffers
159            .iter()
160            // If the Rc count is 1, we haven't loaned the buffer out yet.
161            .find(|&buf| Rc::strong_count(buf) == 1)
162            .map(|buf| Rc::clone(buf))
163            // If we haven't found a buffer we can reuse, create one.
164            .unwrap_or_else(|| self.add_complex_buffer())
165    }
166}
167
168#[test]
169fn test_buffers() {
170    let mut buffers = BufferPool::new(3);
171    let buf_cell1 = buffers.get_real_buffer();
172    {
173        // This buffer won't be dropped until the end of the function
174        let mut buf1 = buf_cell1.borrow_mut();
175        buf1[0] = 5.5;
176    }
177    {
178        // This buffer will be dropped when the scope ends
179        let buf_cell2 = buffers.get_real_buffer();
180        let mut buf2 = buf_cell2.borrow_mut();
181        buf2[1] = 6.6;
182    }
183    {
184        // This buffer will be dropped when the scope ends
185        // It is the same buffer that was just used (i.e., it's a reused buffer)
186        let buf_cell3 = buffers.get_real_buffer();
187        let mut buf3 = buf_cell3.borrow_mut();
188        buf3[2] = 7.7;
189    }
190    // We're peering into the internals of `BufferPool`. This shouldn't normally be done.
191    assert_eq!(&buffers.real_buffers[0].borrow()[..], &[5.5, 0., 0.]);
192    assert_eq!(&buffers.real_buffers[1].borrow()[..], &[0.0, 6.6, 7.7]);
193}