Skip to main content

radio_rs/
lib.rs

1#![allow(unused, non_snake_case, non_camel_case_types)]
2use std::ffi::{CString, OsStr};
3use std::marker::PhantomData;
4use std::os::raw::*;
5use std::time::{Duration, Instant};
6use std::{path::Path, thread};
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 { IsWaveReady(self.clone()) }
81    }
82
83    pub fn export(&self, file_name: impl AsRef<Path>) {
84        let file = CString::new(file_name.as_ref().to_str().unwrap()).unwrap();
85        unsafe {
86            ExportWave(self.clone(), file.as_ptr());
87        }
88    }
89
90    pub fn crop(&mut self, init_sample: i32, final_sample: i32) {
91        unsafe {
92            WaveCrop(
93                self as *mut Wave,
94                init_sample as c_int,
95                final_sample as c_int,
96            );
97        }
98    }
99
100    pub fn format(&mut self, sample_rate: i32, sample_size: i32, channels: i32) {
101        unsafe {
102            WaveFormat(
103                self as *mut Wave,
104                sample_rate as c_int,
105                sample_size as c_int,
106                channels as c_int,
107            );
108        }
109    }
110}
111
112impl Drop for Wave {
113    fn drop(&mut self) {
114        unsafe {
115            UnloadWave(self.clone());
116        }
117    }
118}
119
120impl Default for AudioDevice {
121    fn default() -> Self {
122        Self::new()
123    }
124}
125
126impl AudioDevice {
127    const FPS: u64 = 60;
128    const FRAME_DURATION: Duration = Duration::from_millis(1000 / Self::FPS);
129
130    pub fn new() -> Self {
131        unsafe {
132            InitAudioDevice();
133        }
134        AudioDevice {
135            last_frame_time: Instant::now(),
136        }
137    }
138
139    pub fn is_ready(&self) -> bool {
140        unsafe { IsAudioDeviceReady() }
141    }
142
143    pub fn sync(&mut self) {
144        let now = Instant::now();
145        let elapsed = now.duration_since(self.last_frame_time);
146
147        if elapsed < Self::FRAME_DURATION {
148            thread::sleep(Self::FRAME_DURATION - elapsed);
149        }
150
151        self.last_frame_time = Instant::now();
152    }
153
154    pub fn set_master_volume(&self, volume: f32) {
155        unsafe {
156            SetMasterVolume(volume as c_float);
157        }
158    }
159
160    pub fn get_master_volume(&self) -> f32 {
161        let volume = unsafe { GetMasterVolume() };
162        volume as f32
163    }
164}
165
166impl Drop for AudioDevice {
167    fn drop(&mut self) {
168        unsafe {
169            CloseAudioDevice();
170        }
171    }
172}
173
174impl Sound {
175    pub fn load(path: impl AsRef<Path>) -> Self {
176        let path = path.as_ref();
177        if !path.exists() {
178            panic!("File doesn't exist.");
179        }
180
181        match path.extension().and_then(OsStr::to_str).unwrap() {
182            "wav" | "qoa" | "ogg" | "mp3" | "flac" => {
183                let file = CString::new(path.to_str().unwrap()).unwrap();
184                unsafe { LoadSound(file.as_ptr()) }
185            }
186            _ => panic!("Unsupported file format"),
187        }
188    }
189
190    pub fn load_from_wave(wave: &Wave) -> Self {
191        unsafe { LoadSoundFromWave(wave.clone()) }
192    }
193
194    pub fn is_ready(&self) -> bool {
195        unsafe { IsSoundReady(self.clone()) }
196    }
197
198    pub fn play(&self) {
199        unsafe {
200            PlaySound(self.clone());
201        }
202    }
203
204    pub fn stop(&self) {
205        unsafe {
206            StopSound(self.clone());
207        }
208    }
209
210    pub fn pause(&self) {
211        unsafe {
212            PauseSound(self.clone());
213        }
214    }
215
216    pub fn resume(&self) {
217        unsafe {
218            ResumeSound(self.clone());
219        }
220    }
221
222    pub fn is_playing(&self) -> bool {
223        unsafe { IsSoundPlaying(self.clone()) }
224    }
225
226    pub fn set_volume(&self, volume: f32) {
227        unsafe {
228            SetSoundVolume(self.clone(), volume as c_float);
229        }
230    }
231
232    pub fn set_pitch(&self, pitch: f32) {
233        unsafe {
234            SetSoundPitch(self.clone(), pitch as c_float);
235        }
236    }
237
238    pub fn set_pan(&self, pan: f32) {
239        unsafe {
240            SetSoundPan(self.clone(), pan as c_float);
241        }
242    }
243}
244
245impl Drop for Sound {
246    fn drop(&mut self) {
247        unsafe {
248            UnloadSound(self.clone());
249        }
250    }
251}
252
253impl Music {
254    pub fn load(path: impl AsRef<Path>) -> Self {
255        let path = path.as_ref();
256        if !path.exists() {
257            panic!("File doesn't exist.");
258        }
259
260        match path.extension().and_then(OsStr::to_str).unwrap() {
261            "wav" | "qoa" | "ogg" | "mp3" | "flac" | "xm" => {
262                let file = CString::new(path.to_str().unwrap()).unwrap();
263                unsafe { LoadMusicStream(file.as_ptr()) }
264            }
265            _ => panic!("Unsupported file format"),
266        }
267    }
268
269    pub fn is_ready(&self) -> bool {
270        unsafe { IsMusicReady(self.clone()) }
271    }
272
273    pub fn play(&self) {
274        unsafe {
275            PlayMusicStream(self.clone());
276        }
277    }
278
279    pub fn is_playing(&self) -> bool {
280        unsafe { IsMusicStreamPlaying(self.clone()) }
281    }
282
283    pub fn update(&self) {
284        unsafe {
285            UpdateMusicStream(self.clone());
286        }
287    }
288
289    pub fn stop(&self) {
290        unsafe {
291            StopMusicStream(self.clone());
292        }
293    }
294
295    pub fn pause(&self) {
296        unsafe {
297            PauseMusicStream(self.clone());
298        }
299    }
300
301    pub fn resume(&self) {
302        unsafe {
303            ResumeMusicStream(self.clone());
304        }
305    }
306
307    pub fn seek(&self, position: f32) {
308        unsafe {
309            SeekMusicStream(self.clone(), position as c_float);
310        }
311    }
312
313    pub fn set_volume(&self, volume: f32) {
314        unsafe {
315            SetMusicVolume(self.clone(), volume as c_float);
316        }
317    }
318
319    pub fn set_pitch(&self, pitch: f32) {
320        unsafe {
321            SetMusicPitch(self.clone(), pitch as c_float);
322        }
323    }
324
325    pub fn set_pan(&self, pan: f32) {
326        unsafe {
327            SetMusicPan(self.clone(), pan as c_float);
328        }
329    }
330
331    pub fn duration(&self) -> Duration {
332        let dur = unsafe { GetMusicTimeLength(self.clone()) };
333        Duration::from_secs_f32(dur as f32)
334    }
335
336    pub fn time_played(&self) -> Duration {
337        let dur = unsafe { GetMusicTimePlayed(self.clone()) };
338        Duration::from_secs_f32(dur as f32)
339    }
340}
341
342impl Drop for Music {
343    fn drop(&mut self) {
344        unsafe {
345            UnloadMusicStream(self.clone());
346        }
347    }
348}
349
350unsafe extern "C" {
351    fn InitAudioDevice();
352    fn CloseAudioDevice();
353    fn IsAudioDeviceReady() -> bool;
354    fn SetMasterVolume(volume: c_float);
355    fn GetMasterVolume() -> c_float;
356
357    fn LoadWave(fileName: *const c_char) -> Wave;
358    fn IsWaveReady(wave: Wave) -> bool;
359    fn LoadSound(fileName: *const c_char) -> Sound;
360    fn LoadSoundFromWave(wave: Wave) -> Sound;
361    fn IsSoundReady(sound: Sound) -> bool;
362    fn UnloadWave(wave: Wave);
363    fn UnloadSound(sound: Sound);
364    fn ExportWave(wave: Wave, fileName: *const c_char) -> bool;
365
366    fn PlaySound(sound: Sound);
367    fn StopSound(sound: Sound);
368    fn PauseSound(sound: Sound);
369    fn ResumeSound(sound: Sound);
370    fn IsSoundPlaying(sound: Sound) -> bool;
371    fn SetSoundVolume(sound: Sound, volume: c_float);
372    fn SetSoundPitch(sound: Sound, pitch: c_float);
373    fn SetSoundPan(sound: Sound, pan: c_float);
374    fn WaveCrop(wave: *mut Wave, initSample: c_int, finalSample: c_int);
375    fn WaveFormat(wave: *mut Wave, sampleRate: c_int, sampleSize: c_int, channels: c_int);
376
377    fn LoadMusicStream(fileName: *const c_char) -> Music;
378    fn IsMusicReady(music: Music) -> bool;
379    fn UnloadMusicStream(music: Music);
380    fn PlayMusicStream(music: Music);
381    fn IsMusicStreamPlaying(music: Music) -> bool;
382    fn UpdateMusicStream(music: Music);
383    fn StopMusicStream(music: Music);
384    fn PauseMusicStream(music: Music);
385    fn ResumeMusicStream(music: Music);
386    fn SeekMusicStream(music: Music, position: c_float);
387    fn SetMusicVolume(music: Music, volume: c_float);
388    fn SetMusicPitch(music: Music, pitch: c_float);
389    fn SetMusicPan(music: Music, pan: c_float);
390    fn GetMusicTimeLength(music: Music) -> c_float;
391    fn GetMusicTimePlayed(music: Music) -> c_float;
392}