Skip to content

Commit ed77619

Browse files
committed
Parse example arguments with clap
The examples took a long list of positional arguments with no help text. Give them all named options with defaults instead, matching the new resample_wav example. Also merge fixedout_ramp64 and polyfixedin_ramp64 into ramp_ratio_f64. They only differed by resampler type and which side is fixed, both of which are now options, so the merged example also covers the two combinations neither of them had.
1 parent 4406cf0 commit ed77619

5 files changed

Lines changed: 401 additions & 422 deletions

File tree

examples/adjust_ratio_f64.rs

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
extern crate rubato;
22
use audioadapter_buffers::direct::InterleavedSlice;
3+
use clap::{Parser, ValueEnum};
34
use rubato::{
45
Async, FixedAsync, PolynomialDegree, Resampler, SincInterpolationParameters,
56
SincInterpolationType, Slip, WindowFunction,
67
};
78
use std::convert::TryInto;
8-
use std::env;
99
use std::fs::File;
1010
use std::io::prelude::{Read, Seek, Write};
1111
use std::io::{BufReader, BufWriter};
@@ -18,23 +18,71 @@ use log::LevelFilter;
1818
const BYTE_PER_SAMPLE: usize = 8;
1919

2020
// A resampler app that reads a raw file of little-endian 64 bit floats, and writes the output in the same format.
21-
// Unlike the `process_all_f64` example, which converts between two fixed rates, this one uses one of the
21+
// Unlike the `process_f64` example, which converts between two fixed rates, this one uses one of the
2222
// *adjustable* resamplers to apply a small, constant rate offset. This is the clock-drift / rate-matching case:
2323
// the nominal input and output rates are equal (ratio 1:1), and the resampler is nudged by a user-selected
2424
// offset given in parts per million (ppm). A positive offset produces slightly more output frames than input,
2525
// a negative offset slightly fewer.
2626
//
2727
// The offset is applied through the `Resampler::as_adjustable` capability accessor, so the same code drives every
2828
// adjustable resampler type without knowing the concrete type. The synchronous FFT resamplers cannot change
29-
// ratio and are therefore not offered here; use the `process_all_f64` example for fixed-ratio conversion.
29+
// ratio and are therefore not offered here; use the `process_f64` example for fixed-ratio conversion.
30+
// For a ratio that changes while processing, see the `ramp_ratio_f64` example.
3031
//
31-
// The command line arguments are resampler type, input filename, output filename, number of channels,
32-
// and the rate offset in ppm. The adjustable resamplers support offsets up to roughly +/- 10%.
32+
// The adjustable resamplers support offsets up to roughly +/- 10%.
3333
// To apply a +50 ppm offset to the two-channel file `sine_f64_2ch.raw` using the Slip resampler:
3434
// ```
35-
// cargo run --release --example adjust_ratio_f64 SlipFixedOutput sine_f64_2ch.raw test.raw 2 50
35+
// cargo run --release --example adjust_ratio_f64 sine_f64_2ch.raw test.raw -r SlipFixedOutput -o 50
3636
// ```
3737

