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)]
24#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub enum EventInstant {
27    /// The event should happen when the clock reaches the given time in
28    /// seconds.
29    ///
30    /// The value is an absolute time, *NOT* a delta time. Use
31    /// `FirewheelCtx::audio_clock` to get the current time of the clock.
32    Seconds(InstantSeconds),
33
34    /// The event should happen when the clock reaches the given time in
35    /// samples (of a single channel of audio).
36    ///
37    /// The value is an absolute time, *NOT* a delta time. Use
38    /// `FirewheelCtx::audio_clock` to get the current time of the clock.
39    Samples(InstantSamples),
40
41    /// The event should happen when the musical clock reaches the given
42    /// musical time.
43    #[cfg(feature = "musical_transport")]
44    Musical(InstantMusical),
45}
46
47#[cfg(feature = "scheduled_events")]
48impl EventInstant {
49    pub fn is_musical(&self) -> bool {
50        #[cfg(feature = "musical_transport")]
51        if let EventInstant::Musical(_) = self {
52            return true;
53        } else {
54            return false;
55        }
56
57        #[cfg(not(feature = "musical_transport"))]
58        return false;
59    }
60
61    /// Convert the instant to the given time in samples.
62    ///
63    /// If this instant is of type [`EventInstant::Musical`] and either
64    /// there is no musical transport or the musical transport is not
65    /// currently playing, then this will return `None`.
66    pub fn to_samples(&self, proc_info: &ProcInfo) -> Option<InstantSamples> {
67        match self {
68            EventInstant::Samples(samples) => Some(*samples),
69            EventInstant::Seconds(seconds) => Some(seconds.to_samples(proc_info.sample_rate)),
70            #[cfg(feature = "musical_transport")]
71            EventInstant::Musical(musical) => proc_info.musical_to_samples(*musical),
72        }
73    }
74}
75
76#[cfg(feature = "scheduled_events")]
77impl From<InstantSeconds> for EventInstant {
78    fn from(value: InstantSeconds) -> Self {
79        Self::Seconds(value)
80    }
81}
82
83#[cfg(feature = "scheduled_events")]
84impl From<InstantSamples> for EventInstant {
85    fn from(value: InstantSamples) -> Self {
86        Self::Samples(value)
87    }
88}
89
90#[cfg(feature = "musical_transport")]
91impl From<InstantMusical> for EventInstant {
92    fn from(value: InstantMusical) -> Self {
93        Self::Musical(value)
94    }
95}
96
97#[cfg(feature = "scheduled_events")]
98impl Diff for EventInstant {
99    fn diff<E: crate::diff::EventQueue>(
100        &self,
101        baseline: &Self,
102        path: crate::diff::PathBuilder,
103        event_queue: &mut E,
104    ) {
105        if self != baseline {
106            match self {
107                EventInstant::Seconds(s) => event_queue.push_param(*s, path),
108                EventInstant::Samples(s) => event_queue.push_param(*s, path),
109                #[cfg(feature = "musical_transport")]
110                EventInstant::Musical(m) => event_queue.push_param(*m, path),
111            }
112        }
113    }
114}
115
116#[cfg(feature = "scheduled_events")]
117impl Patch for EventInstant {
118    type Patch = Self;
119
120    fn patch(data: &ParamData, _path: &[u32]) -> Result<Self::Patch, crate::diff::PatchError> {
121        match data {
122            ParamData::InstantSeconds(s) => Ok(EventInstant::Seconds(*s)),
123            ParamData::InstantSamples(s) => Ok(EventInstant::Samples(*s)),
124            #[cfg(feature = "musical_transport")]
125            ParamData::InstantMusical(s) => Ok(EventInstant::Musical(*s)),
126            _ => Err(crate::diff::PatchError::InvalidData),
127        }
128    }
129
130    fn apply(&mut self, patch: Self::Patch) {
131        *self = patch;
132    }
133}
134
135#[cfg(feature = "scheduled_events")]
136impl Diff for Option<EventInstant> {
137    fn diff<E: crate::diff::EventQueue>(
138        &self,
139        baseline: &Self,
140        path: crate::diff::PathBuilder,
141        event_queue: &mut E,
142    ) {
143        if self != baseline {
144            match self {
145                Some(EventInstant::Seconds(s)) => event_queue.push_param(*s, path),
146                Some(EventInstant::Samples(s)) => event_queue.push_param(*s, path),
147                #[cfg(feature = "musical_transport")]
148                Some(EventInstant::Musical(m)) => event_queue.push_param(*m, path),
149                None => event_queue.push_param(ParamData::None, path),
150            }
151        }
152    }
153}
154
155#[cfg(feature = "scheduled_events")]
156impl Patch for Option<EventInstant> {
157    type Patch = Self;
158
159    fn patch(data: &ParamData, _path: &[u32]) -> Result<Self::Patch, crate::diff::PatchError> {
160        match data {
161            ParamData::InstantSeconds(s) => Ok(Some(EventInstant::Seconds(*s))),
162            ParamData::InstantSamples(s) => Ok(Some(EventInstant::Samples(*s))),
163            #[cfg(feature = "musical_transport")]
164            ParamData::InstantMusical(s) => Ok(Some(EventInstant::Musical(*s))),
165            _ => Err(crate::diff::PatchError::InvalidData),
166        }
167    }
168
169    fn apply(&mut self, patch: Self::Patch) {
170        *self = patch;
171    }
172}
173
174/// An absolute audio clock instant in units of seconds.
175#[repr(transparent)]
176#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
177#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
178#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
179pub struct InstantSeconds(pub f64);
180
181impl InstantSeconds {
182    pub const ZERO: Self = Self(0.0);
183
184    pub const fn new(seconds: f64) -> Self {
185        Self(seconds)
186    }
187
188    pub fn to_samples(self, sample_rate: NonZeroU32) -> InstantSamples {
189        InstantSamples(seconds_to_samples(self.0, sample_rate))
190    }
191
192    /// Convert to the corresponding musical time.
193    #[cfg(feature = "musical_transport")]
194    pub fn to_musical(
195        self,
196        transport: &MusicalTransport,
197        transport_start: InstantSeconds,
198        speed_multiplier: f64,
199    ) -> InstantMusical {
200        transport.seconds_to_musical(self, transport_start, speed_multiplier)
201    }
202
203    /// Returns the amount of time elapsed from another instant to this one.
204    ///
205    /// If `earlier` is later than this one, then the returned value will be negative.
206    pub const fn duration_since(&self, earlier: Self) -> DurationSeconds {
207        DurationSeconds(self.0 - earlier.0)
208    }
209
210    /// Returns the amount of time elapsed from another instant to this one, or
211    /// `None`` if that instant is later than this one.
212    pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSeconds> {
213        (self.0 >= earlier.0).then(|| DurationSeconds(self.0 - earlier.0))
214    }
215
216    /// Returns the amount of time elapsed from another instant to this one, or
217    /// zero` if that instant is later than this one.
218    pub const fn saturating_duration_since(&self, earlier: Self) -> DurationSeconds {
219        DurationSeconds((self.0 - earlier.0).max(0.0))
220    }
221}
222
223/// An audio clock duration in units of seconds.
224#[repr(transparent)]
225#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
226#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
227#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
228pub struct DurationSeconds(pub f64);
229
230impl DurationSeconds {
231    pub const ZERO: Self = Self(0.0);
232
233    pub const fn new(seconds: f64) -> Self {
234        Self(seconds)
235    }
236
237    pub fn to_samples(self, sample_rate: NonZeroU32) -> DurationSamples {
238        DurationSamples(seconds_to_samples(self.0, sample_rate))
239    }
240}
241
242fn seconds_to_samples(seconds: f64, sample_rate: NonZeroU32) -> i64 {
243    let seconds_i64 = seconds.floor() as i64;
244    let fract_samples_i64 = (seconds.fract() * f64::from(sample_rate.get())).round() as i64;
245
246    (seconds_i64 * i64::from(sample_rate.get())) + fract_samples_i64
247}
248
249impl Add<DurationSeconds> for InstantSeconds {
250    type Output = InstantSeconds;
251    fn add(self, rhs: DurationSeconds) -> Self::Output {
252        Self(self.0 + rhs.0)
253    }
254}
255
256impl Sub<DurationSeconds> for InstantSeconds {
257    type Output = InstantSeconds;
258    fn sub(self, rhs: DurationSeconds) -> Self::Output {
259        Self(self.0 - rhs.0)
260    }
261}
262
263impl AddAssign<DurationSeconds> for InstantSeconds {
264    fn add_assign(&mut self, rhs: DurationSeconds) {
265        *self = *self + rhs;
266    }
267}
268
269impl SubAssign<DurationSeconds> for InstantSeconds {
270    fn sub_assign(&mut self, rhs: DurationSeconds) {
271        *self = *self - rhs;
272    }
273}
274
275impl Sub<InstantSeconds> for InstantSeconds {
276    type Output = DurationSeconds;
277    fn sub(self, rhs: Self) -> Self::Output {
278        DurationSeconds(self.0 - rhs.0)
279    }
280}
281
282impl Add for DurationSeconds {
283    type Output = Self;
284    fn add(self, rhs: Self) -> Self::Output {
285        Self(self.0 + rhs.0)
286    }
287}
288
289impl Sub for DurationSeconds {
290    type Output = Self;
291    fn sub(self, rhs: Self) -> Self::Output {
292        Self(self.0 - rhs.0)
293    }
294}
295
296impl AddAssign for DurationSeconds {
297    fn add_assign(&mut self, rhs: Self) {
298        self.0 += rhs.0;
299    }
300}
301
302impl SubAssign for DurationSeconds {
303    fn sub_assign(&mut self, rhs: Self) {
304        self.0 -= rhs.0;
305    }
306}
307
308impl Mul<f64> for DurationSeconds {
309    type Output = Self;
310    fn mul(self, rhs: f64) -> Self::Output {
311        Self(self.0 * rhs)
312    }
313}
314
315impl Div<f64> for DurationSeconds {
316    type Output = Self;
317    fn div(self, rhs: f64) -> Self::Output {
318        Self(self.0 / rhs)
319    }
320}
321
322impl MulAssign<f64> for DurationSeconds {
323    fn mul_assign(&mut self, rhs: f64) {
324        self.0 *= rhs;
325    }
326}
327
328impl DivAssign<f64> for DurationSeconds {
329    fn div_assign(&mut self, rhs: f64) {
330        self.0 /= rhs;
331    }
332}
333
334impl From<f64> for InstantSeconds {
335    fn from(value: f64) -> Self {
336        Self(value)
337    }
338}
339
340impl From<InstantSeconds> for f64 {
341    fn from(value: InstantSeconds) -> Self {
342        value.0
343    }
344}
345
346impl From<f64> for DurationSeconds {
347    fn from(value: f64) -> Self {
348        Self(value)
349    }
350}
351
352impl From<DurationSeconds> for f64 {
353    fn from(value: DurationSeconds) -> Self {
354        value.0
355    }
356}
357
358/// An absolute audio clock instant in units of samples (in a single channel of audio).
359#[repr(transparent)]
360#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
361#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
362#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
363pub struct InstantSamples(pub i64);
364
365impl InstantSamples {
366    pub const ZERO: Self = Self(0);
367    pub const MAX: Self = Self(i64::MAX);
368
369    pub const fn new(samples: i64) -> Self {
370        Self(samples)
371    }
372
373    /// (whole seconds, samples *after* whole seconds)
374    pub fn whole_seconds_and_fract(&self, sample_rate: NonZeroU32) -> (i64, u32) {
375        whole_seconds_and_fract(self.0, sample_rate)
376    }
377
378    pub fn fract_second_samples(&self, sample_rate: NonZeroU32) -> u32 {
379        fract_second_samples(self.0, sample_rate)
380    }
381
382    pub fn to_seconds(self, sample_rate: NonZeroU32, sample_rate_recip: f64) -> InstantSeconds {
383        InstantSeconds(samples_to_seconds(self.0, sample_rate, sample_rate_recip))
384    }
385
386    /// Convert to the corresponding musical time.
387    #[cfg(feature = "musical_transport")]
388    pub fn to_musical(
389        self,
390        transport: &MusicalTransport,
391        transport_start: InstantSamples,
392        speed_multiplier: f64,
393        sample_rate: NonZeroU32,
394        sample_rate_recip: f64,
395    ) -> InstantMusical {
396        transport.samples_to_musical(
397            self,
398            transport_start,
399            speed_multiplier,
400            sample_rate,
401            sample_rate_recip,
402        )
403    }
404
405    /// Returns the amount of time elapsed from another instant to this one.
406    ///
407    /// If `earlier` is later than this one, then the returned value will be negative.
408    pub const fn duration_since(&self, earlier: Self) -> DurationSamples {
409        DurationSamples(self.0 - earlier.0)
410    }
411
412    /// Returns the amount of time elapsed from another instant to this one, or
413    /// `None`` if that instant is later than this one.
414    pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSamples> {
415        (self.0 >= earlier.0).then(|| DurationSamples(self.0 - earlier.0))
416    }
417
418    /// Returns the amount of time elapsed from another instant to this one, or
419    /// zero` if that instant is later than this one.
420    pub fn saturating_duration_since(&self, earlier: Self) -> DurationSamples {
421        DurationSamples((self.0 - earlier.0).max(0))
422    }
423}
424
425/// An audio clock duration in units of samples (in a single channel of audio).
426#[repr(transparent)]
427#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
428#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
429#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
430pub struct DurationSamples(pub i64);
431
432impl DurationSamples {
433    pub const ZERO: Self = Self(0);
434
435    pub const fn new(samples: i64) -> Self {
436        Self(samples)
437    }
438
439    /// (whole seconds, samples *after* whole seconds)
440    pub fn whole_seconds_and_fract(&self, sample_rate: NonZeroU32) -> (i64, u32) {
441        whole_seconds_and_fract(self.0, sample_rate)
442    }
443
444    pub fn fract_second_samples(&self, sample_rate: NonZeroU32) -> u32 {
445        fract_second_samples(self.0, sample_rate)
446    }
447
448    pub fn to_seconds(self, sample_rate: NonZeroU32, sample_rate_recip: f64) -> DurationSeconds {
449        DurationSeconds(samples_to_seconds(self.0, sample_rate, sample_rate_recip))
450    }
451}
452
453/// (whole seconds, samples *after* whole seconds)
454fn whole_seconds_and_fract(samples: i64, sample_rate: NonZeroU32) -> (i64, u32) {
455    // Provide optimized implementations for common sample rates.
456    let (whole_seconds, fract_samples) = match sample_rate.get() {
457        44100 => (samples / 44100, samples % 44100),
458        48000 => (samples / 48000, samples % 48000),
459        sample_rate => (
460            samples / i64::from(sample_rate),
461            samples % i64::from(sample_rate),
462        ),
463    };
464
465    if fract_samples < 0 {
466        (
467            whole_seconds - 1,
468            sample_rate.get() - (fract_samples.abs() as u32),
469        )
470    } else {
471        (whole_seconds, fract_samples as u32)
472    }
473}
474
475fn fract_second_samples(samples: i64, sample_rate: NonZeroU32) -> u32 {
476    match sample_rate.get() {
477        44100 => (samples % 44100) as u32,
478        48000 => (samples % 48000) as u32,
479        sample_rate => (samples % i64::from(sample_rate)) as u32,
480    }
481}
482
483fn samples_to_seconds(samples: i64, sample_rate: NonZeroU32, sample_rate_recip: f64) -> f64 {
484    let (whole_seconds, fract_samples) = whole_seconds_and_fract(samples, sample_rate);
485    whole_seconds as f64 + (fract_samples as f64 * sample_rate_recip)
486}
487
488impl Add<DurationSamples> for InstantSamples {
489    type Output = InstantSamples;
490    fn add(self, rhs: DurationSamples) -> Self::Output {
491        Self(self.0 + rhs.0)
492    }
493}
494
495impl Sub<DurationSamples> for InstantSamples {
496    type Output = InstantSamples;
497    fn sub(self, rhs: DurationSamples) -> Self::Output {
498        Self(self.0 - rhs.0)
499    }
500}
501
502impl AddAssign<DurationSamples> for InstantSamples {
503    fn add_assign(&mut self, rhs: DurationSamples) {
504        *self = *self + rhs;
505    }
506}
507
508impl SubAssign<DurationSamples> for InstantSamples {
509    fn sub_assign(&mut self, rhs: DurationSamples) {
510        *self = *self - rhs;
511    }
512}
513
514impl Sub<InstantSamples> for InstantSamples {
515    type Output = DurationSamples;
516    fn sub(self, rhs: Self) -> Self::Output {
517        DurationSamples(self.0 - rhs.0)
518    }
519}
520
521impl Add for DurationSamples {
522    type Output = Self;
523    fn add(self, rhs: Self) -> Self::Output {
524        Self(self.0 + rhs.0)
525    }
526}
527
528impl Sub for DurationSamples {
529    type Output = Self;
530    fn sub(self, rhs: Self) -> Self::Output {
531        Self(self.0 - rhs.0)
532    }
533}
534
535impl AddAssign for DurationSamples {
536    fn add_assign(&mut self, rhs: Self) {
537        self.0 += rhs.0;
538    }
539}
540
541impl SubAssign for DurationSamples {
542    fn sub_assign(&mut self, rhs: Self) {
543        self.0 -= rhs.0;
544    }
545}
546
547impl Mul<i64> for DurationSamples {
548    type Output = Self;
549    fn mul(self, rhs: i64) -> Self::Output {
550        Self(self.0 * rhs)
551    }
552}
553
554impl Div<i64> for DurationSamples {
555    type Output = Self;
556    fn div(self, rhs: i64) -> Self::Output {
557        Self(self.0 / rhs)
558    }
559}
560
561impl MulAssign<i64> for DurationSamples {
562    fn mul_assign(&mut self, rhs: i64) {
563        self.0 *= rhs;
564    }
565}
566
567impl DivAssign<i64> for DurationSamples {
568    fn div_assign(&mut self, rhs: i64) {
569        self.0 /= rhs;
570    }
571}
572
573impl From<i64> for InstantSamples {
574    fn from(value: i64) -> Self {
575        Self(value)
576    }
577}
578
579impl From<InstantSamples> for i64 {
580    fn from(value: InstantSamples) -> Self {
581        value.0
582    }
583}
584
585impl From<i64> for DurationSamples {
586    fn from(value: i64) -> Self {
587        Self(value)
588    }
589}
590
591impl From<DurationSamples> for i64 {
592    fn from(value: DurationSamples) -> Self {
593        value.0
594    }
595}
596
597/// An absolute audio clock instant in units of musical beats.
598#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
599#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
600#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
601#[cfg(feature = "musical_transport")]
602pub struct InstantMusical(pub f64);
603
604#[cfg(feature = "musical_transport")]
605impl InstantMusical {
606    pub const ZERO: Self = Self(0.0);
607
608    pub const fn new(beats: f64) -> Self {
609        Self(beats)
610    }
611
612    /// Convert to the corresponding time in seconds.
613    pub fn to_seconds(&self, beats_per_minute: f64) -> InstantSeconds {
614        InstantSeconds(self.0 * 60.0 / beats_per_minute)
615    }
616
617    /// Convert to the corresponding time in samples.
618    pub fn to_sample_time(&self, beats_per_minute: f64, sample_rate: NonZeroU32) -> InstantSamples {
619        self.to_seconds(beats_per_minute).to_samples(sample_rate)
620    }
621
622    /// Convert to the corresponding time in seconds.
623    pub fn to_seconds_with_spb(&self, seconds_per_beat: f64) -> InstantSeconds {
624        InstantSeconds(self.0 * seconds_per_beat)
625    }
626
627    /// Convert to the corresponding time in samples.
628    pub fn to_sample_time_with_spb(
629        &self,
630        seconds_per_beat: f64,
631        sample_rate: NonZeroU32,
632    ) -> InstantSamples {
633        self.to_seconds_with_spb(seconds_per_beat)
634            .to_samples(sample_rate)
635    }
636}
637
638/// An audio clock duration in units of musical beats.
639#[repr(transparent)]
640#[derive(Default, Debug, Clone, Copy, PartialEq, PartialOrd)]
641#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
642#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
643#[cfg(feature = "musical_transport")]
644pub struct DurationMusical(pub f64);
645
646#[cfg(feature = "musical_transport")]
647impl DurationMusical {
648    pub const ZERO: Self = Self(0.0);
649
650    pub const fn new(beats: f64) -> Self {
651        Self(beats)
652    }
653}
654
655#[cfg(feature = "musical_transport")]
656impl Add<DurationMusical> for InstantMusical {
657    type Output = InstantMusical;
658    fn add(self, rhs: DurationMusical) -> Self::Output {
659        Self(self.0 + rhs.0)
660    }
661}
662
663#[cfg(feature = "musical_transport")]
664impl Sub<DurationMusical> for InstantMusical {
665    type Output = InstantMusical;
666    fn sub(self, rhs: DurationMusical) -> Self::Output {
667        Self(self.0 - rhs.0)
668    }
669}
670
671#[cfg(feature = "musical_transport")]
672impl AddAssign<DurationMusical> for InstantMusical {
673    fn add_assign(&mut self, rhs: DurationMusical) {
674        *self = *self + rhs;
675    }
676}
677
678#[cfg(feature = "musical_transport")]
679impl SubAssign<DurationMusical> for InstantMusical {
680    fn sub_assign(&mut self, rhs: DurationMusical) {
681        *self = *self - rhs;
682    }
683}
684
685#[cfg(feature = "musical_transport")]
686impl Sub<InstantMusical> for InstantMusical {
687    type Output = DurationMusical;
688    fn sub(self, rhs: Self) -> Self::Output {
689        DurationMusical(self.0 - rhs.0)
690    }
691}
692
693#[cfg(feature = "musical_transport")]
694impl Add for DurationMusical {
695    type Output = Self;
696    fn add(self, rhs: Self) -> Self::Output {
697        Self(self.0 + rhs.0)
698    }
699}
700
701#[cfg(feature = "musical_transport")]
702impl Sub for DurationMusical {
703    type Output = Self;
704    fn sub(self, rhs: Self) -> Self::Output {
705        Self(self.0 - rhs.0)
706    }
707}
708
709#[cfg(feature = "musical_transport")]
710impl AddAssign for DurationMusical {
711    fn add_assign(&mut self, rhs: Self) {
712        self.0 += rhs.0;
713    }
714}
715
716#[cfg(feature = "musical_transport")]
717impl SubAssign for DurationMusical {
718    fn sub_assign(&mut self, rhs: Self) {
719        self.0 -= rhs.0;
720    }
721}
722
723#[cfg(feature = "musical_transport")]
724impl Mul<f64> for DurationMusical {
725    type Output = Self;
726    fn mul(self, rhs: f64) -> Self::Output {
727        Self(self.0 * rhs)
728    }
729}
730
731#[cfg(feature = "musical_transport")]
732impl Div<f64> for DurationMusical {
733    type Output = Self;
734    fn div(self, rhs: f64) -> Self::Output {
735        Self(self.0 / rhs)
736    }
737}
738
739#[cfg(feature = "musical_transport")]
740impl MulAssign<f64> for DurationMusical {
741    fn mul_assign(&mut self, rhs: f64) {
742        self.0 *= rhs;
743    }
744}
745
746#[cfg(feature = "musical_transport")]
747impl DivAssign<f64> for DurationMusical {
748    fn div_assign(&mut self, rhs: f64) {
749        self.0 /= rhs;
750    }
751}
752
753#[cfg(feature = "musical_transport")]
754impl From<f64> for InstantMusical {
755    fn from(value: f64) -> Self {
756        Self(value)
757    }
758}
759
760#[cfg(feature = "musical_transport")]
761impl From<InstantMusical> for f64 {
762    fn from(value: InstantMusical) -> Self {
763        value.0
764    }
765}
766
767#[cfg(feature = "musical_transport")]
768impl From<f64> for DurationMusical {
769    fn from(value: f64) -> Self {
770        Self(value)
771    }
772}
773
774#[cfg(feature = "musical_transport")]
775impl From<DurationMusical> for f64 {
776    fn from(value: DurationMusical) -> Self {
777        value.0
778    }
779}
780
781/// The time of the internal audio clock.
782///
783/// Note, due to the nature of audio processing, this clock is is *NOT* synced with
784/// the system's time (`Instant::now`). (Instead it is based on the amount of data
785/// that has been processed.) For applications where the timing of audio events is
786/// critical (i.e. a rythm game), sync the game to this audio clock instead of the
787/// OS's clock (`Instant::now()`).
788#[derive(Debug, Clone, Copy, PartialEq)]
789pub struct AudioClock {
790    /// The timestamp from the audio stream, equal to the number of frames
791    /// (samples in a single channel of audio) of data that have been processed
792    /// since the Firewheel context was first started.
793    ///
794    /// Note, generally this value will always count up, but there may be a
795    /// few edge cases that cause this value to be less than the previous call,
796    /// such as when the sample rate of the stream has been changed.
797    ///
798    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
799    /// does *NOT* account for any output underflows (underruns) that may have
800    /// occured. For applications where the timing of audio events is critical (i.e.
801    /// a rythm game), sync the game to this audio clock.
802    pub samples: InstantSamples,
803
804    /// The timestamp from the audio stream, equal to the number of seconds of
805    /// data that have been processed since the Firewheel context was first started.
806    ///
807    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
808    /// does *NOT* account for any output underflows (underruns) that may have
809    /// occured. For applications where the timing of audio events is critical (i.e.
810    /// a rythm game), sync the game to this audio clock.
811    pub seconds: InstantSeconds,
812
813    /// The current time of the playhead of the musical transport.
814    ///
815    /// If no musical transport is present, then this will be `None`.
816    ///
817    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
818    /// does *NOT* account for any output underflows (underruns) that may have
819    /// occured. For applications where the timing of audio events is critical (i.e.
820    /// a rythm game), sync the game to this audio clock.
821    #[cfg(feature = "musical_transport")]
822    pub musical: Option<InstantMusical>,
823
824    /// This is `true` if a musical transport is present and it is not paused,
825    /// `false` otherwise.
826    #[cfg(feature = "musical_transport")]
827    pub transport_is_playing: bool,
828
829    /// The instant the audio clock was last updated.
830    ///
831    /// If the audio thread is not currently running, then this will be `None`.
832    ///
833    /// Note, if this was returned via `FirewheelCtx::audio_clock_corrected()`, then
834    /// `samples`, `seconds`, and `musical` have already taken this delay into
835    /// account.
836    pub update_instant: Option<Instant>,
837}