Skip to content

Commit d16fd19

Browse files
Merge pull request #2716 from kyzer1023/cursor/waveform-peaks-ilp-4d99
improve(audio): sum waveform peaks four windows at a time
2 parents f3f3ab1 + d9da427 commit d16fd19

2 files changed

Lines changed: 295 additions & 26 deletions

File tree

‎apps/desktop-gpui/kernels/src/waveform.rs‎

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,79 @@ mod tests {
3333
.collect()
3434
}
3535

36+
fn assert_peak_bits(actual: &[f32], expected: &[f32], channels: u16, block_len: usize) {
37+
assert_eq!(actual.len(), expected.len(), "{channels} {block_len}");
38+
for (actual, expected) in actual.iter().zip(expected) {
39+
assert_eq!(
40+
actual.to_bits(),
41+
expected.to_bits(),
42+
"{channels} {block_len}"
43+
);
44+
}
45+
}
46+
3647
#[test]
3748
fn peaks_match_a_sequential_sum_across_uneven_blocks() {
38-
let samples = samples(48_000 * 3 + 1_234);
49+
let uneven = samples(48_000 * 3 + 1_234);
50+
for channels in [1, 2] {
51+
let expected = sequential_peaks(&uneven, channels);
52+
for block_len in [1, 7, 4_799, 9_600, 65_536, 32_768 * usize::from(channels)] {
53+
let blocks: Vec<&[f32]> = uneven.chunks(block_len).collect();
54+
assert_peak_bits(&peaks(&blocks, channels), &expected, channels, block_len);
55+
}
56+
assert_peak_bits(
57+
&peaks(&[&uneven], channels),
58+
&expected,
59+
channels,
60+
uneven.len(),
61+
);
62+
}
63+
64+
let contiguous = samples(48_000 * 120 + 17);
3965
for channels in [1, 2] {
40-
for block_len in [1, 7, 4_799, 9_600, 65_536] {
41-
let blocks: Vec<&[f32]> = samples.chunks(block_len).collect();
42-
let actual = peaks(&blocks, channels);
43-
let expected = sequential_peaks(&samples, channels);
44-
assert_eq!(actual.len(), expected.len());
45-
for (actual, expected) in actual.iter().zip(&expected) {
46-
assert_eq!(
47-
actual.to_bits(),
48-
expected.to_bits(),
49-
"{channels} {block_len}"
50-
);
66+
let expected = sequential_peaks(&contiguous, channels);
67+
assert_peak_bits(
68+
&peaks(&[&contiguous], channels),
69+
&expected,
70+
channels,
71+
contiguous.len(),
72+
);
73+
let block_len = 32_768 * usize::from(channels);
74+
let blocks: Vec<&[f32]> = contiguous.chunks(block_len).collect();
75+
assert_peak_bits(&peaks(&blocks, channels), &expected, channels, block_len);
76+
}
77+
}
78+
79+
fn serial_peaks(blocks: &[&[f32]], channels: u16) -> Vec<f32> {
80+
let block_samples = 48_000usize / 10 * usize::from(channels.max(1));
81+
let mut peaks = Vec::new();
82+
let mut sum = 0.0_f32;
83+
let mut count = 0usize;
84+
for slice in blocks {
85+
let mut slice = *slice;
86+
while !slice.is_empty() {
87+
let (window, rest) = slice.split_at((block_samples - count).min(slice.len()));
88+
sum = window.iter().fold(sum, |sum, sample| sum + sample.abs());
89+
count += window.len();
90+
slice = rest;
91+
if count == block_samples {
92+
peaks.push(sum / count as f32);
93+
sum = 0.0;
94+
count = 0;
5195
}
5296
}
5397
}
98+
if count > 0 {
99+
peaks.push(sum / count as f32);
100+
}
101+
for peak in &mut peaks {
102+
*peak = if *peak > 0.0 {
103+
20.0 * peak.log10()
104+
} else {
105+
-60.0
106+
};
107+
}
108+
peaks
54109
}
55110

56111
#[test]
@@ -61,17 +116,30 @@ mod tests {
61116
for (track, channels) in [("mic", 1_u16), ("system audio", 2)] {
62117
let samples = samples(48_000 * 3_600 * usize::from(channels));
63118
let blocks: Vec<&[f32]> = samples.chunks(32_768 * usize::from(channels)).collect();
64-
let mut runs = Vec::new();
119+
assert_peak_bits(
120+
&peaks(&blocks, channels),
121+
&serial_peaks(&blocks, channels),
122+
channels,
123+
32_768 * usize::from(channels),
124+
);
125+
let mut serial_runs = Vec::new();
126+
let mut lane_runs = Vec::new();
65127
for _ in 0..5 {
128+
let start = Instant::now();
129+
black_box(serial_peaks(&blocks, channels));
130+
serial_runs.push(start.elapsed().as_secs_f64() * 1000.0);
66131
let start = Instant::now();
67132
black_box(peaks(&blocks, channels));
68-
runs.push(start.elapsed().as_secs_f64() * 1000.0);
133+
lane_runs.push(start.elapsed().as_secs_f64() * 1000.0);
69134
}
70-
runs.sort_by(f64::total_cmp);
135+
serial_runs.sort_by(f64::total_cmp);
136+
lane_runs.sort_by(f64::total_cmp);
71137
println!(
72-
"{{\"track\":\"{track}\",\"channels\":{channels},\"duration_seconds\":3600,\"median_ms\":{:.1},\"min_ms\":{:.1}}}",
73-
runs[runs.len() / 2],
74-
runs[0]
138+
"{{\"track\":\"{track}\",\"channels\":{channels},\"duration_seconds\":3600,\"block_frames\":32768,\"serial_median_ms\":{:.1},\"serial_min_ms\":{:.1},\"lanes_median_ms\":{:.1},\"lanes_min_ms\":{:.1}}}",
139+
serial_runs[serial_runs.len() / 2],
140+
serial_runs[0],
141+
lane_runs[lane_runs.len() / 2],
142+
lane_runs[0]
75143
);
76144
}
77145
}

‎crates/audio/src/progressive.rs‎

Lines changed: 209 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use std::{
1717
use tokio::sync::watch;
1818

1919
const BLOCK_FRAMES: usize = 32_768;
20+
// Eight lanes never run on a decoded block: 8 * 4_800 > BLOCK_FRAMES.
21+
const WAVEFORM_PEAK_LANES: usize = 4;
2022

2123
pub fn waveform_peaks<'a>(slices: impl IntoIterator<Item = &'a [f32]>, channels: u16) -> Vec<f32> {
2224
let block_samples = AudioData::SAMPLE_RATE as usize / 10 * usize::from(channels.max(1));
@@ -25,14 +27,20 @@ pub fn waveform_peaks<'a>(slices: impl IntoIterator<Item = &'a [f32]>, channels:
2527
let mut count = 0;
2628
for mut slice in slices {
2729
while !slice.is_empty() {
28-
let (window, rest) = slice.split_at((block_samples - count).min(slice.len()));
29-
sum = window.iter().fold(sum, |sum, sample| sum + sample.abs());
30-
count += window.len();
31-
slice = rest;
32-
if count == block_samples {
33-
peaks.push(sum / count as f32);
34-
sum = 0.0;
35-
count = 0;
30+
if count == 0 && slice.len() >= block_samples * WAVEFORM_PEAK_LANES {
31+
let (group, rest) = slice.split_at(block_samples * WAVEFORM_PEAK_LANES);
32+
push_waveform_peak_lanes(&mut peaks, group, block_samples);
33+
slice = rest;
34+
} else {
35+
let (window, rest) = slice.split_at((block_samples - count).min(slice.len()));
36+
sum = window.iter().fold(sum, |sum, sample| sum + sample.abs());
37+
count += window.len();
38+
slice = rest;
39+
if count == block_samples {
40+
peaks.push(sum / count as f32);
41+
sum = 0.0;
42+
count = 0;
43+
}
3644
}
3745
}
3846
}
@@ -49,6 +57,27 @@ pub fn waveform_peaks<'a>(slices: impl IntoIterator<Item = &'a [f32]>, channels:
4957
peaks
5058
}
5159

60+
fn push_waveform_peak_lanes(peaks: &mut Vec<f32>, group: &[f32], block_samples: usize) {
61+
let (first, rest) = group.split_at(block_samples);
62+
let (second, rest) = rest.split_at(block_samples);
63+
let (third, fourth) = rest.split_at(block_samples);
64+
let mut first_sum = 0.0_f32;
65+
let mut second_sum = 0.0_f32;
66+
let mut third_sum = 0.0_f32;
67+
let mut fourth_sum = 0.0_f32;
68+
for index in 0..block_samples {
69+
first_sum += first[index].abs();
70+
second_sum += second[index].abs();
71+
third_sum += third[index].abs();
72+
fourth_sum += fourth[index].abs();
73+
}
74+
let scale = block_samples as f32;
75+
peaks.push(first_sum / scale);
76+
peaks.push(second_sum / scale);
77+
peaks.push(third_sum / scale);
78+
peaks.push(fourth_sum / scale);
79+
}
80+
5281
pub struct DecodedAudio {
5382
storage: AudioStorage,
5483
channels: u16,
@@ -1202,4 +1231,176 @@ mod tests {
12021231
.is_err()
12031232
);
12041233
}
1234+
1235+
fn xorshift_samples(len: usize) -> Vec<f32> {
1236+
let mut state = 0x1234_5678_u32;
1237+
(0..len)
1238+
.map(|_| {
1239+
state ^= state << 13;
1240+
state ^= state >> 17;
1241+
state ^= state << 5;
1242+
state as f32 / u32::MAX as f32 * 2.0 - 1.0
1243+
})
1244+
.collect()
1245+
}
1246+
1247+
fn sequential_waveform_peaks(samples: &[f32], channels: u16) -> Vec<f32> {
1248+
let window_samples = 4_800 * usize::from(channels.max(1));
1249+
samples
1250+
.chunks(window_samples)
1251+
.map(|window| {
1252+
let average = window
1253+
.iter()
1254+
.fold(0.0_f32, |sum, sample| sum + sample.abs())
1255+
/ window.len() as f32;
1256+
if average > 0.0 {
1257+
20.0 * average.log10()
1258+
} else {
1259+
-60.0
1260+
}
1261+
})
1262+
.collect()
1263+
}
1264+
1265+
fn assert_peak_bits(actual: &[f32], expected: &[f32], channels: u16, block_len: usize) {
1266+
assert_eq!(actual.len(), expected.len(), "{channels} {block_len}");
1267+
for (index, (actual, expected)) in actual.iter().zip(expected).enumerate() {
1268+
assert_eq!(
1269+
actual.to_bits(),
1270+
expected.to_bits(),
1271+
"{channels} {block_len} at {index}"
1272+
);
1273+
}
1274+
}
1275+
1276+
#[test]
1277+
fn waveform_peaks_match_a_sequential_sum_across_uneven_blocks() {
1278+
let samples = xorshift_samples(48_000 * 3 + 1_234);
1279+
for channels in [1, 2] {
1280+
let expected = sequential_waveform_peaks(&samples, channels);
1281+
let window = 4_800 * usize::from(channels);
1282+
for block_len in [
1283+
1,
1284+
7,
1285+
4_799,
1286+
window - 1,
1287+
window,
1288+
window * WAVEFORM_PEAK_LANES - 1,
1289+
window * WAVEFORM_PEAK_LANES,
1290+
window * WAVEFORM_PEAK_LANES + 1,
1291+
32_768 * usize::from(channels),
1292+
] {
1293+
let blocks: Vec<&[f32]> = samples.chunks(block_len).collect();
1294+
assert_peak_bits(
1295+
&waveform_peaks(blocks.iter().copied(), channels),
1296+
&expected,
1297+
channels,
1298+
block_len,
1299+
);
1300+
}
1301+
let mut with_empty: Vec<&[f32]> = Vec::new();
1302+
for chunk in samples.chunks(7) {
1303+
with_empty.push(chunk);
1304+
with_empty.push(&[]);
1305+
}
1306+
assert_peak_bits(
1307+
&waveform_peaks(with_empty, channels),
1308+
&expected,
1309+
channels,
1310+
7,
1311+
);
1312+
assert_peak_bits(
1313+
&waveform_peaks([&samples[..]], channels),
1314+
&expected,
1315+
channels,
1316+
samples.len(),
1317+
);
1318+
}
1319+
1320+
let contiguous = xorshift_samples(48_000 * 120 + 17);
1321+
for channels in [1, 2] {
1322+
let expected = sequential_waveform_peaks(&contiguous, channels);
1323+
assert_peak_bits(
1324+
&waveform_peaks([&contiguous[..]], channels),
1325+
&expected,
1326+
channels,
1327+
contiguous.len(),
1328+
);
1329+
let block_len = 32_768 * usize::from(channels);
1330+
let blocks: Vec<&[f32]> = contiguous.chunks(block_len).collect();
1331+
assert_peak_bits(
1332+
&waveform_peaks(blocks, channels),
1333+
&expected,
1334+
channels,
1335+
block_len,
1336+
);
1337+
}
1338+
}
1339+
1340+
fn serial_waveform_peaks<'a>(
1341+
slices: impl IntoIterator<Item = &'a [f32]>,
1342+
channels: u16,
1343+
) -> Vec<f32> {
1344+
let block_samples = AudioData::SAMPLE_RATE as usize / 10 * usize::from(channels.max(1));
1345+
let mut peaks = Vec::new();
1346+
let mut sum = 0.0_f32;
1347+
let mut count = 0;
1348+
for mut slice in slices {
1349+
while !slice.is_empty() {
1350+
let (window, rest) = slice.split_at((block_samples - count).min(slice.len()));
1351+
sum = window.iter().fold(sum, |sum, sample| sum + sample.abs());
1352+
count += window.len();
1353+
slice = rest;
1354+
if count == block_samples {
1355+
peaks.push(sum / count as f32);
1356+
sum = 0.0;
1357+
count = 0;
1358+
}
1359+
}
1360+
}
1361+
if count > 0 {
1362+
peaks.push(sum / count as f32);
1363+
}
1364+
for peak in &mut peaks {
1365+
*peak = if *peak > 0.0 {
1366+
20.0 * peak.log10()
1367+
} else {
1368+
-60.0
1369+
};
1370+
}
1371+
peaks
1372+
}
1373+
1374+
#[test]
1375+
#[ignore]
1376+
fn benchmark_waveform_peaks() {
1377+
use std::{hint::black_box, time::Instant};
1378+
1379+
for (track, channels) in [("mic", 1_u16), ("system audio", 2)] {
1380+
let samples = xorshift_samples(48_000 * 3_600 * usize::from(channels));
1381+
let blocks: Vec<&[f32]> = samples.chunks(32_768 * usize::from(channels)).collect();
1382+
let serial = serial_waveform_peaks(blocks.iter().copied(), channels);
1383+
let lanes = waveform_peaks(blocks.iter().copied(), channels);
1384+
assert_peak_bits(&lanes, &serial, channels, 32_768 * usize::from(channels));
1385+
let mut serial_runs = Vec::new();
1386+
let mut lane_runs = Vec::new();
1387+
for _ in 0..5 {
1388+
let start = Instant::now();
1389+
black_box(serial_waveform_peaks(blocks.iter().copied(), channels));
1390+
serial_runs.push(start.elapsed().as_secs_f64() * 1000.0);
1391+
let start = Instant::now();
1392+
black_box(waveform_peaks(blocks.iter().copied(), channels));
1393+
lane_runs.push(start.elapsed().as_secs_f64() * 1000.0);
1394+
}
1395+
serial_runs.sort_by(f64::total_cmp);
1396+
lane_runs.sort_by(f64::total_cmp);
1397+
println!(
1398+
"{{\"track\":\"{track}\",\"channels\":{channels},\"duration_seconds\":3600,\"block_frames\":32768,\"serial_median_ms\":{:.1},\"serial_min_ms\":{:.1},\"lanes_median_ms\":{:.1},\"lanes_min_ms\":{:.1}}}",
1399+
serial_runs[serial_runs.len() / 2],
1400+
serial_runs[0],
1401+
lane_runs[lane_runs.len() / 2],
1402+
lane_runs[0]
1403+
);
1404+
}
1405+
}
12051406
}

0 commit comments

Comments
 (0)