Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `audio_thread_priority` feature renamed to `realtime-dbus`.
- `audio_thread_priority` dependency bumped to 0.35.
- `DeviceTrait` now requires `PartialEq + Eq + Hash + Debug + Display` with a stable device ID.
- Error messages are now consistent across all hosts.
- **AAudio**: Device names now include the device type suffix (e.g. "Speaker (Builtin Speaker)")
for easier identification when enumerating devices.
- **AAudio**: `supported_input_configs()` and `supported_output_configs()` now return an error for
Expand Down
36 changes: 16 additions & 20 deletions src/host/aaudio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ impl DeviceTrait for Device {
if matches!(info.direction, DeviceDirection::Output) {
return Err(Error::with_message(
ErrorKind::UnsupportedOperation,
"output-only device does not support input",
"Device does not support input",
));
}
Ok(device_supported_configs(info))
Expand All @@ -583,7 +583,7 @@ impl DeviceTrait for Device {
if matches!(info.direction, DeviceDirection::Input) {
return Err(Error::with_message(
ErrorKind::UnsupportedOperation,
"input-only device does not support output",
"Device does not support output",
));
}
Ok(device_supported_configs(info))
Expand All @@ -598,7 +598,7 @@ impl DeviceTrait for Device {
let range = configs.into_iter().next().ok_or_else(|| {
Error::with_message(
ErrorKind::UnsupportedConfig,
"no supported input configuration",
"No supported input configuration",
)
})?;
let config = range
Expand All @@ -613,7 +613,7 @@ impl DeviceTrait for Device {
let range = configs.into_iter().next().ok_or_else(|| {
Error::with_message(
ErrorKind::UnsupportedConfig,
"no supported output configuration",
"No supported output configuration",
)
})?;
let config = range
Expand All @@ -640,7 +640,7 @@ impl DeviceTrait for Device {
sample_format => {
return Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("{sample_format} format is not supported on Android"),
format!("Sample format {sample_format} is not supported"),
))
}
};
Expand All @@ -651,7 +651,7 @@ impl DeviceTrait for Device {
// TODO: more channels available in native AAudio
return Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("{channels} channels are not supported yet (only 1 or 2)"),
format!("Channel count {channels} is not supported"),
));
}
};
Expand Down Expand Up @@ -689,7 +689,7 @@ impl DeviceTrait for Device {
sample_format => {
return Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("{sample_format} format is not supported on Android"),
format!("Sample format {sample_format} is not supported"),
))
}
};
Expand All @@ -700,7 +700,7 @@ impl DeviceTrait for Device {
// TODO: more channels available in native AAudio
return Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("{channels} channels are not supported yet (only 1 or 2)"),
format!("Channel count {channels} is not supported"),
));
}
};
Expand Down Expand Up @@ -749,42 +749,38 @@ impl Hash for Device {
impl StreamTrait for Stream {
fn play(&self) -> Result<(), Error> {
let stream = self.inner.lock().map_err(|_| {
Error::with_message(ErrorKind::StreamInvalidated, "stream lock poisoned")
Error::with_message(ErrorKind::StreamInvalidated, "Stream lock poisoned")
})?;

stream
.request_start()
.context("failed to start AAudio stream")?;
stream.request_start().context("Failed to start stream")?;
stream
.wait_for_state_change(
ndk::audio::AudioStreamState::Starting,
DEFAULT_TIMEOUT_NANOS,
)
.map(|_| ())
.context("failed to wait for AAudio stream to start")
.context("Failed to wait for stream to start")
}

fn pause(&self) -> Result<(), Error> {
match self.direction {
DeviceDirection::Output => {
let stream = self.inner.lock().map_err(|_| {
Error::with_message(ErrorKind::StreamInvalidated, "stream lock poisoned")
Error::with_message(ErrorKind::StreamInvalidated, "Stream lock poisoned")
})?;

stream
.request_pause()
.context("failed to pause AAudio stream")?;
stream.request_pause().context("Failed to pause stream")?;
stream
.wait_for_state_change(
ndk::audio::AudioStreamState::Pausing,
DEFAULT_TIMEOUT_NANOS,
)
.map(|_| ())
.context("failed to wait for AAudio stream to pause")
.context("Failed to wait for stream to pause")
}
_ => Err(Error::with_message(
ErrorKind::UnsupportedOperation,
"pause only supported on output streams",
"Pause is not supported on input streams",
)),
}
}
Expand All @@ -795,7 +791,7 @@ impl StreamTrait for Stream {

fn buffer_size(&self) -> Result<FrameCount, Error> {
let stream = self.inner.lock().map_err(|_| {
Error::with_message(ErrorKind::StreamInvalidated, "stream lock poisoned")
Error::with_message(ErrorKind::StreamInvalidated, "Stream lock poisoned")
})?;

// frames_per_data_callback is only set for BufferSize::Fixed; for Default AAudio
Expand Down
45 changes: 18 additions & 27 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ pub struct Host {
impl Host {
pub fn new() -> Result<Self, Error> {
let inner = AlsaContext::new().map_err(|e| {
Error::with_message(ErrorKind::HostUnavailable, format!("ALSA unavailable: {e}"))
Error::with_message(
ErrorKind::HostUnavailable,
format!("ALSA is not available: {e}"),
)
})?;
Ok(Self {
inner: Arc::new(inner),
Expand Down Expand Up @@ -365,7 +368,7 @@ impl Device {
if !(min..=max).contains(&requested_size) {
return Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("buffer size {requested_size} is not in the supported range {min}..={max}"),
format!("Buffer size {requested_size} is not in the supported range {min}..={max}"),
));
}
}
Expand All @@ -380,22 +383,13 @@ impl Device {
let hw_params = set_hw_params_from_format(&handle, conf, sample_format)?;
let (buffer_size, period_size) = set_sw_params_from_format(&handle, stream_type)?;
if buffer_size == 0 {
return Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
format!(
"device '{}': initialization resulted in a null buffer",
self.pcm_id
),
));
return Err(ErrorKind::DeviceNotAvailable.into());
}

handle.prepare()?;

if handle.count() == 0 {
return Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
format!("device '{}': poll descriptor count is 0", self.pcm_id),
));
return Err(ErrorKind::DeviceNotAvailable.into());
}

// A zero get_htstamp() at prepare time indicates the device does not support hardware timestamps (e.g. PulseAudio ALSA plugin).
Expand All @@ -417,7 +411,6 @@ impl Device {
dropping: AtomicBool::new(false),
direction: stream_type.into(),
handle,
pcm_id: self.pcm_id.clone(),
sample_format,
sample_rate: conf.sample_rate,
frame_size,
Expand Down Expand Up @@ -607,12 +600,13 @@ impl Device {
match self.supported_configs(stream_t) {
// EINVAL when querying direction the device does not support (input-only or output-only)
Err(err) if err.kind() == ErrorKind::InvalidInput => {
let dir = match stream_t {
alsa::Direction::Capture => "input",
alsa::Direction::Playback => "output",
};
return Err(Error::with_message(
ErrorKind::UnsupportedOperation,
format!(
"device '{}' does not support the requested direction",
self.pcm_id
),
format!("Device does not support {dir}"),
));
}
Err(err) => return Err(err),
Expand All @@ -628,7 +622,7 @@ impl Device {
.unwrap_or_else(|| f.with_max_sample_rate())),
None => Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("device '{}': no supported configuration", self.pcm_id),
"No supported configuration",
)),
}
}
Expand Down Expand Up @@ -745,9 +739,6 @@ struct StreamInner {
// The ALSA handle.
handle: alsa::pcm::PCM,

// ALSA PCM identifier used to open this stream.
pcm_id: String,

// Format of the samples.
sample_format: SampleFormat,

Expand Down Expand Up @@ -1055,7 +1046,7 @@ fn try_resume(handle: &alsa::PCM) -> Result<Poll, Error> {
if !hw_params.can_resume() {
return Err(Error::with_message(
ErrorKind::Xrun, // treat as xrun so the worker calls prepare()
"hardware suspend/resume not supported",
"Device does not support suspend/resume",
));
}

Expand Down Expand Up @@ -1129,7 +1120,7 @@ fn poll_for_period(
if revents.intersects(alsa::poll::Flags::HUP | alsa::poll::Flags::NVAL) {
return Err(Error::with_message(
ErrorKind::DeviceNotAvailable,
format!("device '{}' disconnected", stream.pcm_id),
"Device disconnected",
));
}
// POLLERR signals an xrun or suspend; avail_delay() below returns EPIPE/ESTRPIPE accordingly.
Expand Down Expand Up @@ -1432,7 +1423,7 @@ impl StreamTrait for Stream {
if !hw_params.can_pause() {
return Err(Error::with_message(
ErrorKind::UnsupportedOperation,
format!("device '{}' does not support pausing", self.inner.pcm_id),
"Device does not support pausing",
));
}
if self.inner.handle.state() != alsa::pcm::State::Paused {
Expand Down Expand Up @@ -1558,7 +1549,7 @@ fn sample_format_to_alsa_format(
_ => {
return Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("sample format '{sample_format}' is not supported"),
format!("Sample format {sample_format} is not supported"),
))
}
};
Expand All @@ -1575,7 +1566,7 @@ fn sample_format_to_alsa_format(

Err(Error::with_message(
ErrorKind::UnsupportedConfig,
format!("sample format '{sample_format}' is not supported by hardware in any endianness"),
format!("Sample format {sample_format} is not supported in any byte order"),
))
}

Expand Down
4 changes: 2 additions & 2 deletions src/host/asio/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ impl Device {
if channels == 0 {
return Err(Error::with_message(
ErrorKind::UnsupportedOperation,
"ASIO device reports no channels for this direction",
"Device reports no channels for this direction",
));
}
let sample_format = sample_format.ok_or_else(|| {
Error::with_message(
ErrorKind::UnsupportedOperation,
"no supported sample format for this ASIO device",
"No supported sample format",
)
})?;
Ok(SupportedStreamConfig {
Expand Down
Loading
Loading