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

            
14
use azul_css::F32Vec;
15

            
16
/// Audio stream format.
17
#[repr(C)]
18
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19
pub 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

            
26
impl Default for AudioConfig {
27
    fn default() -> Self {
28
        Self {
29
            sample_rate: 48_000,
30
            channels: 1,
31
        }
32
    }
33
}
34

            
35
impl 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)]
50
pub 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

            
59
impl 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`).
72
impl_option!(AudioFrame, OptionAudioFrame, copy = false, [Clone, Debug]);