Feat: audio input channel selection

This commit is contained in:
2026-03-12 14:54:34 +01:00
parent 35aa97a93d
commit 453ba62403
3 changed files with 53 additions and 41 deletions

View File

@@ -454,6 +454,36 @@ pub(super) const WORDS: &[Word] = &[
compile: Param,
varargs: true,
},
Word {
name: "eqlofreq",
aliases: &[],
category: "Filter",
stack: "(v.. --)",
desc: "Set low shelf frequency (Hz)",
example: "400 eqlofreq",
compile: Param,
varargs: true,
},
Word {
name: "eqmidfreq",
aliases: &[],
category: "Filter",
stack: "(v.. --)",
desc: "Set mid peak frequency (Hz)",
example: "2000 eqmidfreq",
compile: Param,
varargs: true,
},
Word {
name: "eqhifreq",
aliases: &[],
category: "Filter",
stack: "(v.. --)",
desc: "Set high shelf frequency (Hz)",
example: "8000 eqhifreq",
compile: Param,
varargs: true,
},
Word {
name: "tilt",
aliases: &[],

View File

@@ -246,6 +246,16 @@ pub(super) const WORDS: &[Word] = &[
compile: Param,
varargs: true,
},
Word {
name: "inchan",
aliases: &[],
category: "Sample",
stack: "(v.. --)",
desc: "Select input channel for live input (0-indexed)",
example: "0 inchan",
compile: Param,
varargs: true,
},
Word {
name: "cut",
aliases: &[],

View File

@@ -355,9 +355,6 @@ pub fn build_stream(
let registry = Arc::clone(&engine.sample_registry);
const INPUT_BUFFER_SIZE: usize = 8192;
let (input_producer, input_consumer) = HeapRb::<f32>::new(INPUT_BUFFER_SIZE).split();
let input_device = config
.input_device
.as_ref()
@@ -374,6 +371,12 @@ pub fn build_stream(
.and_then(|dev| dev.default_input_config().ok())
.map_or(0, |cfg| cfg.channels() as usize);
engine.input_channels = input_channels;
const INPUT_BUFFER_BASE: usize = 8192;
let input_buffer_size = INPUT_BUFFER_BASE * (input_channels.max(2) / 2);
let (input_producer, input_consumer) = HeapRb::<f32>::new(input_buffer_size).split();
let input_stream = input_device.and_then(|dev| {
let input_cfg = match dev.default_input_config() {
Ok(cfg) => cfg,
@@ -476,47 +479,16 @@ pub fn build_stream(
}
}
// doux expects stereo interleaved live_input (CHANNELS=2)
let stereo_len = buffer_samples * 2;
if live_scratch.len() < stereo_len {
live_scratch.resize(stereo_len, 0.0);
}
match input_channels {
0 => {
live_scratch[..stereo_len].fill(0.0);
}
1 => {
for i in 0..buffer_samples {
let s = input_consumer.try_pop().unwrap_or(0.0);
live_scratch[i * 2] = s;
live_scratch[i * 2 + 1] = s;
}
}
2 => {
for sample in &mut live_scratch[..stereo_len] {
*sample = input_consumer.try_pop().unwrap_or(0.0);
}
}
_ => {
for i in 0..buffer_samples {
let l = input_consumer.try_pop().unwrap_or(0.0);
let r = input_consumer.try_pop().unwrap_or(0.0);
for _ in 2..input_channels {
input_consumer.try_pop();
}
live_scratch[i * 2] = l;
live_scratch[i * 2 + 1] = r;
}
}
}
// Discard excess if input produced more than we consumed
let excess = input_consumer.occupied_len().saturating_sub(INPUT_BUFFER_SIZE / 2);
for _ in 0..excess {
input_consumer.try_pop();
let nch_in = input_channels.max(1);
let raw_len = buffer_samples * nch_in;
if live_scratch.len() < raw_len {
live_scratch.resize(raw_len, 0.0);
}
live_scratch[..raw_len].fill(0.0);
input_consumer.pop_slice(&mut live_scratch[..raw_len]);
engine.metrics.load.set_buffer_time(buffer_time_ns);
engine.process_block(data, &[], &live_scratch[..stereo_len]);
engine.process_block(data, &[], &live_scratch[..raw_len]);
scope_buffer.write(data);
// Feed mono mix to analysis thread via ring buffer (non-blocking)