@@ -119,8 +119,9 @@ struct TexturedVertex {
119119// Uniforms buffer
120120struct Uniforms {
121121 float2 viewport_size;
122+ float2 texture_size;
122123 float time;
123- float padding;
124+ float padding[3] ;
124125};
125126
126127struct VertexOut {
@@ -153,15 +154,35 @@ fragment float4 fragment_colored(VertexOut in [[stage_in]]) {
153154 return in.color;
154155}
155156
156- // Fullscreen quad vertex shader
157- vertex TexturedVertexOut vertex_fullscreen(uint vid [[vertex_id]]) {
157+ // Fullscreen quad vertex shader with aspect ratio preservation
158+ vertex TexturedVertexOut vertex_fullscreen(
159+ uint vid [[vertex_id]],
160+ constant Uniforms& uniforms [[buffer(0)]]
161+ ) {
158162 TexturedVertexOut out;
159- // Generate fullscreen triangle strip from vertex id
163+
164+ // Calculate aspect ratios
165+ float viewportAspect = uniforms.viewport_size.x / uniforms.viewport_size.y;
166+ float textureAspect = uniforms.texture_size.x / uniforms.texture_size.y;
167+
168+ // Scale to fit while preserving aspect ratio (letterbox/pillarbox)
169+ float scaleX = 1.0;
170+ float scaleY = 1.0;
171+
172+ if (textureAspect > viewportAspect) {
173+ // Texture is wider - pillarbox (black bars top/bottom)
174+ scaleY = viewportAspect / textureAspect;
175+ } else {
176+ // Texture is taller - letterbox (black bars left/right)
177+ scaleX = textureAspect / viewportAspect;
178+ }
179+
180+ // Generate quad vertices centered in viewport
160181 float2 positions[4] = {
161- float2(-1.0 , -1.0 ),
162- float2( 1.0 , -1.0 ),
163- float2(-1.0 , 1.0 ),
164- float2( 1.0 , 1.0 )
182+ float2(-scaleX , -scaleY ),
183+ float2( scaleX , -scaleY ),
184+ float2(-scaleX , scaleY ),
185+ float2( scaleX , scaleY )
165186 };
166187 float2 texcoords[4] = {
167188 float2(0.0, 1.0),
@@ -226,13 +247,14 @@ impl TexturedVertex {
226247 }
227248}
228249
229- /// Uniforms buffer (16 bytes, 16-byte aligned for GPU)
250+ /// Uniforms buffer (32 bytes, 16-byte aligned for GPU)
230251#[ repr( C ) ]
231252#[ derive( Clone , Copy , Debug ) ]
232253struct Uniforms {
233254 viewport_size : [ f32 ; 2 ] , // 8 bytes
255+ texture_size : [ f32 ; 2 ] , // 8 bytes - for aspect ratio calculation
234256 time : f32 , // 4 bytes
235- _padding : f32 , // 4 bytes (ensures 16 -byte alignment)
257+ _padding : [ f32 ; 3 ] , // 12 bytes (ensures 32 -byte alignment)
236258}
237259
238260// ============================================================================
@@ -992,28 +1014,32 @@ fn main() {
9921014 } ;
9931015 vertex_builder. text ( & font, & status, 8.0 , 8.0 , 2.0 , [ 0.2 , 1.0 , 0.3 , 1.0 ] ) ;
9941016
995- // Waveform panel
1017+ // Waveform panel - centered and larger
9961018 if overlay. show_waveform {
997- let wave_x = 16.0 ;
998- let wave_y = ( height - 180.0 ) . floor ( ) ;
999- let wave_w = 280.0 ;
1000- let wave_h = 80.0 ;
1001- let meter_w = 24.0 ;
1002- let meter_h = 120.0 ;
1003- let panel_w = wave_w + meter_w * 2.0 + 48.0 ;
1004- let panel_h = 160.0 ;
1019+ let wave_w = 400.0 ;
1020+ let wave_h = 120.0 ;
1021+ let meter_w = 32.0 ;
1022+ let meter_h = 160.0 ;
1023+ let panel_w = wave_w + meter_w * 2.0 + 64.0 ;
1024+ let panel_h = 220.0 ;
1025+
1026+ // Center the panel
1027+ let panel_x = ( width - panel_w) / 2.0 ;
1028+ let panel_y = ( height - panel_h) / 2.0 ;
1029+ let wave_x = panel_x + 16.0 ;
1030+ let wave_y = panel_y + 40.0 ;
10051031
10061032 // Panel background
10071033 vertex_builder. rect (
1008- wave_x - 8.0 ,
1009- wave_y - 24.0 ,
1034+ panel_x ,
1035+ panel_y ,
10101036 panel_w,
10111037 panel_h,
1012- [ 0.12 , 0.12 , 0.15 , 0.9 ] ,
1038+ [ 0.12 , 0.12 , 0.15 , 0.95 ] ,
10131039 ) ;
10141040 vertex_builder. rect_outline (
1015- wave_x - 8.0 ,
1016- wave_y - 24.0 ,
1041+ panel_x ,
1042+ panel_y ,
10171043 panel_w,
10181044 panel_h,
10191045 2.0 ,
@@ -1023,10 +1049,10 @@ fn main() {
10231049 // Title
10241050 vertex_builder. text (
10251051 & font,
1026- "AUDIO" ,
1027- wave_x ,
1028- wave_y - 20 .0,
1029- 2.0 ,
1052+ "AUDIO MONITOR " ,
1053+ panel_x + 16.0 ,
1054+ panel_y + 12 .0,
1055+ 2.5 ,
10301056 [ 1.0 , 1.0 , 1.0 , 1.0 ] ,
10311057 ) ;
10321058
@@ -1043,16 +1069,16 @@ fn main() {
10431069
10441070 // Horizontal VU meter for system audio
10451071 let audio_level = capture_state. audio_waveform . lock ( ) . unwrap ( ) . rms ( 2048 ) ;
1046- vertex_builder. vu_meter ( audio_level, wave_x, wave_y + wave_h + 8 .0, wave_w, 12 .0) ;
1072+ vertex_builder. vu_meter ( audio_level, wave_x, wave_y + wave_h + 12 .0, wave_w, 16 .0) ;
10471073
10481074 // Vertical meters on the right side
1049- let meters_x = wave_x + wave_w + 16 .0;
1075+ let meters_x = wave_x + wave_w + 24 .0;
10501076
10511077 // System audio vertical meter
10521078 vertex_builder. vu_meter_vertical (
10531079 audio_level,
10541080 meters_x,
1055- wave_y - 8.0 ,
1081+ wave_y,
10561082 meter_w,
10571083 meter_h,
10581084 "SYS" ,
@@ -1063,18 +1089,21 @@ fn main() {
10631089 let mic_level = capture_state. mic_waveform . lock ( ) . unwrap ( ) . rms ( 2048 ) ;
10641090 vertex_builder. vu_meter_vertical (
10651091 mic_level,
1066- meters_x + meter_w + 8 .0,
1067- wave_y - 8.0 ,
1092+ meters_x + meter_w + 12 .0,
1093+ wave_y,
10681094 meter_w,
10691095 meter_h,
10701096 "MIC" ,
10711097 & font,
10721098 ) ;
10731099 }
10741100
1075- // Help overlay
1101+ // Help overlay - center it
10761102 if overlay. show_help {
1077- vertex_builder. help_overlay ( & font, width - 230.0 , 40.0 , capturing. load ( Ordering :: Relaxed ) ) ;
1103+ let help_w = 260.0 ;
1104+ let help_x = ( width - help_w) / 2.0 ;
1105+ let help_y = height - 140.0 ;
1106+ vertex_builder. help_overlay ( & font, help_x, help_y, capturing. load ( Ordering :: Relaxed ) ) ;
10781107 }
10791108
10801109 // Build GPU buffer
@@ -1087,8 +1116,9 @@ fn main() {
10871116 // Uniforms - pass capture texture dimensions for aspect ratio
10881117 let uniforms = Uniforms {
10891118 viewport_size : [ width, height] ,
1119+ texture_size : [ tex_width, tex_height] ,
10901120 time,
1091- _padding : 0.0 ,
1121+ _padding : [ 0.0 ; 3 ] ,
10921122 } ;
10931123 let uniforms_buffer = device. new_buffer_with_data (
10941124 ( & uniforms as * const Uniforms ) . cast ( ) ,
@@ -1115,6 +1145,7 @@ fn main() {
11151145 // First pass: Draw captured frame as background (if available)
11161146 if let Some ( ref texture) = capture_texture {
11171147 encoder. set_render_pipeline_state ( & fullscreen_pipeline) ;
1148+ encoder. set_vertex_buffer ( 0 , Some ( & uniforms_buffer) , 0 ) ;
11181149 encoder. set_fragment_texture ( 0 , Some ( texture) ) ;
11191150 encoder. draw_primitives ( MTLPrimitiveType :: TriangleStrip , 0 , 4 ) ;
11201151 }
0 commit comments