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