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}