stereokit_rust/
sound.rs

1use crate::{
2    StereoKitError,
3    maths::{Bool32T, Vec3},
4    system::IAsset,
5};
6use std::{
7    ffi::{CStr, CString, c_char},
8    path::Path,
9    ptr::NonNull,
10};
11
12/// This class represents a sound effect! Excellent for blips and bloops and little clips that you might play around
13/// your scene. Right now, this supports .wav, .mp3, and procedurally generated noises!
14///
15/// On HoloLens 2, sounds are automatically processed on the HPU, freeing up the CPU for more of your app’s code. To
16/// simulate this same effect on your development PC, you need to enable spatial sound on your audio endpoint. To do
17/// this, right click the speaker icon in your system tray, navigate to “Spatial sound”, and choose “Windows Sonic for
18/// Headphones.” For more information, visit <https://docs.microsoft.com/en-us/windows/win32/coreaudio/spatial-sound>
19/// <https://stereokit.net/Pages/StereoKit/Sound.html>
20///
21/// ### Examples
22/// ```
23/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
24/// use stereokit_rust::{maths::{Vec3, Quat, Matrix}, mesh::Mesh, material::Material,  
25///                      sound::Sound, util::named_colors};
26///
27/// let mesh = Mesh::generate_cube(Vec3::ONE * 1.6, None);
28/// let material = Material::unlit().tex_file_copy("textures/sound.jpeg", true, None)
29///                    .expect("sound.jpeg should be there");
30/// let mut position = Vec3::new(-0.5, 0.0, 0.5);
31/// let rotation = Quat::from_angles(45.0, 45.0, 45.0);
32/// let mut transform = Matrix::IDENTITY;
33///
34/// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3")
35///                          .expect("plane_engine.mp3 should be there");
36/// plane_sound.id("sound_plane").decibels(70.0);
37///
38/// let mut plane_sound_inst = plane_sound.play(position, Some(1.0));
39///
40/// number_of_steps = 150;
41/// filename_scr = "screenshots/sound.jpeg";
42/// test_screenshot!( // !!!! Get a proper main loop !!!!
43///     transform.update_t_r(&position, &rotation);
44///     mesh.draw(token, &material, transform, Some(named_colors::CYAN.into()), None);
45///     if iter == 0 {
46///         assert!(plane_sound_inst.is_playing());
47///         position = Vec3::new(0.0, 0.0, -1.0);
48///         plane_sound_inst
49///             .position(position)
50///             .volume(0.5);
51///     } else if iter == 100 {
52///         assert!(plane_sound_inst.is_playing());
53///         assert_eq!(plane_sound_inst.get_position(), Vec3::new(0.0, 0.0, -1.0));
54///         assert_eq!(plane_sound_inst.get_volume(), 0.5);
55///         plane_sound_inst.stop();
56///         assert!(!plane_sound_inst.is_playing());
57///    }
58/// );
59/// ```
60/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/sound.jpeg" alt="screenshot" width="200">
61#[repr(C)]
62#[derive(Debug, PartialEq)]
63pub struct Sound(pub NonNull<_SoundT>);
64
65impl Drop for Sound {
66    fn drop(&mut self) {
67        unsafe { sound_release(self.0.as_ptr()) };
68    }
69}
70impl AsRef<Sound> for Sound {
71    fn as_ref(&self) -> &Sound {
72        self
73    }
74}
75
76/// StereoKit internal type.
77#[repr(C)]
78#[derive(Debug)]
79pub struct _SoundT {
80    _unused: [u8; 0],
81}
82
83/// StereoKit ffi type.
84pub type SoundT = *mut _SoundT;
85
86unsafe impl Send for Sound {}
87unsafe impl Sync for Sound {}
88
89unsafe extern "C" {
90    pub fn sound_find(id: *const c_char) -> SoundT;
91    pub fn sound_set_id(sound: SoundT, id: *const c_char);
92    pub fn sound_get_id(sound: SoundT) -> *const c_char;
93    pub fn sound_create(filename_utf8: *const c_char) -> SoundT;
94    pub fn sound_create_stream(buffer_duration: f32) -> SoundT;
95    pub fn sound_create_samples(in_arr_samples_at_48000s: *const f32, sample_count: u64) -> SoundT;
96    pub fn sound_generate(
97        audio_generator: Option<unsafe extern "C" fn(sample_time: f32) -> f32>,
98        duration: f32,
99    ) -> SoundT;
100    pub fn sound_write_samples(sound: SoundT, in_arr_samples: *const f32, sample_count: u64);
101    pub fn sound_read_samples(sound: SoundT, out_arr_samples: *mut f32, sample_count: u64) -> u64;
102    pub fn sound_unread_samples(sound: SoundT) -> u64;
103    pub fn sound_total_samples(sound: SoundT) -> u64;
104    pub fn sound_cursor_samples(sound: SoundT) -> u64;
105    pub fn sound_get_decibels(sound: SoundT) -> f32;
106    pub fn sound_set_decibels(sound: SoundT, decibels: f32);
107    pub fn sound_play(sound: SoundT, at: Vec3, volume: f32) -> SoundInst;
108    pub fn sound_duration(sound: SoundT) -> f32;
109    pub fn sound_addref(sound: SoundT);
110    pub fn sound_release(sound: SoundT);
111}
112
113impl IAsset for Sound {
114    // fn id(&mut self, id: impl AsRef<str>) {
115    //     self.id(id);
116    // }
117
118    fn get_id(&self) -> &str {
119        self.get_id()
120    }
121}
122
123// Default is click
124impl Default for Sound {
125    fn default() -> Self {
126        Sound::click()
127    }
128}
129
130impl Sound {
131    /// Create a sound used for streaming audio in or out! This is useful for things like reading from a microphone
132    /// stream, or playing audio from a source streaming over the network, or even procedural sounds that are generated on the fly!
133    /// Use stream sounds with the WriteSamples and ReadSamples functions.
134    /// <https://stereokit.net/Pages/StereoKit/Sound/CreateStream.html>
135    /// * `stream_buffer_duration` - How much audio time should this stream be able to hold without writing back over
136    ///   itself?
137    ///
138    /// see also [`sound_create_stream`] [`Sound::from_samples`]
139    /// ### Examples
140    /// ```
141    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
142    /// use stereokit_rust::sound::Sound;
143    ///
144    /// let mut stream_sound = Sound::create_stream(0.5).
145    ///                            expect("A sound stream should be created");
146    /// assert!(stream_sound.get_id().starts_with("auto/sound_"));
147    /// stream_sound.id("sound_stream");
148    ///
149    /// let mut samples: Vec<f32> = vec![0.0; 48000];
150    /// for i in 0..48000 {
151    ///     samples[i] = (i as f32 / 48000.0).sin();
152    /// }
153    /// stream_sound.write_samples(samples.as_slice(), Some(48000));
154    /// assert_eq!(stream_sound.get_duration(), 0.5);
155    ///
156    /// let mut stream_sound_inst = stream_sound.play([0.0, 0.0, -0.5], Some(0.5));
157    ///
158    /// filename_scr = "screenshots/sound_stream.jpeg";
159    /// number_of_steps = 150;
160    /// test_steps!( // !!!! Get a proper main loop !!!!
161    ///     if iter == 0 {
162    ///         assert!(stream_sound_inst.is_playing());
163    ///     } else if iter == 150 - 2 {
164    ///         assert!(stream_sound_inst.is_playing());
165    ///         stream_sound_inst.stop();
166    ///         assert!(!stream_sound_inst.is_playing());
167    ///     }
168    /// );
169    /// ```
170    pub fn create_stream(stream_buffer_duration: f32) -> Result<Sound, StereoKitError> {
171        Ok(Sound(
172            NonNull::new(unsafe { sound_create_stream(stream_buffer_duration) })
173                .ok_or(StereoKitError::SoundCreate("create_stream failed".into()))?,
174        ))
175    }
176
177    /// Loads a sound effect from file! Currently, StereoKit supports .wav and .mp3 files. Audio is converted to mono.
178    /// <https://stereokit.net/Pages/StereoKit/Sound/FromFile.html>
179    /// * `file_utf8` - Name of the audio file! Supports .wav and .mp3 files.
180    ///
181    /// see also [`sound_create`]
182    /// ### Examples
183    /// ```
184    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
185    /// use stereokit_rust::{maths::Vec3, sound::Sound};
186    ///
187    /// let mut position = Vec3::new(-0.5, 0.0, 0.5);
188    ///
189    /// let mut plane_sound = Sound::from_file("sounds/no.wav")
190    ///                           .expect("no.wav should be in the sounds folder");
191    /// assert_eq!(plane_sound.get_id(), "sounds/no.wav");
192    /// plane_sound.id("sound_plane").decibels(90.0);
193    ///
194    /// let mut plane_sound_inst = plane_sound.play(position, Some(1.0));
195    ///
196    /// number_of_steps = 150;
197    /// test_steps!( // !!!! Get a proper main loop !!!!
198    ///     //TODO: assert!(plane_sound_inst.is_playing());
199    /// );
200    /// ```
201    pub fn from_file(file_utf8: impl AsRef<Path>) -> Result<Sound, StereoKitError> {
202        let path_buf = file_utf8.as_ref().to_path_buf();
203        let c_str = CString::new(path_buf.clone().to_str().ok_or(StereoKitError::SoundFile(path_buf.clone()))?)?;
204
205        Ok(Sound(
206            NonNull::new(unsafe { sound_create(c_str.as_ptr()) }).ok_or(StereoKitError::SoundFile(path_buf))?,
207        ))
208    }
209
210    /// This function will create a sound from an array of samples. Values should range from -1 to +1, and there should
211    /// be 48,000 values per second of audio.
212    /// <https://stereokit.net/Pages/StereoKit/Sound/FromSamples.html>
213    /// * `in_arr_samples_at_48000s` - Values should range from -1 to +1, and there should be 48,000 per second of audio.
214    ///
215    /// see also [`sound_create_samples`] [`Sound::write_samples`]
216    /// ### Examples
217    /// ```
218    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
219    /// use stereokit_rust::sound::Sound;
220    ///
221    /// let mut samples: Vec<f32> = vec![0.0; 48000];
222    /// for i in 0..48000 {
223    ///     samples[i] = (i as f32 / 48000.0).sin();
224    /// }
225    /// let mut sound = Sound::from_samples(&samples)
226    ///                     .expect("Sound should be created from samples");
227    /// assert!(sound.get_id().starts_with("auto/sound_"));
228    /// sound.id("sound_samples");
229    ///
230    /// let mut sound_inst = sound.play([0.0, 0.0, -0.5], Some(0.5));
231    ///
232    /// test_steps!( // !!!! Get a proper main loop !!!!
233    ///     assert!(sound_inst.is_playing());
234    /// );
235    /// ```
236    pub fn from_samples(in_arr_samples_at_48000s: &[f32]) -> Result<Sound, StereoKitError> {
237        Ok(Sound(
238            NonNull::new(unsafe {
239                sound_create_samples(in_arr_samples_at_48000s.as_ptr(), in_arr_samples_at_48000s.len() as u64)
240            })
241            .ok_or(StereoKitError::SoundCreate("from_samples failed".into()))?,
242        ))
243    }
244
245    /// This function will generate a sound from a function you provide! The function is called once for each sample in
246    /// the duration. As an example, it may be called 48,000 times for each second of duration.
247    /// <https://stereokit.net/Pages/StereoKit/Sound/Generate.html>
248    /// * `generator` - This function takes a time value as an argument, which will range from 0-duration, and should
249    ///   return a value from -1 - +1 representing the audio wave at that point in time.
250    /// * `duration` - The duration of the sound in seconds.
251    ///
252    /// see also [`sound_generate`]
253    /// ### Examples
254    /// ```
255    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
256    /// use stereokit_rust::sound::Sound;
257    ///
258    /// unsafe extern "C" fn generator(sample_time: f32) -> f32 {
259    ///     (sample_time * 440.0 * 2.0 * std::f32::consts::PI).sin()
260    /// }
261    /// let mut sound = Sound::generate(generator, 1.0)
262    ///                     .expect("Sound should be created from generator");
263    /// assert!(sound.get_id().starts_with("auto/sound_"));
264    /// sound.id("sound_generator");
265    ///
266    /// let mut sound_inst = sound.play([0.0, 0.0, -0.5], Some(0.5));
267    ///
268    /// number_of_steps = 150;
269    /// test_steps!( // !!!! Get a proper main loop !!!!
270    ///     //assert!(sound_inst.is_playing());
271    /// );
272    /// ```
273    pub fn generate(generator: unsafe extern "C" fn(f32) -> f32, duration: f32) -> Result<Sound, StereoKitError> {
274        Ok(Sound(
275            NonNull::new(unsafe { sound_generate(Some(generator), duration) })
276                .ok_or(StereoKitError::SoundCreate("sound_generate failed".into()))?,
277        ))
278    }
279
280    /// ooks for a Sound asset that’s already loaded, matching the given id!
281    /// <https://stereokit.net/Pages/StereoKit/Sound/Find.html>
282    /// * `id` - Which Sound are you looking for?
283    ///
284    /// see also [`sound_find`] [`Sound::clone_ref`]
285    /// ### Examples
286    /// ```
287    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
288    /// use stereokit_rust::sound::Sound;
289    ///
290    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3")
291    ///                           .expect("plane_engine.mp3 should be in the sounds folder");
292    /// plane_sound.id("sound_plane").decibels(70.0);
293    ///
294    /// let same_sound = Sound::find("sound_plane")
295    ///                             .expect("sound_plane should be found");
296    /// assert_eq!(plane_sound.get_id(), same_sound.get_id());
297    /// assert_eq!(plane_sound, same_sound);
298    /// ```
299    pub fn find<S: AsRef<str>>(id: S) -> Result<Sound, StereoKitError> {
300        let cstr_id = CString::new(id.as_ref())?;
301        Ok(Sound(
302            NonNull::new(unsafe { sound_find(cstr_id.as_ptr()) })
303                .ok_or(StereoKitError::SoundFind(id.as_ref().to_string(), "not found".to_owned()))?,
304        ))
305    }
306
307    /// Creates a clone of the same reference. Basically, the new variable is the same asset. This is what you get by
308    /// calling find() method.
309    /// <https://stereokit.net/Pages/StereoKit/Sound/Find.html>
310    ///
311    /// see also [`sound_find`] [`Sound::find`]
312    /// ### Examples
313    /// ```
314    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
315    /// use stereokit_rust::sound::Sound;
316    ///
317    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3")
318    ///                           .expect("plane_engine.mp3 should be in the sounds folder");
319    ///
320    /// let same_sound =  plane_sound.clone_ref();
321    ///
322    /// assert_eq!(plane_sound.get_id(), same_sound.get_id());
323    /// assert_eq!(plane_sound, same_sound);
324    /// ```
325    pub fn clone_ref(&self) -> Sound {
326        Sound(NonNull::new(unsafe { sound_find(sound_get_id(self.0.as_ptr())) }).expect("<asset>::clone_ref failed!"))
327    }
328
329    /// Sets the unique identifier of this asset resource! This can be helpful for debugging,
330    /// managing your assets, or finding them later on!
331    /// <https://stereokit.net/Pages/StereoKit/Sound/Id.html>
332    ///
333    /// see also [`sound_set_id`]
334    /// ### Examples
335    /// ```
336    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
337    /// use stereokit_rust::sound::Sound;
338    ///
339    /// // A sound from a file will have its file path as its id
340    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3")
341    ///                           .expect("plane_engine.mp3 should be in the sounds folder");
342    /// assert_eq!(plane_sound.get_id(), "sounds/plane_engine.mp3");
343    /// plane_sound.id("plane_sound");
344    /// assert_eq!(plane_sound.get_id(), "plane_sound");
345    ///
346    /// // A sound other than from a file will have an auto id
347    /// let mut stream_sound = Sound::create_stream(0.5).
348    ///                            expect("A sound stream should be created");
349    /// assert!(stream_sound.get_id().starts_with("auto/sound_"));
350    /// stream_sound.id("sound_stream");
351    /// ```
352    pub fn id<S: AsRef<str>>(&mut self, id: S) -> &mut Self {
353        let cstr_id = CString::new(id.as_ref()).unwrap();
354        unsafe { sound_set_id(self.0.as_ptr(), cstr_id.as_ptr()) };
355        self
356    }
357
358    /// Plays the sound at the 3D location specified, using the volume parameter as an additional volume control option!
359    /// Sound volume falls off from 3D location, and can also indicate direction and location through spatial audio
360    /// cues. So make sure the position is where you want people to think it’s from! Currently, if this sound is playing
361    /// somewhere else, it’ll be canceled, and moved to this location.
362    /// <https://stereokit.net/Pages/StereoKit/Sound/Play.html>
363    /// * `at` - World space location for the audio to play at.
364    /// * `volume` - Volume modifier for the effect! 1 means full volume, and 0 means completely silent. If None will
365    ///   have default value of 1.0
366    ///
367    /// see also [`sound_play`] [`SoundInst::position`]
368    /// ### Examples
369    /// ```
370    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
371    /// use stereokit_rust::{maths::Vec3, sound::Sound};
372    ///
373    /// let mut position = Vec3::new(-0.5, 0.0, 0.5);
374    ///
375    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
376    ///                           expect("A sound should be created");
377    /// plane_sound.id("sound_plane").decibels(70.0);
378    ///
379    /// let mut plane_sound_inst = plane_sound.play(position, Some(1.0));
380    ///
381    /// test_steps!( // !!!! Get a proper main loop !!!!
382    ///     assert!(plane_sound_inst.is_playing());
383    ///     if iter == 2 {
384    ///        // Move the sound to the other side
385    ///        plane_sound_inst.position(Vec3::new(0.5, 0.0, 0.5));
386    ///     }
387    /// );
388    /// ```
389    pub fn play(&self, at: impl Into<Vec3>, volume: Option<f32>) -> SoundInst {
390        let volume = volume.unwrap_or(1.0);
391        unsafe { sound_play(self.0.as_ptr(), at.into(), volume) }
392    }
393
394    /// <https://stereokit.net/Pages/StereoKit/Sound/Decibels.html>
395    ///
396    /// see also [`sound_set_decibels`]
397    /// ### Examples
398    /// ```
399    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
400    /// use stereokit_rust::{maths::Vec3, sound::Sound};
401    ///
402    /// let mut position = Vec3::new(-0.5, 0.0, 0.5);
403    ///
404    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
405    ///                           expect("A sound should be created");
406    /// plane_sound.id("sound_plane").decibels(70.0);
407    ///
408    /// let mut plane_sound_inst = plane_sound.play(position, Some(1.0));
409    ///
410    /// test_steps!( // !!!! Get a proper main loop !!!!
411    ///     assert!(plane_sound_inst.is_playing());
412    ///     if iter == 1 {
413    ///         // Change decibel for all instances
414    ///          assert_eq!(plane_sound.get_decibels(), 70.0);
415    ///         plane_sound.decibels(10.0);
416    ///     } else if iter == 2 {
417    ///         assert_eq!(plane_sound.get_decibels(), 10.0);
418    ///     }
419    /// );
420    /// ```
421    pub fn decibels(&self, decibels: f32) {
422        unsafe { sound_set_decibels(self.0.as_ptr(), decibels) }
423    }
424
425    /// This will read samples from the sound stream, starting from the first unread sample. Check UnreadSamples for how
426    /// many samples are available to read.
427    /// <https://stereokit.net/Pages/StereoKit/Sound/ReadSamples.html>
428    /// * `out_arr_samples` - A pre-allocated buffer to read the samples into! This function will stop reading when this
429    ///   buffer is full, or when the sound runs out of unread samples.
430    /// * `sample_count` - The maximum number of samples to read, this should be less than or equal to the number of
431    ///   samples the sampleBuffer can contain.
432    ///
433    /// see also [`sound_read_samples`]
434    /// ### Examples
435    /// ```
436    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
437    /// use stereokit_rust::sound::Sound;
438    ///
439    /// // Half of the samples won't be kept in the buffer (0.5 instead of 1.0)
440    /// let mut stream_sound = Sound::create_stream(0.5).
441    ///                            expect("A sound stream should be created");
442    ///
443    /// let mut samples: Vec<f32> = vec![0.0; 48000];
444    /// for i in 0..48000 {
445    ///     samples[i] = (i as f32 / 48000.0).sin();
446    /// }
447    /// stream_sound.write_samples(samples.as_slice(), Some(48000));
448    ///
449    /// assert_eq!(stream_sound.get_unread_samples(), 24000);
450    ///
451    /// let mut read_samples: Vec<f32> = vec![0.0; 48000];
452    /// let read_count = stream_sound.read_samples(read_samples.as_mut_slice(), Some(48000));
453    /// assert_eq!(read_count, 24000);
454    /// for i in 0..24000 {
455    ///     assert_eq!(samples[i], read_samples[i]);
456    /// }
457    ///
458    /// let read_count = stream_sound.read_samples(read_samples.as_mut_slice(), Some(48000));
459    /// assert_eq!(read_count, 0);
460    /// ```
461    pub fn read_samples(&self, out_arr_samples: &mut [f32], sample_count: Option<u64>) -> u64 {
462        let sample_count = sample_count.unwrap_or(out_arr_samples.len() as u64);
463        unsafe { sound_read_samples(self.0.as_ptr(), out_arr_samples.as_mut_ptr(), sample_count) }
464    }
465
466    /// Only works if this Sound is a stream type! This writes a number of audio samples to the sample buffer, and
467    /// samples should be between -1 and +1. Streams are stored as ring buffers of a fixed size, so writing beyond the
468    /// capacity of the ring buffer will overwrite the oldest samples.
469    ///
470    /// StereoKit uses 48,000 samples per second of audio.
471    ///
472    /// This variation of the method bypasses marshalling memory into C#, so it is the most optimal way to copy sound
473    /// data if your source is already in native memory!
474    /// <https://stereokit.net/Pages/StereoKit/Sound/WriteSamples.html>
475    /// * `in_arr_samples` - An array of audio samples, where each sample is between -1 and +1.
476    /// * `sample_count` - You can use this to write only a subset of the samples in the array, rather than the entire
477    ///   array!
478    ///
479    /// see also [`sound_write_samples`]
480    /// ### Examples
481    /// ```
482    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
483    /// use stereokit_rust::sound::Sound;
484    ///
485    /// // Half of the samples won't be kept in the buffer (0.5 instead of 1.0)
486    /// let mut stream_sound = Sound::create_stream(1.0).
487    ///                            expect("A sound stream should be created");
488    ///
489    /// let mut samples: Vec<f32> = vec![0.0; 48000];
490    /// for i in 0..48000 {
491    ///     samples[i] = (i as f32 / 48000.0).sin();
492    /// }
493    /// stream_sound.write_samples(samples.as_slice(), Some(48000));
494    ///
495    /// assert_eq!(stream_sound.get_unread_samples(), 48000);
496    /// ```
497    pub fn write_samples(&self, in_arr_samples: &[f32], sample_count: Option<u64>) {
498        let sample_count = sample_count.unwrap_or(in_arr_samples.len() as u64);
499        unsafe { sound_write_samples(self.0.as_ptr(), in_arr_samples.as_ptr(), sample_count) };
500    }
501
502    /// The id of this sound
503    /// <https://stereokit.net/Pages/StereoKit/Sound/Id.html>
504    ///
505    /// see also [`sound_get_id`]
506    /// see example in [`Sound::id`]
507    pub fn get_id(&self) -> &str {
508        unsafe { CStr::from_ptr(sound_get_id(self.0.as_ptr())) }.to_str().unwrap()
509    }
510
511    /// This is the current position of the playback cursor, measured in samples from the start of the audio data.
512    /// <https://stereokit.net/Pages/StereoKit/Sound/CursorSamples.html>
513    ///
514    /// see also [`sound_cursor_samples`]
515    /// ### Examples
516    /// ```
517    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
518    /// use stereokit_rust::sound::Sound;
519    ///
520    /// let mut samples: Vec<f32> = vec![0.0; 48000];
521    /// for i in 0..48000 {
522    ///     samples[i] = (i as f32 / 48000.0).sin();
523    /// }
524    /// let mut sound = Sound::from_samples(&samples)
525    ///                     .expect("Sound should be created from samples");
526    ///
527    /// assert_eq!(sound.get_cursor_samples(), 0);
528    ///
529    /// let mut sound_inst = sound.play([0.0, 0.0, -0.5], Some(0.5));
530    /// sound_inst.stop();
531    ///
532    /// test_steps!( // !!!! Get a proper main loop !!!!
533    ///     if iter == 1 {
534    ///         assert_eq!(sound.get_total_samples(), 48000);
535    ///         assert_eq!(sound.get_cursor_samples(), 0);
536    ///         sound.write_samples(&samples, None);
537    ///     } else if iter == 2 {
538    ///        assert_eq!(sound.get_cursor_samples(), 0);
539    ///     }
540    /// );
541    ///
542    pub fn get_cursor_samples(&self) -> u64 {
543        unsafe { sound_cursor_samples(self.0.as_ptr()) }
544    }
545
546    /// This will return the total length of the sound in seconds.
547    /// <https://stereokit.net/Pages/StereoKit/Sound/Duration.html>
548    ///
549    /// see also [`sound_duration`]
550    /// ### Examples
551    /// ```
552    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
553    /// use stereokit_rust::sound::Sound;
554    ///
555    /// let mut samples: Vec<f32> = vec![0.0; 48000];
556    /// for i in 0..48000 {
557    ///     samples[i] = (i as f32 / 48000.0).sin();
558    /// }
559    /// let mut sound = Sound::from_samples(&samples)
560    ///                     .expect("Sound should be created from samples");
561    /// assert_eq!(sound.get_duration(), 1.0);
562    ///
563    /// let mut sound_file = Sound::from_file("sounds/no.wav")
564    ///                          .expect("Sound should be created from file");
565    /// assert_eq!(sound_file.get_duration(), 1.4830834);
566    /// ```
567    pub fn get_duration(&self) -> f32 {
568        unsafe { sound_duration(self.0.as_ptr()) }
569    }
570
571    /// <https://stereokit.net/Pages/StereoKit/Sound/Decibels.html>
572    ///
573    /// see also [`sound_get_decibels`]
574    /// see example in [`Sound::decibels`]
575    pub fn get_decibels(&self) -> f32 {
576        unsafe { sound_get_decibels(self.0.as_ptr()) }
577    }
578
579    /// This will return the total number of audio samples used by the sound! StereoKit currently uses 48,000 samples
580    /// per second for all audio.
581    /// <https://stereokit.net/Pages/StereoKit/Sound/TotalSamples.html>
582    ///
583    /// see also [`sound_total_samples`]
584    /// ### Examples
585    /// ```
586    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
587    /// use stereokit_rust::sound::Sound;
588    ///
589    /// let mut samples: Vec<f32> = vec![0.0; 48000];
590    /// for i in 0..48000 {
591    ///     samples[i] = (i as f32 / 48000.0).sin();
592    /// }
593    /// let mut sound = Sound::from_samples(&samples)
594    ///                     .expect("Sound should be created from samples");
595    /// assert_eq!(sound.get_total_samples(), 48000);
596    ///
597    /// let mut sound_file = Sound::from_file("sounds/no.wav")
598    ///                          .expect("Sound should be created from file");
599    /// assert_eq!(sound_file.get_duration(), 1.4830834);
600    /// // 1.4830834 * 48000 = 71188
601    /// assert_eq!(sound_file.get_total_samples(), 71188);
602    /// ```
603    pub fn get_total_samples(&self) -> u64 {
604        unsafe { sound_total_samples(self.0.as_ptr()) }
605    }
606
607    /// This is the maximum number of samples in the sound that are currently available for reading via ReadSamples!
608    /// ReadSamples will reduce this number by the amount of samples read. This is only really valid for Stream
609    /// sounds, all other sound types will just return 0.
610    /// <https://stereokit.net/Pages/StereoKit/Sound/UnreadSamples.html>
611    ///
612    /// see also [`sound_unread_samples`]
613    /// ### Examples
614    /// ```
615    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
616    /// use stereokit_rust::sound::Sound;
617    ///
618    /// // Half of the samples won't be kept in the buffer (0.5 instead of 1.0)
619    /// let mut stream_sound = Sound::create_stream(1.0).
620    ///                            expect("A sound stream should be created");
621    ///
622    /// let mut samples: Vec<f32> = vec![0.0; 48000];
623    /// for i in 0..48000 {
624    ///     samples[i] = (i as f32 / 48000.0).sin();
625    /// }
626    /// stream_sound.write_samples(samples.as_slice(), Some(48000));
627    ///
628    /// assert_eq!(stream_sound.get_unread_samples(), 48000);
629    ///
630    /// let mut read_samples: Vec<f32> = vec![0.0; 48000];
631    /// let read_count = stream_sound.read_samples(read_samples.as_mut_slice(), Some(48000));
632    /// assert_eq!(read_count, 48000);
633    /// assert_eq!(stream_sound.get_unread_samples(), 0);
634    /// ```
635    pub fn get_unread_samples(&self) -> u64 {
636        unsafe { sound_unread_samples(self.0.as_ptr()) }
637    }
638
639    /// A default click sound that lasts for 300ms. It’s a procedurally generated sound based on a mouse press, with
640    /// extra low frequencies in it.
641    /// <https://stereokit.net/Pages/StereoKit/Sound/Click.html>
642    ///
643    /// ### Examples
644    /// ```
645    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
646    /// use stereokit_rust::{maths::Vec3, sound::Sound};
647    ///
648    /// let mut click_sound = Sound::click();
649    /// assert_eq!(click_sound.get_id(), "default/sound_click");
650    ///
651    /// let mut click_sound_inst = click_sound.play([0.0, 0.0, -0.5], Some(0.5));
652    ///
653    /// number_of_steps = 100;
654    /// test_steps!( // !!!! Get a proper main loop !!!!
655    ///     // TODO: assert!(grab_sound_inst.is_playing());
656    /// );
657    /// ```
658    pub fn click() -> Self {
659        let cstr_id = CString::new("default/sound_click").unwrap();
660        Sound(NonNull::new(unsafe { sound_find(cstr_id.as_ptr()) }).unwrap())
661    }
662
663    /// A default unclick sound that lasts for 300ms. It’s a procedurally generated sound based on a mouse press, with
664    /// extra low frequencies in it.
665    /// <https://stereokit.net/Pages/StereoKit/Sound/Unclick.html>
666    ///
667    /// ### Examples
668    /// ```
669    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
670    /// use stereokit_rust::{maths::Vec3, sound::Sound};
671    ///
672    /// let mut unclick_sound = Sound::unclick();
673    /// assert_eq!(unclick_sound.get_id(), "default/sound_unclick");
674    ///
675    /// let mut unclick_sound_inst = unclick_sound.play([0.0, 0.0, -0.5], Some(0.5));
676    ///
677    /// number_of_steps = 100;
678    /// test_steps!( // !!!! Get a proper main loop !!!!
679    ///     // TODO: assert!(grab_sound_inst.is_playing());
680    /// );
681    /// ```
682    pub fn unclick() -> Self {
683        let cstr_id = CString::new("default/sound_unclick").unwrap();
684        Sound(NonNull::new(unsafe { sound_find(cstr_id.as_ptr()) }).unwrap())
685    }
686
687    /// A default grab sound
688    /// <https://stereokit.net/Pages/StereoKit/Sound.html>
689    ///
690    /// ### Examples
691    /// ```
692    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
693    /// use stereokit_rust::{maths::Vec3, sound::Sound};
694    ///
695    /// let mut grab_sound = Sound::grab();
696    /// assert_eq!(grab_sound.get_id(), "default/sound_grab");
697    ///
698    /// let mut grab_sound_inst = grab_sound.play([0.0, 0.0, -0.5], Some(0.5));
699    ///
700    /// number_of_steps = 100;
701    /// test_steps!( // !!!! Get a proper main loop !!!!
702    ///     // TODO: assert!(grab_sound_inst.is_playing());
703    /// );
704    /// ```
705    pub fn grab() -> Self {
706        let cstr_id = CString::new("default/sound_grab").unwrap();
707        Sound(NonNull::new(unsafe { sound_find(cstr_id.as_ptr()) }).unwrap())
708    }
709
710    /// A default ungrab sound
711    /// <https://stereokit.net/Pages/StereoKit/Sound.html>
712    ///
713    /// ### Examples
714    /// ```
715    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
716    /// use stereokit_rust::{maths::Vec3, sound::Sound};
717    ///
718    /// let mut ungrab_sound = Sound::ungrab();
719    /// assert_eq!(ungrab_sound.get_id(), "default/sound_ungrab");
720    ///
721    /// let mut ungrab_sound_inst = ungrab_sound.play([0.0, 0.0, -0.5], Some(0.5));
722    ///
723    /// number_of_steps = 100;
724    /// test_steps!( // !!!! Get a proper main loop !!!!
725    ///     // TODO: assert!(ungrab_sound_inst.is_playing());
726    /// );
727    /// ```
728    pub fn ungrab() -> Self {
729        let cstr_id = CString::new("default/sound_ungrab").unwrap();
730        Sound(NonNull::new(unsafe { sound_find(cstr_id.as_ptr()) }).unwrap())
731    }
732}
733
734/// This represents a play instance of a Sound! You can get one when you call Sound::play(). This allows you to do things
735/// like cancel a piece of audio early, or change the volume and position of it as it’s playing.
736/// <https://stereokit.net/Pages/StereoKit/SoundInst.html>
737///
738/// see also: [`Sound`]
739/// /// ### Examples
740/// ```
741/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
742/// use stereokit_rust::{maths::{Vec3, Matrix}, mesh::Mesh, material::Material,
743///                      sound::Sound, util::named_colors};
744///
745/// let sphere = Mesh::generate_sphere(0.5, None);
746/// let material = Material::pbr().tex_file_copy("textures/sound.jpeg", true, None)
747///                    .expect("sound.jpeg should be there");
748/// let mut position1 = Vec3::new(-0.5, 0.0, 0.5);
749/// let mut position2 = Vec3::new( 0.5, 0.0, 0.5);
750///
751/// let mut plane_sound1 = Sound::from_file("sounds/no.wav")
752///                           .expect("no.wav should be there");
753/// plane_sound1.id("sound_plane1").decibels(70.0);
754/// let mut plane_sound_inst1 = plane_sound1.play(position1, Some(1.0));
755///
756/// let mut plane_sound2 = Sound::from_file("sounds/no.wav")
757///                           .expect("no.wav should be there");
758/// plane_sound2.id("sound_plane2").decibels(70.0);
759/// let mut plane_sound_inst2 = plane_sound2.play(position2, Some(1.0));
760/// plane_sound_inst2.stop();
761///
762/// number_of_steps = 150;
763/// filename_scr = "screenshots/sound_inst.jpeg";
764/// test_screenshot!( // !!!! Get a proper main loop !!!!
765///     let transform1 = Matrix::t(position1);
766///     let transform2 = Matrix::t(position2);
767///     sphere.draw(token, &material, transform1, Some(named_colors::PINK.into()), None  );
768///     sphere.draw(token, &material, transform2, Some(named_colors::LIGHT_GREEN.into()), None  );
769///
770///     if iter == 0 {
771///         //TODO: assert!(plane_sound_inst1.is_playing());
772///         assert!(!plane_sound_inst2.is_playing());
773///         position1 = Vec3::new(-0.3, 0.0, 0.3);
774///         plane_sound_inst1
775///             .position(position1)
776///             .volume(0.5);
777///     } else if iter == 150 - 2 {
778///         //TODO: assert!(plane_sound_inst1.is_playing());
779///         position2 = Vec3::new(0.3, 0.0, 0.3);
780///         plane_sound_inst2 = plane_sound2.play(position2, Some(1.0));
781///         assert!(plane_sound_inst2.is_playing());
782///    }
783/// );
784/// ```
785/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/sound_inst.jpeg" alt="screenshot" width="200">
786#[repr(C)]
787#[derive(Debug, Copy, Clone, PartialEq)]
788pub struct SoundInst {
789    pub _id: u16,
790    pub _slot: i16,
791}
792
793unsafe extern "C" {
794    pub fn sound_inst_stop(sound_inst: SoundInst);
795    pub fn sound_inst_is_playing(sound_inst: SoundInst) -> Bool32T;
796    pub fn sound_inst_set_pos(sound_inst: SoundInst, pos: Vec3);
797    pub fn sound_inst_get_pos(sound_inst: SoundInst) -> Vec3;
798    pub fn sound_inst_set_volume(sound_inst: SoundInst, volume: f32);
799    pub fn sound_inst_get_volume(sound_inst: SoundInst) -> f32;
800    pub fn sound_inst_get_intensity(sound_inst: SoundInst) -> f32;
801}
802
803impl SoundInst {
804    /// This stops the sound early if it’s still playing. consume the SoundInst as it will not be playable again.
805    /// <https://stereokit.net/Pages/StereoKit/SoundInst/Stop.html>
806    ///
807    /// see also [`sound_inst_stop`]
808    /// ### Examples
809    /// ```
810    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
811    /// use stereokit_rust::{maths::Vec3, sound::Sound};
812    ///
813    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
814    ///                           expect("A sound should be created");
815    /// let mut plane_sound_inst = plane_sound.play([0.0, 0.0, 0.0], Some(1.0));
816    ///
817    /// test_steps!( // !!!! Get a proper main loop !!!!
818    ///     if iter == 1 {
819    ///         plane_sound_inst.stop();
820    ///         assert!(!plane_sound_inst.is_playing());
821    ///     }
822    /// );
823    /// ```
824    pub fn stop(self) {
825        unsafe { sound_inst_stop(self) }
826    }
827
828    /// The 3D position in world space this sound instance is currently playing at. If this instance is no longer
829    /// valid, the position will be at zero.
830    /// <https://stereokit.net/Pages/StereoKit/SoundInst/Position.html>
831    ///
832    /// see also [`sound_inst_set_pos`]
833    /// ### Examples
834    /// ```
835    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
836    /// use stereokit_rust::{maths::Vec3, sound::Sound};
837    ///
838    /// let mut position = Vec3::new(-2.5, 0.0, 0.5);
839    ///
840    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
841    ///                           expect("A sound should be created");
842    /// plane_sound.id("sound_plane").decibels(70.0);
843    ///
844    /// let mut plane_sound_inst = plane_sound.play(position, None);
845    /// assert_eq!(plane_sound_inst.get_position(), position);
846    ///
847    /// number_of_steps = 150;
848    /// test_steps!( // !!!! Get a proper main loop !!!!
849    ///     position += Vec3::new(0.0001, 0.0, 0.0);
850    ///     plane_sound_inst.position(position);
851    /// );
852    /// ```
853    pub fn position(&mut self, at: impl Into<Vec3>) -> &mut Self {
854        unsafe { sound_inst_set_pos(*self, at.into()) }
855        self
856    }
857
858    /// The volume multiplier of this Sound instance! A number between 0 and 1, where 0 is silent, and 1 is full volume.
859    /// <https://stereokit.net/Pages/StereoKit/SoundInst/Volume.html>
860    ///
861    /// see also [`sound_inst_set_volume`]
862    /// ### Examples
863    /// ```
864    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
865    /// use stereokit_rust::{maths::Vec3, sound::Sound, system::Assets};
866    ///
867    /// let mut position = Vec3::new(0.0, 0.0, 0.5);
868    /// let mut volume = 0.0;
869    ///
870    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
871    ///                           expect("A sound should be created");
872    /// plane_sound.id("sound_plane");
873    /// Assets::block_for_priority(i32::MAX);
874    ///
875    /// let mut plane_sound_inst = plane_sound.play(position, None);
876    /// plane_sound_inst.volume(0.005);
877    ///
878    /// number_of_steps = 150;
879    /// test_steps!( // !!!! Get a proper main loop !!!!
880    ///     volume += 0.01;
881    ///     plane_sound_inst.volume(volume);
882    /// );
883    /// ```
884    pub fn volume(&mut self, volume: f32) -> &mut Self {
885        unsafe { sound_inst_set_volume(*self, volume) }
886        self
887    }
888
889    /// The 3D position in world space this sound instance is currently playing at. If this instance is no longer
890    /// valid, the position will be at zero.
891    /// <https://stereokit.net/Pages/StereoKit/SoundInst/Position.html>
892    ///
893    /// see also [`sound_inst_get_pos`]
894    /// see example in [`SoundInst::position`]
895    pub fn get_position(&self) -> Vec3 {
896        unsafe { sound_inst_get_pos(*self) }
897    }
898
899    /// The volume multiplier of this Sound instance! A number between 0 and 1, where 0 is silent, and 1 is full volume.
900    /// <https://stereokit.net/Pages/StereoKit/SoundInst/Volume.html>
901    ///
902    /// see also [`sound_inst_get_volume`]
903    /// see example in [`SoundInst::volume`]
904    pub fn get_volume(&self) -> f32 {
905        unsafe { sound_inst_get_volume(*self) }
906    }
907
908    /// The maximum intensity of the sound data since the last frame, as a value from 0-1. This is unaffected by its 3d
909    /// position or volume settings, and is straight from the audio file's data.
910    /// <https://stereokit.net/Pages/StereoKit/SoundInst/Intensity.html>
911    ///
912    /// see also [`sound_inst_get_intensity`]
913    /// ### Examples
914    /// ```
915    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
916    /// use stereokit_rust::{maths::Vec3, sound::Sound};
917    ///
918    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
919    ///                           expect("A sound should be created");
920    /// plane_sound.id("sound_plane").decibels(70.0);
921    ///
922    /// let mut plane_sound_inst = plane_sound.play([0.0, 0.0, 0.0], Some(1.0));
923    /// plane_sound_inst.volume(1.0);
924    ///
925    ///
926    /// test_steps!( // !!!! Get a proper main loop !!!!
927    ///     assert_eq!(plane_sound_inst.get_intensity(), 0.0);
928    ///     plane_sound_inst.stop();
929    /// );
930    /// ```
931    pub fn get_intensity(&self) -> f32 {
932        unsafe { sound_inst_get_intensity(*self) }
933    }
934
935    /// Is this Sound instance currently playing? For streaming assets, this will be true even if they don’t have any
936    /// new data in them, and they’re just idling at the end of their data.
937    /// <https://stereokit.net/Pages/StereoKit/SoundInst/IsPlaying.html>
938    ///
939    /// see also [`sound_inst_is_playing`]
940    /// ### Examples
941    /// ```
942    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
943    /// use stereokit_rust::{maths::Vec3, sound::Sound};
944    ///
945    /// let mut plane_sound = Sound::from_file("sounds/plane_engine.mp3").
946    ///                           expect("A sound should be created");
947    /// let mut plane_sound_inst = plane_sound.play([0.0, 0.0, 0.0], Some(1.0));
948    ///
949    /// test_steps!( // !!!! Get a proper main loop !!!!
950    ///     if iter == 1 {
951    ///         assert!(plane_sound_inst.is_playing());
952    ///         plane_sound_inst.stop();
953    ///     } else if iter > 1 {
954    ///         assert!(!plane_sound_inst.is_playing());
955    ///     }
956    /// );
957    /// ```
958    pub fn is_playing(&self) -> bool {
959        unsafe { sound_inst_is_playing(*self) != 0 }
960    }
961}