Merge pull request #60 from tomaka/handle-channels-pos

Handle channels positionning
This commit is contained in:
tomaka 2015-09-10 12:41:22 +02:00
commit 74286fcbf0
4 changed files with 132 additions and 21 deletions

View File

@ -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; }
}
},
}

View File

@ -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<ChannelPosition>,
pub samples_rate: SamplesRate,
pub data_type: SampleFormat,
}

View File

@ -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<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 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,
}

View File

@ -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,