1use std::time::Duration;
4
5use crate::{
6 common::{ChannelCount, SampleRate},
7 math::{nz, TAU},
8 source::SeekError,
9 Float, Sample, Source,
10};
11
12#[inline]
14pub fn chirp(
15 sample_rate: SampleRate,
16 start_frequency: Float,
17 end_frequency: Float,
18 duration: Duration,
19) -> Chirp {
20 Chirp::new(sample_rate, start_frequency, end_frequency, duration)
21}
22
23#[derive(Clone, Debug)]
26pub struct Chirp {
27 start_frequency: Float,
28 end_frequency: Float,
29 sample_rate: SampleRate,
30 total_samples: u64,
31 elapsed_samples: u64,
32}
33
34impl Chirp {
35 fn new(
36 sample_rate: SampleRate,
37 start_frequency: Float,
38 end_frequency: Float,
39 duration: Duration,
40 ) -> Self {
41 Self {
42 sample_rate,
43 start_frequency,
44 end_frequency,
45 total_samples: (duration.as_secs_f64() * sample_rate.get() as f64) as u64,
46 elapsed_samples: 0,
47 }
48 }
49
50 #[allow(dead_code)]
51 fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
52 let mut target = (pos.as_secs_f64() * self.sample_rate.get() as f64) as u64;
53 if target >= self.total_samples {
54 target = self.total_samples;
55 }
56
57 self.elapsed_samples = target;
58 Ok(())
59 }
60}
61
62impl Iterator for Chirp {
63 type Item = Sample;
64
65 fn next(&mut self) -> Option<Self::Item> {
66 let i = self.elapsed_samples;
67 if i >= self.total_samples {
68 return None; }
70
71 let ratio = (i as f64 / self.total_samples as f64) as Float;
72 let freq = self.start_frequency * (1.0 - ratio) + self.end_frequency * ratio;
73 let t = (i as f64 / self.sample_rate().get() as f64) as Float * TAU * freq;
74
75 self.elapsed_samples += 1;
76 Some(t.sin())
77 }
78
79 fn size_hint(&self) -> (usize, Option<usize>) {
80 let remaining = self.total_samples - self.elapsed_samples;
81 (remaining as usize, Some(remaining as usize))
82 }
83}
84
85impl ExactSizeIterator for Chirp {}
86
87impl Source for Chirp {
88 fn current_span_len(&self) -> Option<usize> {
89 None
90 }
91
92 fn channels(&self) -> ChannelCount {
93 nz!(1)
94 }
95
96 fn sample_rate(&self) -> SampleRate {
97 self.sample_rate
98 }
99
100 fn total_duration(&self) -> Option<Duration> {
101 let secs = self.total_samples as f64 / self.sample_rate.get() as f64;
102 Some(Duration::from_secs_f64(secs))
103 }
104}