1use std::{
2 cmp::Ordering, collections::HashMap, fmt::Debug, ops::BitOr, sync::mpsc::{self, Receiver, Sender}
3};
4
5use cpal::{
6 traits::{DeviceTrait, HostTrait, StreamTrait},
7 Device, FromSample, OutputCallbackInfo, Sample, SampleFormat, SampleRate, SizedSample, Stream,
8 StreamConfig, SupportedStreamConfigRange,
9};
10
11#[derive(Debug)]
12struct SynthBackend {
13 commands: Receiver<SynthCommand>,
14 sample_rate: u32,
15 channels: usize,
16 synths: HashMap<SynthId, SynthChannel>,
17}
18
19impl SynthBackend {
20 fn new(rx: Receiver<SynthCommand>, sample_rate: u32, channels: usize) -> SynthBackend {
21 SynthBackend {
22 commands: rx,
23 sample_rate,
24 channels,
25 synths: HashMap::new(),
26 }
27 }
28 fn write_samples<T: Sample + FromSample<f32>>(
29 &mut self,
30 data: &mut [T],
31 _info: &OutputCallbackInfo,
32 ) {
33 for sample in data.iter_mut() {
34 *sample = T::EQUILIBRIUM;
35 }
36 while let Ok(cmd) = self.commands.try_recv() {
37 let synth = self
38 .synths
39 .entry(cmd.synth)
40 .or_insert_with(SynthChannel::default);
41 match cmd.operation {
42 SynthOp::SetVolume(vol) => synth.volume.tween_to(vol, cmd.lerp_time),
43 SynthOp::SetPitch(pitch) => synth.frequency.tween_to(pitch, cmd.lerp_time),
44 SynthOp::SetChannelMask(cm) => synth.channel_mask = cm,
45 }
46 }
47 let timestep = 1.0 / (self.sample_rate as f32);
48 for sample in 0..data.len() / self.channels {
53 let mut channels = vec![0.0; self.channels];
54 for synth in self.synths.values_mut() {
55 let val = synth.next_sample(timestep);
56 for i in 0..self.channels {
57 if synth.channel_mask.0 & (1 << i) != 0 {
58 channels[i] += val;
59 }
60 }
61 }
62 for (i, channel) in channels.into_iter().enumerate() {
63 data[sample * self.channels + i] = T::from_sample(channel);
64 }
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
70struct SynthChannel {
71 frequency: Tweenable,
72 volume: Tweenable,
73 channel_mask: ChannelMask,
74 oscillator: f32,
75}
76
77impl Default for SynthChannel {
78 fn default() -> Self {
79 Self {
80 frequency: Tweenable::new(220.0),
81 volume: Tweenable::new(0.0),
82 channel_mask: ChannelMask::STEREO,
83 oscillator: 0.0,
84 }
85 }
86}
87
88impl SynthChannel {
89 #[inline]
90 fn next_sample(&mut self, amount: f32) -> f32 {
91 let val = (self.oscillator * std::f32::consts::PI * 2.0).sin() * self.volume.current();
92 self.oscillator = (self.oscillator + amount * self.frequency.current()) % 1.0;
93 self.frequency.advance(amount);
94 self.volume.advance(amount);
95 val
96 }
97}
98
99#[derive(Debug, Clone)]
101pub struct Tweenable {
102 current: f32,
103 tween: Option<(f32, f32)>,
104}
105
106impl Tweenable {
107 pub fn new(val: f32) -> Tweenable {
108 Tweenable {
109 current: val,
110 tween: None,
111 }
112 }
113 #[inline(always)]
114 pub fn current(&self) -> f32 {
115 self.current
116 }
117 #[inline]
118 pub fn advance(&mut self, amount: f32) -> f32 {
119 if let Some((target, remaining)) = self.tween {
120 if amount >= remaining {
121 self.current = target;
122 self.tween = None;
123 } else {
124 self.current += (target - self.current) * (amount / remaining);
125 self.tween = Some((target, remaining - amount));
126 }
127 }
128 self.current
129 }
130 #[inline]
131 pub fn tween_to(&mut self, target: f32, time: f32) {
132 self.tween = Some((target, time));
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
137pub struct ChannelMask(pub usize);
138
139impl ChannelMask {
140 pub const STEREO: Self = ChannelMask(3);
141 pub const LEFT: Self = ChannelMask(1 << 0);
142 pub const RIGHT: Self = ChannelMask(1 << 1);
143 pub const CENTER: Self = ChannelMask(1 << 2);
144 pub const SUB: Self = ChannelMask(1 << 3);
145 pub const REAR_LEFT: Self = ChannelMask(1 << 4);
146 pub const REAR_RIGHT: Self = ChannelMask(1 << 5);
147 pub const SIDE_LEFT: Self = ChannelMask(1 << 6);
148 pub const SIDE_RIGHT: Self = ChannelMask(1 << 7);
149}
150
151impl BitOr for ChannelMask {
152 type Output = usize;
153
154 fn bitor(self, rhs: Self) -> Self::Output {
155 self.0.bitor(rhs.0)
156 }
157}
158
159impl From<usize> for ChannelMask {
160 fn from(value: usize) -> Self {
161 ChannelMask(value)
162 }
163}
164
165impl Default for ChannelMask {
166 fn default() -> Self {
167 Self::STEREO
168 }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
172pub struct SynthId(pub usize);
173
174#[derive(Debug, Clone, Copy)]
175enum SynthOp {
176 SetVolume(f32),
177 SetPitch(f32),
178 SetChannelMask(ChannelMask),
179}
180
181#[derive(Debug, Clone)]
182struct SynthCommand {
183 synth: SynthId,
184 operation: SynthOp,
185 lerp_time: f32,
186}
187
188pub struct Synth {
189 _stream: Box<dyn StreamTrait>,
190 command_channel: Sender<SynthCommand>,
191 pub channel_count: usize,
192 pub next_synth: usize,
193}
194
195const DEFAULT_LERP_TIME: f32 = 0.01;
196
197pub struct AudioSink {
198 pub host_name: String,
199 pub device_name: String,
200 pub device: Device,
201}
202
203impl Debug for AudioSink {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 f.debug_struct("AudioSink")
206 .field("host_name", &self.host_name)
207 .field("device_name", &self.device_name)
208 .finish_non_exhaustive()
209 }
210}
211
212#[derive(Debug)]
213pub struct SinkList(pub Vec<AudioSink>);
214
215impl From<SinkList> for Device {
216 fn from(mut value: SinkList) -> Self {
217 value.0.remove(0).device
218 }
219}
220
221impl SinkList {
222 pub fn get_sinks() -> SinkList {
223 let mut list = Vec::new();
224 for host_id in cpal::available_hosts() {
225 let host = cpal::host_from_id(host_id).unwrap();
226 for device in host.output_devices().unwrap() {
227 list.push(AudioSink {
228 host_name: host_id.name().to_string(),
229 device_name: device.name().unwrap(),
230 device,
231 });
232 }
233 }
234 SinkList(list)
235 }
236 pub fn sort_default(mut self) -> Self {
237 self.0.sort_by_cached_key(|dev| {
238 -if dev.device_name.ends_with("_in") {
239 -10
240 } else if dev.host_name == "ALSA" {
241 match dev.device_name.as_str() {
242 "pipewire" => -1,
243 "jack" => -2,
244 "pulse" => -3,
245 "default" => -4,
246 _ => -5,
247 }
248 } else if dev.host_name == "JACK" && dev.device_name.ends_with("_out") {
249 1
250 } else {
251 0
252 }
253 });
254 self
255 }
256}
257
258fn device_sr(d: &SupportedStreamConfigRange) -> u32 {
259 48000.clamp(d.min_sample_rate().0, d.max_sample_rate().0)
260}
261
262fn sr_score(a: u32) -> i32 {
263 match a {
264 88200 | 96000 | 24000 | 22050 => 1,
265 44100..=48000 => 2,
266 0..=44099 => 96000 - (a as i32) * 2,
267 _ => 48000 - (a as i32),
268 }
269}
270fn fmt_score(a: SampleFormat) -> i32 {
271 match a {
272 SampleFormat::I8 => -4,
273 SampleFormat::I16 => -3,
274 SampleFormat::I32 => -2,
275 SampleFormat::I64 => -3,
276 SampleFormat::U8 => -4,
277 SampleFormat::U16 => -3,
278 SampleFormat::U32 => -2,
279 SampleFormat::U64 => -3,
280 SampleFormat::F32 => 0,
281 SampleFormat::F64 => -1,
282 _ => -10,
283 }
284}
285fn chan_score(a: u16) -> i32 {
286 match a {
287 2 => 14,
288 3..=13 => a as i32,
289 1 => 1,
290 _ => -(a as i32),
291 }
292}
293
294impl Synth {
295 pub fn new() -> Synth {
296 Synth::new_on_sink(Synth::get_sinks().sort_default())
297 }
298 pub fn get_sinks() -> SinkList {
299 SinkList::get_sinks()
300 }
301 pub fn new_on_sink(audio_sink: impl Into<Device>) -> Synth {
302 let device: Device = audio_sink.into();
303 let selected_config = device
304 .supported_output_configs()
305 .unwrap()
306 .reduce(|a, b| {
307 match sr_score(device_sr(&a)).cmp(&sr_score(device_sr(&b))) {
308 Ordering::Less => return b,
309 Ordering::Greater => return a,
310 _ => {}
311 }
312 match fmt_score(a.sample_format()).cmp(&fmt_score(b.sample_format())) {
313 Ordering::Less => return b,
314 Ordering::Greater => return a,
315 _ => {}
316 }
317 match chan_score(a.channels()).cmp(&chan_score(b.channels())) {
318 Ordering::Less => return b,
319 Ordering::Greater => return a,
320 _ => {}
321 }
322
323 a
324 })
325 .unwrap();
326 let sr = device_sr(&selected_config);
327 let selected_config = selected_config.with_sample_rate(SampleRate(sr));
328 fn build_stream<T: SizedSample + FromSample<f32>>(
329 device: &Device,
330 config: &StreamConfig,
331 mut backend: SynthBackend,
332 ) -> Stream {
333 let err_fn = |e| eprintln!("CPAL Error: {e}");
334 let stream = device
335 .build_output_stream(
336 config,
337 move |data, info| backend.write_samples::<T>(data, info),
338 err_fn,
339 None,
340 )
341 .unwrap();
342 stream.play().unwrap();
343 stream
344 }
345 let (tx, rx) = mpsc::channel();
346 let sample_rate = selected_config.sample_rate();
347 let sample_format = selected_config.sample_format();
348 let channel_count = selected_config.channels() as usize;
349 let backend = SynthBackend::new(rx, sample_rate.0, channel_count);
350 let stream = {
351 let config = selected_config.into();
352 match sample_format {
353 SampleFormat::I8 => build_stream::<i8>(&device, &config, backend),
354 SampleFormat::I16 => build_stream::<i16>(&device, &config, backend),
355 SampleFormat::I32 => build_stream::<i32>(&device, &config, backend),
356 SampleFormat::I64 => build_stream::<i64>(&device, &config, backend),
357 SampleFormat::U8 => build_stream::<u8>(&device, &config, backend),
358 SampleFormat::U16 => build_stream::<u16>(&device, &config, backend),
359 SampleFormat::U32 => build_stream::<u32>(&device, &config, backend),
360 SampleFormat::U64 => build_stream::<u64>(&device, &config, backend),
361 SampleFormat::F32 => build_stream::<f32>(&device, &config, backend),
362 SampleFormat::F64 => build_stream::<f64>(&device, &config, backend),
363 _ => todo!(),
364 }
365 };
366 Synth {
367 _stream: Box::new(stream),
368 command_channel: tx,
369 channel_count,
370 next_synth: 0,
371 }
372 }
373 pub fn new_synth(&mut self, volume: f32, pitch: f32) -> SynthId {
374 let synth = SynthId(self.next_synth);
375 self.next_synth += 1;
376 self.set_volume(synth, volume);
377 self.set_pitch(synth, pitch);
378 self.set_channel_mask(synth, ChannelMask::default());
379 synth
380 }
381 pub fn set_volume(&mut self, synth: SynthId, volume: f32) {
382 self.set_volume_lerp(synth, volume, DEFAULT_LERP_TIME);
383 }
384 pub fn set_volume_lerp(&mut self, synth: SynthId, volume: f32, lerp_time: f32) {
385 self.command_channel
386 .send(SynthCommand {
387 synth,
388 operation: SynthOp::SetVolume(volume),
389 lerp_time,
390 })
391 .unwrap();
392 }
393 pub fn set_pitch(&mut self, synth: SynthId, pitch: f32) {
394 self.set_pitch_lerp(synth, pitch, DEFAULT_LERP_TIME);
395 }
396 pub fn set_pitch_lerp(&mut self, synth: SynthId, pitch: f32, lerp_time: f32) {
397 self.command_channel
398 .send(SynthCommand {
399 synth,
400 operation: SynthOp::SetPitch(pitch),
401 lerp_time,
402 })
403 .unwrap();
404 }
405 pub fn set_channel_mask(&mut self, synth: SynthId, channel_mask: ChannelMask) {
406 self.set_channel_mask_lerp(synth, channel_mask, DEFAULT_LERP_TIME);
407 }
408 pub fn set_channel_mask_lerp(
409 &mut self,
410 synth: SynthId,
411 channel_mask: ChannelMask,
412 lerp_time: f32,
413 ) {
414 self.command_channel
415 .send(SynthCommand {
416 synth,
417 operation: SynthOp::SetChannelMask(channel_mask),
418 lerp_time,
419 })
420 .unwrap();
421 }
422 pub fn stop_all(&mut self) {
423 for s in 0..self.next_synth {
424 self.set_volume(SynthId(s), 0.0);
425 }
426 }
427}