Handle channels positionning
This commit is contained in:
parent
0960f3c37d
commit
48282a068d
27
src/lib.rs
27
src/lib.rs
|
@ -107,14 +107,37 @@ impl Endpoint {
|
||||||
/// Number of channels.
|
/// Number of channels.
|
||||||
pub type ChannelsCount = u16;
|
pub type ChannelsCount = u16;
|
||||||
|
|
||||||
|
/// Possible position of a channel.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ChannelPosition {
|
||||||
|
FrontLeft,
|
||||||
|
FrontRight,
|
||||||
|
FrontCenter,
|
||||||
|
LowFrequency,
|
||||||
|
BackLeft,
|
||||||
|
BackRight,
|
||||||
|
FrontLeftOfCenter,
|
||||||
|
FrontRightOfCenter,
|
||||||
|
BackCenter,
|
||||||
|
SideLeft,
|
||||||
|
SideRight,
|
||||||
|
TopCenter,
|
||||||
|
TopFrontLeft,
|
||||||
|
TopFrontCenter,
|
||||||
|
TopFrontRight,
|
||||||
|
TopBackLeft,
|
||||||
|
TopBackCenter,
|
||||||
|
TopBackRight,
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct SamplesRate(pub u32);
|
pub struct SamplesRate(pub u32);
|
||||||
|
|
||||||
/// Describes a format.
|
/// Describes a format.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Format {
|
pub struct Format {
|
||||||
pub channels: ChannelsCount,
|
pub channels: Vec<ChannelPosition>,
|
||||||
pub samples_rate: SamplesRate,
|
pub samples_rate: SamplesRate,
|
||||||
pub data_type: SampleFormat,
|
pub data_type: SampleFormat,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use std::mem;
|
||||||
|
|
||||||
use Format;
|
use Format;
|
||||||
use FormatsEnumerationError;
|
use FormatsEnumerationError;
|
||||||
|
use ChannelPosition;
|
||||||
use SamplesRate;
|
use SamplesRate;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
|
|
||||||
|
@ -18,6 +19,26 @@ pub use self::voice::{Voice, Buffer};
|
||||||
|
|
||||||
pub type SupportedFormatsIterator = OptionIntoIter<Format>;
|
pub type SupportedFormatsIterator = OptionIntoIter<Format>;
|
||||||
|
|
||||||
|
// TODO: these constants should be moved to winapi
|
||||||
|
const SPEAKER_FRONT_LEFT: winapi::DWORD = 0x1;
|
||||||
|
const SPEAKER_FRONT_RIGHT: winapi::DWORD = 0x2;
|
||||||
|
const SPEAKER_FRONT_CENTER: winapi::DWORD = 0x4;
|
||||||
|
const SPEAKER_LOW_FREQUENCY: winapi::DWORD = 0x8;
|
||||||
|
const SPEAKER_BACK_LEFT: winapi::DWORD = 0x10;
|
||||||
|
const SPEAKER_BACK_RIGHT: winapi::DWORD = 0x20;
|
||||||
|
const SPEAKER_FRONT_LEFT_OF_CENTER: winapi::DWORD = 0x40;
|
||||||
|
const SPEAKER_FRONT_RIGHT_OF_CENTER: winapi::DWORD = 0x80;
|
||||||
|
const SPEAKER_BACK_CENTER: winapi::DWORD = 0x100;
|
||||||
|
const SPEAKER_SIDE_LEFT: winapi::DWORD = 0x200;
|
||||||
|
const SPEAKER_SIDE_RIGHT: winapi::DWORD = 0x400;
|
||||||
|
const SPEAKER_TOP_CENTER: winapi::DWORD = 0x800;
|
||||||
|
const SPEAKER_TOP_FRONT_LEFT: winapi::DWORD = 0x1000;
|
||||||
|
const SPEAKER_TOP_FRONT_CENTER: winapi::DWORD = 0x2000;
|
||||||
|
const SPEAKER_TOP_FRONT_RIGHT: winapi::DWORD = 0x4000;
|
||||||
|
const SPEAKER_TOP_BACK_LEFT: winapi::DWORD = 0x8000;
|
||||||
|
const SPEAKER_TOP_BACK_CENTER: winapi::DWORD = 0x10000;
|
||||||
|
const SPEAKER_TOP_BACK_RIGHT: winapi::DWORD = 0x20000;
|
||||||
|
|
||||||
mod com;
|
mod com;
|
||||||
mod enumerate;
|
mod enumerate;
|
||||||
mod voice;
|
mod voice;
|
||||||
|
@ -119,21 +140,57 @@ impl Endpoint {
|
||||||
};
|
};
|
||||||
|
|
||||||
let format = {
|
let format = {
|
||||||
let data_type = match (*format_ptr).wFormatTag {
|
let (channels, data_type) = match (*format_ptr).wFormatTag {
|
||||||
winapi::WAVE_FORMAT_PCM => SampleFormat::I16,
|
winapi::WAVE_FORMAT_PCM => {
|
||||||
|
(
|
||||||
|
vec![ChannelPosition::FrontLeft, ChannelPosition::FrontRight],
|
||||||
|
SampleFormat::I16
|
||||||
|
)
|
||||||
|
},
|
||||||
winapi::WAVE_FORMAT_EXTENSIBLE => {
|
winapi::WAVE_FORMAT_EXTENSIBLE => {
|
||||||
let format_ptr = format_ptr as *const winapi::WAVEFORMATEXTENSIBLE;
|
let format_ptr = format_ptr as *const winapi::WAVEFORMATEXTENSIBLE;
|
||||||
match (*format_ptr).SubFormat {
|
|
||||||
|
let channels = {
|
||||||
|
let mut channels = Vec::new();
|
||||||
|
|
||||||
|
let mask = (*format_ptr).dwChannelMask;
|
||||||
|
if (mask & SPEAKER_FRONT_LEFT) != 0 { channels.push(ChannelPosition::FrontLeft); }
|
||||||
|
if (mask & SPEAKER_FRONT_RIGHT) != 0 { channels.push(ChannelPosition::FrontRight); }
|
||||||
|
if (mask & SPEAKER_FRONT_CENTER) != 0 { channels.push(ChannelPosition::FrontCenter); }
|
||||||
|
if (mask & SPEAKER_LOW_FREQUENCY) != 0 { channels.push(ChannelPosition::LowFrequency); }
|
||||||
|
if (mask & SPEAKER_BACK_LEFT) != 0 { channels.push(ChannelPosition::BackLeft); }
|
||||||
|
if (mask & SPEAKER_BACK_RIGHT) != 0 { channels.push(ChannelPosition::BackRight); }
|
||||||
|
if (mask & SPEAKER_FRONT_LEFT_OF_CENTER) != 0 { channels.push(ChannelPosition::FrontLeftOfCenter); }
|
||||||
|
if (mask & SPEAKER_FRONT_RIGHT_OF_CENTER) != 0 { channels.push(ChannelPosition::FrontRightOfCenter); }
|
||||||
|
if (mask & SPEAKER_BACK_CENTER) != 0 { channels.push(ChannelPosition::BackCenter); }
|
||||||
|
if (mask & SPEAKER_SIDE_LEFT) != 0 { channels.push(ChannelPosition::SideLeft); }
|
||||||
|
if (mask & SPEAKER_SIDE_RIGHT) != 0 { channels.push(ChannelPosition::SideRight); }
|
||||||
|
if (mask & SPEAKER_TOP_CENTER) != 0 { channels.push(ChannelPosition::TopCenter); }
|
||||||
|
if (mask & SPEAKER_TOP_FRONT_LEFT) != 0 { channels.push(ChannelPosition::TopFrontLeft); }
|
||||||
|
if (mask & SPEAKER_TOP_FRONT_CENTER) != 0 { channels.push(ChannelPosition::TopFrontCenter); }
|
||||||
|
if (mask & SPEAKER_TOP_FRONT_RIGHT) != 0 { channels.push(ChannelPosition::TopFrontRight); }
|
||||||
|
if (mask & SPEAKER_TOP_BACK_LEFT) != 0 { channels.push(ChannelPosition::TopBackLeft); }
|
||||||
|
if (mask & SPEAKER_TOP_BACK_CENTER) != 0 { channels.push(ChannelPosition::TopBackCenter); }
|
||||||
|
if (mask & SPEAKER_TOP_BACK_RIGHT) != 0 { channels.push(ChannelPosition::TopBackRight); }
|
||||||
|
|
||||||
|
assert_eq!((*format_ptr).Format.nChannels as usize, channels.len());
|
||||||
|
channels
|
||||||
|
};
|
||||||
|
|
||||||
|
let format = match (*format_ptr).SubFormat {
|
||||||
winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT => SampleFormat::F32,
|
winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT => SampleFormat::F32,
|
||||||
winapi::KSDATAFORMAT_SUBTYPE_PCM => SampleFormat::I16,
|
winapi::KSDATAFORMAT_SUBTYPE_PCM => SampleFormat::I16,
|
||||||
g => panic!("Unknown SubFormat GUID returned by GetMixFormat: {:?}", g)
|
g => panic!("Unknown SubFormat GUID returned by GetMixFormat: {:?}", g)
|
||||||
}
|
};
|
||||||
|
|
||||||
|
(channels, format)
|
||||||
},
|
},
|
||||||
|
|
||||||
f => panic!("Unknown data format returned by GetMixFormat: {:?}", f)
|
f => panic!("Unknown data format returned by GetMixFormat: {:?}", f)
|
||||||
};
|
};
|
||||||
|
|
||||||
Format {
|
Format {
|
||||||
channels: (*format_ptr).nChannels,
|
channels: channels,
|
||||||
samples_rate: SamplesRate((*format_ptr).nSamplesPerSec),
|
samples_rate: SamplesRate((*format_ptr).nSamplesPerSec),
|
||||||
data_type: data_type,
|
data_type: data_type,
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::ptr;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use CreationError;
|
use CreationError;
|
||||||
|
use ChannelPosition;
|
||||||
use Format;
|
use Format;
|
||||||
use SampleFormat;
|
use SampleFormat;
|
||||||
|
|
||||||
|
@ -52,12 +53,12 @@ impl Voice {
|
||||||
SampleFormat::F32 => winapi::WAVE_FORMAT_EXTENSIBLE,
|
SampleFormat::F32 => winapi::WAVE_FORMAT_EXTENSIBLE,
|
||||||
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
SampleFormat::U16 => return Err(CreationError::FormatNotSupported),
|
||||||
},
|
},
|
||||||
nChannels: format.channels as winapi::WORD,
|
nChannels: format.channels.len() as winapi::WORD,
|
||||||
nSamplesPerSec: format.samples_rate.0 as winapi::DWORD,
|
nSamplesPerSec: format.samples_rate.0 as winapi::DWORD,
|
||||||
nAvgBytesPerSec: format.channels as winapi::DWORD *
|
nAvgBytesPerSec: format.channels.len() as winapi::DWORD *
|
||||||
format.samples_rate.0 as winapi::DWORD *
|
format.samples_rate.0 as winapi::DWORD *
|
||||||
format.data_type.get_sample_size() as winapi::DWORD,
|
format.data_type.get_sample_size() as winapi::DWORD,
|
||||||
nBlockAlign: format.channels as winapi::WORD *
|
nBlockAlign: format.channels.len() as winapi::WORD *
|
||||||
format.data_type.get_sample_size() as winapi::WORD,
|
format.data_type.get_sample_size() as winapi::WORD,
|
||||||
wBitsPerSample: 8 * format.data_type.get_sample_size() as winapi::WORD,
|
wBitsPerSample: 8 * format.data_type.get_sample_size() as winapi::WORD,
|
||||||
cbSize: match format.data_type {
|
cbSize: match format.data_type {
|
||||||
|
@ -68,7 +69,40 @@ impl Voice {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Samples: 8 * format.data_type.get_sample_size() as winapi::WORD,
|
Samples: 8 * format.data_type.get_sample_size() as winapi::WORD,
|
||||||
dwChannelMask: 3, // LEFT | RIGHT
|
dwChannelMask: {
|
||||||
|
let mut mask = 0;
|
||||||
|
for &channel in format.channels.iter() {
|
||||||
|
let raw_value = match channel {
|
||||||
|
ChannelPosition::FrontLeft => super::SPEAKER_FRONT_LEFT,
|
||||||
|
ChannelPosition::FrontRight => super::SPEAKER_FRONT_RIGHT,
|
||||||
|
ChannelPosition::FrontCenter => super::SPEAKER_FRONT_CENTER,
|
||||||
|
ChannelPosition::LowFrequency => super::SPEAKER_LOW_FREQUENCY,
|
||||||
|
ChannelPosition::BackLeft => super::SPEAKER_BACK_LEFT,
|
||||||
|
ChannelPosition::BackRight => super::SPEAKER_BACK_RIGHT,
|
||||||
|
ChannelPosition::FrontLeftOfCenter => super::SPEAKER_FRONT_LEFT_OF_CENTER,
|
||||||
|
ChannelPosition::FrontRightOfCenter => super::SPEAKER_FRONT_RIGHT_OF_CENTER,
|
||||||
|
ChannelPosition::BackCenter => super::SPEAKER_BACK_CENTER,
|
||||||
|
ChannelPosition::SideLeft => super::SPEAKER_SIDE_LEFT,
|
||||||
|
ChannelPosition::SideRight => super::SPEAKER_SIDE_RIGHT,
|
||||||
|
ChannelPosition::TopCenter => super::SPEAKER_TOP_CENTER,
|
||||||
|
ChannelPosition::TopFrontLeft => super::SPEAKER_TOP_FRONT_LEFT,
|
||||||
|
ChannelPosition::TopFrontCenter => super::SPEAKER_TOP_FRONT_CENTER,
|
||||||
|
ChannelPosition::TopFrontRight => super::SPEAKER_TOP_FRONT_RIGHT,
|
||||||
|
ChannelPosition::TopBackLeft => super::SPEAKER_TOP_BACK_LEFT,
|
||||||
|
ChannelPosition::TopBackCenter => super::SPEAKER_TOP_BACK_CENTER,
|
||||||
|
ChannelPosition::TopBackRight => super::SPEAKER_TOP_BACK_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
// channels must be in the right order
|
||||||
|
if raw_value <= mask {
|
||||||
|
return Err(CreationError::FormatNotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = mask | raw_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask
|
||||||
|
},
|
||||||
SubFormat: match format.data_type {
|
SubFormat: match format.data_type {
|
||||||
SampleFormat::I16 => winapi::KSDATAFORMAT_SUBTYPE_PCM,
|
SampleFormat::I16 => winapi::KSDATAFORMAT_SUBTYPE_PCM,
|
||||||
SampleFormat::F32 => winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
|
SampleFormat::F32 => winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
|
||||||
|
|
Loading…
Reference in New Issue