#![cfg_attr(
not(bela_device),
allow(
dead_code,
reason = "only the fallback main is reachable off-device; the application code should still compile and lint"
)
)]
use core::num::NonZeroU32;
use core::sync::atomic::{AtomicBool, Ordering};
use std::process::ExitCode;
use std::sync::Arc;
use bela::{BelaApplication, RenderContext, SetupContext, ThreadInfo};
const MEASUREMENTS_PER_CYCLE: u32 = 2000;
const fn cycle() -> NonZeroU32 {
NonZeroU32::new(MEASUREMENTS_PER_CYCLE).expect("the cycle length is a non-zero constant")
}
struct Observe {
monitored: Arc<AtomicBool>,
}
impl BelaApplication for Observe {
type RenderState = ();
fn setup(&mut self, context: &SetupContext) -> bool {
self.monitored
.store(context.cpu_usage().is_some(), Ordering::Relaxed);
false
}
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
struct Abort;
impl BelaApplication for Abort {
type RenderState = ();
fn setup(&mut self, _context: &SetupContext) -> bool {
false
}
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
struct Idle;
impl BelaApplication for Idle {
type RenderState = ();
fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
fn render(&self, _state: &mut (), _context: &mut RenderContext) {}
}
#[cfg(bela_device)]
mod checks {
use core::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use bela::{Bela, Error, Settings};
use super::{Abort, Idle, Observe, cycle};
pub fn fifo_probe(period_size: u32) {
let settings = Settings::new().period_size(period_size).verbose(true);
drop(Bela::new(Abort, &settings));
}
pub fn second_new() {
let outcome = match Bela::new(Idle, &Settings::new()) {
Err(error) => format!("first-new-failed-{error}"),
Ok(first) => {
let second = match Bela::new(Idle, &Settings::new()) {
Err(Error::AudioSystemExists) => "refused",
Err(_) => "failed-otherwise",
Ok(_) => "created",
};
drop(first);
second.to_owned()
}
};
println!("rules: second-new={outcome}");
}
pub fn monitoring(requested: bool) {
let settings = if requested {
Settings::new().cpu_monitoring(cycle())
} else {
Settings::new()
};
let monitored = Arc::new(AtomicBool::new(false));
let app = Observe {
monitored: Arc::clone(&monitored),
};
let observed = match Bela::new(app, &settings) {
Err(Error::Init(_)) => true,
Err(_) => false,
Ok(bela) => {
drop(bela);
true
}
};
let seen = if !observed {
"setup-not-reached"
} else if monitored.load(Ordering::Relaxed) {
"some"
} else {
"none"
};
println!("rules: monitoring={seen}");
}
pub fn poisoned() {
let first = match Bela::new(Abort, &Settings::new()) {
Err(Error::Init(_)) => "failed",
Err(_) => "failed-otherwise",
Ok(bela) => {
drop(bela);
"created"
}
};
let second = match Bela::new(Idle, &Settings::new()) {
Err(Error::AudioSystemPoisoned) => "refused",
Err(_) => "failed-otherwise",
Ok(bela) => {
drop(bela);
"created"
}
};
println!("rules: first-init={first} poisoned-new={second}");
}
}
#[cfg(bela_device)]
fn main() -> ExitCode {
use std::env::args;
let arguments: Vec<String> = args().skip(1).collect();
let check: Vec<&str> = arguments.iter().map(String::as_str).collect();
match check.as_slice() {
["fifo-probe", frames] => {
let Ok(frames) = frames.parse() else {
eprintln!("fifo-probe takes a period size in frames, not {frames:?}");
return ExitCode::FAILURE;
};
checks::fifo_probe(frames);
}
["second-new"] => checks::second_new(),
["monitoring", "on"] => checks::monitoring(true),
["monitoring", "off"] => checks::monitoring(false),
["poisoned"] => checks::poisoned(),
_ => {
eprintln!(
"usage: monitoring_rules (fifo-probe <frames> | second-new | monitoring on|off\n\
\x20 | poisoned)\n\
one check per run: three of these abort from `setup`, which makes \
`Bela::new` give up on it"
);
return ExitCode::FAILURE;
}
}
ExitCode::SUCCESS
}
#[cfg(not(bela_device))]
fn main() -> ExitCode {
eprintln!("This example must be cross-compiled for Bela Gem (aarch64-unknown-linux-gnu).");
ExitCode::FAILURE
}