Feat: audio input channel selection
This commit is contained in:
@@ -454,6 +454,36 @@ pub(super) const WORDS: &[Word] = &[
|
|||||||
compile: Param,
|
compile: Param,
|
||||||
varargs: true,
|
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 {
|
Word {
|
||||||
name: "tilt",
|
name: "tilt",
|
||||||
aliases: &[],
|
aliases: &[],
|
||||||
|
|||||||
@@ -246,6 +246,16 @@ pub(super) const WORDS: &[Word] = &[
|
|||||||
compile: Param,
|
compile: Param,
|
||||||
varargs: true,
|
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 {
|
Word {
|
||||||
name: "cut",
|
name: "cut",
|
||||||
aliases: &[],
|
aliases: &[],
|
||||||
|
|||||||
@@ -355,9 +355,6 @@ pub fn build_stream(
|
|||||||
|
|
||||||
let registry = Arc::clone(&engine.sample_registry);
|
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
|
let input_device = config
|
||||||
.input_device
|
.input_device
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -374,6 +371,12 @@ pub fn build_stream(
|
|||||||
.and_then(|dev| dev.default_input_config().ok())
|
.and_then(|dev| dev.default_input_config().ok())
|
||||||
.map_or(0, |cfg| cfg.channels() as usize);
|
.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_stream = input_device.and_then(|dev| {
|
||||||
let input_cfg = match dev.default_input_config() {
|
let input_cfg = match dev.default_input_config() {
|
||||||
Ok(cfg) => cfg,
|
Ok(cfg) => cfg,
|
||||||
@@ -476,47 +479,16 @@ pub fn build_stream(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// doux expects stereo interleaved live_input (CHANNELS=2)
|
let nch_in = input_channels.max(1);
|
||||||
let stereo_len = buffer_samples * 2;
|
let raw_len = buffer_samples * nch_in;
|
||||||
if live_scratch.len() < stereo_len {
|
if live_scratch.len() < raw_len {
|
||||||
live_scratch.resize(stereo_len, 0.0);
|
live_scratch.resize(raw_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();
|
|
||||||
}
|
}
|
||||||
|
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.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);
|
scope_buffer.write(data);
|
||||||
|
|
||||||
// Feed mono mix to analysis thread via ring buffer (non-blocking)
|
// Feed mono mix to analysis thread via ring buffer (non-blocking)
|
||||||
|
|||||||
Reference in New Issue
Block a user