Skip to main content

azul_core/
audio.rs

1//! POD types for the audio surface (SUPER_PLAN_2 ยง4 P7).
2//!
3//! Audio playback + microphone capture (rodio / cpal on the desktop;
4//! AVAudioEngine / AAudio on mobile). Capture mirrors the sensor manager (the
5//! backend pushes [`AudioFrame`]s to a process-global channel; the layout pass
6//! drains them and a callback reads them); playback queues frames to the
7//! backend. The mic permission is the existing
8//! `azul_layout::managers::permission::Capability::Microphone`.
9//!
10//! Defined in `azul-core` so the config + frame types cross the FFI without
11//! `azul-layout` (or rodio / cpal) as a dependency. For azul-meet (P8),
12//! [`AudioFrame`] is the unit captured -> sent over UDP -> played back.
13
14use azul_css::F32Vec;
15
16/// Audio stream format.
17#[repr(C)]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct AudioConfig {
20    /// Samples per second per channel (e.g. 48000).
21    pub sample_rate: u32,
22    /// Channel count (1 = mono, 2 = stereo).
23    pub channels: u16,
24}
25
26impl Default for AudioConfig {
27    fn default() -> Self {
28        Self {
29            sample_rate: 48_000,
30            channels: 1,
31        }
32    }
33}
34
35impl AudioConfig {
36    /// A config with the given rate + channel count.
37    pub fn new(sample_rate: u32, channels: u16) -> Self {
38        Self {
39            sample_rate,
40            channels,
41        }
42    }
43}
44
45/// A chunk of audio - interleaved `f32` samples in `[-1.0, 1.0]`. For stereo
46/// the layout is `L, R, L, R, ...`. This is the unit the mic backend delivers,
47/// playback consumes, and (P8) azul-meet sends over UDP.
48#[repr(C)]
49#[derive(Debug, Clone, PartialEq)]
50pub struct AudioFrame {
51    /// Samples per second per channel.
52    pub sample_rate: u32,
53    /// Channel count (1 = mono, 2 = stereo).
54    pub channels: u16,
55    /// Interleaved `f32` samples.
56    pub samples: F32Vec,
57}
58
59impl AudioFrame {
60    /// Number of sample *frames* (samples per channel) in this chunk.
61    pub fn frame_count(&self) -> usize {
62        if self.channels == 0 {
63            0
64        } else {
65            self.samples.as_ref().len() / self.channels as usize
66        }
67    }
68}
69
70// FFI Option wrapper for accessors that may have no frame yet. `copy = false`
71// because AudioFrame holds a F32Vec (matches the convention in `json.rs`).
72impl_option!(AudioFrame, OptionAudioFrame, copy = false, [Clone, Debug]);