Skip to main content

wayle_audio/
lib.rs

1//! PulseAudio integration for managing audio devices and streams.
2//!
3//! # Overview
4//!
5//! Provides reactive access to PulseAudio through [`AudioService`].
6//! All state is exposed via [`Property`] fields that automatically update when
7//! PulseAudio state changes.
8//!
9//! # Reactive Properties
10//!
11//! Every field on [`AudioService`], [`OutputDevice`], [`InputDevice`], and
12//! [`AudioStream`] is a [`Property<T>`] with two access patterns:
13//!
14//! - **Snapshot**: Call `.get()` for the current value
15//! - **Stream**: Call `.watch()` for a `Stream<Item = T>` that yields on changes
16//!
17//! ```rust,no_run
18//! use wayle_audio::AudioService;
19//! use futures::StreamExt;
20//!
21//! # async fn example() -> Result<(), wayle_audio::Error> {
22//! let audio = AudioService::new().await?;
23//!
24//! // Snapshot: get current default output device
25//! if let Some(device) = audio.default_output.get() {
26//!     println!("Default output: {}", device.description.get());
27//!     println!("Volume: {:?}", device.volume.get());
28//!     println!("Muted: {}", device.muted.get());
29//! }
30//!
31//! // Stream: react to default output changes
32//! let mut stream = audio.default_output.watch();
33//! while let Some(maybe_device) = stream.next().await {
34//!     match maybe_device {
35//!         Some(device) => println!("Default changed to: {}", device.description.get()),
36//!         None => println!("No default output"),
37//!     }
38//! }
39//! # Ok(())
40//! # }
41//! ```
42//!
43//! # Live vs Snapshot Instances
44//!
45//! Devices from [`AudioService`] fields (`output_devices`, `default_output`, etc.)
46//! are **live**: their properties update when PulseAudio state changes.
47//!
48//! The explicit lookup methods differ:
49//!
50//! | Method | Returns | Properties Update? |
51//! |--------|---------|-------------------|
52//! | `output_device()` | `OutputDevice` | No (snapshot) |
53//! | `output_device_monitored()` | `Arc<OutputDevice>` | Yes (live) |
54//!
55//! ```rust,no_run
56//! # use wayle_audio::{AudioService, types::device::{DeviceKey, DeviceType}};
57//! # async fn example() -> Result<(), wayle_audio::Error> {
58//! # let audio = AudioService::new().await?;
59//! # let key = DeviceKey::new(0, DeviceType::Output);
60//! // Snapshot: properties won't update
61//! let snapshot = audio.output_device(key).await?;
62//! let vol_at_query_time = snapshot.volume.get();
63//!
64//! // Live: properties update automatically
65//! let live = audio.output_device_monitored(key).await?;
66//! let mut vol_stream = live.volume.watch();
67//! // vol_stream yields whenever volume changes in PulseAudio
68//! # Ok(())
69//! # }
70//! ```
71//!
72//! # Controlling Devices
73//!
74//! [`OutputDevice`] and [`InputDevice`] have control methods:
75//!
76//! ```rust,no_run
77//! # use wayle_audio::{AudioService, volume::types::Volume};
78//! # async fn example() -> Result<(), wayle_audio::Error> {
79//! # let audio = AudioService::new().await?;
80//! if let Some(device) = audio.default_output.get() {
81//!     // Mute/unmute
82//!     device.set_mute(true).await?;
83//!
84//!     // Set volume (0.0 to 1.0 per channel)
85//!     device.set_volume(Volume::stereo(0.5, 0.5)).await?;
86//!
87//!     // Make this device the default
88//!     device.set_as_default().await?;
89//! }
90//! # Ok(())
91//! # }
92//! ```
93//!
94//! # Configuration
95//!
96//! | Method | Effect |
97//! |--------|--------|
98//! | `with_daemon()` | Control audio from scripts or other processes |
99//!
100//! ```rust,no_run
101//! use wayle_audio::AudioService;
102//!
103//! # async fn example() -> Result<(), wayle_audio::Error> {
104//! let audio = AudioService::builder()
105//!     .with_daemon()
106//!     .build()
107//!     .await?;
108//! # Ok(())
109//! # }
110//! ```
111//!
112//! # D-Bus Interface
113//!
114//! When `with_daemon()` is enabled, the service registers on the session bus.
115//!
116//! - **Service:** `com.wayle.Audio1`
117//! - **Path:** `/com/wayle/Audio`
118//! - **Interface:** `com.wayle.Audio1`
119//!
120//! See [`dbus.md`](https://github.com/wayle-rs/wayle/blob/master/crates/wayle-audio/dbus.md) for the full interface specification.
121//!
122//! # Service Fields
123//!
124//! [`AudioService`] exposes these reactive properties:
125//!
126//! | Field | Type | Description |
127//! |-------|------|-------------|
128//! | [`output_devices`] | `Vec<Arc<OutputDevice>>` | All sinks (speakers, headphones) |
129//! | [`input_devices`] | `Vec<Arc<InputDevice>>` | All sources (microphones) |
130//! | [`default_output`] | `Option<Arc<OutputDevice>>` | Current default sink |
131//! | [`default_input`] | `Option<Arc<InputDevice>>` | Current default source |
132//! | [`playback_streams`] | `Vec<Arc<AudioStream>>` | Active playback (apps playing audio) |
133//! | [`recording_streams`] | `Vec<Arc<AudioStream>>` | Active recording (apps capturing audio) |
134//!
135//! [`output_devices`]: AudioService::output_devices
136//! [`input_devices`]: AudioService::input_devices
137//! [`default_output`]: AudioService::default_output
138//! [`default_input`]: AudioService::default_input
139//! [`playback_streams`]: AudioService::playback_streams
140//! [`recording_streams`]: AudioService::recording_streams
141//! [`Property`]: wayle_core::Property
142//! [`Property<T>`]: wayle_core::Property
143//! [`OutputDevice`]: core::device::output::OutputDevice
144//! [`InputDevice`]: core::device::input::InputDevice
145//! [`AudioStream`]: core::stream::AudioStream
146
147#![cfg_attr(test, allow(clippy::panic))]
148
149mod backend;
150mod builder;
151/// Core domain models
152pub mod core;
153/// D-Bus interface for external control
154pub mod dbus;
155mod error;
156mod events;
157mod monitoring;
158mod service;
159mod tokio_mainloop;
160/// Types for the audio service
161pub mod types;
162/// Volume control domain
163pub mod volume;
164
165pub use builder::AudioServiceBuilder;
166pub use error::Error;
167pub use service::AudioService;
168
169#[doc = include_str!("../README.md")]
170#[cfg(doctest)]
171pub struct ReadmeDocTests;