1use std::os::raw;
11#[cfg(doc)]
12use std::ptr;
13
14
15unsafe fn cstr_eq(a: *const raw::c_char, b: *const raw::c_char) -> bool {
16 if a.is_null() && b.is_null() {
17 true
18 } else if a.is_null() != b.is_null() {
19 false
20 } else {
21 unsafe { libc::strcmp(a, b) == 0 }
22 }
23}
24
25
26#[repr(C)]
27#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
28pub enum Format {
29 LE = 0,
30 BE = 1,
31}
32
33#[expect(clippy::derivable_impls, reason = "Does not take cfg() into account")]
34impl Default for Format {
35 fn default() -> Self {
36 #[cfg(target_endian = "little")]
37 { Self::LE }
38 #[cfg(target_endian = "big")]
39 { Self::BE }
40 }
41}
42
43
44#[repr(C)]
45#[derive(Debug)]
46pub struct Track {
47 pub bits: raw::c_int,
48 pub num_channels: raw::c_int,
49 pub sample_rate: raw::c_int,
50 pub num_samples: raw::c_int,
51 pub samples: *mut raw::c_short,
52}
53
54
55#[repr(C)]
56#[derive(Debug, Default, PartialEq, Eq)]
57pub struct ID {
58 pub volume: raw::c_int,
59 pub format: Format,
60 pub callbacks: *const Plugin,
61 pub private_data: *mut raw::c_void,
62 pub working: raw::c_int,
63}
64
65#[repr(C)]
66#[derive(Debug, Default, Eq)]
67#[expect(clippy::partial_pub_fields, reason="Reserved field should never be accessed but is used in FFI")]
68pub struct Params {
70 pub oss_device: *const raw::c_char,
71 pub alsa_device: *const raw::c_char,
72 pub nas_server: *const raw::c_char,
73 pub pulse_device: *const raw::c_char,
74 pub pulse_min_length: *const raw::c_char,
75 reserved: [*const raw::c_char; 5],
76}
77
78impl Params {
79 pub const FIELDS: [&'static str; 5] = [
80 "audio_oss_device",
81 "audio_alsa_device",
82 "audio_nas_server",
83 "audio_pulse_device",
84 "audio_pulse_min_length",
85 ];
86
87 pub unsafe fn set_by_name(&mut self, name: &str, value: *const raw::c_char) -> bool {
96 unsafe fn set_field(field: &mut *const raw::c_char, value: *const raw::c_char) -> bool {
97 unsafe { libc::free(*field as *mut _) }; *field = value; true }
101
102 match name {
103 "audio_oss_device" => unsafe { set_field(&mut self.oss_device, value) },
104 "audio_alsa_device" => unsafe { set_field(&mut self.alsa_device, value) },
105 "audio_nas_server" => unsafe { set_field(&mut self.nas_server, value) },
106 "audio_pulse_device" => unsafe { set_field(&mut self.pulse_device, value) },
107 "audio_pulse_min_length" => unsafe { set_field(&mut self.pulse_min_length, value) },
108
109 _ => false
110 }
111 }
112
113
114 #[must_use]
116 pub fn get_by_name(&self, name: &str) -> Option<*const raw::c_char> {
117 match name {
118 "audio_oss_device" => Some(self.oss_device),
119 "audio_alsa_device" => Some(self.alsa_device),
120 "audio_nas_server" => Some(self.nas_server),
121 "audio_pulse_device" => Some(self.pulse_device),
122 "audio_pulse_min_length" => Some(self.pulse_min_length),
123 _ => None,
124 }
125 }
126}
127
128impl PartialEq for Params {
129 fn eq(&self, other: &Self) -> bool {
130 (unsafe { cstr_eq(self.oss_device, other.oss_device) }) &&
131 (unsafe { cstr_eq(self.alsa_device, other.alsa_device) }) &&
132 (unsafe { cstr_eq(self.nas_server, other.nas_server) }) &&
133 (unsafe { cstr_eq(self.pulse_device, other.pulse_device) }) &&
134 (unsafe { cstr_eq(self.pulse_min_length, other.pulse_min_length) })
135 }
136}
137
138
139#[repr(C)]
162#[derive(Debug)]
163pub struct Plugin {
164 pub name: *const raw::c_char,
165
166 pub open: unsafe extern "C" fn(params: *const Params) -> *mut ID,
167
168 pub play: Option<unsafe extern "C" fn(id: *mut ID, track: Track) -> raw::c_int>,
170
171 pub stop: Option<unsafe extern "C" fn(id: *mut ID) -> raw::c_int>,
173
174 pub close: Option<unsafe extern "C" fn(id: *mut ID) -> raw::c_int>,
176
177
178 pub set_volume: Option<unsafe extern "C" fn(id: *mut ID, volume: raw::c_int) -> raw::c_int>,
179
180 pub set_loglevel: unsafe extern "C" fn(level: raw::c_int),
181
182 pub get_playcmd: unsafe extern "C" fn() -> *const raw::c_char,
184
185
186 pub begin: Option<unsafe extern "C" fn(id: *mut ID, track: Track) -> raw::c_int>,
192
193 pub feed_sync: Option<unsafe extern "C" fn(id: *mut ID, track: Track) -> raw::c_int>,
198
199 pub feed_sync_overlap: Option<unsafe extern "C" fn(id: *mut ID, track: Track) -> raw::c_int>,
208
209 pub end: Option<unsafe extern "C" fn(id: *mut ID) -> raw::c_int>,
213}
214
215
216unsafe extern "C" {
217 #[must_use]
218 pub unsafe fn spd_audio_open(name: *const raw::c_char, params: *const Params, error: *mut *mut raw::c_char) -> *mut ID;
219
220 #[must_use]
221 pub unsafe fn spd_audio_play(id: *mut ID, track: Track, format: Format) -> raw::c_int;
222
223 #[must_use]
224 pub unsafe fn spd_audio_begin(id: *mut ID, track: Track, format: Format) -> raw::c_int;
225
226 #[must_use]
227 pub unsafe fn spd_audio_feed_sync(id: *mut ID, track: Track, format: Format) -> raw::c_int;
228
229 #[must_use]
230 pub unsafe fn spd_audio_feed_sync_overlap(id: *mut ID, track: Track, format: Format) -> raw::c_int;
231
232 #[must_use]
233 pub unsafe fn spd_audio_end(id: *mut ID) -> raw::c_int;
234
235 #[must_use]
236 pub unsafe fn spd_audio_stop(id: *mut ID) -> raw::c_int;
237
238 #[must_use]
239 pub unsafe fn spd_audio_close(id: *mut ID) -> raw::c_int;
240
241 #[must_use]
242 pub unsafe fn spd_audio_set_volume(id: *mut ID, volume: raw::c_int) -> raw::c_int;
243
244 pub unsafe fn spd_audio_set_loglevel(id: *mut ID, level: raw::c_int);
245
246 #[must_use]
247 pub unsafe fn spd_audio_get_playcmd(id: *mut ID) -> *const raw::c_char;
248}