nandi/jolt-nativepublic Fork 0
90f8b89576cb60fadfc57e138ac4499fcf1d2213
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

synth_tones.rs · 193 lines · 6.0 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago1/* This example expose parameter to pass generator of sample.
2Good starting point for integration of cpal into your application.
3*/
4
5extern crate anyhow;
6extern crate clap;
7extern crate cpal;
8
9use cpal::{
10 traits::{DeviceTrait, HostTrait, StreamTrait},
11 SizedSample, I24, U24,
12};
13use cpal::{FromSample, Sample};
14
15fn main() -> anyhow::Result<()> {
16 let stream = stream_setup_for()?;
17 stream.play()?;
18 std::thread::sleep(std::time::Duration::from_millis(4000));
19 Ok(())
20}
21
22pub enum Waveform {
23 Sine,
24 Square,
25 Saw,
26 Triangle,
27}
28
29pub struct Oscillator {
30 pub sample_rate: f32,
31 pub waveform: Waveform,
32 pub current_sample_index: f32,
33 pub frequency_hz: f32,
34}
35
36impl Oscillator {
37 fn advance_sample(&mut self) {
38 self.current_sample_index = (self.current_sample_index + 1.0) % self.sample_rate;
39 }
40
41 fn set_waveform(&mut self, waveform: Waveform) {
42 self.waveform = waveform;
43 }
44
45 fn calculate_sine_output_from_freq(&self, freq: f32) -> f32 {
46 let two_pi = 2.0 * std::f32::consts::PI;
47 (self.current_sample_index * freq * two_pi / self.sample_rate).sin()
48 }
49
50 fn is_multiple_of_freq_above_nyquist(&self, multiple: f32) -> bool {
51 self.frequency_hz * multiple > self.sample_rate / 2.0
52 }
53
54 fn sine_wave(&mut self) -> f32 {
55 self.advance_sample();
56 self.calculate_sine_output_from_freq(self.frequency_hz)
57 }
58
59 fn generative_waveform(&mut self, harmonic_index_increment: i32, gain_exponent: f32) -> f32 {
60 self.advance_sample();
61 let mut output = 0.0;
62 let mut i = 1;
63 while !self.is_multiple_of_freq_above_nyquist(i as f32) {
64 let gain = 1.0 / (i as f32).powf(gain_exponent);
65 output += gain * self.calculate_sine_output_from_freq(self.frequency_hz * i as f32);
66 i += harmonic_index_increment;
67 }
68 output
69 }
70
71 fn square_wave(&mut self) -> f32 {
72 self.generative_waveform(2, 1.0)
73 }
74
75 fn saw_wave(&mut self) -> f32 {
76 self.generative_waveform(1, 1.0)
77 }
78
79 fn triangle_wave(&mut self) -> f32 {
80 self.generative_waveform(2, 2.0)
81 }
82
83 fn tick(&mut self) -> f32 {
84 match self.waveform {
85 Waveform::Sine => self.sine_wave(),
86 Waveform::Square => self.square_wave(),
87 Waveform::Saw => self.saw_wave(),
88 Waveform::Triangle => self.triangle_wave(),
89 }
90 }
91}
92
93pub fn stream_setup_for() -> Result<cpal::Stream, anyhow::Error>
94where
95{
96 let (_host, device, config) = host_device_setup()?;
97
98 match config.sample_format() {
99 cpal::SampleFormat::I8 => make_stream::<i8>(&device, config.into()),
100 cpal::SampleFormat::I16 => make_stream::<i16>(&device, config.into()),
101 cpal::SampleFormat::I24 => make_stream::<I24>(&device, config.into()),
102 cpal::SampleFormat::I32 => make_stream::<i32>(&device, config.into()),
103 cpal::SampleFormat::I64 => make_stream::<i64>(&device, config.into()),
104 cpal::SampleFormat::U8 => make_stream::<u8>(&device, config.into()),
105 cpal::SampleFormat::U16 => make_stream::<u16>(&device, config.into()),
106 cpal::SampleFormat::U24 => make_stream::<U24>(&device, config.into()),
107 cpal::SampleFormat::U32 => make_stream::<u32>(&device, config.into()),
108 cpal::SampleFormat::U64 => make_stream::<u64>(&device, config.into()),
109 cpal::SampleFormat::F32 => make_stream::<f32>(&device, config.into()),
110 cpal::SampleFormat::F64 => make_stream::<f64>(&device, config.into()),
111 sample_format => Err(anyhow::Error::msg(format!(
112 "Unsupported sample format '{sample_format}'"
113 ))),
114 }
115}
116
117pub fn host_device_setup(
118) -> Result<(cpal::Host, cpal::Device, cpal::SupportedStreamConfig), anyhow::Error> {
119 let host = cpal::default_host();
120
121 let device = host
122 .default_output_device()
123 .ok_or_else(|| anyhow::Error::msg("Default output device is not available"))?;
124 println!("Output device: {}", device.id()?);
125
126 let config = device.default_output_config()?;
127 println!("Default output config: {config:?}");
128
129 Ok((host, device, config))
130}
131
132pub fn make_stream<T>(
133 device: &cpal::Device,
134 config: cpal::StreamConfig,
135) -> Result<cpal::Stream, anyhow::Error>
136where
137 T: SizedSample + FromSample<f32>,
138{
139 let num_channels = config.channels as usize;
140 let mut oscillator = Oscillator {
141 waveform: Waveform::Sine,
142 sample_rate: config.sample_rate as f32,
143 current_sample_index: 0.0,
144 frequency_hz: 440.0,
145 };
146 let err_fn = |err| eprintln!("Error building output sound stream: {err}");
147
148 let time_at_start = std::time::Instant::now();
149 println!("Time at start: {time_at_start:?}");
150
151 let stream = device.build_output_stream(
152 config,
153 move |output: &mut [T], _: &cpal::OutputCallbackInfo| {
154 // for 0-1s play sine, 1-2s play square, 2-3s play saw, 3-4s play triangle_wave
155 let time_since_start = std::time::Instant::now()
156 .duration_since(time_at_start)
157 .as_secs_f32();
158 if time_since_start < 1.0 {
159 oscillator.set_waveform(Waveform::Sine);
160 } else if time_since_start < 2.0 {
161 oscillator.set_waveform(Waveform::Triangle);
162 } else if time_since_start < 3.0 {
163 oscillator.set_waveform(Waveform::Square);
164 } else if time_since_start < 4.0 {
165 oscillator.set_waveform(Waveform::Saw);
166 } else {
167 oscillator.set_waveform(Waveform::Sine);
168 }
169 process_frame(output, &mut oscillator, num_channels)
170 },
171 err_fn,
172 None,
173 )?;
174
175 Ok(stream)
176}
177
178fn process_frame<SampleType>(
179 output: &mut [SampleType],
180 oscillator: &mut Oscillator,
181 num_channels: usize,
182) where
183 SampleType: Sample + FromSample<f32>,
184{
185 for frame in output.chunks_mut(num_channels) {
186 let value: SampleType = SampleType::from_sample(oscillator.tick());
187
188 // copy the same value to all channels
189 for sample in frame.iter_mut() {
190 *sample = value;
191 }
192 }
193}