thindx_xaudio2/xa28/source_format.rs
1#[allow(unused_imports)] use super::*;
2
3use winapi::shared::mmreg::*;
4
5use core::marker::PhantomData;
6use core::mem::size_of;
7use core::ops::*;
8
9
10
11/// A [WAVEFORMATEX] or [WAVEFORMATEXTENSIBLE] suitable for passing to [IXAudio2::CreateSourceVoice].
12///
13/// XAudio2 supports the following PCM formats:
14/// * 8-bit (unsigned) integer PCM
15/// * 16-bit integer PCM (optimal format for XAudio2)
16/// * 20-bit integer PCM (either in 24 or 32 bit containers)
17/// * 24-bit integer PCM (either in 24 or 32 bit containers)
18/// * 32-bit integer PCM
19/// * 32-bit float PCM (preferred format after 16-bit integer)
20///
21/// [WAVEFORMATEX]: https://learn.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatex
22/// [WAVEFORMATEXTENSIBLE]: https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ksmedia/ns-ksmedia-waveformatextensible
23/// [IXAudio2::CreateSourceVoice]: https://learn.microsoft.com/en-us/windows/win32/api/xaudio2/nf-xaudio2-ixaudio2-createsourcevoice
24#[derive(Clone, Copy)] pub struct SourceFormat(WAVEFORMATEX);
25
26impl AsRef<SourceFormat> for SourceFormat { fn as_ref(&self) -> &SourceFormat { self } }
27
28impl SourceFormat {
29 /// Construct [SourceFormat] from a [WAVEFORMATEX](https://learn.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatex).
30 ///
31 /// ### Safety
32 /// The exact safety requirements are a bit murky. However:
33 /// * `WAVEFORMATEX::cbSize` in particular must be valid to avoid buffer overruns.
34 /// * `WAVEFORMATEX::wFormatTag` should be valid for `self` to avoid buffer overruns / unhandled switch cases.
35 /// * Particularly large integers could easily lead to integer overflow related undefined behavior.
36 /// * Unexpected enum values could easily lead to exercising undefined behavior via unhandled switch cases.
37 pub unsafe fn from_wave_format_ex(format: WAVEFORMATEX) -> Self {
38 assert!(format.wFormatTag != WAVE_FORMAT_EXTENSIBLE, "use WAVEFORMATEXTENSIBLE instead for WAVE_FORMAT_EXTENSIBLE");
39 assert!(format.cbSize == 0, "WAVEFORMATEX cannot store any trailing data");
40 Self(format)
41 }
42
43 /// [IXAudio2::CreateSourceVoice]-friendly parameter.
44 pub fn as_source_format(&self) -> *const WAVEFORMATEX { &self.0 }
45
46 fn basic<S: Sized, const C: usize>(fmt: u16, hz: u32) -> TypedSourceFormat<[S; C]> {
47 let sc_size = if let Ok(n) = u16::try_from(size_of::<[S; C]>()) { n } else { panic!("size_of::<[S; C]>() > u16::MAX") };
48 let s_size = if let Ok(n) = u16::try_from(size_of::< S >()) { n } else { panic!("size_of::<S>() > u16::MAX") };
49 unsafe{TypedSourceFormat::new(SourceFormat::from_wave_format_ex(WAVEFORMATEX{
50 wFormatTag: fmt,
51 nChannels: C.try_into().expect("too many channels"),
52 nSamplesPerSec: hz,
53 nAvgBytesPerSec: hz * (sc_size as u32),
54 nBlockAlign: sc_size,
55 wBitsPerSample: s_size * 8,
56 cbSize: 0,
57 }))}
58 }
59}
60
61// TODO: impl Debug for SourceFormat
62
63
64
65/// [SourceFormat], but with additional type information.
66#[derive(Clone, Copy)] pub struct TypedSourceFormat<Sample>(SourceFormat, PhantomData<Sample>);
67
68impl<S> AsRef<SourceFormat> for TypedSourceFormat<S> { fn as_ref(&self) -> &SourceFormat { &self.0 } }
69impl<S> Deref for TypedSourceFormat<S> { fn deref(&self) -> &Self::Target { &self.0 } type Target = SourceFormat; }
70
71impl<S> TypedSourceFormat<S> {
72 /// Construct a [TypedSourceFormat] from a [SourceFormat]
73 ///
74 /// ### Safety
75 /// * `format` should match the type `S`.
76 pub unsafe fn new(format: SourceFormat) -> Self {
77 Self(format, PhantomData)
78 }
79}
80
81impl<S: HasPcmWaveFormat, const C: usize> TypedSourceFormat<[S; C]> {
82 /// [Pulse-code modulation](https://en.wikipedia.org/wiki/Pulse-code_modulation) format, implemented for [TypedSourceFormat]<\[[u8] | [i16] | [i32] | [f32]; 1 \| 2\]>.
83 ///
84 /// N.B. 8-bit is unsigned, but 16 and 32 bit are *signed*
85 pub fn pcm(hz: u32) -> Self { SourceFormat::basic(S::pcm_wave_format(), hz) }
86}
87
88/// [u8] | [i16] | [i32] | [f32]
89///
90/// N.B. 8-bit is unsigned, but 16 and 32 bit are *signed*
91pub unsafe trait HasPcmWaveFormat {
92 /// \[[microsoft.com](https://learn.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatex#remarks)\]
93 /// WAVE_FORMAT_\* tag values
94 fn pcm_wave_format() -> u16;
95}
96
97unsafe impl HasPcmWaveFormat for u8 { fn pcm_wave_format() -> u16 { WAVE_FORMAT_PCM } }
98unsafe impl HasPcmWaveFormat for i16 { fn pcm_wave_format() -> u16 { WAVE_FORMAT_PCM } }
99unsafe impl HasPcmWaveFormat for i32 { fn pcm_wave_format() -> u16 { WAVE_FORMAT_PCM } }
100unsafe impl HasPcmWaveFormat for f32 { fn pcm_wave_format() -> u16 { WAVE_FORMAT_IEEE_FLOAT } }