zdsr_api/
lib.rs

1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14#![doc = include_str!("../README.md")]
15
16//! 尝试更现代化的开源读屏项目:
17//! [RigelA](http://rigela.site)
18
19use libloading::{Library, Symbol};
20use std::{
21    env::var,
22    io::{Error, ErrorKind},
23    path::Path,
24    sync::OnceLock,
25};
26
27//noinspection SpellCheckingInspection
28#[cfg(target_arch = "x86")]
29const LIB_NAME: &str = "ZDSRAPI.dll";
30//noinspection SpellCheckingInspection
31#[cfg(target_arch = "x86_64")]
32const LIB_NAME: &str = "ZDSRAPI_x64.dll";
33
34static LIB: OnceLock<Library> = OnceLock::new();
35
36//noinspection SpellCheckingInspection
37/// 初始化语音接口
38///
39/// # Arguments
40///
41/// * `r#type`: 0 读屏通道; 1 独立通道
42/// * `channel_name`: type为0时忽略,传入NULL; type为1时:独立通道名称, NULL或空字符串时,将使用默认名称"API"
43/// * `key_down_interrupt`: TRUE 按键打断; FALSE 按键不打断
44///
45/// returns: Result<i32, Error>
46///     0: 成功
47///     1: 版本不匹配
48///
49/// # Examples
50///
51/// ```
52/// use zdsr_api::init_tts;
53/// init_tts(0, std::ptr::null(), 0).unwrap();
54/// ```
55pub fn init_tts(
56    r#type: i32,
57    channel_name: *const u16,
58    key_down_interrupt: i32,
59) -> Result<i32, Error> {
60    let win_dir = var("WINDIR").unwrap();
61    let zdsr_dir = Path::new(&win_dir)
62        .parent()
63        .unwrap()
64        .join("Program Files (x86)")
65        .join("zdsr");
66    // 检查商业版
67    let mut lib_path = zdsr_dir.join("zdsr").join(LIB_NAME);
68    if !lib_path.exists() {
69        // 检查青春版
70        lib_path = zdsr_dir.join("zdsr_yth").join(LIB_NAME);
71        if !lib_path.exists() {
72            return Err(Error::new(
73                ErrorKind::NotFound,
74                "ZDSR Product is not installed. You can visit `https://zdsr.com` for more information.",
75            ));
76        }
77    }
78    let res = unsafe {
79        let lib = LIB.get_or_init(move || Library::new(lib_path).unwrap());
80        let func: Symbol<unsafe extern "C" fn(i32, *const u16, i32) -> i32> =
81            lib.get(b"InitTTS\0").unwrap();
82        func(r#type, channel_name, key_down_interrupt)
83    };
84    Ok(res)
85}
86
87//noinspection SpellCheckingInspection
88/// 朗读文本
89///
90/// # Arguments
91///
92/// * `text`: 要朗读的文本,Unicode
93/// * `interrupt`: TRUE:清空排队,立刻打断朗读, FALSE:等待空闲时朗读
94///
95/// returns: Result<i32, Error>
96///     0: 成功
97///     1: 版本不匹配
98///     2: ZDSR没有运行
99///
100/// # Examples
101///
102///```
103/// use zdsr_api::speak;
104/// // Speak: ABC
105/// speak([65u16, 66, 67].as_ptr(), 0).unwrap();
106/// ```
107pub fn speak(text: *const u16, interrupt: i32) -> Result<i32, Error> {
108    let Some(lib) = LIB.get() else {
109        return Err(Error::new(
110            ErrorKind::Other,
111            "Can't find or init zdsr library.",
112        ));
113    };
114    let res = unsafe {
115        let func: Symbol<unsafe extern "C" fn(*const u16, i32) -> i32> =
116            lib.get(b"Speak\0").unwrap();
117        func(text, interrupt)
118    };
119    Ok(res)
120}
121
122//noinspection SpellCheckingInspection
123/// 获取朗读状态
124///
125/// returns Result<i32, Error>
126///     1: 版本不匹配
127///     2: ZDSR没有运行
128///     3: 正在朗读
129///     4: 没有朗读
130///
131/// # examples
132///
133/// ```
134/// use zdsr_api::get_speak_state;
135/// get_speak_state().unwrap();
136/// ```
137pub fn get_speak_state() -> Result<i32, Error> {
138    let Some(lib) = LIB.get() else {
139        return Err(Error::new(
140            ErrorKind::Other,
141            "Can't find or init zdsr library.",
142        ));
143    };
144    let res = unsafe {
145        let func: Symbol<unsafe extern "C" fn() -> i32> = lib.get(b"GetSpeakState\0").unwrap();
146        func()
147    };
148    Ok(res)
149}
150
151//noinspection SpellCheckingInspection
152/// 停止朗读
153///
154/// returns: Result<(), Error>
155///
156/// # examples
157///
158/// ```
159/// use zdsr_api::stop_speak;
160/// stop_speak().unwrap();
161/// ```
162pub fn stop_speak() -> Result<(), Error> {
163    let Some(lib) = LIB.get() else {
164        return Err(Error::new(
165            ErrorKind::Other,
166            "Can't find or init zdsr library.",
167        ));
168    };
169    let res = unsafe {
170        let func: Symbol<unsafe extern "C" fn()> = lib.get(b"StopSpeak\0").unwrap();
171        func()
172    };
173    Ok(res)
174}
175
176//noinspection SpellCheckingInspection
177#[cfg(test)]
178mod test_zdsr {
179    use crate::{get_speak_state, init_tts, speak, stop_speak};
180
181    #[test]
182    fn main() {
183        assert_eq!(0, init_tts(0, std::ptr::null(), 0).unwrap());
184        assert_eq!(0, speak([65u16, 66, 67].as_ptr(), 0).unwrap());
185        assert_eq!(3, get_speak_state().unwrap());
186        stop_speak().unwrap();
187    }
188}