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