Skip to content

Commit

Permalink
Merge pull request #360 from b-ma/fix/device-id
Browse files Browse the repository at this point in the history
Fix: improve device id
  • Loading branch information
orottier authored Sep 19, 2023
2 parents 00a2606 + b11f762 commit 0f5250c
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 62 deletions.
2 changes: 1 addition & 1 deletion examples/microphone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn ask_sink_id() -> String {

let input = std::io::stdin().lines().next().unwrap().unwrap();
match input.trim() {
"0" => "none".to_string(),
"0" => "".to_string(),
i => i.to_string(),
}
}
Expand Down
65 changes: 42 additions & 23 deletions src/io/cpal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use cpal::{
BuildStreamError, Device, OutputCallbackInfo, SampleFormat, Stream, StreamConfig,
SupportedBufferSize,
};
use crossbeam_channel::Receiver;

use super::{AudioBackendManager, RenderThreadInit};

Expand All @@ -18,8 +19,6 @@ use crate::media_devices::{MediaDeviceInfo, MediaDeviceInfoKind};
use crate::render::RenderThread;
use crate::{AtomicF64, MAX_CHANNELS};

use crossbeam_channel::Receiver;

// I doubt this construct is entirely safe. Stream is not Send/Sync (probably for a good reason) so
// it should be managed from a single thread instead.
// <https://github.com/orottier/web-audio-api-rs/issues/357>
Expand Down Expand Up @@ -403,29 +402,49 @@ impl AudioBackendManager for CpalBackend {
{
let host = get_host();

let input_devices = host
.input_devices()
.unwrap()
.map(|d| (d, MediaDeviceInfoKind::AudioInput));

let output_devices = host
.output_devices()
.unwrap()
.map(|d| (d, MediaDeviceInfoKind::AudioOutput));

input_devices
.chain(output_devices)
.enumerate()
.map(|(index, (device, kind))| {
MediaDeviceInfo::new(
(index + 1).to_string(),
None,
let input_devices = host.input_devices().unwrap().map(|d| {
let num_channels = d.default_input_config().unwrap().channels();
(d, MediaDeviceInfoKind::AudioInput, num_channels)
});

let output_devices = host.output_devices().unwrap().map(|d| {
let num_channels = d.default_output_config().unwrap().channels();
(d, MediaDeviceInfoKind::AudioOutput, num_channels)
});

// cf. https://github.com/orottier/web-audio-api-rs/issues/356
let mut list = Vec::<MediaDeviceInfo>::new();

for (device, kind, num_channels) in input_devices.chain(output_devices) {
let mut index = 0;

loop {
let device_id = crate::media_devices::DeviceId::as_string(
kind,
"cpal".to_string(),
device.name().unwrap(),
Box::new(device),
)
})
.collect()
num_channels,
index,
);

if !list.iter().any(|d| d.device_id() == device_id) {
let device = MediaDeviceInfo::new(
device_id,
None,
kind,
device.name().unwrap(),
Box::new(device),
);

list.push(device);
break;
} else {
index += 1;
}
}
}

list
}
}

Expand Down
75 changes: 38 additions & 37 deletions src/io/cubeb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,46 +394,47 @@ impl AudioBackendManager for CubebBackend {
where
Self: Sized,
{
let mut index = 0;
let context = Context::init(None, None).unwrap();

let mut inputs: Vec<MediaDeviceInfo> = Context::init(None, None)
.unwrap()
.enumerate_devices(DeviceType::INPUT)
.unwrap()
.iter()
.map(|d| {
index += 1;

MediaDeviceInfo::new(
format!("{}", index),
d.group_id().map(str::to_string),
MediaDeviceInfoKind::AudioInput,
d.friendly_name().unwrap().into(),
Box::new(d.devid()),
)
})
.collect();
let inputs = context.enumerate_devices(DeviceType::INPUT).unwrap();
let input_devices = inputs.iter().map(|d| (d, MediaDeviceInfoKind::AudioInput));

let mut outputs: Vec<MediaDeviceInfo> = Context::init(None, None)
.unwrap()
.enumerate_devices(DeviceType::OUTPUT)
.unwrap()
let outputs = context.enumerate_devices(DeviceType::OUTPUT).unwrap();
let output_devices = outputs
.iter()
.map(|d| {
index += 1;

MediaDeviceInfo::new(
format!("{}", index),
d.group_id().map(str::to_string),
MediaDeviceInfoKind::AudioOutput,
d.friendly_name().unwrap().into(),
Box::new(d.devid()),
)
})
.collect();

inputs.append(&mut outputs);
.map(|d| (d, MediaDeviceInfoKind::AudioOutput));

let mut list = Vec::<MediaDeviceInfo>::new();

for (device, kind) in input_devices.chain(output_devices) {
let mut index = 0;

loop {
let device_id = crate::media_devices::DeviceId::as_string(
kind,
"cubeb".to_string(),
device.friendly_name().unwrap().into(),
device.max_channels().try_into().unwrap(),
index,
);

if !list.iter().any(|d| d.device_id() == device_id) {
let device = MediaDeviceInfo::new(
device_id,
device.group_id().map(str::to_string),
kind,
device.friendly_name().unwrap().into(),
Box::new(device.devid()),
);

list.push(device);
break;
} else {
index += 1;
}
}
}

inputs
list
}
}
38 changes: 37 additions & 1 deletion src/media_devices/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
//!
//! <https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices>
use rustc_hash::FxHasher;
use std::hash::{Hash, Hasher};

use crate::context::{AudioContextLatencyCategory, AudioContextOptions};
use crate::media_streams::MediaStream;

Expand All @@ -24,8 +27,41 @@ pub fn enumerate_devices_sync() -> Vec<MediaDeviceInfo> {
crate::io::enumerate_devices_sync()
}

// Internal struct to derive a stable id for a given input / output device
// cf. https://github.com/orottier/web-audio-api-rs/issues/356
#[derive(Hash)]
pub(crate) struct DeviceId {
kind: MediaDeviceInfoKind,
host: String,
device_name: String,
num_channels: u16,
index: u8,
}

impl DeviceId {
pub(crate) fn as_string(
kind: MediaDeviceInfoKind,
host: String,
device_name: String,
num_channels: u16,
index: u8,
) -> String {
let device_info = Self {
kind,
host,
device_name,
num_channels,
index,
};

let mut hasher = FxHasher::default();
device_info.hash(&mut hasher);
format!("{}", hasher.finish())
}
}

/// Describes input/output type of a media device
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum MediaDeviceInfoKind {
VideoInput,
AudioInput,
Expand Down

0 comments on commit 0f5250c

Please sign in to comment.