#![allow(dead_code)]
use std::time::{SystemTime, UNIX_EPOCH};
use atome::output::{device_name, OutputType, SampleRate};
use atome::{AudioEngine, SampleType};
pub fn host() -> OutputType {
if cfg!(target_os = "windows") {
OutputType::WASAPI
} else {
OutputType::CoreAudio
}
}
pub fn pick<T>(items: &[T]) -> Option<&T> {
if items.is_empty() {
return None;
}
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|elapsed| elapsed.subsec_nanos() as usize)
.unwrap_or(0);
items.get(nanos % items.len())
}
pub fn describe<S: SampleType>(engine: &AudioEngine<S>) {
println!("\nengine: {} Hz, buffer {:?}", engine.sample_rate().hz(), engine.buffer_size());
if !engine.plugins().is_empty() {
println!(" engine-wide plugins: {}", names(engine.plugins()));
}
println!(" outputs:");
for (index, output) in engine.outputs().iter().enumerate() {
print!(
" [{index}] {} — {} ch",
output.device().name(),
output.output().channels()
);
if output.device().plugins().is_empty() {
println!();
} else {
println!(", plugins: {}", names(output.device().plugins()));
}
}
println!(" inputs:");
for (index, input) in engine.inputs().iter().enumerate() {
let destinations: Vec<String> = input
.routes()
.iter()
.map(|route| engine.outputs()[*route].device().name())
.collect();
print!(
" [{index}] {} — {} ch -> {}",
input.device().name(),
input.input().channels(),
if destinations.is_empty() {
"nothing".to_string()
} else {
destinations.join(", ")
}
);
if input.device().plugins().is_empty() {
println!();
} else {
println!(", plugins: {}", names(input.device().plugins()));
}
}
}
fn names(plugins: &[atome::Plugin]) -> String {
plugins
.iter()
.map(|plugin| plugin.name.as_str())
.collect::<Vec<_>>()
.join(" -> ")
}
pub fn note_no_audio() {
println!(
"\nno audio will be heard: input-to-output forwarding is not implemented \
yet (planning/TODO.md 2.3). This example shows the wiring."
);
}
pub fn name_of(device: &cpal::Device) -> String {
device_name(device)
}
pub fn shared_rate(input: &cpal::Device, output: &cpal::Device) -> SampleRate {
use cpal::traits::DeviceTrait;
let default_of = |config: Result<cpal::SupportedStreamConfig, _>| {
config
.ok()
.and_then(|config| SampleRate::from_hz(config.sample_rate()))
};
let candidates = [
default_of(output.default_output_config()),
default_of(input.default_input_config()),
Some(SampleRate::Hz48k),
Some(SampleRate::Hz44_1k),
];
candidates
.into_iter()
.flatten()
.find(|rate| supports(input, *rate, false) && supports(output, *rate, true))
.unwrap_or(SampleRate::Hz48k)
}
fn supports(device: &cpal::Device, rate: SampleRate, output: bool) -> bool {
use cpal::traits::DeviceTrait;
let hz = rate.hz() as cpal::SampleRate;
let covers = |config: &cpal::SupportedStreamConfigRange| {
config.min_sample_rate() <= hz && hz <= config.max_sample_rate()
};
if output {
device
.supported_output_configs()
.map(|mut configs| configs.any(|config| covers(&config)))
.unwrap_or(false)
} else {
device
.supported_input_configs()
.map(|mut configs| configs.any(|config| covers(&config)))
.unwrap_or(false)
}
}