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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
to select 48 kHz or 44.1 kHz from a range.
- `realtime` feature for high-priority audio scheduling without a D-Bus build dependency.
- Add `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md` and `SECURITY.md`.
- `BufferSize` now implements `Default` (returns `BufferSize::Default`).
- `SupportedBufferSize` now implements `Default` (returns `SupportedBufferSize::Unknown`).
- `SupportedStreamConfig` now implements `Copy`.
- **AAudio**: Streams now request `PERFORMANCE_MODE_LOW_LATENCY` when the `realtime` feature is
enabled; stream error callback receives `ErrorKind::RealtimeDenied` if not granted.
- **AAudio**: `Device` now implements `PartialEq`, `Eq`, `Hash`, and `Debug`.
- **AAudio**: `Host` and `Device` now implement `Default`; added `Device::new()`.
- **ALSA**: `device_by_id()` now accepts PCM shorthand names such as `hw:0,0` and `plughw:foo`.
- **ASIO**: `Device` now implements `Debug`.
- **AudioWorklet**: `Device` and `Host` now implement `Default`; added `Device::new()`.
- **CoreAudio**: `Host` now implements `Default`.
- **CoreAudio**: tvOS target support (Tier 3, requires nightly).
- **CoreAudio**: `Device` now implements `PartialEq`, `Eq`, and `Hash`.
- **CoreAudio (iOS)**: `Device` now implements `Default`; added `Device::new()`.
- **JACK**: `Device` now implements `PartialEq`, `Eq`, and `Hash`.
- **Null**: `Device`, `Host`, `Stream`, `SupportedInputConfigs`, and `SupportedOutputConfigs` now
implement `Default`, added `new()` on each.
- **PipeWire**: New host for Linux and some BSDs using the PipeWire API.
- **PulseAudio**: New host for Linux and some BSDs using the PulseAudio API.
- **WASAPI**: `Device` now implements `PartialEq`, `Eq`, `Hash`, and `Debug`.
- **WASAPI**: `Host` now implements `Default`.
- **WebAudio**: `Device` and `Host` now implement `Default`; added `Device::new()`.

### Changed

