@@ -573,6 +573,39 @@ impl AudioEngine {
573573 Ok ( ( ) )
574574 }
575575
576+ pub fn run_pink_noise ( settings : AudioSettings , cancel : Arc < AtomicBool > ) -> Result < ( ) , AudioError > {
577+ let host = preferred_host ( ) ?;
578+ let output_entries = enumerate_output_devices ( & host) ?;
579+ let output_device =
580+ select_device ( & host, & output_entries, settings. output_device_index , false ) ?;
581+ let ( output_config, output_format) =
582+ choose_output_config ( & output_device, settings. output_sample_rate ) ?;
583+ let channels = output_config. channels as usize ;
584+
585+ let noise_state = Arc :: new ( Mutex :: new ( PinkNoiseState :: new ( 0.25 ) ) ) ;
586+ let err_fn = |err| {
587+ eprintln ! ( "pink noise stream error: {err}" ) ;
588+ } ;
589+
590+ let stream = build_pink_output_stream (
591+ & output_device,
592+ & output_config,
593+ output_format,
594+ OutputRouting :: Both ,
595+ channels,
596+ noise_state,
597+ err_fn,
598+ ) ?;
599+ stream. play ( ) ?;
600+
601+ while !cancel. load ( Ordering :: SeqCst ) {
602+ std:: thread:: sleep ( Duration :: from_millis ( 50 ) ) ;
603+ }
604+
605+ drop ( stream) ;
606+ Ok ( ( ) )
607+ }
608+
576609 pub fn run_sweep_fr_test (
577610 settings : AudioSettings ,
578611 mut request : SweepFrRequest ,
@@ -2502,6 +2535,234 @@ fn route_sample(sample: f32, routing: OutputRouting) -> (f32, f32) {
25022535 }
25032536}
25042537
2538+ struct PinkNoiseState {
2539+ b0 : f32 ,
2540+ b1 : f32 ,
2541+ b2 : f32 ,
2542+ b3 : f32 ,
2543+ b4 : f32 ,
2544+ b5 : f32 ,
2545+ b6 : f32 ,
2546+ seed : u64 ,
2547+ gain : f32 ,
2548+ }
2549+
2550+ impl PinkNoiseState {
2551+ fn new ( gain : f32 ) -> Self {
2552+ let seed = SystemTime :: now ( )
2553+ . duration_since ( SystemTime :: UNIX_EPOCH )
2554+ . unwrap_or_default ( )
2555+ . as_nanos ( ) as u64
2556+ ^ 0x9E37_79B9_7F4A_7C15 ;
2557+
2558+ Self {
2559+ b0 : 0.0 ,
2560+ b1 : 0.0 ,
2561+ b2 : 0.0 ,
2562+ b3 : 0.0 ,
2563+ b4 : 0.0 ,
2564+ b5 : 0.0 ,
2565+ b6 : 0.0 ,
2566+ seed : if seed == 0 { 0xA5A5_A5A5_A5A5_A5A5 } else { seed } ,
2567+ gain : gain. clamp ( 0.0 , 1.0 ) ,
2568+ }
2569+ }
2570+
2571+ fn next_white ( & mut self ) -> f32 {
2572+ let mut x = self . seed ;
2573+ x ^= x << 13 ;
2574+ x ^= x >> 7 ;
2575+ x ^= x << 17 ;
2576+ self . seed = x;
2577+ let unit = ( x as f64 ) / ( u64:: MAX as f64 ) ;
2578+ ( unit as f32 ) * 2.0 - 1.0
2579+ }
2580+
2581+ fn next_sample ( & mut self ) -> f32 {
2582+ let x = self . next_white ( ) ;
2583+ self . b0 = 0.99886 * self . b0 + x * 0.055_517_9 ;
2584+ self . b1 = 0.99332 * self . b1 + x * 0.075_075_9 ;
2585+ self . b2 = 0.96900 * self . b2 + x * 0.153_852_0 ;
2586+ self . b3 = 0.86650 * self . b3 + x * 0.310_485_6 ;
2587+ self . b4 = 0.55000 * self . b4 + x * 0.532_952_2 ;
2588+ self . b5 = -0.7616 * self . b5 - x * 0.016_898_0 ;
2589+ let y = self . b0 + self . b1 + self . b2 + self . b3 + self . b4 + self . b5 + self . b6 + x * 0.5362 ;
2590+ self . b6 = x * 0.115_926 ;
2591+
2592+ // 0.11 keeps the Paul Kellet filter output in a comfortable playback range.
2593+ ( y * 0.11 * self . gain ) . clamp ( -1.0 , 1.0 )
2594+ }
2595+ }
2596+
2597+ fn build_pink_output_stream (
2598+ device : & Device ,
2599+ config : & StreamConfig ,
2600+ format : SampleFormat ,
2601+ routing : OutputRouting ,
2602+ channels : usize ,
2603+ noise_state : Arc < Mutex < PinkNoiseState > > ,
2604+ err_fn : impl Fn ( cpal:: StreamError ) + Send + ' static + Copy ,
2605+ ) -> Result < Stream , AudioError > {
2606+ match format {
2607+ SampleFormat :: F32 => {
2608+ let state = noise_state. clone ( ) ;
2609+ Ok ( device. build_output_stream (
2610+ config,
2611+ move |data : & mut [ f32 ] , _| {
2612+ write_pink_f32 ( data, channels, routing, & state) ;
2613+ } ,
2614+ err_fn,
2615+ None ,
2616+ ) ?)
2617+ }
2618+ SampleFormat :: I16 => {
2619+ let state = noise_state. clone ( ) ;
2620+ Ok ( device. build_output_stream (
2621+ config,
2622+ move |data : & mut [ i16 ] , _| {
2623+ write_pink_i16 ( data, channels, routing, & state) ;
2624+ } ,
2625+ err_fn,
2626+ None ,
2627+ ) ?)
2628+ }
2629+ SampleFormat :: U16 => {
2630+ let state = noise_state. clone ( ) ;
2631+ Ok ( device. build_output_stream (
2632+ config,
2633+ move |data : & mut [ u16 ] , _| {
2634+ write_pink_u16 ( data, channels, routing, & state) ;
2635+ } ,
2636+ err_fn,
2637+ None ,
2638+ ) ?)
2639+ }
2640+ SampleFormat :: U8 => {
2641+ let state = noise_state. clone ( ) ;
2642+ Ok ( device. build_output_stream (
2643+ config,
2644+ move |data : & mut [ u8 ] , _| {
2645+ write_pink_u8 ( data, channels, routing, & state) ;
2646+ } ,
2647+ err_fn,
2648+ None ,
2649+ ) ?)
2650+ }
2651+ other => Err ( AudioError :: UnsupportedSampleFormat ( format ! ( "{other:?}" ) ) ) ,
2652+ }
2653+ }
2654+
2655+ fn write_pink_f32 (
2656+ data : & mut [ f32 ] ,
2657+ channels : usize ,
2658+ routing : OutputRouting ,
2659+ noise_state : & Arc < Mutex < PinkNoiseState > > ,
2660+ ) {
2661+ if let Ok ( mut state) = noise_state. lock ( ) {
2662+ for frame in data. chunks_mut ( channels. max ( 1 ) ) {
2663+ let mono = state. next_sample ( ) ;
2664+ let ( left, right) = route_sample ( mono, routing) ;
2665+ for ( ch, sample) in frame. iter_mut ( ) . enumerate ( ) {
2666+ * sample = if ch == 0 {
2667+ left
2668+ } else if ch == 1 {
2669+ right
2670+ } else if ch % 2 == 0 {
2671+ left
2672+ } else {
2673+ right
2674+ } ;
2675+ }
2676+ }
2677+ } else {
2678+ data. fill ( 0.0 ) ;
2679+ }
2680+ }
2681+
2682+ fn write_pink_i16 (
2683+ data : & mut [ i16 ] ,
2684+ channels : usize ,
2685+ routing : OutputRouting ,
2686+ noise_state : & Arc < Mutex < PinkNoiseState > > ,
2687+ ) {
2688+ if let Ok ( mut state) = noise_state. lock ( ) {
2689+ for frame in data. chunks_mut ( channels. max ( 1 ) ) {
2690+ let mono = state. next_sample ( ) ;
2691+ let ( left, right) = route_sample ( mono, routing) ;
2692+ for ( ch, sample) in frame. iter_mut ( ) . enumerate ( ) {
2693+ let value = if ch == 0 {
2694+ left
2695+ } else if ch == 1 {
2696+ right
2697+ } else if ch % 2 == 0 {
2698+ left
2699+ } else {
2700+ right
2701+ } ;
2702+ * sample = ( value * i16:: MAX as f32 ) as i16 ;
2703+ }
2704+ }
2705+ } else {
2706+ data. fill ( 0 ) ;
2707+ }
2708+ }
2709+
2710+ fn write_pink_u16 (
2711+ data : & mut [ u16 ] ,
2712+ channels : usize ,
2713+ routing : OutputRouting ,
2714+ noise_state : & Arc < Mutex < PinkNoiseState > > ,
2715+ ) {
2716+ if let Ok ( mut state) = noise_state. lock ( ) {
2717+ for frame in data. chunks_mut ( channels. max ( 1 ) ) {
2718+ let mono = state. next_sample ( ) ;
2719+ let ( left, right) = route_sample ( mono, routing) ;
2720+ for ( ch, sample) in frame. iter_mut ( ) . enumerate ( ) {
2721+ let value = if ch == 0 {
2722+ left
2723+ } else if ch == 1 {
2724+ right
2725+ } else if ch % 2 == 0 {
2726+ left
2727+ } else {
2728+ right
2729+ } ;
2730+ * sample = ( ( value * 0.5 + 0.5 ) * u16:: MAX as f32 ) as u16 ;
2731+ }
2732+ }
2733+ } else {
2734+ data. fill ( u16:: MAX / 2 ) ;
2735+ }
2736+ }
2737+
2738+ fn write_pink_u8 (
2739+ data : & mut [ u8 ] ,
2740+ channels : usize ,
2741+ routing : OutputRouting ,
2742+ noise_state : & Arc < Mutex < PinkNoiseState > > ,
2743+ ) {
2744+ if let Ok ( mut state) = noise_state. lock ( ) {
2745+ for frame in data. chunks_mut ( channels. max ( 1 ) ) {
2746+ let mono = state. next_sample ( ) ;
2747+ let ( left, right) = route_sample ( mono, routing) ;
2748+ for ( ch, sample) in frame. iter_mut ( ) . enumerate ( ) {
2749+ let value = if ch == 0 {
2750+ left
2751+ } else if ch == 1 {
2752+ right
2753+ } else if ch % 2 == 0 {
2754+ left
2755+ } else {
2756+ right
2757+ } ;
2758+ * sample = ( ( value * 0.5 + 0.5 ) * u8:: MAX as f32 ) as u8 ;
2759+ }
2760+ }
2761+ } else {
2762+ data. fill ( u8:: MAX / 2 ) ;
2763+ }
2764+ }
2765+
25052766struct MonitorStats {
25062767 current_dbfs : f32 ,
25072768 peak_dbfs : f32 ,
0 commit comments