1#[cfg(not(feature = "std"))]
2use num_traits::Float;
3
4use bevy_platform::time::Instant;
5use core::num::NonZeroU32;
6use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
7
8#[cfg(feature = "scheduled_events")]
9use crate::node::ProcInfo;
10
11#[cfg(feature = "musical_transport")]
12mod transport;
13#[cfg(feature = "musical_transport")]
14pub use transport::*;
15
16#[cfg(feature = "scheduled_events")]
18#[derive(Debug, Clone, Copy, PartialEq)]
19#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub enum EventInstant {
22 AtClockSeconds(InstantSeconds),
28
29 AtClockSamples(InstantSamples),
35
36 DelaySeconds(DurationSeconds),
42
43 DelaySamples(DurationSamples),
49
50 DelaySecondsFromMarker(DurationSeconds),
61
62 DelaySamplesFromMarker(DurationSamples),
74
75 #[cfg(feature = "musical_transport")]
78 AtClockMusical(InstantMusical),
79}
80
81#[cfg(feature = "scheduled_events")]
82impl EventInstant {
83 pub fn is_musical(&self) -> bool {
84 #[cfg(feature = "musical_transport")]
85 return matches!(self, EventInstant::AtClockMusical(_));
86
87 #[cfg(not(feature = "musical_transport"))]
88 return false;
89 }
90
91 pub fn to_samples(&self, proc_info: &ProcInfo) -> Option<InstantSamples> {
97 match self {
98 EventInstant::AtClockSamples(samples) => Some(*samples),
99 EventInstant::AtClockSeconds(seconds) => {
100 Some(seconds.to_samples(proc_info.sample_rate))
101 }
102 EventInstant::DelaySamples(samples) => Some(proc_info.clock_samples + *samples),
103 EventInstant::DelaySeconds(seconds) => {
104 Some(proc_info.clock_samples + seconds.to_samples(proc_info.sample_rate))
105 }
106 EventInstant::DelaySamplesFromMarker(samples) => {
107 Some(proc_info.last_marker_instant + *samples)
108 }
109 EventInstant::DelaySecondsFromMarker(seconds) => {
110 Some(proc_info.last_marker_instant + seconds.to_samples(proc_info.sample_rate))
111 }
112 #[cfg(feature = "musical_transport")]
113 EventInstant::AtClockMusical(musical) => proc_info.musical_to_samples(*musical),
114 }
115 }
116}
117
118#[cfg(feature = "scheduled_events")]
119impl From<InstantSeconds> for EventInstant {
120 fn from(value: InstantSeconds) -> Self {
121 Self::AtClockSeconds(value)
122 }
123}
124
125#[cfg(feature = "scheduled_events")]
126impl From<InstantSamples> for EventInstant {
127 fn from(value: InstantSamples) -> Self {
128 Self::AtClockSamples(value)
129 }
130}
131
132#[cfg(feature = "scheduled_events")]
133impl From<DurationSeconds> for EventInstant {
134 fn from(value: DurationSeconds) -> Self {
135 Self::DelaySeconds(value)
136 }
137}
138
139#[cfg(feature = "scheduled_events")]
140impl From<DurationSamples> for EventInstant {
141 fn from(value: DurationSamples) -> Self {
142 Self::DelaySamples(value)
143 }
144}
145
146#[cfg(feature = "musical_transport")]
147impl From<InstantMusical> for EventInstant {
148 fn from(value: InstantMusical) -> Self {
149 Self::AtClockMusical(value)
150 }
151}
152
153#[repr(transparent)]
155#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
156#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
157#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
158pub struct InstantSeconds(pub f64);
159
160impl InstantSeconds {
161 pub const ZERO: Self = Self(0.0);
162
163 pub const fn new(seconds: f64) -> Self {
164 Self(seconds)
165 }
166
167 pub fn to_samples(self, sample_rate: NonZeroU32) -> InstantSamples {
168 InstantSamples(seconds_to_samples(self.0, sample_rate))
169 }
170
171 #[cfg(feature = "musical_transport")]
173 pub fn to_musical(
174 self,
175 transport: &MusicalTransport,
176 transport_start: InstantSeconds,
177 speed_multiplier: f64,
178 ) -> InstantMusical {
179 transport.seconds_to_musical(self, transport_start, speed_multiplier)
180 }
181
182 pub const fn duration_since(&self, earlier: Self) -> DurationSeconds {
186 DurationSeconds(self.0 - earlier.0)
187 }
188
189 pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSeconds> {
192 (self.0 >= earlier.0).then_some(DurationSeconds(self.0 - earlier.0))
193 }
194
195 pub const fn saturating_duration_since(&self, earlier: Self) -> DurationSeconds {
198 DurationSeconds((self.0 - earlier.0).max(0.0))
199 }
200}
201
202#[repr(transparent)]
204#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
205#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
206#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
207pub struct DurationSeconds(pub f64);
208
209impl DurationSeconds {
210 pub const ZERO: Self = Self(0.0);
211
212 pub const fn new(seconds: f64) -> Self {
213 Self(seconds)
214 }
215
216 pub fn to_samples(self, sample_rate: NonZeroU32) -> DurationSamples {
217 DurationSamples(seconds_to_samples(self.0, sample_rate))
218 }
219}
220
221fn seconds_to_samples(seconds: f64, sample_rate: NonZeroU32) -> i64 {
222 let seconds_i64 = seconds.floor() as i64;
223 let fract_samples_i64 = (seconds.fract() * f64::from(sample_rate.get())).round() as i64;
224
225 (seconds_i64 * i64::from(sample_rate.get())) + fract_samples_i64
226}
227
228impl Add<DurationSeconds> for InstantSeconds {
229 type Output = InstantSeconds;
230 fn add(self, rhs: DurationSeconds) -> Self::Output {
231 Self(self.0 + rhs.0)
232 }
233}
234
235impl Sub<DurationSeconds> for InstantSeconds {
236 type Output = InstantSeconds;
237 fn sub(self, rhs: DurationSeconds) -> Self::Output {
238 Self(self.0 - rhs.0)
239 }
240}
241
242impl AddAssign<DurationSeconds> for InstantSeconds {
243 fn add_assign(&mut self, rhs: DurationSeconds) {
244 *self = *self + rhs;
245 }
246}
247
248impl SubAssign<DurationSeconds> for InstantSeconds {
249 fn sub_assign(&mut self, rhs: DurationSeconds) {
250 *self = *self - rhs;
251 }
252}
253
254impl Sub<InstantSeconds> for InstantSeconds {
255 type Output = DurationSeconds;
256 fn sub(self, rhs: Self) -> Self::Output {
257 DurationSeconds(self.0 - rhs.0)
258 }
259}
260
261impl Add for DurationSeconds {
262 type Output = Self;
263 fn add(self, rhs: Self) -> Self::Output {
264 Self(self.0 + rhs.0)
265 }
266}
267
268impl Sub for DurationSeconds {
269 type Output = Self;
270 fn sub(self, rhs: Self) -> Self::Output {
271 Self(self.0 - rhs.0)
272 }
273}
274
275impl AddAssign for DurationSeconds {
276 fn add_assign(&mut self, rhs: Self) {
277 self.0 += rhs.0;
278 }
279}
280
281impl SubAssign for DurationSeconds {
282 fn sub_assign(&mut self, rhs: Self) {
283 self.0 -= rhs.0;
284 }
285}
286
287impl Mul<f64> for DurationSeconds {
288 type Output = Self;
289 fn mul(self, rhs: f64) -> Self::Output {
290 Self(self.0 * rhs)
291 }
292}
293
294impl Div<f64> for DurationSeconds {
295 type Output = Self;
296 fn div(self, rhs: f64) -> Self::Output {
297 Self(self.0 / rhs)
298 }
299}
300
301impl MulAssign<f64> for DurationSeconds {
302 fn mul_assign(&mut self, rhs: f64) {
303 self.0 *= rhs;
304 }
305}
306
307impl DivAssign<f64> for DurationSeconds {
308 fn div_assign(&mut self, rhs: f64) {
309 self.0 /= rhs;
310 }
311}
312
313impl From<f64> for InstantSeconds {
314 fn from(value: f64) -> Self {
315 Self(value)
316 }
317}
318
319impl From<InstantSeconds> for f64 {
320 fn from(value: InstantSeconds) -> Self {
321 value.0
322 }
323}
324
325impl From<f64> for DurationSeconds {
326 fn from(value: f64) -> Self {
327 Self(value)
328 }
329}
330
331impl From<DurationSeconds> for f64 {
332 fn from(value: DurationSeconds) -> Self {
333 value.0
334 }
335}
336
337#[repr(transparent)]
339#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
340#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
341#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
342pub struct InstantSamples(pub i64);
343
344impl InstantSamples {
345 pub const ZERO: Self = Self(0);
346 pub const MAX: Self = Self(i64::MAX);
347
348 pub const fn new(samples: i64) -> Self {
349 Self(samples)
350 }
351
352 pub fn whole_seconds_and_fract(&self, sample_rate: NonZeroU32) -> (i64, u32) {
354 whole_seconds_and_fract(self.0, sample_rate)
355 }
356
357 pub fn fract_second_samples(&self, sample_rate: NonZeroU32) -> u32 {
358 fract_second_samples(self.0, sample_rate)
359 }
360
361 pub fn to_seconds(self, sample_rate: NonZeroU32, sample_rate_recip: f64) -> InstantSeconds {
362 InstantSeconds(samples_to_seconds(self.0, sample_rate, sample_rate_recip))
363 }
364
365 #[cfg(feature = "musical_transport")]
367 pub fn to_musical(
368 self,
369 transport: &MusicalTransport,
370 transport_start: InstantSamples,
371 speed_multiplier: f64,
372 sample_rate: NonZeroU32,
373 sample_rate_recip: f64,
374 ) -> InstantMusical {
375 transport.samples_to_musical(
376 self,
377 transport_start,
378 speed_multiplier,
379 sample_rate,
380 sample_rate_recip,
381 )
382 }
383
384 pub const fn duration_since(&self, earlier: Self) -> DurationSamples {
388 DurationSamples(self.0 - earlier.0)
389 }
390
391 pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSamples> {
394 (self.0 >= earlier.0).then(|| DurationSamples(self.0 - earlier.0))
395 }
396
397 pub fn saturating_duration_since(&self, earlier: Self) -> DurationSamples {
400 DurationSamples((self.0 - earlier.0).max(0))
401 }
402}
403
404#[repr(transparent)]
406#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
407#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
408#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
409pub struct DurationSamples(pub i64);
410
411impl DurationSamples {
412 pub const ZERO: Self = Self(0);
413
414 pub const fn new(samples: i64) -> Self {
415 Self(samples)
416 }
417
418 pub fn whole_seconds_and_fract(&self, sample_rate: NonZeroU32) -> (i64, u32) {
420 whole_seconds_and_fract(self.0, sample_rate)
421 }
422
423 pub fn fract_second_samples(&self, sample_rate: NonZeroU32) -> u32 {
424 fract_second_samples(self.0, sample_rate)
425 }
426
427 pub fn to_seconds(self, sample_rate: NonZeroU32, sample_rate_recip: f64) -> DurationSeconds {
428 DurationSeconds(samples_to_seconds(self.0, sample_rate, sample_rate_recip))
429 }
430}
431
432fn whole_seconds_and_fract(samples: i64, sample_rate: NonZeroU32) -> (i64, u32) {
434 let (whole_seconds, fract_samples) = match sample_rate.get() {
436 44100 => (samples / 44100, samples % 44100),
437 48000 => (samples / 48000, samples % 48000),
438 sample_rate => (
439 samples / i64::from(sample_rate),
440 samples % i64::from(sample_rate),
441 ),
442 };
443
444 if fract_samples < 0 {
445 (
446 whole_seconds - 1,
447 sample_rate.get() - (fract_samples.unsigned_abs() as u32),
448 )
449 } else {
450 (whole_seconds, fract_samples as u32)
451 }
452}
453
454fn fract_second_samples(samples: i64, sample_rate: NonZeroU32) -> u32 {
455 match sample_rate.get() {
456 44100 => (samples % 44100) as u32,
457 48000 => (samples % 48000) as u32,
458 sample_rate => (samples % i64::from(sample_rate)) as u32,
459 }
460}
461
462fn samples_to_seconds(samples: i64, sample_rate: NonZeroU32, sample_rate_recip: f64) -> f64 {
463 let (whole_seconds, fract_samples) = whole_seconds_and_fract(samples, sample_rate);
464 whole_seconds as f64 + (fract_samples as f64 * sample_rate_recip)
465}
466
467impl Add<DurationSamples> for InstantSamples {
468 type Output = InstantSamples;
469 fn add(self, rhs: DurationSamples) -> Self::Output {
470 Self(self.0 + rhs.0)
471 }
472}
473
474impl Sub<DurationSamples> for InstantSamples {
475 type Output = InstantSamples;
476 fn sub(self, rhs: DurationSamples) -> Self::Output {
477 Self(self.0 - rhs.0)
478 }
479}
480
481impl AddAssign<DurationSamples> for InstantSamples {
482 fn add_assign(&mut self, rhs: DurationSamples) {
483 *self = *self + rhs;
484 }
485}
486
487impl SubAssign<DurationSamples> for InstantSamples {
488 fn sub_assign(&mut self, rhs: DurationSamples) {
489 *self = *self - rhs;
490 }
491}
492
493impl Sub<InstantSamples> for InstantSamples {
494 type Output = DurationSamples;
495 fn sub(self, rhs: Self) -> Self::Output {
496 DurationSamples(self.0 - rhs.0)
497 }
498}
499
500impl Add for DurationSamples {
501 type Output = Self;
502 fn add(self, rhs: Self) -> Self::Output {
503 Self(self.0 + rhs.0)
504 }
505}
506
507impl Sub for DurationSamples {
508 type Output = Self;
509 fn sub(self, rhs: Self) -> Self::Output {
510 Self(self.0 - rhs.0)
511 }
512}
513
514impl AddAssign for DurationSamples {
515 fn add_assign(&mut self, rhs: Self) {
516 self.0 += rhs.0;
517 }
518}
519
520impl SubAssign for DurationSamples {
521 fn sub_assign(&mut self, rhs: Self) {
522 self.0 -= rhs.0;
523 }
524}
525
526impl Mul<i64> for DurationSamples {
527 type Output = Self;
528 fn mul(self, rhs: i64) -> Self::Output {
529 Self(self.0 * rhs)
530 }
531}
532
533impl Div<i64> for DurationSamples {
534 type Output = Self;
535 fn div(self, rhs: i64) -> Self::Output {
536 Self(self.0 / rhs)
537 }
538}
539
540impl MulAssign<i64> for DurationSamples {
541 fn mul_assign(&mut self, rhs: i64) {
542 self.0 *= rhs;
543 }
544}
545
546impl DivAssign<i64> for DurationSamples {
547 fn div_assign(&mut self, rhs: i64) {
548 self.0 /= rhs;
549 }
550}
551
552impl From<i64> for InstantSamples {
553 fn from(value: i64) -> Self {
554 Self(value)
555 }
556}
557
558impl From<InstantSamples> for i64 {
559 fn from(value: InstantSamples) -> Self {
560 value.0
561 }
562}
563
564impl From<i64> for DurationSamples {
565 fn from(value: i64) -> Self {
566 Self(value)
567 }
568}
569
570impl From<DurationSamples> for i64 {
571 fn from(value: DurationSamples) -> Self {
572 value.0
573 }
574}
575
576#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
578#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
579#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
580#[cfg(feature = "musical_transport")]
581pub struct InstantMusical(pub f64);
582
583#[cfg(feature = "musical_transport")]
584impl InstantMusical {
585 pub const ZERO: Self = Self(0.0);
586
587 pub const fn new(beats: f64) -> Self {
588 Self(beats)
589 }
590
591 pub fn to_seconds(&self, beats_per_minute: f64) -> InstantSeconds {
593 InstantSeconds(self.0 * 60.0 / beats_per_minute)
594 }
595
596 pub fn to_sample_time(&self, beats_per_minute: f64, sample_rate: NonZeroU32) -> InstantSamples {
598 self.to_seconds(beats_per_minute).to_samples(sample_rate)
599 }
600
601 pub fn to_seconds_with_spb(&self, seconds_per_beat: f64) -> InstantSeconds {
603 InstantSeconds(self.0 * seconds_per_beat)
604 }
605
606 pub fn to_sample_time_with_spb(
608 &self,
609 seconds_per_beat: f64,
610 sample_rate: NonZeroU32,
611 ) -> InstantSamples {
612 self.to_seconds_with_spb(seconds_per_beat)
613 .to_samples(sample_rate)
614 }
615}
616
617#[repr(transparent)]
619#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
620#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
621#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
622#[cfg(feature = "musical_transport")]
623pub struct DurationMusical(pub f64);
624
625#[cfg(feature = "musical_transport")]
626impl DurationMusical {
627 pub const ZERO: Self = Self(0.0);
628
629 pub const fn new(beats: f64) -> Self {
630 Self(beats)
631 }
632}
633
634#[cfg(feature = "musical_transport")]
635impl Add<DurationMusical> for InstantMusical {
636 type Output = InstantMusical;
637 fn add(self, rhs: DurationMusical) -> Self::Output {
638 Self(self.0 + rhs.0)
639 }
640}
641
642#[cfg(feature = "musical_transport")]
643impl Sub<DurationMusical> for InstantMusical {
644 type Output = InstantMusical;
645 fn sub(self, rhs: DurationMusical) -> Self::Output {
646 Self(self.0 - rhs.0)
647 }
648}
649
650#[cfg(feature = "musical_transport")]
651impl AddAssign<DurationMusical> for InstantMusical {
652 fn add_assign(&mut self, rhs: DurationMusical) {
653 *self = *self + rhs;
654 }
655}
656
657#[cfg(feature = "musical_transport")]
658impl SubAssign<DurationMusical> for InstantMusical {
659 fn sub_assign(&mut self, rhs: DurationMusical) {
660 *self = *self - rhs;
661 }
662}
663
664#[cfg(feature = "musical_transport")]
665impl Sub<InstantMusical> for InstantMusical {
666 type Output = DurationMusical;
667 fn sub(self, rhs: Self) -> Self::Output {
668 DurationMusical(self.0 - rhs.0)
669 }
670}
671
672#[cfg(feature = "musical_transport")]
673impl Add for DurationMusical {
674 type Output = Self;
675 fn add(self, rhs: Self) -> Self::Output {
676 Self(self.0 + rhs.0)
677 }
678}
679
680#[cfg(feature = "musical_transport")]
681impl Sub for DurationMusical {
682 type Output = Self;
683 fn sub(self, rhs: Self) -> Self::Output {
684 Self(self.0 - rhs.0)
685 }
686}
687
688#[cfg(feature = "musical_transport")]
689impl AddAssign for DurationMusical {
690 fn add_assign(&mut self, rhs: Self) {
691 self.0 += rhs.0;
692 }
693}
694
695#[cfg(feature = "musical_transport")]
696impl SubAssign for DurationMusical {
697 fn sub_assign(&mut self, rhs: Self) {
698 self.0 -= rhs.0;
699 }
700}
701
702#[cfg(feature = "musical_transport")]
703impl Mul<f64> for DurationMusical {
704 type Output = Self;
705 fn mul(self, rhs: f64) -> Self::Output {
706 Self(self.0 * rhs)
707 }
708}
709
710#[cfg(feature = "musical_transport")]
711impl Div<f64> for DurationMusical {
712 type Output = Self;
713 fn div(self, rhs: f64) -> Self::Output {
714 Self(self.0 / rhs)
715 }
716}
717
718#[cfg(feature = "musical_transport")]
719impl MulAssign<f64> for DurationMusical {
720 fn mul_assign(&mut self, rhs: f64) {
721 self.0 *= rhs;
722 }
723}
724
725#[cfg(feature = "musical_transport")]
726impl DivAssign<f64> for DurationMusical {
727 fn div_assign(&mut self, rhs: f64) {
728 self.0 /= rhs;
729 }
730}
731
732#[cfg(feature = "musical_transport")]
733impl From<f64> for InstantMusical {
734 fn from(value: f64) -> Self {
735 Self(value)
736 }
737}
738
739#[cfg(feature = "musical_transport")]
740impl From<InstantMusical> for f64 {
741 fn from(value: InstantMusical) -> Self {
742 value.0
743 }
744}
745
746#[cfg(feature = "musical_transport")]
747impl From<f64> for DurationMusical {
748 fn from(value: f64) -> Self {
749 Self(value)
750 }
751}
752
753#[cfg(feature = "musical_transport")]
754impl From<DurationMusical> for f64 {
755 fn from(value: DurationMusical) -> Self {
756 value.0
757 }
758}
759
760#[derive(Debug, Clone, Copy, PartialEq)]
768pub struct AudioClock {
769 pub samples: InstantSamples,
782
783 pub seconds: InstantSeconds,
791
792 #[cfg(feature = "musical_transport")]
801 pub musical: Option<InstantMusical>,
802
803 #[cfg(feature = "musical_transport")]
806 pub transport_is_playing: bool,
807
808 pub update_instant: Option<Instant>,
816}