access the windows core audio api in rust

To access the Windows core audio API in Rust, you can use the winapi crate which provides Rust bindings for the Windows API. The core audio API is part of the Windows multimedia API and can be found in the mmdeviceapi.h header file.

To use the core audio API in Rust, you first need to define the necessary structs and enums. Here's an example of how to do this for the audio client interface:

main.rs
use winapi::um::mmdeviceapi::{IMMDevice, eRender, eMultimedia};
use winapi::shared::guiddef::{GUID, IID};
use winapi::um::unknwnbase::IUnknown;
use winapi::Interface;

#[repr(C)]
struct WAVEFORMATEX {
    wFormatTag: u16,
    nChannels: u16,
    nSamplesPerSec: u32,
    nAvgBytesPerSec: u32,
    nBlockAlign: u16,
    wBitsPerSample: u16,
    cbSize: u16,
}

#[repr(C)]
struct AUDCLNT_BUFFER_FLAGS {
    BufferFlags: u32,
    Periodicity: u32,
}

#[com_interface("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")]
#[repr(C)]
trait IAudioClient: IUnknown {
    unsafe fn initialize(&self, share_mode: u32, stream_flags: u32,
        buffer_duration: u64, periodicity: u64, format: *const WAVEFORMATEX,
        audio_session_guid: *const GUID) -> winapi::HRESULT;

    unsafe fn get_buffer_size(&self, buffer_size: *mut u32) -> winapi::HRESULT;

    unsafe fn get_service<T>(&self, riid: &IID, ppv: *mut *mut T) -> winapi::HRESULT
        where T: Interface;

    unsafe fn start(&self) -> winapi::HRESULT;

    unsafe fn stop(&self) -> winapi::HRESULT;
}

#[com_interface("D666063F-1587-4E43-81F1-B948E807363F")]
#[repr(C)]
struct IAudioRenderClient(IUnknown);

impl IAudioRenderClient {
    unsafe fn get_buffer(&self, num_frames: u32,
        data: *mut *mut winapi::ctypes::c_void) -> winapi::HRESULT {
        let hr = ((*self.0.lpVtbl).QueryInterface)(&self.0.lpVtbl,
            &IAudioRenderClient::uuidof(), &mut std::ptr::null_mut());
        if hr != 0 { return hr; }
        let hr = ((*self.0.lpVtbl).GetBuffer)(&self.0,
            num_frames, data as *mut *mut _);
        hr
    }

    unsafe fn release_buffer(&self, num_frames: u32,
        buffer_flags: *const AUDCLNT_BUFFER_FLAGS) -> winapi::HRESULT {
        let hr = ((*self.0.lpVtbl).QueryInterface)(&self.0.lpVtbl,
            &IAudioRenderClient::uuidof(), &mut std::ptr::null_mut());
        if hr != 0 { return hr; }
        let hr = ((*self.0.lpVtbl).ReleaseBuffer)(&self.0,
            num_frames, buffer_flags);
        hr
    }

    unsafe fn uuidof() -> IID {
        IID {
            Data1: 0xd666063f,
            Data2: 0x1587,
            Data3: 0x4e43,
            Data4: [0x81, 0xf1, 0xb9, 0x48, 0xe8, 0x07, 0x36, 0x3f],
        }
    }
}

#[com_interface("A95664D2-9614-4F35-A746-DE8DB63617E6")]
#[repr(C)]
struct IMMDevice(IUnknown);

impl IMMDevice {
    unsafe fn activate<T>(&self, iid: &IID, dwClsCtx: u32,
        pActivationParams: *mut winapi::ctypes::c_void, ppInterface: *mut *mut T)
        -> winapi::HRESULT where T: Interface {
        let hr = ((*self.0.lpVtbl).QueryInterface)(&self.0.lpVtbl,
            &IMMDevice::uuidof(), &mut std::ptr::null_mut());
        if hr != 0 { return hr; }
        let hr = ((*self.0.lpVtbl).Activate)(&self.0,
            iid, dwClsCtx, pActivationParams, ppInterface as *mut *mut _);
        hr
    }

    unsafe fn uuidof() -> IID {
        IID {
            Data1: 0xd666063f,
            Data2: 0x1587,
            Data3: 0x4e43,
            Data4: [0x81, 0xf1, 0xb9, 0x48, 0xe8, 0x07, 0x36, 0x3f],
        }
    }
}

const DEVICE_STATE_ACTIVE: u32 = 0x00000001;