Expand Down Expand Up @@ -178,6 +189,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **ASIO**: Fix `driver.sample_rate()` failures at stream creation being silently ignored.
- **ASIO**: Fix callbacks firing before `build_*_stream` returns the `Stream` handle.
- **ASIO**: Fix overrun not being reported when the driver reports `kAsioOverload`.
- **AudioWorklet**: Fix `Devices::default()` to enumerate only if `Host::is_available()`.
- **CoreAudio**: Fix default output streams silently stopping when the system default output
device is unplugged; they now reroute automatically or report `ErrorKind::DeviceNotAvailable`.
- **CoreAudio**: Fix undefined behaviour and silent failure in loopback device creation.
Expand Down
2 changes: 1 addition & 1 deletion src/host/aaudio/java_interface/audio_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::{
/**
* The Android audio features
*/
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy)]
pub enum AudioFeature {
LowLatency,
Output,
Expand Down
3 changes: 2 additions & 1 deletion src/host/aaudio/java_interface/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ pub struct AudioDeviceInfo {
/**
* The type of audio device
*/
#[derive(Debug, Clone, Copy, FromPrimitive)]
#[derive(Debug, Clone, Copy, Default, FromPrimitive)]
#[non_exhaustive]
#[repr(i32)]
pub enum AudioDeviceType {
#[default]
Unknown = 0,
AuxLine = 19,
BleBroadcast = 30,
Expand Down
16 changes: 12 additions & 4 deletions src/host/aaudio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,18 @@ const SAMPLE_RATES: [i32; 15] = [
/// The same default for blocking operations as Oboe uses
const DEFAULT_TIMEOUT_NANOS: i64 = 2_000_000_000;

#[derive(Default)]
pub struct Host;
#[derive(Clone, Debug)]

#[derive(Clone, Debug, Default)]
pub struct Device(Option<AudioDeviceInfo>);

impl Device {
pub fn new() -> Self {
Self::default()
}
}

/// Stream wraps AudioStream in Arc<Mutex<>> to provide Send + Sync semantics.
///
/// While the underlying ndk::audio::AudioStream is neither Send nor Sync in ndk 0.9.0
Expand Down Expand Up @@ -191,16 +199,16 @@ impl HostTrait for Host {
.collect::<Vec<_>>()
.into_iter())
} else {
Ok(vec![Device(None)].into_iter())
Ok(vec![Device::new()].into_iter())
}
}

fn default_input_device(&self) -> Option<Self::Device> {
Some(Device(None))
Some(Device::default())
}

fn default_output_device(&self) -> Option<Self::Device> {
Some(Device(None))
Some(Device::default())
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ impl Default for Device {
}

/// Strategy for pre-filling an output buffer with the equilibrium value.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Debug)]
enum EquilibriumFill {
/// Equilibrium is represented as a single repeating byte value.
Byte(u8),
Expand Down Expand Up @@ -704,7 +704,7 @@ impl EquilibriumFill {
}

// How callback timestamps are produced.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TimestampMode {
// Hardware timestamps are unavailable (e.g. PulseAudio ALSA plugin returns zero htstamp).
// Timestamps are monotonic elapsed time since stream creation, sourced from Instant::now().
Expand Down
15 changes: 4 additions & 11 deletions src/host/asio/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::{

/// Shared state for extending the 32-bit `timeGetTime()` millisecond counter into a
/// monotonic 64-bit nanosecond value, shared between `now()` and audio callbacks.
#[derive(Default)]
struct TimeBase {
last_ns: AtomicU64,
epoch_ns: AtomicU64,
Expand All @@ -33,13 +34,6 @@ struct TimeBase {
const TIMEGETIME_WRAP_NS: u64 = (u32::MAX as u64 + 1) * 1_000_000;

impl TimeBase {
fn new() -> Self {
Self {
last_ns: AtomicU64::new(0),
epoch_ns: AtomicU64::new(0),
}
}

/// Convert a nanosecond timestamp to a monotonic `StreamInstant`.
fn to_stream_instant(&self, ns: u64) -> StreamInstant {
// `Relaxed` is sufficient: callbacks run on a single ASIO thread. The only
Expand All @@ -60,9 +54,8 @@ impl TimeBase {
const ASIO_EVENT_DEBOUNCE: Duration = Duration::from_millis(500);

#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq)]
enum StreamState {
#[default]
Starting = 0,
Paused = 1,
Playing = 2,
Expand Down Expand Up @@ -211,7 +204,7 @@ impl Device {
let mut current_buffer_size = buffer_size as i32;
let mut last_buffer_index: i32 = -1;

let time_base = Arc::new(TimeBase::new());
let time_base = Arc::new(TimeBase::default());
let time_base_cb = Arc::clone(&time_base);

// Set the input callback.
Expand Down Expand Up @@ -544,7 +537,7 @@ impl Device {
let mut current_buffer_size = buffer_size as i32;
let mut last_buffer_index: i32 = -1;

let time_base = Arc::new(TimeBase::new());
let time_base = Arc::new(TimeBase::default());
let time_base_cb = Arc::clone(&time_base);

let callback_id = driver.add_callback(move |callback_info| unsafe {
Expand Down
17 changes: 14 additions & 3 deletions src/host/audioworklet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,23 @@ use crate::dependent_module;
/// Content is false if the iterator is empty.
pub struct Devices(bool);

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Device;

impl Device {
pub fn new() -> Self {
Self
}
}

impl fmt::Display for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = self.description().map_err(|_| fmt::Error)?;
f.write_str(desc.name())
}
}

#[derive(Default)]
Comment thread
roderickvd marked this conversation as resolved.
pub struct Host;

pub struct Stream {
Expand Down Expand Up @@ -103,7 +110,11 @@ impl HostTrait for Host {
}

fn default_output_device(&self) -> Option<Self::Device> {
Some(Device)
if Self::is_available() {
Some(Device)
} else {
None
}
}
}

Expand Down Expand Up @@ -408,7 +419,7 @@ impl Drop for Stream {

impl Default for Devices {
fn default() -> Self {
Self(true)
Self(Host::is_available())
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/host/coreaudio/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use session_event_manager::SessionEventManager;
// These days the default of iOS is now F32 and no longer I16
const SUPPORTED_SAMPLE_FORMAT: SampleFormat = SampleFormat::F32;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Device;

impl fmt::Display for Device {
Expand All @@ -46,6 +46,7 @@ impl fmt::Display for Device {
}
}

#[derive(Default)]
pub struct Host;

impl Host {
Expand Down Expand Up @@ -76,6 +77,10 @@ impl HostTrait for Host {
}

impl Device {
pub fn new() -> Self {
Self
}

fn description(&self) -> Result<DeviceDescription, Error> {
// Query AVAudioSession to determine actual input/output availability
// SAFETY: AVAudioSession::sharedInstance() returns the global audio session singleton
Expand Down
2 changes: 1 addition & 1 deletion src/host/coreaudio/macos/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ fn set_sample_rate(
Ok(())
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Copy)]
enum AudioUnitMode {
/// HAL Output AudioUnit with input enabled, pinned to a specific device.
Input,
Expand Down
2 changes: 1 addition & 1 deletion src/host/coreaudio/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod property_listener;
pub use device::Device;

/// Coreaudio host, the default host on macOS.
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Host;

impl Host {
Expand Down
3 changes: 1 addition & 2 deletions src/host/jack/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ use crate::{
};

#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[derive(Clone, Copy, PartialEq, Eq)]
enum StreamState {
#[default]
Starting = 0,
Paused = 1,
Playing = 2,
Expand Down
33 changes: 29 additions & 4 deletions src/host/null/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
#[derive(Default)]
pub struct Devices;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Device;

impl fmt::Display for Device {
Expand All @@ -25,27 +25,52 @@ impl fmt::Display for Device {
}
}

#[derive(Default)]
pub struct Host;

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct Stream;

// Compile-time assertion that Stream is Send and Sync
crate::assert_stream_send!(Stream);
crate::assert_stream_sync!(Stream);

#[derive(Clone)]
#[derive(Clone, Default)]
pub struct SupportedInputConfigs;
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct SupportedOutputConfigs;

impl Device {
pub fn new() -> Self {
Self
}
}

impl Host {
#[allow(dead_code)]
pub fn new() -> Result<Self, Error> {
Ok(Self)
}
}

impl Stream {
pub fn new() -> Self {
Self
}
}

impl SupportedInputConfigs {
pub fn new() -> Self {
Self
}
}

impl SupportedOutputConfigs {
pub fn new() -> Self {
Self
}
}

impl Devices {
pub fn new() -> Result<Self, Error> {
Ok(Self)
Expand Down
6 changes: 3 additions & 3 deletions src/host/pipewire/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ pub(crate) enum Class {
}

#[derive(Clone, Debug, Default, Copy)]
pub enum Role {
Sink,
enum Role {
#[default]
Sink,
Source,
Duplex,
StreamOutput,
Expand Down Expand Up @@ -668,7 +668,7 @@ impl DeviceTrait for Device {
}
}

#[derive(Debug, Clone, Default)]
#[derive(Clone, Default)]
struct Settings {
rate: SampleRate,
allow_rates: Box<[SampleRate]>,
Expand Down
5 changes: 2 additions & 3 deletions src/host/pipewire/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ impl Drop for PwInitGuard {
}
}

#[derive(Debug, Clone, Copy)]
pub enum StreamCommand {
pub(super) enum StreamCommand {
Toggle(bool),
Stop,
}
Expand All @@ -83,7 +82,7 @@ pub struct Stream {
}

impl Stream {
pub(crate) fn new(
pub(super) fn new(
handle: JoinHandle<()>,
controller: pw::channel::Sender<StreamCommand>,
last_quantum: Arc<AtomicU64>,
Expand Down
2 changes: 1 addition & 1 deletion src/host/wasapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod stream;
/// Note: If you use a WASAPI output device as an input device it will
/// transparently enable loopback mode (see
/// https://docs.microsoft.com/en-us/windows/win32/coreaudio/loopback-recording).
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Host;

impl Host {
Expand Down
Loading
Loading