38+
/// Apply a small constant rate offset to a raw file of 64 bit floats.
39+
#[derive(Parser)]
40+
#[command(version)]
41+
struct Options {
42+
/// Raw file of little-endian 64 bit floats to read.
43+
input: String,
44+
45+
/// Raw file to write, in the same format.
46+
output: String,
47+
48+
/// Resampler to use. Only the adjustable types are offered, since the
49+
/// synchronous FFT resamplers cannot change ratio.
50+
#[arg(short, long, value_enum, ignore_case = true, default_value_t = ResamplerType::SlipFixedOutput)]
51+
resampler: ResamplerType,
52+
53+
/// Number of channels in the file.
54+
#[arg(short, long, default_value_t = 2)]
55+
channels: usize,
56+
57+
/// Rate offset in parts per million. Positive gives slightly more output
58+
/// frames than input, negative slightly fewer.
59+
#[arg(short, long, default_value_t = 50.0, allow_negative_numbers = true)]
60+
offset: f64,
61+
}
62+
63+
/// The adjustable resampler types this example can build.
64+
#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
65+
enum ResamplerType {
66+
/// Sinc interpolation, fixed input size.
67+
#[value(name = "SincFixedInput")]
68+
SincFixedInput,
69+
/// Sinc interpolation, fixed output size.
70+
#[value(name = "SincFixedOutput")]
71+
SincFixedOutput,
72+
/// Polynomial interpolation, fixed input size.
73+
#[value(name = "PolyFixedInput")]
74+
PolyFixedInput,
75+
/// Polynomial interpolation, fixed output size.
76+
#[value(name = "PolyFixedOutput")]
77+
PolyFixedOutput,
78+
/// Slip resampler, fixed input size.
79+
#[value(name = "SlipFixedInput")]
80+
SlipFixedInput,
81+
/// Slip resampler, fixed output size.
82+
#[value(name = "SlipFixedOutput")]
83+
SlipFixedOutput,
84+
}
85+
3886
/// Helper to read an entire file to memory as f64 values
3987
fn read_file<R: Read + Seek>(inbuffer: &mut R) -> Vec<f64> {
4088
let mut buffer = vec![0u8; BYTE_PER_SAMPLE];
@@ -66,31 +114,19 @@ fn main() {
66114
let mut builder = Builder::from_default_env();
67115
builder.filter(None, LevelFilter::Debug).init();
68116

69-
let resampler_type = env::args().nth(1).expect(
70-
"Please specify a resampler type, one of:\nSincFixedInput\nSincFixedOutput\nPolyFixedInput\nPolyFixedOutput\nSlipFixedInput\nSlipFixedOutput",
71-
);
72-
73-
let file_in = env::args().nth(2).expect("Please specify an input file.");
74-
let file_out = env::args().nth(3).expect("Please specify an output file.");
75-
println!("Opening files: {}, {}", file_in, file_out);
117+
let opts = Options::parse();
118+
let channels = opts.channels;
119+
let offset_ppm = opts.offset;
120+
println!("Opening files: {}, {}", opts.input, opts.output);
76121

77-
let channels_str = env::args()
78-
.nth(4)
79-
.expect("Please specify number of channels");
80-
let channels = channels_str.parse::<usize>().unwrap();
81-
82-
let offset_str = env::args()
83-
.nth(5)
84-
.expect("Please specify the rate offset in ppm");
85-
let offset_ppm = offset_str.parse::<f64>().unwrap();
86122
let rel_ratio = 1.0 + offset_ppm / 1_000_000.0;
87123
println!(
88124
"Applying a rate offset of {} ppm (relative ratio {})",
89125
offset_ppm, rel_ratio
90126
);
91127

92128
println!("Copy input file to buffer");
93-
let file_in_disk = File::open(file_in).expect("Can't open file");
129+
let file_in_disk = File::open(&opts.input).expect("Can't open file");
94130
let mut file_in_reader = BufReader::new(file_in_disk);
95131
let indata = read_file(&mut file_in_reader);
96132
let nbr_input_frames = indata.len() / channels;
@@ -103,8 +139,8 @@ fn main() {
103139
// Every branch is built at the nominal ratio of 1.0. The asynchronous resamplers get a maximum
104140
// relative ratio of 1.1, matching the Slip resampler's built-in +/- 10% range.
105141
let chunk_size = 1024;
106-
let mut resampler: Box<dyn Resampler<f64>> = match resampler_type.as_str() {
107-
"SincFixedInput" => {
142+
let mut resampler: Box<dyn Resampler<f64>> = match opts.resampler {
143+
ResamplerType::SincFixedInput => {
108144
let params = SincInterpolationParameters::new(128, WindowFunction::Blackman2)
109145
.oversampling_factor(256)
110146
.interpolation(SincInterpolationType::Quadratic);
@@ -113,7 +149,7 @@ fn main() {
113149
.unwrap(),
114150
)
115151
}
116-
"SincFixedOutput" => {
152+
ResamplerType::SincFixedOutput => {
117153
let params = SincInterpolationParameters::new(128, WindowFunction::Blackman2)
118154
.oversampling_factor(256)
119155
.interpolation(SincInterpolationType::Quadratic);
@@ -122,7 +158,7 @@ fn main() {
122158
.unwrap(),
123159
)
124160
}
125-
"PolyFixedInput" => Box::new(
161+
ResamplerType::PolyFixedInput => Box::new(
126162
Async::<f64>::new_poly(
127163
1.0,
128164
1.1,
@@ -133,7 +169,7 @@ fn main() {
133169
)
134170
.unwrap(),
135171
),
136-
"PolyFixedOutput" => Box::new(
172+
ResamplerType::PolyFixedOutput => Box::new(
137173
Async::<f64>::new_poly(
138174
1.0,
139175
1.1,
@@ -144,16 +180,12 @@ fn main() {
144180
)
145181
.unwrap(),
146182
),
147-
"SlipFixedInput" => {
183+
ResamplerType::SlipFixedInput => {
148184
Box::new(Slip::<f64>::new(chunk_size, channels, FixedAsync::Input).unwrap())
149185
}
150-
"SlipFixedOutput" => {
186+
ResamplerType::SlipFixedOutput => {
151187
Box::new(Slip::<f64>::new(chunk_size, channels, FixedAsync::Output).unwrap())
152188
}
153-
_ => panic!(
154-
"Unknown or non-adjustable resampler type {}\nMust be one of SincFixedInput, SincFixedOutput, PolyFixedInput, PolyFixedOutput, SlipFixedInput, SlipFixedOutput",
155-
resampler_type
156-
),
157189
};
158190

159191
// Recover the adjust-ratio capability from the trait object and apply the offset once. Since the
@@ -193,6 +225,6 @@ fn main() {
193225
);
194226

195227
println!("Write output to file, trimming off the silent frames from both ends.");
196-
let mut file_out_disk = BufWriter::new(File::create(file_out).unwrap());
228+
let mut file_out_disk = BufWriter::new(File::create(&opts.output).unwrap());
197229
write_file(&outdata, &mut file_out_disk, nbr_out * channels);
198230
}

examples/fixedout_ramp64.rs

Lines changed: 0 additions & 170 deletions
This file was deleted.

0 commit comments

Comments
 (0)