1use nix::libc;
2use std::{
3 fs::File,
4 mem,
5 os::{fd::AsRawFd, unix::fs::OpenOptionsExt},
6};
7
8pub const AFMT_QUERY: u32 = 0x00000000;
10pub const AFMT_MU_LAW: u32 = 0x00000001;
11pub const AFMT_A_LAW: u32 = 0x00000002;
12pub const AFMT_IMA_ADPCM: u32 = 0x00000004;
13pub const AFMT_U8: u32 = 0x00000008;
14pub const AFMT_S16_LE: u32 = 0x00000010;
15pub const AFMT_S16_BE: u32 = 0x00000020;
16pub const AFMT_S8: u32 = 0x00000040;
17pub const AFMT_U16_LE: u32 = 0x00000080;
18pub const AFMT_U16_BE: u32 = 0x00000100;
19pub const AFMT_MPEG: u32 = 0x00000200;
20pub const AFMT_AC3: u32 = 0x00000400;
21pub const AFMT_S32_LE: u32 = 0x00001000;
22pub const AFMT_S32_BE: u32 = 0x00002000;
23pub const AFMT_U32_LE: u32 = 0x00004000;
24pub const AFMT_U32_BE: u32 = 0x00008000;
25pub const AFMT_S24_LE: u32 = 0x00010000;
26pub const AFMT_S24_BE: u32 = 0x00020000;
27pub const AFMT_U24_LE: u32 = 0x00040000;
28pub const AFMT_U24_BE: u32 = 0x00080000;
29pub const AFMT_STEREO: u32 = 0x10000000;
30pub const AFMT_WEIRD: u32 = 0x20000000;
31pub const AFMT_FULLDUPLEX: u32 = 0x80000000;
32
33pub const PCM_ENABLE_INPUT: u32 = 0x00000001;
35pub const PCM_ENABLE_OUTPUT: u32 = 0x00000002;
36
37#[derive(Debug)]
38pub struct Config {
39 pub dsp: File,
40 pub channels: i32,
41 pub rate: i32,
42 pub bytes: i32,
43 pub format: u32,
44 pub samples: i32,
45 pub chsamples: i32,
46}
47
48impl Config {
49 pub fn new(path: &str, rate: i32, bits: i32, mmap: bool) -> Config {
50 let mut binding = File::options();
51 let options = binding.write(true);
52 if mmap {
53 options.custom_flags(libc::O_RDONLY | libc::O_EXCL | libc::O_NONBLOCK);
54 }
55 let mut c = Config {
56 dsp: options.open(path).unwrap(),
57 channels: 0,
58 rate,
59 bytes: 0,
60 format: AFMT_S32_LE,
61 samples: 0,
62 chsamples: 0,
63 };
64 if bits == 32 {
65 c.format = AFMT_S32_LE;
66 } else if bits == 16 {
67 c.format = AFMT_S16_LE;
68 } else if bits == 8 {
69 c.format = AFMT_S8;
70 } else {
71 panic!("No format with {} bits", bits);
72 }
73 let mut audio_info = AudioInfo::new();
74 let mut buffer_info = BufferInfo::new();
75 unsafe {
76 let fd = c.dsp.as_raw_fd();
77
78 if mmap {
80 oss_set_cooked(fd, 0).expect("Failed to disable cooked mode");
81 oss_set_trigger(fd, 0).expect("Failed to set trigger");
82 }
83 oss_audio_info(fd, &mut audio_info).expect("Failed to get info on device");
84
85 oss_set_format(fd, &mut c.format).expect("Failed to set format");
87 oss_channels(fd, &mut audio_info.max_channels)
88 .expect("Failed to set number of channels");
89 oss_set_speed(fd, &mut c.rate).expect("Failed to set sample rate");
90
91 oss_buffer_info(fd, &mut buffer_info).expect("Failed to get info on buffer size");
93 oss_caps(fd, &mut audio_info.caps).expect("Failed to get capabilities of the device");
94 }
95 c.channels = audio_info.max_channels;
96 c.bytes = buffer_info.bytes;
97 c.samples = c.bytes / mem::size_of::<i32>() as i32;
98 c.chsamples = c.samples / c.channels;
99 c
100 }
101}
102
103#[repr(C)]
104struct AudioInfo {
105 pub dev: libc::c_int,
106 pub name: [libc::c_char; 64],
107 pub busy: libc::c_int,
108 pub pid: libc::c_int,
109 pub caps: libc::c_int,
110 pub iformats: libc::c_int,
111 pub oformats: libc::c_int,
112 pub magic: libc::c_int,
113 pub cmd: [libc::c_char; 64],
114 pub card_number: libc::c_int,
115 pub port_number: libc::c_int,
116 pub mixer_dev: libc::c_int,
117 pub legacy_device: libc::c_int,
118 pub enabled: libc::c_int,
119 pub flags: libc::c_int,
120 pub min_rate: libc::c_int,
121 pub max_rate: libc::c_int,
122 pub min_channels: libc::c_int,
123 pub max_channels: libc::c_int,
124 pub binding: libc::c_int,
125 pub rate_source: libc::c_int,
126 pub handle: [libc::c_char; 32],
127 pub nrates: libc::c_uint,
128 pub rates: [libc::c_uint; 20],
129 pub song_name: [libc::c_char; 64],
130 pub label: [libc::c_char; 16],
131 pub latency: libc::c_int,
132 pub devnode: [libc::c_char; 32],
133 pub next_play_engine: libc::c_int,
134 pub next_rec_engine: libc::c_int,
135 pub filler: [libc::c_int; 184],
136}
137
138impl AudioInfo {
139 pub fn new() -> AudioInfo {
140 AudioInfo {
141 dev: 0,
142 name: [0; 64],
143 busy: 0,
144 pid: 0,
145 caps: 0,
146 iformats: 0,
147 oformats: 0,
148 magic: 0,
149 cmd: [0; 64],
150 card_number: 0,
151 port_number: 0,
152 mixer_dev: 0,
153 legacy_device: 0,
154 enabled: 0,
155 flags: 0,
156 min_rate: 0,
157 max_rate: 0,
158 min_channels: 0,
159 max_channels: 0,
160 binding: 0,
161 rate_source: 0,
162 handle: [0; 32],
163 nrates: 0,
164 rates: [0; 20],
165 song_name: [0; 64],
166 label: [0; 16],
167 latency: 0,
168 devnode: [0; 32],
169 next_play_engine: 0,
170 next_rec_engine: 0,
171 filler: [0; 184],
172 }
173 }
174}
175
176#[repr(C)]
177struct BufferInfo {
178 pub fragments: libc::c_int,
179 pub fragstotal: libc::c_int,
180 pub fragsize: libc::c_int,
181 pub bytes: libc::c_int,
182}
183
184impl BufferInfo {
185 pub fn new() -> BufferInfo {
186 BufferInfo {
187 fragments: 0,
188 fragstotal: 0,
189 fragsize: 0,
190 bytes: 0,
191 }
192 }
193}
194
195const SNDCTL_DSP_MAGIC: u8 = b'P';
196const SNDCTL_DSP_SPEED: u8 = 2;
197const SNDCTL_DSP_SETFMT: u8 = 5;
198const SNDCTL_DSP_CHANNELS: u8 = 6;
199const SNDCTL_DSP_GETOSPACE: u8 = 12;
200const SNDCTL_DSP_GETCAPS: u8 = 15;
201const SNDCTL_DSP_SETTRIGGER: u8 = 16;
202const SNDCTL_DSP_COOKEDMODE: u8 = 30;
203nix::ioctl_readwrite!(oss_channels, SNDCTL_DSP_MAGIC, SNDCTL_DSP_CHANNELS, i32);
204nix::ioctl_read!(
205 oss_buffer_info,
206 SNDCTL_DSP_MAGIC,
207 SNDCTL_DSP_GETOSPACE,
208 BufferInfo
209);
210nix::ioctl_read!(oss_caps, SNDCTL_DSP_MAGIC, SNDCTL_DSP_GETCAPS, i32);
211nix::ioctl_readwrite!(oss_set_format, SNDCTL_DSP_MAGIC, SNDCTL_DSP_SETFMT, u32);
212nix::ioctl_readwrite!(oss_set_speed, SNDCTL_DSP_MAGIC, SNDCTL_DSP_SPEED, i32);
213nix::ioctl_write_int!(oss_set_cooked, SNDCTL_DSP_MAGIC, SNDCTL_DSP_COOKEDMODE);
214nix::ioctl_write_int!(oss_set_trigger, SNDCTL_DSP_MAGIC, SNDCTL_DSP_SETTRIGGER);
215
216const SNDCTL_INFO_MAGIC: u8 = b'X';
217const SNDCTL_ENGINEINFO: u8 = 12;
218nix::ioctl_readwrite!(
219 oss_audio_info,
220 SNDCTL_INFO_MAGIC,
221 SNDCTL_ENGINEINFO,
222 AudioInfo
223);
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use std::io::Write;
229 use std::slice::from_raw_parts;
230 use wavers::Wav;
231
232 #[test]
233 fn it_works() {
234 let mut oss = Config::new("/dev/dsp", 48000, 32, false);
235 let mut wav: Wav<i32> = Wav::from_path("./stereo32.wav").unwrap();
236 let nchannels: usize = wav.n_channels().into();
237 let mut out: Vec<i32> = vec![];
238 let mut iter = wav.frames();
239
240 'outer: loop {
241 let mut i = 0;
242 while i < oss.chsamples {
243 match iter.next() {
244 Some(ref samples) => {
245 for ch in 0..nchannels {
246 out.push(samples[ch]);
247 i += 1;
248 }
249 }
250 None => break 'outer,
251 }
252 }
253 let bytes = unsafe { from_raw_parts(out.as_ptr() as *const u8, out.len() * 4) };
254 let _ret = oss.dsp.write(bytes);
255 out.clear();
256 }
257 }
259
260 #[test]
261 #[ignore]
262 fn mmap_mode() {
263 let _oss = Config::new("/dev/dsp4", 48000, 32, true);
264 }
266}