Skip to content

Commit 94212ad

Browse files
committed
fix: improve device id, cf. #356
1 parent 00a2606 commit 94212ad

File tree

4 files changed

+102
-65
lines changed

4 files changed

+102
-65
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ repos:
6464
--locked,
6565
--workspace,
6666
--features,
67-
cubeb,
67+
cpal,
6868
--all-targets,
6969
--,
7070
-D,
@@ -83,7 +83,7 @@ repos:
8383
--locked,
8484
--workspace,
8585
--features,
86-
cubeb,
86+
cpal,
8787
--no-deps,
8888
--document-private-items,
8989
]

src/io/cpal.rs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,17 @@ use cpal::{
88
BuildStreamError, Device, OutputCallbackInfo, SampleFormat, Stream, StreamConfig,
99
SupportedBufferSize,
1010
};
11+
use crossbeam_channel::Receiver;
1112

1213
use super::{AudioBackendManager, RenderThreadInit};
1314

1415
use crate::buffer::AudioBuffer;
1516
use crate::context::AudioContextOptions;
1617
use crate::io::microphone::MicrophoneRender;
17-
use crate::media_devices::{MediaDeviceInfo, MediaDeviceInfoKind};
18+
use crate::media_devices::{create_device_id, MediaDeviceInfo, MediaDeviceInfoKind};
1819
use crate::render::RenderThread;
1920
use crate::{AtomicF64, MAX_CHANNELS};
2021

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

406-
let input_devices = host
407-
.input_devices()
408-
.unwrap()
409-
.map(|d| (d, MediaDeviceInfoKind::AudioInput));
410-
411-
let output_devices = host
412-
.output_devices()
413-
.unwrap()
414-
.map(|d| (d, MediaDeviceInfoKind::AudioOutput));
415-
416-
input_devices
417-
.chain(output_devices)
418-
.enumerate()
419-
.map(|(index, (device, kind))| {
420-
MediaDeviceInfo::new(
421-
(index + 1).to_string(),
422-
None,
423-
kind,
405+
let input_devices = host.input_devices().unwrap().map(|d| {
406+
let num_channels = d.default_input_config().unwrap().channels();
407+
(d, MediaDeviceInfoKind::AudioInput, num_channels)
408+
});
409+
410+
let output_devices = host.output_devices().unwrap().map(|d| {
411+
let num_channels = d.default_output_config().unwrap().channels();
412+
(d, MediaDeviceInfoKind::AudioOutput, num_channels)
413+
});
414+
415+
// cf. https://github.com/orottier/web-audio-api-rs/issues/356
416+
let mut list = Vec::<MediaDeviceInfo>::new();
417+
418+
for (device, kind, num_channels) in input_devices.chain(output_devices) {
419+
let mut index = 0;
420+
421+
loop {
422+
let device_id = create_device_id(
423+
kind as u8,
424+
"cpal".to_string(),
424425
device.name().unwrap(),
425-
Box::new(device),
426-
)
427-
})
428-
.collect()
426+
num_channels,
427+
index,
428+
);
429+
430+
if !list.iter().any(|d| d.device_id() == device_id) {
431+
let device = MediaDeviceInfo::new(
432+
device_id,
433+
None,
434+
kind,
435+
device.name().unwrap(),
436+
Box::new(device),
437+
);
438+
439+
list.push(device);
440+
break;
441+
} else {
442+
index += 1;
443+
}
444+
}
445+
}
446+
447+
list
429448
}
430449
}
431450

src/io/cubeb.rs

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::{AudioBackendManager, RenderThreadInit};
55
use crate::buffer::AudioBuffer;
66
use crate::context::AudioContextOptions;
77
use crate::io::microphone::MicrophoneRender;
8-
use crate::media_devices::{MediaDeviceInfo, MediaDeviceInfoKind};
8+
use crate::media_devices::{create_device_id, MediaDeviceInfo, MediaDeviceInfoKind};
99
use crate::render::RenderThread;
1010
use crate::{MAX_CHANNELS, RENDER_QUANTUM_SIZE};
1111

