From 48282a068d42ac8c8ad454b4059442f5174e249a Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Sep 2015 11:44:19 +0200 Subject: [PATCH 1/2] Handle channels positionning --- src/lib.rs | 27 ++++++++++++++++-- src/wasapi/mod.rs | 67 +++++++++++++++++++++++++++++++++++++++++---- src/wasapi/voice.rs | 42 +++++++++++++++++++++++++--- 3 files changed, 125 insertions(+), 11 deletions(-) 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, From 9d56c4c6162350c7e43c8d5a71c5c29a61ac7c93 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Thu, 10 Sep 2015 12:00:52 +0200 Subject: [PATCH 2/2] Update the beep example --- examples/beep.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/examples/beep.rs b/examples/beep.rs index 09e3312..83b52b2 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -6,31 +6,28 @@ fn main() { let mut channel = cpal::Voice::new(&endpoint, &format).unwrap(); // Produce a sinusoid of maximum amplitude. - let mut data_source = (0u64..).map(|t| t as f32 * 0.03) + let mut data_source = (0u64..).map(|t| t as f32 * 440.0 * 2.0 * 3.141592 / format.samples_rate.0 as f32) // 440 Hz .map(|t| t.sin()); loop { match channel.append_data(32768) { cpal::UnknownTypeBuffer::U16(mut buffer) => { - for (sample, value) in buffer.chunks_mut(2).zip(&mut data_source) { + for (sample, value) in buffer.chunks_mut(format.channels.len()).zip(&mut data_source) { let value = ((value * 0.5 + 0.5) * std::u16::MAX as f32) as u16; - sample[0] = value; - sample[1] = value; + for out in sample.iter_mut() { *out = value; } } }, cpal::UnknownTypeBuffer::I16(mut buffer) => { - for (sample, value) in buffer.chunks_mut(2).zip(&mut data_source) { + for (sample, value) in buffer.chunks_mut(format.channels.len()).zip(&mut data_source) { let value = (value * std::i16::MAX as f32) as i16; - sample[0] = value; - sample[1] = value; + for out in sample.iter_mut() { *out = value; } } }, cpal::UnknownTypeBuffer::F32(mut buffer) => { - for (sample, value) in buffer.chunks_mut(2).zip(&mut data_source) { - sample[0] = value; - sample[1] = value; + for (sample, value) in buffer.chunks_mut(format.channels.len()).zip(&mut data_source) { + for out in sample.iter_mut() { *out = value; } } }, }