unsafe fn get_default_audio_renderer() -> Option<IMMDevice> {
    let mut pDeviceEnumerator = std::ptr::null_mut();
    let clsid = winapi::um::combaseapi::CLSID_MMDeviceEnumerator;
    let hr = winapi::um::combaseapi::CoCreateInstance(&clsid, std::ptr::null_mut(),
        winapi::um::combaseapi::CLSCTX_ALL, &IMMDeviceEnumerator::uuidof(),
        &mut pDeviceEnumerator as *mut *mut _ as *mut *mut _);
    if hr != 0 { return None; }
    let pDeviceEnumerator = &*pDeviceEnumerator;
    let mut pDefaultDevice = std::ptr::null_mut();
    let hr = ((*pDeviceEnumerator.lpVtbl).GetDefaultAudioEndpoint)(pDeviceEnumerator,
        eRender, eMultimedia, &mut pDefaultDevice);
    if hr != 0 { return None; }
    let pDefaultDevice = &*pDefaultDevice;
    let mut state = 0u32;
    let hr = ((*pDefaultDevice.0.lpVtbl).GetState)(&pDefaultDevice.0, &mut state);
    if hr != 0 || state != DEVICE_STATE_ACTIVE {
        return None;
    }
    Some(*pDefaultDevice)
}
4060 chars
123 lines

With the necessary structs and enums defined, you can now use the audio client interface to initialize the audio device and start playing audio. Here's an example:

main.rs
fn play_audio_file(file_path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let mut audio_file = std::fs::File::open(file_path)?;
    let mut audio_buffer = [0u8; 4096];

    let default_device = match unsafe { get_default_audio_renderer() } {
        Some(device) => device,
        None => return Err("Failed to get default audio endpoint".into()),
    };

    let audio_client: IAudioClient = unsafe {
        let mut audio_client = std::ptr::null_mut();
        let hr = default_device.activate(&IAudioClient::uuidof(),
            winapi::um::objbase::CLSCTX_ALL, std::ptr::null_mut(),
            &mut audio_client as *mut *mut _);
        assert_eq!(hr, 0);
        IAudioClient(&*audio_client)
    };

    // Configure audio stream format and initialize audio client
    let mix_format = WAVEFORMATEX {
        wFormatTag: winapi::um::mmreg::WAVE_FORMAT_PCM,
        nChannels: 2,
        nSamplesPerSec: 44100,
        nAvgBytesPerSec: 176400,
        nBlockAlign: 4,
        wBitsPerSample: 16,
        cbSize: 0,
    };
    unsafe {
        let hr = audio_client.initialize(
            winapi::um::audiosessiontypes::AUDCLNT_SHAREMODE_SHARED,
            0, 0, 0, &mix_format, std::ptr::null_mut());
        assert_eq!(hr, 0);
    }

    // Start audio playback
    unsafe { audio_client.start(); }

    // Play audio file
    let render_client: IAudioRenderClient = unsafe {
        let mut render_client = std::ptr::null_mut();
        let hr = audio_client.get_service(&IAudioRenderClient::uuidof(),
            &mut render_client as *mut *mut _);
        assert_eq!(hr, 0);
        IAudioRenderClient(&*render_client)
    };
    
    loop {
        let bytes_read = audio_file.read(&mut audio_buffer)?;
        let frames_read = bytes_read as u32 / (mix_format.nChannels as u32 * (mix_format.wBitsPerSample / 8));
        let mut data: *mut winapi::ctypes::c_void = std::ptr::null_mut();
        if frames_read > 0 {
            let hr = unsafe { render_client.get_buffer(frames_read, &mut data) };
            if hr == winapi::um::winerror::AUDCLNT_E_BUFFER_TOO_LARGE { continue; }
            assert_eq!(hr, 0);
            let samples_to_copy = frames_read * mix_format.nChannels as u32;
            let audio_buffer_ptr = audio_buffer.as_mut_ptr() as *mut i16;
            let audio_data_ptr = data as *mut i16;
            unsafe {
                std::ptr::copy_nonoverlapping(audio_buffer_ptr,
                    audio_data_ptr, samples_to_copy as usize);
                let hr = render_client.release_buffer(frames_read, std::ptr::null());
                assert_eq!(hr, 0);
            }
        } else {
            std::thread::sleep(std::time::Duration::from_millis(16));
        }
    }
}
2728 chars
70 lines

In this example, we first get the default audio rendering device using the IMMDeviceEnumerator interface. We then use the IAudioClient interface to initialize the audio stream with a given audio format and start audio playback. Finally, we use the IAudioRenderClient interface to play audio data from a file by repeatedly calling get_buffer() and release_buffer(), and copying audio data to the buffer.

gistlibby LogSnag