Skip to content

Commit b341fd7

Browse files
committed
rename Output to AvailableOutput + try_from device_id
1 parent 174ce9b commit b341fd7

3 files changed

Lines changed: 68 additions & 11 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ wav_output = ["dep:hound"]
3434
# Enable structured observability and instrumentation
3535
tracing = ["dep:tracing"]
3636
# Experimental features using atomic floating-point operations
37-
experimental = ["dep:atomic_float"]
37+
experimental = ["dep:atomic_float", "dep:serde"]
3838
# Perform all calculations with 64-bit floats (instead of 32)
3939
64bit = []
4040

@@ -126,6 +126,7 @@ symphonia = { version = "0.5.5", optional = true, default-features = false }
126126
crossbeam-channel = { version = "0.5.15", optional = true }
127127
thiserror = "2"
128128

129+
serde = { version = "1", features = ["derive"], optional = true }
129130
rand = { version = "0.10", optional = true }
130131
rand_distr = { version = "0.6", optional = true }
131132
tracing = { version = "0.1.40", optional = true }

src/speakers.rs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,25 @@
9898
//! # Ok(())
9999
//! # }
100100
//! ```
101+
//!
102+
//! # Storing & Loading Output
103+
//!
104+
//! ```no_run
105+
//! use cpal::DeviceId;
106+
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
107+
//! // load
108+
//! let device_id = DeviceId::from_str("Some stored input")?;
109+
//! let output = AvailableOutput::try_from(device_id)?;
110+
//!
111+
//! // store
112+
//! let device_id = output.device_id()?;
113+
//! let device_id = device_id.to_string();
114+
//! # }
115+
//! ```
101116
102117
use core::fmt;
103-
104118
use cpal::traits::{DeviceTrait, HostTrait};
119+
use cpal::DeviceId;
105120

106121
use crate::common::assert_error_traits;
107122

@@ -117,14 +132,46 @@ pub use config::{BufferSize, OutputConfig};
117132
pub struct ListError(#[source] cpal::DevicesError);
118133
assert_error_traits! {ListError}
119134

135+
#[derive(Debug, thiserror::Error)]
136+
pub enum NotAvailable {
137+
#[error(
138+
"The OS audio API the saved output used ({expected}) while supported on this system is not available."
139+
)]
140+
Host { expected: String },
141+
#[error("There is not device with id: {device_id} available on this system.")]
142+
NoDevice { device_id: DeviceId },
143+
}
144+
145+
impl TryFrom<DeviceId> for AvailableOutput {
146+
type Error = NotAvailable;
147+
148+
fn try_from(device_id: DeviceId) -> Result<Self, NotAvailable> {
149+
let host_id = device_id.0;
150+
let host = cpal::platform::host_from_id(host_id).map_err(|_| NotAvailable::Host {
151+
expected: host_id.to_string(),
152+
})?;
153+
let device = host
154+
.device_by_id(&device_id)
155+
.ok_or_else(|| NotAvailable::NoDevice {
156+
device_id: device_id.clone(),
157+
})?;
158+
let default_id = host.default_output_device().map(|d| d.id());
159+
160+
Ok(Self {
161+
inner: device,
162+
default: default_id.is_some_and(|id| id.is_ok_and(|id| id == device_id)),
163+
})
164+
}
165+
}
166+
120167
/// An output device
121168
#[derive(Clone)]
122-
pub struct Output {
169+
pub struct AvailableOutput {
123170
inner: cpal::Device,
124171
default: bool,
125172
}
126173

127-
impl Output {
174+
impl AvailableOutput {
128175
/// Whether this output is the default sound output for the OS
129176
pub fn is_default(&self) -> bool {
130177
self.default
@@ -133,9 +180,13 @@ impl Output {
133180
pub(crate) fn into_inner(self) -> cpal::Device {
134181
self.inner
135182
}
183+
184+
pub fn device_id(&self) -> Result<DeviceId, cpal::DeviceIdError> {
185+
self.inner.id()
186+
}
136187
}
137188

138-
impl fmt::Debug for Output {
189+
impl fmt::Debug for AvailableOutput {
139190
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140191
f.debug_struct("Device")
141192
.field(
@@ -149,7 +200,7 @@ impl fmt::Debug for Output {
149200
}
150201
}
151202

152-
impl fmt::Display for Output {
203+
impl fmt::Display for AvailableOutput {
153204
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154205
write!(
155206
f,
@@ -162,12 +213,15 @@ impl fmt::Display for Output {
162213
}
163214

164215
/// Returns a list of available output devices on the system.
165-
pub fn available_outputs() -> Result<Vec<Output>, ListError> {
216+
pub fn available_outputs() -> Result<Vec<AvailableOutput>, ListError> {
166217
let host = cpal::default_host();
167218
let default = host.default_output_device().map(|d| d.id());
168-
let devices = host.output_devices().map_err(ListError)?.map(|dev| Output {
169-
default: Some(dev.id()) == default,
170-
inner: dev,
171-
});
219+
let devices = host
220+
.output_devices()
221+
.map_err(ListError)?
222+
.map(|dev| AvailableOutput {
223+
default: Some(dev.id()) == default,
224+
inner: dev,
225+
});
172226
Ok(devices.collect())
173227
}

0 commit comments

Comments
 (0)