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}