diff --git a/src/lib.rs b/src/lib.rs index f3c6c22..c1d8dac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,14 +107,37 @@ impl Endpoint { /// Number of channels. 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)] pub struct SamplesRate(pub u32); /// Describes a format. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Format { - pub channels: ChannelsCount, + pub channels: Vec, pub samples_rate: SamplesRate, pub data_type: SampleFormat, } diff --git a/src/wasapi/mod.rs b/src/wasapi/mod.rs index b5035d2..21c28a3 100644 --- a/src/wasapi/mod.rs +++ b/src/wasapi/mod.rs @@ -9,6 +9,7 @@ use std::mem; use Format; use FormatsEnumerationError; +use ChannelPosition; use SamplesRate; use SampleFormat; @@ -18,6 +19,26 @@ pub use self::voice::{Voice, Buffer}; pub type SupportedFormatsIterator = OptionIntoIter; +// 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 enumerate; mod voice; @@ -119,21 +140,57 @@ impl Endpoint { }; let format = { - let data_type = match (*format_ptr).wFormatTag { - winapi::WAVE_FORMAT_PCM => SampleFormat::I16, + let (channels, data_type) = match (*format_ptr).wFormatTag { + winapi::WAVE_FORMAT_PCM => { + ( + vec![ChannelPosition::FrontLeft, ChannelPosition::FrontRight], + SampleFormat::I16 + ) + }, winapi::WAVE_FORMAT_EXTENSIBLE => { 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_PCM => SampleFormat::I16, g => panic!("Unknown SubFormat GUID returned by GetMixFormat: {:?}", g) - } + }; + + (channels, format) }, + f => panic!("Unknown data format returned by GetMixFormat: {:?}", f) }; Format { - channels: (*format_ptr).nChannels, + channels: channels, samples_rate: SamplesRate((*format_ptr).nSamplesPerSec), data_type: data_type, } diff --git a/src/wasapi/voice.rs b/src/wasapi/voice.rs index 1cd3154..f8abb04 100644 --- a/src/wasapi/voice.rs +++ b/src/wasapi/voice.rs @@ -11,6 +11,7 @@ use std::ptr; use std::marker::PhantomData; use CreationError; +use ChannelPosition; use Format; use SampleFormat; @@ -52,12 +53,12 @@ impl Voice { SampleFormat::F32 => winapi::WAVE_FORMAT_EXTENSIBLE, 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, - nAvgBytesPerSec: format.channels as winapi::DWORD * + nAvgBytesPerSec: format.channels.len() as winapi::DWORD * format.samples_rate.0 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, wBitsPerSample: 8 * format.data_type.get_sample_size() as winapi::WORD, cbSize: match format.data_type { @@ -68,7 +69,40 @@ impl Voice { }, }, 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 { SampleFormat::I16 => winapi::KSDATAFORMAT_SUBTYPE_PCM, SampleFormat::F32 => winapi::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,