@@ -17,6 +17,8 @@ use std::{
1717use tokio:: sync:: watch;
1818
1919const 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
2123pub 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+
5281pub 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