Run as 'vois': install via make, config file, Auto-Tune, more presets, custom chains
- 'vois' installs to PATH (make install) and works with no flags: auto device selection (pipewire/pulse/default) and 48 kHz default - ~/.config/vois/config.toml for defaults + custom preset chains (serde) - Auto-Tune pitch correction (snap to nearest semitone, smoothed) - 5 new presets: Chipmunk, Helium, Vader, Telephone, Growl (17 total) - --pitch-correct flag, --no-virtual-mic to disable the default virtual mic - no dead-code allowances; clippy/fmt/tests clean
This commit is contained in:
parent
b0d685a110
commit
aacaf7d9cf
14 changed files with 753 additions and 285 deletions
|
|
@ -6,6 +6,8 @@ use cpal::{
|
|||
};
|
||||
use ringbuf::traits::Producer;
|
||||
|
||||
const PREFERRED_INPUTS: [&str; 3] = ["pipewire", "pulse", "default"];
|
||||
|
||||
pub fn find_input(host: &Host, name: Option<&str>) -> Result<Device> {
|
||||
match name {
|
||||
Some(name) => host
|
||||
|
|
@ -13,9 +15,20 @@ pub fn find_input(host: &Host, name: Option<&str>) -> Result<Device> {
|
|||
.context("failed to enumerate input devices")?
|
||||
.find(|d| d.name().map(|n| n == name).unwrap_or(false))
|
||||
.with_context(|| format!("input device {name:?} not found")),
|
||||
None => host
|
||||
.default_input_device()
|
||||
.context("no default input device found"),
|
||||
None => {
|
||||
let mut devices = host
|
||||
.input_devices()
|
||||
.context("failed to enumerate input devices")?;
|
||||
if let Some(device) = devices.find(|d| {
|
||||
d.name()
|
||||
.map(|n| PREFERRED_INPUTS.iter().any(|p| n == *p))
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
return Ok(device);
|
||||
}
|
||||
host.default_input_device()
|
||||
.context("no default input device found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,29 +53,42 @@ pub fn run(cfg: &AppConfig) -> Result<()> {
|
|||
let in_supported = input_device
|
||||
.default_input_config()
|
||||
.context("failed to read default input config")?;
|
||||
|
||||
let rate = cfg
|
||||
.sample_rate
|
||||
.map(SampleRate)
|
||||
.unwrap_or_else(|| in_supported.sample_rate());
|
||||
|
||||
let in_supported = if cfg.sample_rate.is_some() {
|
||||
pick_config(
|
||||
input_device.supported_input_configs()?,
|
||||
in_supported.channels(),
|
||||
rate,
|
||||
)
|
||||
.with_context(|| format!("input device does not support sample rate {} Hz", rate.0))?
|
||||
} else {
|
||||
in_supported
|
||||
};
|
||||
|
||||
let in_channels = in_supported.channels();
|
||||
let out_default = output_device
|
||||
.default_output_config()
|
||||
.context("failed to read default output config")?;
|
||||
let out_channels = out_default.channels();
|
||||
|
||||
// Negotiate one sample rate both devices support, preferring 48 kHz.
|
||||
let rate = if let Some(r) = cfg.sample_rate {
|
||||
SampleRate(r)
|
||||
} else {
|
||||
let in_ranges = input_device.supported_input_configs()?;
|
||||
let out_ranges = output_device.supported_output_configs()?;
|
||||
let in_ranges: Vec<_> = in_ranges.collect();
|
||||
let out_ranges: Vec<_> = out_ranges.collect();
|
||||
let mut chosen = in_supported.sample_rate();
|
||||
for r in [
|
||||
SampleRate(48000),
|
||||
SampleRate(44100),
|
||||
in_supported.sample_rate(),
|
||||
] {
|
||||
if pick_config(in_ranges.iter().cloned(), in_channels, r).is_some()
|
||||
&& pick_config(out_ranges.iter().cloned(), out_channels, r).is_some()
|
||||
{
|
||||
chosen = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chosen
|
||||
};
|
||||
|
||||
let in_supported = pick_config(input_device.supported_input_configs()?, in_channels, rate)
|
||||
.with_context(|| format!("input device does not support sample rate {} Hz", rate.0))?;
|
||||
|
||||
let out_supported = pick_config(
|
||||
output_device.supported_output_configs()?,
|
||||
out_default.channels(),
|
||||
out_channels,
|
||||
rate,
|
||||
)
|
||||
.with_context(|| {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ use cpal::{
|
|||
};
|
||||
use ringbuf::traits::Consumer;
|
||||
|
||||
const PREFERRED_OUTPUTS: [&str; 3] = ["pipewire", "pulse", "default"];
|
||||
|
||||
pub fn find_output(host: &Host, name: Option<&str>) -> Result<Device> {
|
||||
match name {
|
||||
Some(name) => host
|
||||
|
|
@ -13,9 +15,20 @@ pub fn find_output(host: &Host, name: Option<&str>) -> Result<Device> {
|
|||
.context("failed to enumerate output devices")?
|
||||
.find(|d| d.name().map(|n| n == name).unwrap_or(false))
|
||||
.with_context(|| format!("output device {name:?} not found")),
|
||||
None => host
|
||||
.default_output_device()
|
||||
.context("no default output device found"),
|
||||
None => {
|
||||
let mut devices = host
|
||||
.output_devices()
|
||||
.context("failed to enumerate output devices")?;
|
||||
if let Some(device) = devices.find(|d| {
|
||||
d.name()
|
||||
.map(|n| PREFERRED_OUTPUTS.iter().any(|p| n == *p))
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
return Ok(device);
|
||||
}
|
||||
host.default_output_device()
|
||||
.context("no default output device found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue