linear_model_allen/
device.rs

1use crate::{AllenError, AllenResult, Context};
2use std::{ffi::CStr, ptr, sync::Arc};
3use oal_sys_windows::*;
4
5pub(crate) struct DeviceInner {
6    pub(crate) handle: *mut ALCdevice,
7}
8
9impl Drop for DeviceInner {
10    fn drop(&mut self) {
11        unsafe { alcCloseDevice(self.handle) };
12    }
13}
14
15/// An OpenAL device.
16#[derive(Clone)]
17pub struct Device {
18    pub(crate) inner: Arc<DeviceInner>,
19}
20
21impl Device {
22    /// Opens a device with the specified name. Passing `None` will open the default device.
23    pub fn open(device_name: Option<&CStr>) -> Option<Self> {
24        let handle =
25            unsafe { alcOpenDevice(device_name.map(|s| s.as_ptr()).unwrap_or(ptr::null())) };
26
27        if handle == ptr::null_mut() {
28            None
29        } else {
30            Some(Device {
31                inner: Arc::new(DeviceInner { handle }),
32            })
33        }
34    }
35
36    /// The name of the device.
37    pub fn device_name(&self) -> &str {
38        unsafe { CStr::from_ptr(alcGetString(self.inner.handle, ALC_DEVICE_SPECIFIER)) }
39            .to_str()
40            .unwrap()
41    }
42
43    /// Creates a context under the device.
44    pub fn create_context(&self) -> AllenResult<Context> {
45        Context::new(self.clone())
46    }
47
48    pub fn is_extension_present(&self, name: &CStr) -> AllenResult<bool> {
49        let result = unsafe { alcIsExtensionPresent(self.inner.handle, name.as_ptr()) };
50        self.check_alc_error()?;
51        Ok(result != 0)
52    }
53
54    pub fn check_alc_extension(&self, name: &CStr) -> AllenResult<()> {
55        if self.is_extension_present(name)? {
56            Ok(())
57        } else {
58            Err(AllenError::MissingExtension(
59                // This seemed to be the best non error-prone way to convert &CStr to String.
60                name.to_string_lossy().to_string(),
61            ))
62        }
63    }
64
65    pub(crate) fn check_alc_error(&self) -> AllenResult<()> {
66        let error = unsafe { alcGetError(self.inner.handle) };
67
68        if error == ALC_NO_ERROR {
69            Ok(())
70        } else {
71            Err(match error {
72                ALC_INVALID_DEVICE => AllenError::InvalidDevice,
73                ALC_INVALID_CONTEXT => AllenError::InvalidContext,
74                ALC_INVALID_ENUM => AllenError::InvalidEnum,
75                ALC_INVALID_VALUE => AllenError::InvalidValue,
76                ALC_OUT_OF_MEMORY => AllenError::OutOfMemory,
77                e => AllenError::Unknown(e),
78            })
79        }
80    }
81}