Skip to main content

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::node::ProcInfo;
10
11#[cfg(feature = "musical_transport")]
12mod transport;
13#[cfg(feature = "musical_transport")]
14pub use transport::*;
15
16/// When a particular audio event should occur.
17#[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    /// The event should happen when the clock reaches the given time in
23    /// seconds.
24    ///
25    /// The value is an absolute time, *NOT* a delta time. Use
26    /// `FirewheelContext::audio_clock` to get the current time of the clock.
27    AtClockSeconds(InstantSeconds),
28
29    /// The event should happen when the clock reaches the given time in
30    /// samples (of a single channel of audio).
31    ///
32    /// The value is an absolute time, *NOT* a delta time. Use
33    /// `FirewheelContext::audio_clock` to get the current time of the clock.
34    AtClockSamples(InstantSamples),
35
36    /// The event should happen the given number of seconds after the
37    /// Firewheel processor receives this event.
38    ///
39    /// This can be useful for creating a sequence of events that can be
40    /// triggered at the lowest latency possible.
41    DelaySeconds(DurationSeconds),
42
43    /// The event should happen the given number of samples (of a single channel
44    /// of audio) after the Firewheel processor receives this event.
45    ///
46    /// This can be useful for creating a sequence of events that can be
47    /// triggered at the lowest latency possible.
48    DelaySamples(DurationSamples),
49
50    /// The event should happen the given number of seconds after the
51    /// last [`NodeEventType::Marker`](crate::event::NodeEventType::Marker)
52    /// event that was sent to this node.
53    ///
54    /// This can be useful for creating a sequence of rapid-fire events that are
55    /// triggered with the lowest latency possible.
56    ///
57    /// If a [`NodeEventType::Marker`](crate::event::NodeEventType::Marker)
58    /// event was never sent to this node, then the start of the stream will be used
59    /// as the marker.
60    DelaySecondsFromMarker(DurationSeconds),
61
62    /// The event should happen the given number of samples (of a single channel
63    /// of audio) after the
64    /// [`NodeEventType::Marker`](crate::event::NodeEventType::Marker)
65    /// event that was sent to this node.
66    ///
67    /// This can be useful for creating a sequence of rapid-fire events that are
68    /// triggered with the lowest latency possible.
69    ///
70    /// If a [`NodeEventType::Marker`](crate::event::NodeEventType::Marker)
71    /// event was never sent to this node, then the start of the stream will be used
72    /// as the marker.
73    DelaySamplesFromMarker(DurationSamples),
74
75    /// The event should happen when the musical clock reaches the given
76    /// musical time.
77    #[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    /// Convert the instant to the given time in samples.
92    ///
93    /// This may return `None` if this instant is of type [`EventInstant::AtClockMusical`]
94    /// and either there is no musical transport or the musical transport is not currently
95    /// playing.
96    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/// An absolute audio clock instant in units of seconds.
154#[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    /// Convert to the corresponding musical time.
172    #[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    /// Returns the amount of time elapsed from another instant to this one.
183    ///
184    /// If `earlier` is later than this one, then the returned value will be negative.
185    pub const fn duration_since(&self, earlier: Self) -> DurationSeconds {
186        DurationSeconds(self.0 - earlier.0)
187    }
188
189    /// Returns the amount of time elapsed from another instant to this one, or
190    /// `None`` if that instant is later than this one.
191    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    /// Returns the amount of time elapsed from another instant to this one, or
196    /// zero` if that instant is later than this one.
197    pub const fn saturating_duration_since(&self, earlier: Self) -> DurationSeconds {
198        DurationSeconds((self.0 - earlier.0).max(0.0))
199    }
200}
201
202/// An audio clock duration in units of seconds.
203#[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/// An absolute audio clock instant in units of samples (in a single channel of audio).
338#[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    /// (whole seconds, samples *after* whole seconds)
353    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    /// Convert to the corresponding musical time.
366    #[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    /// Returns the amount of time elapsed from another instant to this one.
385    ///
386    /// If `earlier` is later than this one, then the returned value will be negative.
387    pub const fn duration_since(&self, earlier: Self) -> DurationSamples {
388        DurationSamples(self.0 - earlier.0)
389    }
390
391    /// Returns the amount of time elapsed from another instant to this one, or
392    /// `None`` if that instant is later than this one.
393    pub fn checked_duration_since(&self, earlier: Self) -> Option<DurationSamples> {
394        (self.0 >= earlier.0).then(|| DurationSamples(self.0 - earlier.0))
395    }
396
397    /// Returns the amount of time elapsed from another instant to this one, or
398    /// zero` if that instant is later than this one.
399    pub fn saturating_duration_since(&self, earlier: Self) -> DurationSamples {
400        DurationSamples((self.0 - earlier.0).max(0))
401    }
402}
403
404/// An audio clock duration in units of samples (in a single channel of audio).
405#[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    /// (whole seconds, samples *after* whole seconds)
419    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
432/// (whole seconds, samples *after* whole seconds)
433fn whole_seconds_and_fract(samples: i64, sample_rate: NonZeroU32) -> (i64, u32) {
434    // Provide optimized implementations for common sample rates.
435    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/// An absolute audio clock instant in units of musical beats.
577#[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    /// Convert to the corresponding time in seconds.
592    pub fn to_seconds(&self, beats_per_minute: f64) -> InstantSeconds {
593        InstantSeconds(self.0 * 60.0 / beats_per_minute)
594    }
595
596    /// Convert to the corresponding time in samples.
597    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    /// Convert to the corresponding time in seconds.
602    pub fn to_seconds_with_spb(&self, seconds_per_beat: f64) -> InstantSeconds {
603        InstantSeconds(self.0 * seconds_per_beat)
604    }
605
606    /// Convert to the corresponding time in samples.
607    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/// An audio clock duration in units of musical beats.
618#[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/// The time of the internal audio clock.
761///
762/// Note, due to the nature of audio processing, this clock is is *NOT* synced with
763/// the system's time (`Instant::now`). (Instead it is based on the amount of data
764/// that has been processed.) For applications where the timing of audio events is
765/// critical (i.e. a rhythm game), sync the game to this audio clock instead of the
766/// OS's clock (`Instant::now()`).
767#[derive(Debug, Clone, Copy, PartialEq)]
768pub struct AudioClock {
769    /// The timestamp from the audio stream, equal to the number of frames
770    /// (samples in a single channel of audio) of data that have been processed
771    /// since the Firewheel context was first started.
772    ///
773    /// Note, generally this value will always count up, but there may be a
774    /// few edge cases that cause this value to be less than the previous call,
775    /// such as when the sample rate of the stream has been changed.
776    ///
777    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
778    /// does *NOT* account for any output underflows (underruns) that may have
779    /// occurred. For applications where the timing of audio events is critical (i.e.
780    /// a rhythm game), sync the game to this audio clock.
781    pub samples: InstantSamples,
782
783    /// The timestamp from the audio stream, equal to the number of seconds of
784    /// data that have been processed since the Firewheel context was first started.
785    ///
786    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
787    /// does *NOT* account for any output underflows (underruns) that may have
788    /// occurred. For applications where the timing of audio events is critical (i.e.
789    /// a rhythm game), sync the game to this audio clock.
790    pub seconds: InstantSeconds,
791
792    /// The current time of the playhead of the musical transport.
793    ///
794    /// If no musical transport is present, then this will be `None`.
795    ///
796    /// Note, this value is *NOT* synced to the system's time (`Instant::now`), and
797    /// does *NOT* account for any output underflows (underruns) that may have
798    /// occurred. For applications where the timing of audio events is critical (i.e.
799    /// a rhythm game), sync the game to this audio clock.
800    #[cfg(feature = "musical_transport")]
801    pub musical: Option<InstantMusical>,
802
803    /// This is `true` if a musical transport is present and it is not paused,
804    /// `false` otherwise.
805    #[cfg(feature = "musical_transport")]
806    pub transport_is_playing: bool,
807
808    /// The instant the audio clock was last updated.
809    ///
810    /// If the audio thread is not currently running, then this will be `None`.
811    ///
812    /// Note, if this was returned via `FirewheelContext::audio_clock_corrected()`, then
813    /// `samples`, `seconds`, and `musical` have already taken this delay into
814    /// account.
815    pub update_instant: Option<Instant>,
816}