Skip to content

Commit 6056e7d

Browse files
committed
Refactor sample_rate_converter tool
1 parent eb61570 commit 6056e7d

1 file changed

Lines changed: 68 additions & 44 deletions

File tree

tools/sample_rate_converter.cpp

Lines changed: 68 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -28,64 +28,88 @@ int main(int argc, char** argv)
2828

2929
// Initialize WAV reader and get file sample rate
3030
audio_reader_wav<double> reader(open_file_for_reading(argv[1]));
31+
const size_t channels = reader.format().channels;
3132
const size_t input_sr = static_cast<size_t>(reader.format().samplerate);
3233

33-
// Read channels of audio
34-
univector2d<double> input_channels = reader.read_channels(reader.format().length);
35-
36-
// Prepare conversion
37-
univector2d<double> output_channels;
38-
println("Input channels: ", reader.format().channels);
34+
println("Input channels: ", channels);
3935
println("Input sample rate: ", reader.format().samplerate);
4036
println("Input bit depth: ", audio_sample_bit_depth(reader.format().type));
4137

42-
for (size_t ch = 0; ch < input_channels.size(); ++ch)
43-
{
44-
println("Processing ", ch, " of ", reader.format().channels);
45-
const univector<double>& input = input_channels[ch];
38+
// Initialize WAV writer
39+
audio_writer_wav<double> writer(open_file_for_writing(argv[2]),
40+
audio_format{ channels, reader.format().type, kfr::fmax(output_sr) });
4641

47-
// Initialize resampler
48-
auto r = resampler<double>(resample_quality::high, output_sr, input_sr);
42+
std::vector<samplerate_converter<double>> resamplers(channels);
43+
for (size_t ch = 0; ch < channels; ++ch)
44+
{
45+
resamplers[ch] = resampler<double>(resample_quality::high, output_sr, input_sr);
46+
}
47+
auto& resampler0 = resamplers.front();
48+
49+
constexpr size_t output_chunk_size = 16384;
50+
univector2d<double> output_chunk(channels, univector<double>(output_chunk_size));
51+
univector<double> output_chunk_interleaved(output_chunk_size * channels);
52+
53+
const size_t input_delay_compensation = resampler0.input_size_for_output(resampler0.get_delay());
54+
const size_t input_chunk_size = output_chunk_size * input_sr / output_sr + 1 + input_delay_compensation;
55+
univector<double> input_chunk_interleaved(input_chunk_size * channels);
56+
univector2d<double> input_chunk(channels, univector<double>(input_chunk_size));
57+
58+
bool first_chunk = true;
59+
bool last_chunk = false;
60+
std::chrono::high_resolution_clock::duration resampling_time{};
61+
// Process audio in chunks
62+
println("Resampling...");
63+
fflush(stdout);
64+
for (;;)
65+
{
66+
const size_t frames_to_read =
67+
resampler0.input_size_for_output(output_chunk_size + (first_chunk ? resampler0.get_delay() : 0));
4968

50-
// Calculate output size and initialize output buffer
51-
const size_t output_size = input.size() * output_sr / input_sr;
52-
univector<double> output(output_size);
69+
// Read channels of audio
70+
const size_t samples_read = reader.read(input_chunk_interleaved.truncate(frames_to_read * channels));
71+
const size_t frames_read = samples_read / channels;
72+
deinterleave(input_chunk, input_chunk_interleaved.truncate(samples_read));
5373

54-
// Skip the first r.get_delay() samples (FIR filter delay). Returns new input pos
55-
size_t input_pos = r.skip(r.get_delay(), input.slice());
74+
size_t frames_to_write = output_chunk_size;
75+
if (frames_read < frames_to_read)
76+
{
77+
last_chunk = true;
78+
frames_to_write = resampler0.output_size_for_input(frames_read) + resampler0.get_delay();
79+
}
80+
if (frames_to_write <= resampler0.get_delay())
81+
{
82+
println("Error: input file is too short for resampling");
83+
return 2;
84+
}
5685

57-
std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now();
58-
size_t output_pos = 0;
59-
for (;;)
86+
const std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();
87+
for (size_t ch = 0; ch < channels; ++ch)
6088
{
61-
const size_t block_size = std::min(size_t(16384), output.size() - output_pos);
62-
if (block_size == 0)
63-
break;
89+
auto& r = resamplers[ch];
90+
auto&& output = output_chunk[ch].truncate(frames_to_write).ref();
91+
auto&& input = input_chunk[ch].truncate(frames_read);
92+
if (first_chunk)
93+
{
94+
// Skip the first r.get_delay() samples (FIR filter delay).
95+
r.skip(r.get_delay(), input);
96+
}
6497

6598
// Process new block of audio
66-
input_pos += r.process(output.slice(output_pos, block_size).ref(), input.slice(input_pos));
67-
output_pos += block_size;
99+
r.process(output, input);
68100
}
69-
70-
std::chrono::high_resolution_clock::duration time =
71-
std::chrono::high_resolution_clock::now() - start_time;
72-
const double duration = static_cast<double>(output.size()) / output_sr;
73-
println("time: ",
74-
fmt<'f', 6, 2>(std::chrono::duration_cast<std::chrono::microseconds>(time).count() /
75-
duration * 0.001),
76-
"ms per 1 second of audio");
77-
78-
// Place buffer to the list of output channels
79-
output_channels.push_back(std::move(output));
101+
resampling_time += std::chrono::high_resolution_clock::now() - t1;
102+
interleave(output_chunk_interleaved.truncate(frames_to_write * channels).ref(), output_chunk);
103+
104+
// Write audio
105+
writer.write(output_chunk_interleaved.truncate(frames_to_write * channels));
106+
first_chunk = false;
107+
if (last_chunk)
108+
break;
80109
}
81-
82-
// Initialize WAV writer
83-
audio_writer_wav<double> writer(
84-
open_file_for_writing(argv[2]),
85-
audio_format{ reader.format().channels, reader.format().type, kfr::fmax(output_sr) });
86-
87-
// Write audio
88-
writer.write_channels(output_channels);
110+
double duration = std::chrono::duration_cast<std::chrono::nanoseconds>(resampling_time).count() / 1e9;
111+
double length = reader.format().length / reader.format().samplerate;
112+
println("done in ", duration, " seconds", " (", fmt<'f', 4, 1>(length / duration), "x real-time)");
89113

90114
return 0;
91115
}

0 commit comments

Comments
 (0)