use std::collections::{BTreeSet, HashMap};
use crate::{AudioClient, DataRange, Device, SampleType, WasapiRes, WaveFormat, covered_by_any};
use windows::Win32::Media::KernelStreaming::WAVE_FORMAT_EXTENSIBLE;
pub const DEFAULT_MAX_CHANNELS: usize = 32;
const FAMILY_48_RATES: &[usize] = &[48000, 96000, 192000, 384000, 768000];
const FAMILY_44_RATES: &[usize] = &[44100, 88200, 176400, 352800, 705600];
const REMAINING_RATES: &[usize] = &[
24000, 12000, 6000, 22050, 11025, 5512, 16000, 8000, 32000, 64000,
];
const ALL_RATES: &[usize] = &[
5512, 6000, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
176400, 192000, 352800, 384000, 705600, 768000,
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Candidate {
storebits: usize,
validbits: usize,
sample_type: SampleType,
}
impl Candidate {
const fn new(storebits: usize, validbits: usize, sample_type: SampleType) -> Self {
Candidate {
storebits,
validbits,
sample_type,
}
}
}
const CANDIDATE_FORMATS: &[Candidate] = &[
Candidate::new(16, 16, SampleType::Int),
Candidate::new(24, 24, SampleType::Int),
Candidate::new(32, 24, SampleType::Int),
Candidate::new(32, 32, SampleType::Int),
Candidate::new(32, 32, SampleType::Float),
];
type ChannelMaskMap = HashMap<usize, u32>;
trait FormatChecker {
fn check_exclusive(&self, wave_fmt: &WaveFormat) -> WasapiRes<WaveFormat>;
}
impl FormatChecker for AudioClient {
fn check_exclusive(&self, wave_fmt: &WaveFormat) -> WasapiRes<WaveFormat> {
self.is_supported_exclusive_with_quirks(wave_fmt)
}
}
pub struct CapabilityProbe {
client: AudioClient,
channel_masks: ChannelMaskMap,
data_ranges: Vec<DataRange>,
}
impl CapabilityProbe {
pub fn new(device: &Device) -> WasapiRes<Self> {
let client = device.get_iaudioclient()?;
let data_ranges = device.get_data_ranges().unwrap_or_else(|err| {
debug!("Could not read the data ranges of the device, {err}");
Vec::new()
});
Ok(CapabilityProbe {
client,
channel_masks: ChannelMaskMap::new(),
data_ranges,
})
}
pub fn data_ranges(&self) -> &[DataRange] {
&self.data_ranges
}
pub fn set_data_ranges(&mut self, data_ranges: Vec<DataRange>) {
self.data_ranges = data_ranges;
}
pub fn supported_formats(&mut self, samplerate: usize, channels: usize) -> Vec<WaveFormat> {
self.probing()
.formats(samplerate, channels, CANDIDATE_FORMATS)
.into_iter()
.map(|(_, wave_fmt)| wave_fmt)
.collect()
}
pub fn supported_formats_at_rate(&mut self, samplerate: usize) -> Vec<WaveFormat> {
let mut probing = self.probing();
let ceiling = probing.channel_ceiling();
probing
.rate(samplerate, 1..=ceiling, CANDIDATE_FORMATS, false)
.formats
}
pub fn supported_formats_all_rates(&mut self) -> Vec<WaveFormat> {
let mut probing = self.probing();
let ceiling = probing.channel_ceiling();
probing.all_rates(ceiling)
}
fn probing(&mut self) -> Probing<'_, AudioClient> {
Probing {
checker: &self.client,
channel_masks: &mut self.channel_masks,
data_ranges: &self.data_ranges,
}
}
}
struct Probing<'a, C: FormatChecker> {
checker: &'a C,
channel_masks: &'a mut ChannelMaskMap,
data_ranges: &'a [DataRange],
}
impl<C: FormatChecker> Probing<'_, C> {
fn formats(
&mut self,
samplerate: usize,
channels: usize,
candidates: &[Candidate],
) -> Vec<(Candidate, WaveFormat)> {
let mut supported = Vec::new();
if channels == 0 {
return supported;
}
let mut preferred_mask = self.channel_masks.get(&channels).copied();
if let Some(mask) = preferred_mask {
trace!("Probing {samplerate} Hz, {channels} ch using cached channel mask {mask:#010x}");
}
for candidate in candidates {
let requested = WaveFormat::new(
candidate.storebits,
candidate.validbits,
&candidate.sample_type,
samplerate,
channels,
preferred_mask,
);
if !covered_by_any(self.data_ranges, &requested) {
trace!(
"Skipping {samplerate} Hz, {channels} ch, format {candidate:?}, the driver declares no range for it"
);
continue;
}
let Ok(accepted) = self.checker.check_exclusive(&requested) else {
trace!("Unsupported {samplerate} Hz, {channels} ch, format {candidate:?}");
continue;
};
trace!("Supported {samplerate} Hz, {channels} ch, format {candidate:?}");
if accepted.wave_fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE as u16 {
let mask = accepted.get_dwchannelmask();
if self.channel_masks.insert(channels, mask) != Some(mask) {
debug!("Channel count {channels} will use channel mask {mask:#010x}");
}
preferred_mask = Some(mask);
supported.push((*candidate, accepted));
} else {
trace!("Accepted as WAVEFORMATEX, reporting the WAVEFORMATEXTENSIBLE form");
supported.push((*candidate, requested));
}
}
supported
}
fn rate<I>(
&mut self,
samplerate: usize,
channel_counts: I,
candidates: &[Candidate],
narrow: bool,
) -> RateProbe
where
I: IntoIterator<Item = usize>,
{
trace!("Probing {samplerate} Hz using sample formats {candidates:?}");
let mut result = RateProbe {
formats: Vec::new(),
channel_counts: BTreeSet::new(),
supported_candidates: Vec::new(),
};
let mut narrowed: Option<Vec<Candidate>> = None;
for channels in channel_counts {
let active = narrowed.as_deref().unwrap_or(candidates);
let supported = self.formats(samplerate, channels, active);
if supported.is_empty() {
trace!("No supported formats at {samplerate} Hz, {channels} ch");
continue;
}
let found: Vec<Candidate> = supported.iter().map(|(candidate, _)| *candidate).collect();
debug!("Found support at {samplerate} Hz, {channels} ch with formats {found:?}");
if narrow && narrowed.is_none() && found.len() < candidates.len() {
debug!(
"Narrowing the formats for the rest of the {samplerate} Hz sweep to {found:?}"
);
narrowed = Some(found.clone());
}
for candidate in found {
if !result.supported_candidates.contains(&candidate) {
result.supported_candidates.push(candidate);
}
}
result.channel_counts.insert(channels);
result
.formats
.extend(supported.into_iter().map(|(_, wave_fmt)| wave_fmt));
}
result
}
fn channel_ceiling(&self) -> usize {
self.data_ranges
.iter()
.map(|range| range.max_channels as usize)
.max()
.unwrap_or(DEFAULT_MAX_CHANNELS)
}
fn all_rates(&mut self, ceiling: usize) -> Vec<WaveFormat> {
if !self.data_ranges.is_empty() {
return self.all_rates_within_ranges(ceiling);
}
self.all_rates_staged(ceiling)
}
fn all_rates_within_ranges(&mut self, max_channels: usize) -> Vec<WaveFormat> {
let declared_channels = self
.data_ranges
.iter()
.map(|range| range.max_channels as usize)
.max()
.unwrap_or(0);
let ceiling = declared_channels.min(max_channels);
debug!(
"Starting exclusive mode scan with channel ceiling {ceiling}, \
the driver declares at most {declared_channels} channels"
);
let mut formats = Vec::new();
for &rate in ALL_RATES {
let declared = self.data_ranges.iter().any(|range| {
(range.min_samplerate..=range.max_samplerate).contains(&(rate as u32))
});
if !declared {
trace!("Skipping {rate} Hz, the driver declares no range for it");
continue;
}
let result = self.rate(rate, 1..=ceiling, CANDIDATE_FORMATS, false);
formats.extend(result.formats);
}
debug!("The exclusive mode scan found {} formats", formats.len());
formats
}
fn all_rates_staged(&mut self, max_channels: usize) -> Vec<WaveFormat> {
debug!("Starting staged exclusive mode scan with channel ceiling {max_channels}");
let mut formats = Vec::new();
let mut channel_counts = BTreeSet::new();
let mut learned: Option<Vec<Candidate>> = None;
let families = [FAMILY_48_RATES, FAMILY_44_RATES];
let mut channel_limit = 0;
let mut hit = [false; 2];
let mut active = [true; 2];
for step in 0..FAMILY_48_RATES.len().max(FAMILY_44_RATES.len()) {
if !active.iter().any(|is_active| *is_active) {
debug!("Stopping the upward scan, both families are inactive");
break;
}
for (family_nbr, family) in families.iter().enumerate() {
if !active[family_nbr] {
continue;
}
let Some(&rate) = family.get(step) else {
continue;
};
let limit = if channel_limit > 0 {
channel_limit
} else {
max_channels
};
let candidates = learned.as_deref().unwrap_or(CANDIDATE_FORMATS);
let result = self.rate(rate, 1..=limit, candidates, true);
if let Some(&highest) = result.channel_counts.iter().next_back() {
hit[family_nbr] = true;
channel_limit = channel_limit.max(highest);
debug!(
"Rate {rate} Hz gave at most {highest} channels, limit is now {channel_limit}"
);
if learned.is_none() {
debug!(
"Learned the sample formats {:?} from {rate} Hz, reusing them",
result.supported_candidates
);
learned = Some(result.supported_candidates);
}
} else if hit[family_nbr] {
active[family_nbr] = false;
debug!("Stopping at {rate} Hz, this family had a miss after its earlier hits");
}
channel_counts.extend(&result.channel_counts);
formats.extend(result.formats);
}
}
let remaining_counts: Vec<usize> = if channel_counts.is_empty() {
debug!(
"Probing the remaining rates with the full channel range, nothing was found so far"
);
(1..=max_channels).collect()
} else {
debug!("Probing the remaining rates with the channel counts {channel_counts:?}");
channel_counts.iter().copied().collect()
};
for &rate in REMAINING_RATES {
let candidates = learned.as_deref().unwrap_or(CANDIDATE_FORMATS);
let result = self.rate(rate, remaining_counts.iter().copied(), candidates, true);
if learned.is_none() && !result.supported_candidates.is_empty() {
debug!(
"Learned the sample formats {:?} from {rate} Hz, reusing them",
result.supported_candidates
);
learned = Some(result.supported_candidates);
}
formats.extend(result.formats);
}
debug!("The exclusive mode scan found {} formats", formats.len());
formats
}
}
struct RateProbe {
formats: Vec<WaveFormat>,
channel_counts: BTreeSet<usize>,
supported_candidates: Vec<Candidate>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{WasapiError, make_channelmasks};
use std::cell::RefCell;
fn probing<'a>(
device: &'a FakeDevice,
channel_masks: &'a mut ChannelMaskMap,
) -> Probing<'a, FakeDevice> {
Probing {
checker: device,
channel_masks,
data_ranges: &[],
}
}
fn probing_with<'a>(
device: &'a FakeDevice,
channel_masks: &'a mut ChannelMaskMap,
data_ranges: &'a [DataRange],
) -> Probing<'a, FakeDevice> {
Probing {
checker: device,
channel_masks,
data_ranges,
}
}
fn declared(max_channels: u32, bits: (u32, u32), rates: (u32, u32)) -> DataRange {
DataRange {
max_channels,
min_bits_per_sample: bits.0,
max_bits_per_sample: bits.1,
min_samplerate: rates.0,
max_samplerate: rates.1,
subformat: windows::Win32::Media::KernelStreaming::KSDATAFORMAT_SUBTYPE_PCM,
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Query {
samplerate: usize,
channels: usize,
mask: u32,
candidate: Candidate,
}
struct FakeDevice {
rates: Vec<usize>,
max_channels: usize,
skipped_channels: Vec<usize>,
formats: Vec<Candidate>,
mono_only_formats: Vec<Candidate>,
queries: RefCell<Vec<Query>>,
}
impl FakeDevice {
fn new(rates: &[usize], max_channels: usize, formats: &[Candidate]) -> Self {
FakeDevice {
rates: rates.to_vec(),
max_channels,
skipped_channels: Vec::new(),
formats: formats.to_vec(),
mono_only_formats: Vec::new(),
queries: RefCell::new(Vec::new()),
}
}
fn only_with_one_channel(mut self, formats: &[Candidate]) -> Self {
self.mono_only_formats = formats.to_vec();
self
}
fn without_channels(mut self, channels: &[usize]) -> Self {
self.skipped_channels = channels.to_vec();
self
}
fn accepted_mask(channels: usize) -> u32 {
*make_channelmasks(channels).last().unwrap()
}
fn queries(&self) -> Vec<Query> {
self.queries.borrow().clone()
}
fn queries_for(&self, samplerate: usize, channels: usize) -> Vec<Query> {
self.queries()
.into_iter()
.filter(|q| q.samplerate == samplerate && q.channels == channels)
.collect()
}
}
impl FormatChecker for FakeDevice {
fn check_exclusive(&self, wave_fmt: &WaveFormat) -> WasapiRes<WaveFormat> {
let channels = wave_fmt.get_nchannels() as usize;
let candidate = Candidate::new(
wave_fmt.get_bitspersample() as usize,
wave_fmt.get_validbitspersample() as usize,
wave_fmt.get_subformat()?,
);
let samplerate = wave_fmt.get_samplespersec() as usize;
self.queries.borrow_mut().push(Query {
samplerate,
channels,
mask: wave_fmt.get_dwchannelmask(),
candidate,
});
if !self.rates.contains(&samplerate)
|| channels > self.max_channels
|| self.skipped_channels.contains(&channels)
|| !self.formats.contains(&candidate)
|| (channels > 1 && self.mono_only_formats.contains(&candidate))
{
return Err(WasapiError::UnsupportedFormat);
}
let mut accepted = wave_fmt.clone();
accepted.wave_fmt.dwChannelMask = Self::accepted_mask(channels);
Ok(accepted)
}
}
const S16: Candidate = Candidate::new(16, 16, SampleType::Int);
const S24_3: Candidate = Candidate::new(24, 24, SampleType::Int);
const S32: Candidate = Candidate::new(32, 32, SampleType::Int);
fn describe(wave_fmt: &WaveFormat) -> (u32, u16, u16, u16) {
(
wave_fmt.get_samplespersec(),
wave_fmt.get_nchannels(),
wave_fmt.get_bitspersample(),
wave_fmt.get_validbitspersample(),
)
}
#[test]
fn probe_returns_the_supported_formats() {
let device = FakeDevice::new(&[48000], 2, &[S16, S32]);
let mut masks = ChannelMaskMap::new();
let supported = probing(&device, &mut masks).formats(48000, 2, CANDIDATE_FORMATS);
let found: Vec<Candidate> = supported.iter().map(|(c, _)| *c).collect();
assert_eq!(found, vec![S16, S32]);
assert_eq!(describe(&supported[0].1), (48000, 2, 16, 16));
assert_eq!(describe(&supported[1].1), (48000, 2, 32, 32));
assert_eq!(device.queries().len(), CANDIDATE_FORMATS.len());
}
#[test]
fn probe_returns_nothing_for_unsupported_rates_and_channel_counts() {
let device = FakeDevice::new(&[48000], 2, &[S16]);
let mut masks = ChannelMaskMap::new();
assert!(
probing(&device, &mut masks)
.formats(44100, 2, CANDIDATE_FORMATS)
.is_empty()
);
assert!(
probing(&device, &mut masks)
.formats(48000, 4, CANDIDATE_FORMATS)
.is_empty()
);
assert!(
probing(&device, &mut masks)
.formats(48000, 0, CANDIDATE_FORMATS)
.is_empty()
);
}
#[test]
fn the_accepted_channel_mask_is_cached_and_reused() {
let device = FakeDevice::new(&[48000, 96000], 2, &[S16, S32]);
let mut masks = ChannelMaskMap::new();
let accepted = FakeDevice::accepted_mask(2);
probing(&device, &mut masks).formats(48000, 2, CANDIDATE_FORMATS);
assert_eq!(masks.get(&2), Some(&accepted));
assert_ne!(device.queries_for(48000, 2)[0].mask, accepted);
probing(&device, &mut masks).formats(96000, 2, CANDIDATE_FORMATS);
assert!(
device
.queries_for(96000, 2)
.iter()
.all(|q| q.mask == accepted)
);
}
#[test]
fn the_formats_are_narrowed_after_the_first_channel_count() {
let device = FakeDevice::new(&[48000], 4, &[S32]);
let mut masks = ChannelMaskMap::new();
let result = probing(&device, &mut masks).rate(48000, 1..=4, CANDIDATE_FORMATS, true);
assert_eq!(result.supported_candidates, vec![S32]);
assert_eq!(result.channel_counts, BTreeSet::from([1, 2, 3, 4]));
assert_eq!(device.queries_for(48000, 1).len(), CANDIDATE_FORMATS.len());
for channels in 2..=4 {
let queries = device.queries_for(48000, channels);
assert_eq!(queries.len(), 1);
assert_eq!(queries[0].candidate, S32);
}
}
#[test]
fn without_narrowing_all_formats_are_probed_at_every_channel_count() {
let device = FakeDevice::new(&[48000], 4, &[S16, S32]).only_with_one_channel(&[S32]);
let mut masks = ChannelMaskMap::new();
let result = probing(&device, &mut masks).rate(48000, 1..=4, CANDIDATE_FORMATS, false);
assert_eq!(result.supported_candidates, vec![S16, S32]);
for channels in 1..=4 {
assert_eq!(
device.queries_for(48000, channels).len(),
CANDIDATE_FORMATS.len()
);
}
}
#[test]
fn a_rate_probe_covers_all_channel_counts() {
let device = FakeDevice::new(&[48000], 4, &[S16]).without_channels(&[3]);
let mut masks = ChannelMaskMap::new();
let mut result =
probing(&device, &mut masks).rate(48000, [2, 3, 4], CANDIDATE_FORMATS, true);
result.formats.retain(|fmt| fmt.get_nchannels() == 4);
assert_eq!(result.channel_counts, BTreeSet::from([2, 4]));
assert_eq!(result.formats.len(), 1);
}
#[test]
fn the_full_scan_finds_all_the_supported_combinations() {
let device = FakeDevice::new(&[44100, 48000, 96000, 32000], 2, &[S16, S24_3]);
let mut masks = ChannelMaskMap::new();
let mut found: Vec<(u32, u16, u16, u16)> = probing(&device, &mut masks)
.all_rates(8)
.iter()
.map(describe)
.collect();
found.sort_unstable();
let mut expected = Vec::new();
for rate in [32000, 44100, 48000, 96000] {
for channels in [1, 2] {
expected.push((rate, channels, 16, 16));
expected.push((rate, channels, 24, 24));
}
}
expected.sort_unstable();
assert_eq!(found, expected);
}
#[test]
fn the_full_scan_stops_a_family_after_a_miss() {
let device = FakeDevice::new(&[48000, 96000, 384000], 2, &[S32]);
let mut masks = ChannelMaskMap::new();
let found: Vec<u32> = probing(&device, &mut masks)
.all_rates(8)
.iter()
.map(|fmt| fmt.get_samplespersec())
.collect();
assert!(found.contains(&96000));
assert!(!found.contains(&384000));
assert!(!device.queries().iter().any(|q| q.samplerate == 384000));
assert!(!device.queries().iter().any(|q| q.samplerate == 768000));
assert!(device.queries().iter().any(|q| q.samplerate == 705600));
}
#[test]
fn the_full_scan_limits_the_channel_counts_of_the_later_rates() {
let device = FakeDevice::new(&[48000, 44100, 32000], 2, &[S16]);
let mut masks = ChannelMaskMap::new();
probing(&device, &mut masks).all_rates(8);
assert!(device.queries().iter().any(|q| q.channels == 8));
assert!(
!device
.queries()
.iter()
.any(|q| q.samplerate == 44100 && q.channels > 2)
);
assert!(
!device
.queries()
.iter()
.any(|q| q.samplerate == 32000 && q.channels > 2)
);
}
#[test]
fn the_declared_ranges_keep_the_scan_inside_them() {
let device = FakeDevice::new(&[44100, 48000], 2, &[S16, S32]);
let mut masks = ChannelMaskMap::new();
let ranges = [declared(2, (16, 16), (44100, 48000))];
let found: Vec<(u32, u16, u16, u16)> = probing_with(&device, &mut masks, &ranges)
.all_rates(DEFAULT_MAX_CHANNELS)
.iter()
.map(describe)
.collect();
assert_eq!(
found,
vec![
(44100, 1, 16, 16),
(44100, 2, 16, 16),
(48000, 1, 16, 16),
(48000, 2, 16, 16)
]
);
assert!(
device
.queries()
.iter()
.all(|q| q.channels <= 2 && q.candidate == S16)
);
assert!(
!device
.queries()
.iter()
.any(|q| q.samplerate < 44100 || q.samplerate > 48000)
);
}
#[test]
fn the_declared_ranges_find_a_rate_that_the_staged_scan_misses() {
let device = FakeDevice::new(&[48000, 96000, 384000], 2, &[S32]);
let mut masks = ChannelMaskMap::new();
let staged: Vec<u32> = probing(&device, &mut masks)
.all_rates(8)
.iter()
.map(|fmt| fmt.get_samplespersec())
.collect();
assert!(!staged.contains(&384000));
let device = FakeDevice::new(&[48000, 96000, 384000], 2, &[S32]);
let mut masks = ChannelMaskMap::new();
let ranges = [declared(2, (16, 32), (48000, 384000))];
let bounded: Vec<u32> = probing_with(&device, &mut masks, &ranges)
.all_rates(8)
.iter()
.map(|fmt| fmt.get_samplespersec())
.collect();
assert!(bounded.contains(&384000));
}
#[test]
fn the_declared_ranges_keep_all_the_formats_of_every_channel_count() {
let device = FakeDevice::new(&[48000], 4, &[S16, S32]).only_with_one_channel(&[S32]);
let mut masks = ChannelMaskMap::new();
let ranges = [declared(4, (16, 32), (48000, 48000))];
let found = probing_with(&device, &mut masks, &ranges).all_rates(4);
assert_eq!(describe(&found[0]), (48000, 1, 16, 16));
assert_eq!(describe(&found[1]), (48000, 1, 32, 32));
for channels in 1..=4 {
let queries = device.queries_for(48000, channels);
assert_eq!(queries.len(), 4);
assert!(
queries
.iter()
.all(|q| q.candidate.sample_type == SampleType::Int)
);
}
}
#[test]
fn every_rate_is_in_the_combined_list() {
let mut combined: Vec<usize> = FAMILY_48_RATES
.iter()
.chain(FAMILY_44_RATES)
.chain(REMAINING_RATES)
.copied()
.collect();
combined.sort_unstable();
assert_eq!(combined, ALL_RATES);
}
#[test]
fn the_full_scan_of_a_device_without_support_finds_nothing() {
let device = FakeDevice::new(&[], 0, &[]);
let mut masks = ChannelMaskMap::new();
assert!(probing(&device, &mut masks).all_rates(2).is_empty());
assert!(masks.is_empty());
assert!(
device
.queries()
.iter()
.any(|q| q.samplerate == 32000 && q.channels == 2)
);
}
}