My friend is working on some stuff in rust and it’s popped up in my job a few times so I wrote an asio sine wave generator in rust to familiarize myself.

FYI the code below is not debugged/cleaned up and definitely has stuff that can be taken out.
use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait}, DevicesError, FromSample, OutputCallbackInfo, Sample, SizedSample
};
fn main() {
let host;
#[cfg(target_os = "windows")]
{
host = cpal::host_from_id(cpal::HostId::Asio).expect("failed to initialise ASIO host");
let devices = host.output_devices().expect("No devices Found");
println!("Searching for ASIO Devices...");
let mut counter = 0;
for device_from_list in devices
{
counter = counter +1;
let name: String = device_from_list.name().expect("YOU LITERALLY JUST GAVE ME THIS, IF YOU FAILED THE PROGRAM SHOULD HAVE STOPPED WHY IS THIS ALLOWED");
println!("{counter} : {name}");
}
let searchString = "Focusrite USB ASIO";
let device: Option<cpal::Device> = host.output_devices().expect("we already did this!").find(|x| x.name().map(|y| y == searchString).unwrap_or(false));
let device_ptr: &cpal::Device = device.as_ref().unwrap();
let devicePrintName: String = device_ptr.name().expect("How would this not have a name at this point.");
println!("Connected To {devicePrintName}");
let config_ptr:&cpal::SupportedStreamConfig = &device_ptr.default_output_config().unwrap();
println!("Default output config: {:?}", config_ptr);
//These can by dyanmic but I'm keeping them hardcoded to my fosurite settings.
let mut audioOut = [0_f64; 1024];
let mut currentTime: f64 = 0.0;
let mut freqOut : f64 = 1000.0;
let mut ampOut : f64 = 0.5;
let sampleRate : f64 = 192000.0;
let freqAsRad: f64 = 2.0*3.14*freqOut;
for (sampleIdx,sampleVal) in audioOut.iter_mut().enumerate() {
currentTime = currentTime + 1.0/sampleRate;
*sampleVal = ampOut * (freqAsRad*currentTime).sin();
}
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let channels = config_ptr.channels() as usize;
let myStreamConfig = cpal::StreamConfig{
channels : 2,
sample_rate : cpal::SampleRate(192000),
buffer_size : cpal::BufferSize::Fixed(1024),
};
let stream = device_ptr.build_output_stream(&myStreamConfig,
getData2,err_fn,None).expect("Well I'm stummped");
stream.play();
std::thread::sleep(std::time::Duration::from_millis(1000));
}
fn getData2(output: &mut [i32], callbackInfo : &OutputCallbackInfo )
{
static mut currentTime: f64 = 0.0; //uhhgg
unsafe{
let mut freqOut : f64 = 440.0;
let mut ampOut : f64 = std::i32::MAX as f64 / 32.0;
let sampleRate : f64 = 192000.0;
let freqAsRad: f64 = 2.0*3.14*freqOut;
for frame in output.chunks_mut(100) {
for sample in frame.iter_mut() {
currentTime = currentTime + 1.0/sampleRate;
*sample = (ampOut * (freqAsRad*currentTime).sin()).trunc() as i32;
}
}
}
}
}
Anyone who knows rust probably wont like the way I wrote this, but it does what I set out to do. Which brings me to the stuff I don’t really like about rust on first impressions:
1.) It expects you’re going to develop multi-threaded applications: this is totally reasonable as that is the primary draw of using rust. However if you’re trying to pump out an easy single threaded connect + execute style of device control it can be quite frustrating trying to understand how an API functions.
2.) A lot of error handling: I’m cool with this concept but this is probably going to end up with me putting .except() or ? at the end of majority of my function calls.
3.) Optional static typing: this has always been a pet peeve of mine
The plus sides
1.) Cargo is great
2.) Forcing error management is probably a good practice
3.) Explicitly calling out mutability is a positive in my head
4.) Unsafe blocks are smart (because the primary goal is to make everything thread safe)
If you want this code to work you gotta use cargo to clone down cpal and follow the instructions here: https://github.com/RustAudio/cpal