@@ -394,46 +394,47 @@ impl AudioBackendManager for CubebBackend {
394394
where
395395
Self: Sized,
396396
{
397-
let mut index = 0;
397+
let context = Context::init(None, None).unwrap();
398398

399-
let mut inputs: Vec<MediaDeviceInfo> = Context::init(None, None)
400-
.unwrap()
401-
.enumerate_devices(DeviceType::INPUT)
402-
.unwrap()
403-
.iter()
404-
.map(|d| {
405-
index += 1;
406-
407-
MediaDeviceInfo::new(
408-
format!("{}", index),
409-
d.group_id().map(str::to_string),
410-
MediaDeviceInfoKind::AudioInput,
411-
d.friendly_name().unwrap().into(),
412-
Box::new(d.devid()),
413-
)
414-
})
415-
.collect();
399+
let inputs = context.enumerate_devices(DeviceType::INPUT).unwrap();
400+
let input_devices = inputs.iter().map(|d| (d, MediaDeviceInfoKind::AudioInput));
416401

417-
let mut outputs: Vec<MediaDeviceInfo> = Context::init(None, None)
418-
.unwrap()
419-
.enumerate_devices(DeviceType::OUTPUT)
420-
.unwrap()
402+
let outputs = context.enumerate_devices(DeviceType::OUTPUT).unwrap();
403+
let output_devices = outputs
421404
.iter()
422-
.map(|d| {
423-
index += 1;
424-
425-
MediaDeviceInfo::new(
426-
format!("{}", index),
427-
d.group_id().map(str::to_string),
428-
MediaDeviceInfoKind::AudioOutput,
429-
d.friendly_name().unwrap().into(),
430-
Box::new(d.devid()),
431-
)
432-
})
433-
.collect();
434-
435-
inputs.append(&mut outputs);
405+
.map(|d| (d, MediaDeviceInfoKind::AudioOutput));
406+
407+
let mut list = Vec::<MediaDeviceInfo>::new();
408+
409+
for (device, kind) in input_devices.chain(output_devices) {
410+
let mut index = 0;
411+
412+
loop {
413+
let device_id = create_device_id(
414+
kind as u8,
415+
"cubeb".to_string(),
416+
device.friendly_name().unwrap().into(),
417+
device.max_channels().try_into().unwrap(),
418+
index,
419+
);
420+
421+
if !list.iter().any(|d| d.device_id() == device_id) {
422+
let device = MediaDeviceInfo::new(
423+
device_id,
424+
device.group_id().map(str::to_string),
425+
kind,
426+
device.friendly_name().unwrap().into(),
427+
Box::new(device.devid()),
428+
);
429+
430+
list.push(device);
431+
break;
432+
} else {
433+
index += 1;
434+
}
435+
}
436+
}
436437

437-
inputs
438+
list
438439
}
439440
}

src/media_devices/mod.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
//!
55
//! <https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices>
66
7+
use std::hash::Hasher;
8+
9+
use rustc_hash::FxHasher;
10+
711
use crate::context::{AudioContextLatencyCategory, AudioContextOptions};
812
use crate::media_streams::MediaStream;
913

@@ -32,6 +36,19 @@ pub enum MediaDeviceInfoKind {
3236
AudioOutput,
3337
}
3438

39+
pub(crate) fn create_device_id(
40+
kind: u8,
41+
host: String,
42+
device_name: String,
43+
num_channels: u16,
44+
index: u8,
45+
) -> String {
46+
let mut hasher = FxHasher::default();
47+
let id_string = format!("{}{}{}{}{}", kind, host, device_name, num_channels, index);
48+
hasher.write(id_string.as_bytes());
49+
format!("{}", hasher.finish())
50+
}
51+
3552
/// Describes a single media input or output device
3653
///
3754
/// Call [`enumerate_devices_sync`] to obtain a list of devices for your hardware.

0 commit comments

Comments
 (0)