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