radio_rs/
lib.rs

1#![allow(unused, non_snake_case, non_camel_case_types)]
2use std::ffi::{CString, OsStr};
3use std::os::raw::*;
4use std::time::{Duration, Instant};
5use std::{thread, path::Path};
6use std::marker::PhantomData;
7
8#[repr(C)]
9#[derive(Clone, Debug)]
10pub struct Wave {
11    pub frame_count: c_uint,
12    pub sample_rate: c_uint,
13    pub sample_size: c_uint,
14    pub channels: c_uint,
15    data: *mut c_void
16}
17
18#[derive(Debug)]
19pub struct AudioDevice {
20    last_frame_time: Instant
21}
22
23// for opaque structs - https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs (credit)
24#[repr(C)]
25pub struct rAudioBuffer {
26    _data: [u8; 0],
27    _marker: PhantomData<(*mut u8, core::marker::PhantomPinned)>
28}
29
30#[repr(C)]
31struct rAudioProcessor {
32    _data: [u8; 0],
33    _marker: PhantomData<(*mut u8, core::marker::PhantomPinned)>
34}
35
36#[repr(C)]
37#[derive(Clone, Debug)]
38pub struct AudioStream {
39    buffer: *mut rAudioBuffer,
40    processor: *mut rAudioProcessor,
41    pub sample_rate: c_uint,
42    pub sample_size: c_uint,
43    pub channels: c_uint
44}
45
46#[repr(C)]
47#[derive(Clone, Debug)]
48pub struct Sound {
49    pub stream: AudioStream,
50    pub frame_count: c_uint
51}
52
53#[repr(C)]
54#[derive(Clone, Debug)]
55pub struct Music {
56    pub stream: AudioStream,
57    pub frame_count: c_uint,
58    looping: bool,
59    ctx_type: c_int,
60    ctx_data: *mut c_void
61}
62
63impl Wave {
64    pub fn load(path: impl AsRef<Path>) -> Self {
65        let path = path.as_ref();
66        if !path.exists() {
67            panic!("File doesn't exist.");
68        }
69
70        match path.extension().and_then(OsStr::to_str).unwrap() {
71            "wav" => {
72                let file = CString::new(path.to_str().unwrap()).unwrap();
73                unsafe { LoadWave(file.as_ptr()) }
74            },
75            _ => panic!("Unsupported file format")
76        }
77    }
78
79    pub fn is_ready(&self) -> bool {
80        unsafe {
81            IsWaveReady(self.clone())
82        }
83    }
84
85    pub fn export(&self, file_name: impl AsRef<Path>) {
86        let file = CString::new(file_name.as_ref().to_str().unwrap()).unwrap();
87        unsafe {
88            ExportWave(self.clone(), file.as_ptr());
89        }
90    }
91
92    pub fn crop(&mut self, init_sample: i32, final_sample: i32) {
93        unsafe {
94            WaveCrop(self as *mut Wave, init_sample as c_int, final_sample as c_int);
95        }
96    }
97
98    pub fn format(&mut self, sample_rate: i32, sample_size: i32, channels: i32) {
99        unsafe {
100            WaveFormat(self as *mut Wave, sample_rate as c_int, sample_size as c_int, channels as c_int);
101        }
102    }
103}
104
105impl Drop for Wave {
106    fn drop(&mut self) {
107        unsafe {
108            UnloadWave(self.clone());
109        }
110    }
111}
112
113impl Default for AudioDevice {
114    fn default() -> Self {
115        Self::new()
116    }
117}
118
119impl AudioDevice {
120    const FPS: u64 = 60;
121    const FRAME_DURATION: Duration = Duration::from_millis(1000 / Self::FPS);
122
123    pub fn new() -> Self {
124        unsafe {
125            InitAudioDevice();
126        }
127        AudioDevice {last_frame_time: Instant::now()}
128    }
129
130    pub fn is_ready(&self) -> bool {
131        unsafe {
132            IsAudioDeviceReady()
133        }
134    }
135
136    pub fn sync(&mut self) {
137        let now = Instant::now();
138        let elapsed = now.duration_since(self.last_frame_time);
139
140        if elapsed < Self::FRAME_DURATION {
141            thread::sleep(Self::FRAME_DURATION - elapsed);
142        }
143
144        self.last_frame_time = Instant::now();
145    }
146
147    pub fn set_master_volume(&self, volume: f32) {
148        unsafe {
149            SetMasterVolume(volume as c_float);
150        }
151    }
152
153    pub fn get_master_volume(&self) -> f32 {
154        let volume  = unsafe {
155            GetMasterVolume()
156        };
157        volume as f32
158    }
159}
160
161impl Drop for AudioDevice {
162    fn drop(&mut self) {
163        unsafe {
164            CloseAudioDevice();
165        }
166    }
167}
168
169impl Sound {
170    pub fn load(path: impl AsRef<Path>) -> Self {
171        let path = path.as_ref();
172        if !path.exists() {
173            panic!("File doesn't exist.");
174        }
175
176        match path.extension().and_then(OsStr::to_str).unwrap() {
177            "wav" | "qoa" | "ogg" | "mp3" | "flac" => {
178                let file = CString::new(path.to_str().unwrap()).unwrap();
179                unsafe { LoadSound(file.as_ptr()) }
180            },
181            _ => panic!("Unsupported file format")
182        }
183    }
184
185    pub fn load_from_wave(wave: &Wave) -> Self {
186        unsafe {
187            LoadSoundFromWave(wave.clone())
188        }
189    }
190
191    pub fn is_ready(&self) -> bool {
192        unsafe {
193            IsSoundReady(self.clone())
194        }
195    }
196
197    pub fn play(&self) {
198        unsafe { PlaySound(self.clone()); }
199    }
200
201    pub fn stop(&self) {
202        unsafe { StopSound(self.clone()); }
203    }
204
205    pub fn pause(&self) {
206        unsafe { PauseSound(self.clone()); }
207    }
208
209    pub fn resume(&self) {
210        unsafe { ResumeSound(self.clone()); }
211    }
212
213    pub fn is_playing(&self) -> bool {
214        unsafe { IsSoundPlaying(self.clone()) }
215    }
216
217    pub fn set_voume(&self, volume: f32) {
218        unsafe { SetSoundVolume(self.clone(), volume as c_float); }
219    }
220
221    pub fn set_pitch(&self, pitch: f32) {
222        unsafe { SetSoundPitch(self.clone(), pitch as c_float); }
223    }
224
225    pub fn set_pan(&self, pan: f32) {
226        unsafe { SetSoundPan(self.clone(), pan as c_float); }
227    }
228}
229
230impl Drop for Sound {
231    fn drop(&mut self) {
232        unsafe {
233            UnloadSound(self.clone());
234        }
235    }
236}
237
238impl Music {
239    pub fn load(path: impl AsRef<Path>) -> Self {
240        let path = path.as_ref();
241        if !path.exists() {
242            panic!("File doesn't exist.");
243        }
244        
245        match path.extension().and_then(OsStr::to_str).unwrap() {
246            "wav" | "qoa" | "ogg" | "mp3" | "flac" | "xm" => {
247                let file = CString::new(path.to_str().unwrap()).unwrap();
248                unsafe { LoadMusicStream(file.as_ptr()) }
249            },
250            _ => panic!("Unsupported file format")
251        }
252    }
253
254    pub fn is_ready(&self) -> bool {
255        unsafe {
256            IsMusicReady(self.clone())
257        }
258    }
259
260    pub fn play(&self) {
261        unsafe { PlayMusicStream(self.clone()); }
262    }
263
264    pub fn is_playing(&self) -> bool {
265        unsafe {IsMusicStreamPlaying(self.clone())}
266    }
267
268    pub fn update(&self) {
269        unsafe {UpdateMusicStream(self.clone());}
270    }
271
272    pub fn stop(&self) {
273        unsafe {StopMusicStream(self.clone());}
274    }
275
276    pub fn pause(&self) {
277        unsafe {PauseMusicStream(self.clone());}
278    }
279
280    pub fn resume(&self) {
281        unsafe {ResumeMusicStream(self.clone());}
282    }
283
284    pub fn seek(&self, position: f32) {
285        unsafe {SeekMusicStream(self.clone(), position as c_float);}
286    }
287
288    pub fn set_volume(&self, volume: f32) {
289        unsafe {SetMusicVolume(self.clone(), volume as c_float);}
290    }
291
292    pub fn set_pitch(&self, pitch: f32) {
293        unsafe { SetMusicPitch(self.clone(), pitch as c_float); }
294    }
295
296    pub fn set_pan(&self, pan: f32) {
297        unsafe { SetMusicPan(self.clone(), pan as c_float); }
298    }
299
300    pub fn duration(&self) -> Duration {
301        let dur = unsafe {
302            GetMusicTimeLength(self.clone())
303        };
304        Duration::from_secs_f32(dur as f32)
305    }
306
307    pub fn time_played(&self) -> Duration {
308        let dur = unsafe {
309            GetMusicTimePlayed(self.clone())
310        };
311        Duration::from_secs_f32(dur as f32)
312    }
313}
314
315impl Drop for Music {
316    fn drop(&mut self) {
317        unsafe {
318            UnloadMusicStream(self.clone());
319        }
320    }
321}
322
323#[no_mangle]
324#[link(name = "audio")]
325extern "C" {
326    fn InitAudioDevice();
327    fn CloseAudioDevice();
328    fn IsAudioDeviceReady() -> bool;
329    fn SetMasterVolume(volume: c_float);
330    fn GetMasterVolume() -> c_float;
331
332    fn LoadWave(fileName: *const c_char) -> Wave;
333    fn IsWaveReady(wave: Wave) -> bool;
334    fn LoadSound(fileName: *const c_char) -> Sound;
335    fn LoadSoundFromWave(wave: Wave) -> Sound;
336    fn IsSoundReady(sound: Sound) -> bool;
337    fn UnloadWave(wave: Wave);
338    fn UnloadSound(sound: Sound);
339    fn ExportWave(wave: Wave, fileName: *const c_char) -> bool;
340
341    fn PlaySound(sound: Sound);
342    fn StopSound(sound: Sound);
343    fn PauseSound(sound: Sound);
344    fn ResumeSound(sound: Sound);
345    fn IsSoundPlaying(sound: Sound) -> bool;
346    fn SetSoundVolume(sound: Sound, volume: c_float);
347    fn SetSoundPitch(sound: Sound, pitch: c_float);
348    fn SetSoundPan(sound: Sound, pan: c_float);
349    fn WaveCrop(wave: *mut Wave, initSample: c_int, finalSample: c_int);
350    fn WaveFormat(wave: *mut Wave, sampleRate: c_int, sampleSize: c_int, channels: c_int);
351
352    fn LoadMusicStream(fileName: *const c_char) -> Music;
353    fn IsMusicReady(music: Music) -> bool;
354    fn UnloadMusicStream(music: Music);
355    fn PlayMusicStream(music: Music);
356    fn IsMusicStreamPlaying(music: Music) -> bool;
357    fn UpdateMusicStream(music: Music);
358    fn StopMusicStream(music: Music);
359    fn PauseMusicStream(music: Music);
360    fn ResumeMusicStream(music: Music);
361    fn SeekMusicStream(music: Music, position: c_float);
362    fn SetMusicVolume(music: Music, volume: c_float);
363    fn SetMusicPitch(music: Music, pitch: c_float);
364    fn SetMusicPan(music: Music, pan: c_float);
365    fn GetMusicTimeLength(music: Music) -> c_float;
366    fn GetMusicTimePlayed(music: Music) -> c_float;
367}