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]);