Skip to main content

audio_cpp/
model.rs

1//! 已加载的模型(Model)。
2
3use std::ffi::c_char;
4use std::os::raw::c_int;
5use std::ptr;
6
7use audio_cpp_sys::*;
8
9use crate::error::Error;
10use crate::ffi;
11use crate::registry::RegistryGuard;
12use crate::session::Session;
13use crate::types::{Backend, Capabilities, ModelMetadata, RunMode, TaskKind};
14
15/// 已加载的模型。
16///
17/// 由 [`crate::Registry::load`] 创建,持有一个 C 句柄,并共享持有
18/// C 注册表的所有权——`Registry` 释放后模型仍可继续使用。
19pub struct Model {
20    raw: *mut audiocpp_model,
21    // 共享持有 C 注册表:最后一个派生句柄释放时 C 注册表才释放。
22    _guard: RegistryGuard,
23}
24
25// C ABI 句柄本身不要求 Send/Sync。C 侧 model 内部无锁,多线程共享同一模型
26// 并发建会话属于并发访问,行为未定义。因此只实现 Send,不实现 Sync。
27// (`_guard` 本身是 Send 而非 Sync,`Model` 的 auto-trait 与之前一致。)
28unsafe impl Send for Model {}
29
30impl Model {
31    /// 从原始 C 句柄包装(仅内部使用)。
32    pub(crate) fn from_raw(raw: *mut audiocpp_model, guard: RegistryGuard) -> Self {
33        Self { raw, _guard: guard }
34    }
35
36    /// 模型元数据(family / variant / description / 候选配置与权重)。
37    ///
38    /// # Errors
39    ///
40    /// C ABI 调用失败或返回的 JSON 无法解析时返回对应 [`Error`] 变体。
41    pub fn metadata(&self) -> Result<ModelMetadata, Error> {
42        let mut out: *mut c_char = ptr::null_mut();
43        ffi::check_rc(unsafe { audiocpp_model_metadata_json(self.raw, &mut out) })?;
44        let json = unsafe { ffi::take_string(out)? };
45        serde_json::from_str(&json).map_err(Error::from)
46    }
47
48    /// 模型能力集合。
49    ///
50    /// # Errors
51    ///
52    /// C ABI 调用失败或返回的 JSON 无法解析时返回对应 [`Error`] 变体。
53    pub fn capabilities(&self) -> Result<Capabilities, Error> {
54        let mut out: *mut c_char = ptr::null_mut();
55        ffi::check_rc(unsafe { audiocpp_model_capabilities_json(self.raw, &mut out) })?;
56        let json = unsafe { ffi::take_string(out)? };
57        serde_json::from_str(&json).map_err(Error::from)
58    }
59
60    /// 在模型上创建一次任务会话。
61    ///
62    /// 可从 `metadata()` / `capabilities()` 确认模型是否支持目标任务与模式。
63    /// 返回的 [`Session`] 同样共享持有 C 注册表的所有权;会话创建后释放
64    /// `Model`(乃至 `Registry`)不影响其继续工作。
65    ///
66    /// # Errors
67    ///
68    /// 参数含 NUL / 非 UTF-8 返回 [`Error::Nul`] 或 [`Error::NonUtf8Path`];
69    /// 底层无法创建会话返回 [`Error::NullHandle`]。
70    pub fn create_task_session(
71        &self,
72        task: TaskKind,
73        mode: RunMode,
74        backend: Backend,
75        device: i32,
76        threads: i32,
77        session_options: Option<&str>,
78    ) -> Result<Session, Error> {
79        let task_c = ffi::cstring(task.as_str())?;
80        let mode_c = ffi::cstring(mode.as_str())?;
81        let backend_c = ffi::cstring(backend.as_str())?;
82        let options_c = session_options.map(ffi::cstring).transpose()?;
83
84        let raw = unsafe {
85            audiocpp_model_create_task_session(
86                self.raw,
87                task_c.as_ptr() as *const c_char,
88                mode_c.as_ptr() as *const c_char,
89                backend_c.as_ptr() as *const c_char,
90                device as c_int,
91                threads as c_int,
92                options_c
93                    .as_ref()
94                    .map_or(ptr::null(), |s| s.as_ptr() as *const c_char),
95            )
96        };
97        if raw.is_null() {
98            return Err(Error::NullHandle(ffi::last_error()));
99        }
100        Ok(Session::from_raw(raw, self._guard.clone(), backend))
101    }
102}
103
104impl Drop for Model {
105    fn drop(&mut self) {
106        unsafe {
107            audiocpp_model_free(self.raw);
108        }
109    }
110}