use std::{
fmt::{Debug, Formatter},
future::Future,
mem::size_of,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use windows::Win32::Media::Audio::{
AudioCategory_SoundEffects,
XAudio2::XAUDIO2_VOICE_STATE,
XAudio2::{
IXAudio2, IXAudio2MasteringVoice, IXAudio2SourceVoice, XAudio2CreateWithVersionInfo,
XAUDIO2_BUFFER, XAUDIO2_COMMIT_NOW, XAUDIO2_MAX_FREQ_RATIO, XAUDIO2_USE_DEFAULT_PROCESSOR,
XAUDIO2_VOICE_NOSRC,
},
WAVEFORMATEX, WAVE_FORMAT_PCM,
};
#[allow(dead_code)]
pub struct XAudio2OutputStream {
engine: Arc<IXAudio2>,
mastering_voice: Arc<IXAudio2MasteringVoice>,
source_voice: Arc<IXAudio2SourceVoice>,
}
impl XAudio2OutputStream {
pub fn new(sample_rate: u32, num_channels: u32) -> Self {
let mut engine: Option<IXAudio2> = None;
let mut mastering_voice: Option<IXAudio2MasteringVoice> = None;
let mut source_voice: Option<IXAudio2SourceVoice> = None;
unsafe { XAudio2CreateWithVersionInfo(&mut engine, 0, XAUDIO2_USE_DEFAULT_PROCESSOR, 2) }
.expect("Can't create the XAudio engine.");
if let Some(x) = engine.as_ref() {
unsafe {
x.CreateMasteringVoice(
&mut mastering_voice,
num_channels,
sample_rate,
0,
None,
None,
AudioCategory_SoundEffects,
)
}
.expect("Can't create the mastering voice.");
let block_align = num_channels * 2; let format = WAVEFORMATEX {
cbSize: size_of::<WAVEFORMATEX>() as u16,
nChannels: num_channels as u16,
nSamplesPerSec: sample_rate,
nBlockAlign: block_align as u16,
nAvgBytesPerSec: sample_rate * block_align,
wBitsPerSample: (block_align * 8) as u16,
wFormatTag: WAVE_FORMAT_PCM as u16,
};
unsafe {
x.CreateSourceVoice(
&mut source_voice,
&format,
XAUDIO2_VOICE_NOSRC,
XAUDIO2_MAX_FREQ_RATIO,
None,
None,
None,
)
}
.expect("Can't create the source voice.");
}
Self {
engine: engine.unwrap().into(),
mastering_voice: mastering_voice.unwrap().into(),
source_voice: source_voice.unwrap().into(),
}
}
pub async fn write(&self, data: &[u8]) {
StreamState::new(self.source_voice.clone(), &data).await;
}
#[allow(dead_code)]
pub fn flush(&self) {
unsafe { self.source_voice.FlushSourceBuffers() }.unwrap_or(())
}
pub fn stop(&self) {
unsafe { self.source_voice.Stop(0, 0) }.expect("Can't stop the stream.");
}
pub fn start(&self) {
unsafe { self.source_voice.Start(0, XAUDIO2_COMMIT_NOW) }.expect("Can't start.");
}
}
impl Debug for XAudio2OutputStream {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "AudioOutputStream")
}
}
unsafe impl Send for XAudio2OutputStream {}
unsafe impl Sync for XAudio2OutputStream {}
pub struct StreamState(Arc<IXAudio2SourceVoice>);
impl StreamState {
fn new(source_voice: Arc<IXAudio2SourceVoice>, data: &[u8]) -> Self {
let mut buf = XAUDIO2_BUFFER::default();
buf.pAudioData = data.as_ptr();
buf.AudioBytes = data.len() as u32;
unsafe { source_voice.SubmitSourceBuffer(&buf, None) }.unwrap_or(());
Self(source_voice)
}
}
impl Future for StreamState {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut state = XAUDIO2_VOICE_STATE::default();
unsafe {
self.0.GetState(&mut state, 0);
}
let p = state.BuffersQueued;
if p < 1 {
Poll::Ready(())
} else {
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
unsafe impl Send for StreamState {}
unsafe impl Sync for StreamState {}