1 extern crate anyhow;
2 extern crate clap;
3 extern crate cpal;
4 
5 use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
6 
7 #[derive(Debug)]
8 struct Opt {
9     #[cfg(all(
10         any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"),
11         feature = "jack"
12     ))]
13     jack: bool,
14 
15     device: String,
16 }
17 
18 impl Opt {
from_args() -> Self19     fn from_args() -> Self {
20         let app = clap::App::new("beep").arg_from_usage("[DEVICE] 'The audio device to use'");
21         #[cfg(all(
22             any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"),
23             feature = "jack"
24         ))]
25         let app = app.arg_from_usage("-j, --jack 'Use the JACK host");
26         let matches = app.get_matches();
27         let device = matches.value_of("DEVICE").unwrap_or("default").to_string();
28 
29         #[cfg(all(
30             any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"),
31             feature = "jack"
32         ))]
33         return Opt {
34             jack: matches.is_present("jack"),
35             device,
36         };
37 
38         #[cfg(any(
39             not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")),
40             not(feature = "jack")
41         ))]
42         Opt { device }
43     }
44 }
45 
main() -> anyhow::Result<()>46 fn main() -> anyhow::Result<()> {
47     let opt = Opt::from_args();
48 
49     // Conditionally compile with jack if the feature is specified.
50     #[cfg(all(
51         any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"),
52         feature = "jack"
53     ))]
54     // Manually check for flags. Can be passed through cargo with -- e.g.
55     // cargo run --release --example beep --features jack -- --jack
56     let host = if opt.jack {
57         cpal::host_from_id(cpal::available_hosts()
58             .into_iter()
59             .find(|id| *id == cpal::HostId::Jack)
60             .expect(
61                 "make sure --features jack is specified. only works on OSes where jack is available",
62             )).expect("jack host unavailable")
63     } else {
64         cpal::default_host()
65     };
66 
67     #[cfg(any(
68         not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")),
69         not(feature = "jack")
70     ))]
71     let host = cpal::default_host();
72 
73     let device = if opt.device == "default" {
74         host.default_output_device()
75     } else {
76         host.output_devices()?
77             .find(|x| x.name().map(|y| y == opt.device).unwrap_or(false))
78     }
79     .expect("failed to find output device");
80     println!("Output device: {}", device.name()?);
81 
82     let config = device.default_output_config().unwrap();
83     println!("Default output config: {:?}", config);
84 
85     match config.sample_format() {
86         cpal::SampleFormat::F32 => run::<f32>(&device, &config.into()),
87         cpal::SampleFormat::I16 => run::<i16>(&device, &config.into()),
88         cpal::SampleFormat::U16 => run::<u16>(&device, &config.into()),
89     }
90 }
91 
run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error> where T: cpal::Sample,92 pub fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
93 where
94     T: cpal::Sample,
95 {
96     let sample_rate = config.sample_rate.0 as f32;
97     let channels = config.channels as usize;
98 
99     // Produce a sinusoid of maximum amplitude.
100     let mut sample_clock = 0f32;
101     let mut next_value = move || {
102         sample_clock = (sample_clock + 1.0) % sample_rate;
103         (sample_clock * 440.0 * 2.0 * std::f32::consts::PI / sample_rate).sin()
104     };
105 
106     let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
107 
108     let stream = device.build_output_stream(
109         config,
110         move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
111             write_data(data, channels, &mut next_value)
112         },
113         err_fn,
114     )?;
115     stream.play()?;
116 
117     std::thread::sleep(std::time::Duration::from_millis(1000));
118 
119     Ok(())
120 }
121 
write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32) where T: cpal::Sample,122 fn write_data<T>(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32)
123 where
124     T: cpal::Sample,
125 {
126     for frame in output.chunks_mut(channels) {
127         let value: T = cpal::Sample::from::<f32>(&next_sample());
128         for sample in frame.iter_mut() {
129             *sample = value;
130         }
131     }
132 }
133