Skip to main content

FftBin

Struct FftBin 

Source
#[repr(C)]
pub struct FftBin { pub re: f32, pub im: f32, }
Expand description

One frequency bin: a complex number, single precision.

Laid out as NE10 lays out its own, so a slice of these is handed to the transform as it stands rather than converted. Not num_complex’s Complex32, which this crate would then owe a major version to; the fields are public and the conversions cheap.

Fields§

§re: f32

The real part.

§im: f32

The imaginary part.

Implementations§

Source§

impl FftBin

Source

pub const ZERO: Self

The origin: both parts zero.

The same value Default gives, in a form a const item and an array initialiser can use.

Source

pub const fn new(re: f32, im: f32) -> Self

A bin from its two parts.

Source

pub fn magnitude(self) -> f32

The magnitude, sqrt(re² + im²).

The same quantity Bela’s Fft::fda reports, not the same bits: that one uses sqrtf_neon, an approximation from libraries/math_neon, and short-circuits to 0 when both parts are zero.

Examples found in repository?
examples/fft.rs (line 327)
276    fn analyse(&mut self, context: &BlockContext) {
277        let Some(analysis) = &mut self.analysis else {
278            return;
279        };
280        if context.audio_in_channels() == 0 {
281            return;
282        }
283
284        for frame in 0..context.audio_frames() {
285            analysis.window[analysis.filled] = context.audio_read(frame, 0);
286            analysis.filled += 1;
287            if analysis.filled < analysis.window.len() {
288                continue;
289            }
290            analysis.filled = 0;
291
292            let transformed = {
293                let _section = analysis.timer.measure();
294                analysis
295                    .fft
296                    .forward(&mut analysis.window, &mut analysis.spectrum)
297            };
298            if transformed.is_err() {
299                // Both buffers came from the plan, so their lengths
300                // agree by construction; saying so beats going quiet
301                // if a later edit changes one. Once per window rather
302                // than once per block, which is why this one prints
303                // where the measurement below does not.
304                rt_println!("render_post: the analysis buffers no longer fit the plan");
305                continue;
306            }
307
308            // The loudest bin, skipping DC — which a little offset on
309            // the input would otherwise win every time.
310            let peak = analysis
311                .spectrum
312                .iter()
313                .enumerate()
314                .skip(1)
315                .max_by(|(_, a), (_, b)| a.magnitude_squared().total_cmp(&b.magnitude_squared()));
316            if let Some((bin, value)) = peak {
317                #[allow(
318                    clippy::cast_precision_loss,
319                    reason = "a bin index is far below f32's exact integer range"
320                )]
321                let hz = self.sample_rate * bin as f32 / analysis_length_as_float();
322                self.published
323                    .peak_hz
324                    .store(hz.to_bits(), Ordering::Relaxed);
325                self.published
326                    .peak_magnitude
327                    .store(value.magnitude().to_bits(), Ordering::Relaxed);
328            }
329        }
330    }
Source

pub const fn magnitude_squared(self) -> f32

The magnitude squared, re² + im².

What to compare bins with: it orders them the same way magnitude does and has no square root in it.

Examples found in repository?
examples/fft.rs (line 315)
276    fn analyse(&mut self, context: &BlockContext) {
277        let Some(analysis) = &mut self.analysis else {
278            return;
279        };
280        if context.audio_in_channels() == 0 {
281            return;
282        }
283
284        for frame in 0..context.audio_frames() {
285            analysis.window[analysis.filled] = context.audio_read(frame, 0);
286            analysis.filled += 1;
287            if analysis.filled < analysis.window.len() {
288                continue;
289            }
290            analysis.filled = 0;
291
292            let transformed = {
293                let _section = analysis.timer.measure();
294                analysis
295                    .fft
296                    .forward(&mut analysis.window, &mut analysis.spectrum)
297            };
298            if transformed.is_err() {
299                // Both buffers came from the plan, so their lengths
300                // agree by construction; saying so beats going quiet
301                // if a later edit changes one. Once per window rather
302                // than once per block, which is why this one prints
303                // where the measurement below does not.
304                rt_println!("render_post: the analysis buffers no longer fit the plan");
305                continue;
306            }
307
308            // The loudest bin, skipping DC — which a little offset on
309            // the input would otherwise win every time.
310            let peak = analysis
311                .spectrum
312                .iter()
313                .enumerate()
314                .skip(1)
315                .max_by(|(_, a), (_, b)| a.magnitude_squared().total_cmp(&b.magnitude_squared()));
316            if let Some((bin, value)) = peak {
317                #[allow(
318                    clippy::cast_precision_loss,
319                    reason = "a bin index is far below f32's exact integer range"
320                )]
321                let hz = self.sample_rate * bin as f32 / analysis_length_as_float();
322                self.published
323                    .peak_hz
324                    .store(hz.to_bits(), Ordering::Relaxed);
325                self.published
326                    .peak_magnitude
327                    .store(value.magnitude().to_bits(), Ordering::Relaxed);
328            }
329        }
330    }
Source

pub fn phase(self) -> f32

The phase in radians, from to π.

Trait Implementations§

Source§

impl Clone for FftBin

Source§

fn clone(&self) -> FftBin

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for FftBin

Source§

impl Debug for FftBin

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for FftBin

Source§

fn default() -> FftBin

Returns the “default value” for a type. Read more
Source§

impl From<(f32, f32)> for FftBin

Source§

fn from((re, im): (f32, f32)) -> Self

Converts to this type from the input type.
Source§

impl From<[f32; 2]> for FftBin

Source§

fn from([re, im]: [f32; 2]) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for FftBin

Source§

fn eq(&self, other: &FftBin) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for FftBin

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.