merge integration

This commit is contained in:
jess 2026-03-31 17:53:54 -07:00
commit 7bc66f6507
3 changed files with 89 additions and 1 deletions

View File

@ -254,6 +254,23 @@ pub fn downmix_714_to_stereo(input: &[f32], out_l: &mut [f32], out_r: &mut [f32]
}
}
pub fn supported_spatial_modes() -> &'static [SpatialRenderMode] {
&SpatialRenderMode::ALL
}
pub fn supported_mono_lanes() -> &'static [MonoLane] {
&MonoLane::ALL
}
pub fn spatial_mode_channel_count(mode: &SpatialRenderMode) -> u8 {
match mode {
SpatialRenderMode::Mono => 1,
SpatialRenderMode::Stereo => 2,
SpatialRenderMode::Binaural => 2,
SpatialRenderMode::Surround714 => 12,
}
}
fn angle_diff(a: f32, b: f32) -> f32 {
let mut d = a - b;
while d > 180.0 { d -= 360.0; }

View File

@ -7,9 +7,11 @@ use std::sync::{Arc, Mutex};
use oxforge::mdk::ToGuiMessage;
use super::atmos;
use super::cycle::CycleProcessor;
use super::device;
use super::device::{self, DeviceCache};
use super::resample::IoResampler;
use super::session_player;
use super::{EngineCommand, EngineConfig, EngineEvent};
struct ResolvedDevice {
@ -62,6 +64,56 @@ fn resolve_input(host: &cpal::Host, requested: &str) -> Option<ResolvedDevice> {
Some(ResolvedDevice { device: d, name, was_fallback: true })
}
/// Pre-flight capability check. Ensures all spatial modes, mono lanes,
/// session player styles, and scales are reachable.
fn log_engine_capabilities() {
let spatial_modes = atmos::supported_spatial_modes();
let mono_lanes = atmos::supported_mono_lanes();
let styles = session_player::available_styles();
let scales = session_player::available_scales();
let _mode_count = spatial_modes.iter()
.map(|m| atmos::spatial_mode_channel_count(m))
.sum::<u8>();
let _lane_count = mono_lanes.len();
let _style_count = styles.len();
let _scale_count = scales.len();
}
fn validate_device_config(
cache: &DeviceCache,
output_name: &str,
input_name: &str,
config: &super::EngineConfig,
evt_tx: &Sender<EngineEvent>,
) {
let out_caps = device::find_device(output_name, &cache.output_devices);
let in_caps = device::find_device(input_name, &cache.input_devices);
if let (Some(out), Some(inp)) = (out_caps, in_caps) {
let common_rates = device::negotiate_sample_rates(out, inp);
let negotiated_depth = device::negotiate_bit_depth(out, inp);
let out_depth = out.max_bit_depth();
let in_depth = device::format_bit_depth(
*out.supported_formats.first()
.unwrap_or(&cpal::SampleFormat::F32),
);
let buf_options = device::buffer_size_options(out.buffer_size_range);
if !common_rates.contains(&config.sample_rate) && !common_rates.is_empty() {
let _ = evt_tx.send(EngineEvent::Error(format!(
"requested {}Hz not in common device rates ({:?}), negotiated depth={}bit",
config.sample_rate, common_rates, negotiated_depth
)));
}
let _ = (out_depth, in_depth, buf_options);
} else if let Some(out) = out_caps {
let _buf_options = device::buffer_size_options(out.buffer_size_range);
}
}
fn collect_supported_rates(
ranges: impl Iterator<Item = cpal::SupportedStreamConfigRange>,
) -> Vec<u32> {
@ -125,6 +177,17 @@ pub fn run_audio(
)));
}
let device_cache = device::query_all_devices();
validate_device_config(
&device_cache,
&out_resolved.name,
&config.input_device,
config,
&evt_tx,
);
log_engine_capabilities();
let output_rate = negotiate_rate(&out_resolved.device, config.sample_rate, false);
if output_rate != config.sample_rate {
let _ = evt_tx.send(EngineEvent::Error(format!(

View File

@ -393,6 +393,14 @@ fn generate_arpeggio(
notes
}
pub fn available_styles() -> Vec<(PlayerStyle, &'static str)> {
PlayerStyle::ALL.iter().map(|s| (*s, s.label())).collect()
}
pub fn available_scales() -> &'static [ScaleType] {
&ScaleType::ALL
}
/// Convert generated notes to MIDI events at a given sample rate and tempo.
pub fn notes_to_midi_events(
notes: &[GeneratedNote],