firewheel_core/
clock.rs

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/// When a particular audio event should occur, in units of absolute
21/// audio clock time.
22#[cfg(feature = "scheduled_events")]
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub enum EventInstant {
25    /// The event should happen when the clock reaches the given time in
26    /// seconds.
27    ///
28    /// The value is an absolute time, *NOT* a delta time. Use
29    /// `FirewheelCtx::audio_clock` to get the current time of the clock.
30    Seconds(InstantSeconds),
31
32    /// The event should happen when the clock reaches the given time in
33    /// samples (of a single channel of audio).
34    ///
35    /// The value is an absolute time, *NOT* a delta time. Use
36    /// `FirewheelCtx::audio_clock` to get the current time of the clock.
37    Samples(InstantSamples),
38
39    /// The event should happen when the musical clock reaches the given
40    /// musical time.
41    #[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    /// Convert the instant to the given time in samples.
60    ///
61    /// If this instant is of type [`EventInstant::Musical`] and either
62    /// there is no musical transport or the musical transport is not
63    /// currently playing, then this will return `None`.
64    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/// An absolute audio clock instant in units of seconds.
173#[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    /// Convert to the corresponding musical time.
190    #[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    /// Returns the amount of time elapsed from another instant to this one.
201    ///
202    /// If `earlier` is later than this one, then the returned value will be negative.
203    pub const fn duration_since(&self, earlier: Self) -> DurationSeconds {
204        DurationSeconds(self.0 - earlier.0)
205    }
206
207    /// Returns the amount of time elapsed from another instant to this one, or
208    /// `None`` if that instant is later than this one.
209    pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSeconds> {
210        (self.0 >= earlier.0).then(|| DurationSeconds(self.0 - earlier.0))
211    }
212
213    /// Returns the amount of time elapsed from another instant to this one, or
214    /// zero` if that instant is later than this one.
215    pub const fn saturating_duration_since(&self, earlier: Self) -> DurationSeconds {
216        DurationSeconds((self.0 - earlier.0).max(0.0))
217    }
218}
219
220/// An audio clock duration in units of seconds.
221#[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/// An absolute audio clock instant in units of samples (in a single channel of audio).
355#[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    /// (whole seconds, samples *after* whole seconds)
369    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    /// Convert to the corresponding musical time.
382    #[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    /// Returns the amount of time elapsed from another instant to this one.
401    ///
402    /// If `earlier` is later than this one, then the returned value will be negative.
403    pub const fn duration_since(&self, earlier: Self) -> DurationSamples {
404        DurationSamples(self.0 - earlier.0)
405    }
406
407    /// Returns the amount of time elapsed from another instant to this one, or
408    /// `None`` if that instant is later than this one.
409    pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSamples> {
410        (self.0 >= earlier.0).then(|| DurationSamples(self.0 - earlier.0))
411    }
412
413    /// Returns the amount of time elapsed from another instant to this one, or
414    /// zero` if that instant is later than this one.
415    pub fn saturating_duration_since(&self, earlier: Self) -> DurationSamples {
416        DurationSamples((self.0 - earlier.0).max(0))
417    }
418}
419
420/// An audio clock duration in units of samples (in a single channel of audio).
421#[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    /// (whole seconds, samples *after* whole seconds)
434    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
447/// (whole seconds, samples *after* whole seconds)
448fn whole_seconds_and_fract(samples: i64, sample_rate: NonZeroU32) -> (i64, u32) {
449    // Provide optimized implementations for common sample rates.
450    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/// An absolute audio clock instant in units of musical beats.
592#[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    /// Convert to the corresponding time in seconds.
606    pub fn to_seconds(&self, beats_per_minute: f64) -> InstantSeconds {
607        InstantSeconds(self.0 * 60.0 / beats_per_minute)
608    }
609
610    /// Convert to the corresponding time in samples.
611    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    /// Convert to the corresponding time in seconds.
616    pub fn to_seconds_with_spb(&self, seconds_per_beat: f64) -> InstantSeconds {
617        InstantSeconds(self.0 * seconds_per_beat)
618    }
619
620    /// Convert to the corresponding time in samples.
621    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/// An audio clock duration in units of musical beats.
632#[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/// The time of the internal audio clock.
774///
775/// Note, due to the nature of audio processing, this clock is is *NOT* synced with
776/// the system's time (`Instant::now`). (Instead it is based on the amount of data
777/// that has been processed.) For applications where the timing of audio events is
778/// critical (i.e. a rythm game), sync the game to this audio clock instead of the
779/// OS's clock (`Instant::now()`).
780#[derive(Debug, Clone, Copy, PartialEq)]
781pub struct AudioClock {
782    /// The timestamp from the audio stream, equal to the number of frames
783    /// (samples in a single channel of audio) of data that have been processed
784    /// since the Firewheel context was first started.
785    ///
786    /// Note, generally this value will always count up, but there may be a
787    /// few edge cases that cause this value to be less than the previous call,
788    /// such as when the sample rate of the stream has been changed.
789    ///
790    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
791    /// does *NOT* account for any output underflows (underruns) that may have
792    /// occured. For applications where the timing of audio events is critical (i.e.
793    /// a rythm game), sync the game to this audio clock.
794    pub samples: InstantSamples,
795
796    /// The timestamp from the audio stream, equal to the number of seconds of
797    /// data that have been processed since the Firewheel context was first started.
798    ///
799    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
800    /// does *NOT* account for any output underflows (underruns) that may have
801    /// occured. For applications where the timing of audio events is critical (i.e.
802    /// a rythm game), sync the game to this audio clock.
803    pub seconds: InstantSeconds,
804
805    /// The current time of the playhead of the musical transport.
806    ///
807    /// If no musical transport is present, then this will be `None`.
808    ///
809    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
810    /// does *NOT* account for any output underflows (underruns) that may have
811    /// occured. For applications where the timing of audio events is critical (i.e.
812    /// a rythm game), sync the game to this audio clock.
813    #[cfg(feature = "musical_transport")]
814    pub musical: Option<InstantMusical>,
815
816    /// This is `true` if a musical transport is present and it is not paused,
817    /// `false` otherwise.
818    #[cfg(feature = "musical_transport")]
819    pub transport_is_playing: bool,
820
821    /// The instant the audio clock was last updated.
822    ///
823    /// If the audio thread is not currently running, then this will be `None`.
824    ///
825    /// Note, if this was returned via `FirewheelCtx::audio_clock_corrected()`, then
826    /// `samples`, `seconds`, and `musical` have already taken this delay into
827    /// account.
828    pub update_instant: Option<Instant>,
829}