@@ -13,7 +13,158 @@ pub fn render_waveform_with_envelope(
1313 width : u32 ,
1414 height : u32 ,
1515) -> Result < ( ) > {
16- // For now, delegate to regular waveform rendering
17- // Envelope rendering can be added as enhancement
18- render_waveform ( samples, output_path, width, height)
16+ use plotters:: prelude:: * ;
17+
18+ // Calculate envelope with a window size
19+ let window_size = ( samples. len ( ) / width as usize ) . max ( 1 ) ;
20+ let mut peak_envelope_upper = Vec :: new ( ) ;
21+ let mut peak_envelope_lower = Vec :: new ( ) ;
22+ let mut rms_envelope = Vec :: new ( ) ;
23+
24+ // Process samples in windows
25+ for chunk in samples. chunks ( window_size) {
26+ let mut max_val = 0.0f32 ;
27+ let mut min_val = 0.0f32 ;
28+ let mut rms_sum = 0.0f32 ;
29+
30+ for & sample in chunk {
31+ max_val = max_val. max ( sample) ;
32+ min_val = min_val. min ( sample) ;
33+ rms_sum += sample * sample;
34+ }
35+
36+ peak_envelope_upper. push ( max_val) ;
37+ peak_envelope_lower. push ( min_val) ;
38+
39+ let rms = ( rms_sum / chunk. len ( ) as f32 ) . sqrt ( ) ;
40+ rms_envelope. push ( rms) ;
41+ }
42+
43+ // Create the plot
44+ let root = BitMapBackend :: new ( output_path, ( width, height) ) . into_drawing_area ( ) ;
45+
46+ root. fill ( & WHITE ) . map_err ( |e| {
47+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
48+ "Failed to fill background: {}" ,
49+ e
50+ ) )
51+ } ) ?;
52+
53+ let max_val = samples. iter ( ) . fold ( 0.0f32 , |a, & b| a. max ( b. abs ( ) ) ) ;
54+ let y_range = if max_val > 0.0 { max_val * 1.1 } else { 1.0 } ;
55+
56+ let mut chart = ChartBuilder :: on ( & root)
57+ . margin ( 10 )
58+ . build_cartesian_2d ( 0f32 ..peak_envelope_upper. len ( ) as f32 , -y_range..y_range)
59+ . map_err ( |e| {
60+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
61+ "Failed to build chart: {}" ,
62+ e
63+ ) )
64+ } ) ?;
65+
66+ // Draw peak envelope as filled area
67+ let upper_points: Vec < ( f32 , f32 ) > = peak_envelope_upper
68+ . iter ( )
69+ . enumerate ( )
70+ . map ( |( i, & v) | ( i as f32 , v) )
71+ . collect ( ) ;
72+
73+ let lower_points: Vec < ( f32 , f32 ) > = peak_envelope_lower
74+ . iter ( )
75+ . enumerate ( )
76+ . map ( |( i, & v) | ( i as f32 , v) )
77+ . collect ( ) ;
78+
79+ // Draw filled area between envelopes using polygons
80+ let mut polygon_points = upper_points. clone ( ) ;
81+ polygon_points. extend ( lower_points. iter ( ) . rev ( ) ) ;
82+ polygon_points. push ( upper_points[ 0 ] ) ; // Close the polygon
83+
84+ chart
85+ . draw_series ( std:: iter:: once ( Polygon :: new (
86+ polygon_points,
87+ BLUE . mix ( 0.2 ) . filled ( ) ,
88+ ) ) )
89+ . map_err ( |e| {
90+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
91+ "Failed to draw envelope fill: {}" ,
92+ e
93+ ) )
94+ } ) ?;
95+
96+ // Draw RMS envelope
97+ let rms_points_upper: Vec < ( f32 , f32 ) > = rms_envelope
98+ . iter ( )
99+ . enumerate ( )
100+ . map ( |( i, & v) | ( i as f32 , v) )
101+ . collect ( ) ;
102+
103+ let rms_points_lower: Vec < ( f32 , f32 ) > = rms_envelope
104+ . iter ( )
105+ . enumerate ( )
106+ . map ( |( i, & v) | ( i as f32 , -v) )
107+ . collect ( ) ;
108+
109+ chart
110+ . draw_series ( LineSeries :: new ( rms_points_upper, & RED ) )
111+ . map_err ( |e| {
112+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
113+ "Failed to draw RMS upper: {}" ,
114+ e
115+ ) )
116+ } ) ?;
117+
118+ chart
119+ . draw_series ( LineSeries :: new ( rms_points_lower, & RED ) )
120+ . map_err ( |e| {
121+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
122+ "Failed to draw RMS lower: {}" ,
123+ e
124+ ) )
125+ } ) ?;
126+
127+ // Draw peak envelope outlines
128+ chart
129+ . draw_series ( LineSeries :: new (
130+ upper_points,
131+ ShapeStyle :: from ( & BLUE ) . stroke_width ( 2 ) ,
132+ ) )
133+ . map_err ( |e| {
134+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
135+ "Failed to draw peak upper: {}" ,
136+ e
137+ ) )
138+ } ) ?;
139+
140+ chart
141+ . draw_series ( LineSeries :: new (
142+ lower_points,
143+ ShapeStyle :: from ( & BLUE ) . stroke_width ( 2 ) ,
144+ ) )
145+ . map_err ( |e| {
146+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
147+ "Failed to draw peak lower: {}" ,
148+ e
149+ ) )
150+ } ) ?;
151+
152+ // Draw zero line
153+ chart
154+ . draw_series ( LineSeries :: new (
155+ vec ! [ ( 0.0 , 0.0 ) , ( peak_envelope_upper. len( ) as f32 , 0.0 ) ] ,
156+ ShapeStyle :: from ( & BLACK ) . stroke_width ( 1 ) ,
157+ ) )
158+ . map_err ( |e| {
159+ crate :: utils:: error:: FerrousError :: Visualization ( format ! (
160+ "Failed to draw zero line: {}" ,
161+ e
162+ ) )
163+ } ) ?;
164+
165+ root. present ( ) . map_err ( |e| {
166+ crate :: utils:: error:: FerrousError :: Visualization ( format ! ( "Failed to present: {}" , e) )
167+ } ) ?;
168+
169+ Ok ( ( ) )
19170}
0 commit comments