@@ -155,17 +155,20 @@ pub fn spawn_dsp_thread(
155155 // avoiding the visual "freeze" caused by searching the entire history buffer.
156156 let search_window = ( sample_rate as f32 / 30.0 ) as usize ;
157157 let search_start = msg. audio_data . len ( ) . saturating_sub ( actual_window_samples + search_window) ;
158- let search_limit = msg. audio_data . len ( ) . saturating_sub ( actual_window_samples) ;
158+ // Ensure i+1 stays in bounds during zero-crossing search
159+ let search_limit = msg. audio_data . len ( ) . saturating_sub ( actual_window_samples) . min ( msg. audio_data . len ( ) . saturating_sub ( 1 ) ) ;
159160
160161 let mut start_idx = search_start;
161162 let mut best_slope = 0.0 ;
162163
163- for i in search_start..search_limit {
164- if msg. audio_data [ i] <= 0.0 && msg. audio_data [ i + 1 ] > 0.0 {
165- let slope = msg. audio_data [ i + 1 ] - msg. audio_data [ i] ;
166- if slope > best_slope {
167- best_slope = slope;
168- start_idx = i;
164+ if msg. audio_data . len ( ) >= 2 {
165+ for i in search_start..search_limit {
166+ if msg. audio_data [ i] <= 0.0 && msg. audio_data [ i + 1 ] > 0.0 {
167+ let slope = msg. audio_data [ i + 1 ] - msg. audio_data [ i] ;
168+ if slope > best_slope {
169+ best_slope = slope;
170+ start_idx = i;
171+ }
169172 }
170173 }
171174 }
@@ -174,10 +177,12 @@ pub fn spawn_dsp_thread(
174177 state. raw_waveform . resize ( visual_width, 0.0 ) ;
175178 }
176179
180+ if !msg. audio_data . is_empty ( ) {
177181 for i in 0 ..visual_width {
178182 let sample_idx = start_idx + ( i as f32 * stride) as usize ;
179183 state. raw_waveform [ i] = msg. audio_data [ sample_idx. min ( msg. audio_data . len ( ) - 1 ) ] ;
180184 }
185+ }
181186
182187 let now = Instant :: now ( ) ;
183188 if now. duration_since ( last_waveform_push) >= waveform_push_interval {
@@ -239,7 +244,7 @@ pub trait AudioSource: Send {
239244 fn pre_format_tracker_data ( & mut self ) -> Vec < Vec < String > > { Vec :: new ( ) }
240245 fn get_current_row_string ( & mut self ) -> String { String :: new ( ) }
241246 fn get_video_info ( & mut self ) -> Option < String > { None }
242- fn attach_video_queue ( & mut self , _tx : crossbeam_channel:: Sender < ffmpeg_next:: Packet > ) { }
247+ fn attach_video_queue ( & mut self , _tx : crossbeam_channel:: Sender < ( u64 , ffmpeg_next:: Packet ) > ) { }
243248 fn take_video_parameters ( & mut self ) -> Option < ( ffmpeg_next:: codec:: Parameters , ffmpeg_next:: Rational ) > { None }
244249}
245250
@@ -810,7 +815,8 @@ struct FfmpegSource {
810815 video_info : Option < String > ,
811816 channel_vus : Vec < f32 > ,
812817 video_stream_index : Option < usize > ,
813- video_tx : Option < crossbeam_channel:: Sender < ffmpeg_next:: Packet > > ,
818+ video_tx : Option < crossbeam_channel:: Sender < ( u64 , ffmpeg_next:: Packet ) > > ,
819+ video_epoch : u64 ,
814820 video_params : Option < ffmpeg_next:: codec:: Parameters > ,
815821 video_time_base : Option < ffmpeg_next:: Rational > ,
816822}
@@ -853,7 +859,7 @@ impl FfmpegSource {
853859 for ( stream, packet) in self . ictx . packets ( ) {
854860 if Some ( stream. index ( ) ) == self . video_stream_index {
855861 if let Some ( tx) = & self . video_tx {
856- let _ = tx. try_send ( packet. clone ( ) ) ;
862+ let _ = tx. try_send ( ( self . video_epoch , packet. clone ( ) ) ) ;
857863 }
858864 } else if stream. index ( ) == self . stream_index {
859865 // Send the packet to the decoder
@@ -988,6 +994,7 @@ impl AudioSource for FfmpegSource {
988994 self . decoder . flush ( ) ;
989995 self . buf_pos = self . sample_buf . len ( ) ;
990996 self . current_time = pos;
997+ self . video_epoch += 1 ;
991998 }
992999
9931000 fn get_num_channels ( & mut self ) -> i32 { self . channels as i32 }
@@ -1005,7 +1012,7 @@ impl AudioSource for FfmpegSource {
10051012
10061013 fn get_video_info ( & mut self ) -> Option < String > { self . video_info . clone ( ) }
10071014
1008- fn attach_video_queue ( & mut self , tx : crossbeam_channel:: Sender < ffmpeg_next:: Packet > ) {
1015+ fn attach_video_queue ( & mut self , tx : crossbeam_channel:: Sender < ( u64 , ffmpeg_next:: Packet ) > ) {
10091016 self . video_tx = Some ( tx) ;
10101017 }
10111018
@@ -1024,7 +1031,8 @@ impl AudioSource for FfmpegSource {
10241031struct VideoOnlySource {
10251032 ictx : ffmpeg_next:: format:: context:: Input ,
10261033 video_stream_index : usize ,
1027- video_tx : Option < crossbeam_channel:: Sender < ffmpeg_next:: Packet > > ,
1034+ video_tx : Option < crossbeam_channel:: Sender < ( u64 , ffmpeg_next:: Packet ) > > ,
1035+ video_epoch : u64 ,
10281036 video_params : Option < ffmpeg_next:: codec:: Parameters > ,
10291037 video_time_base : Option < ffmpeg_next:: Rational > ,
10301038 current_time : f64 ,
@@ -1045,7 +1053,7 @@ impl AudioSource for VideoOnlySource {
10451053 if let Some ( ( stream, packet) ) = self . ictx . packets ( ) . next ( ) {
10461054 if stream. index ( ) == self . video_stream_index {
10471055 if let Some ( tx) = & self . video_tx {
1048- let _ = tx. try_send ( packet. clone ( ) ) ;
1056+ let _ = tx. try_send ( ( self . video_epoch , packet. clone ( ) ) ) ;
10491057 }
10501058 }
10511059 packets_read += 1 ;
@@ -1075,6 +1083,7 @@ impl AudioSource for VideoOnlySource {
10751083 }
10761084 }
10771085 self . current_time = pos;
1086+ self . video_epoch += 1 ;
10781087 }
10791088
10801089 fn get_num_channels ( & mut self ) -> i32 { 2 }
@@ -1091,7 +1100,7 @@ impl AudioSource for VideoOnlySource {
10911100 fn get_current_row ( & mut self ) -> i32 { 0 }
10921101 fn get_video_info ( & mut self ) -> Option < String > { self . video_info . clone ( ) }
10931102
1094- fn attach_video_queue ( & mut self , tx : crossbeam_channel:: Sender < ffmpeg_next:: Packet > ) {
1103+ fn attach_video_queue ( & mut self , tx : crossbeam_channel:: Sender < ( u64 , ffmpeg_next:: Packet ) > ) {
10951104 self . video_tx = Some ( tx) ;
10961105 }
10971106
@@ -1145,6 +1154,7 @@ fn try_ffmpeg(file_path: &str) -> Result<Box<dyn AudioSource>> {
11451154 ictx,
11461155 video_stream_index : v_idx,
11471156 video_tx : None ,
1157+ video_epoch : 0 ,
11481158 video_params,
11491159 video_time_base : video_tb,
11501160 current_time : 0.0 ,
@@ -1195,6 +1205,7 @@ fn try_ffmpeg(file_path: &str) -> Result<Box<dyn AudioSource>> {
11951205 channel_vus : vec ! [ 0.0 ; channels as usize ] ,
11961206 video_stream_index,
11971207 video_tx : None ,
1208+ video_epoch : 0 ,
11981209 video_params,
11991210 video_time_base : video_tb,
12001211 } ) )
@@ -1495,9 +1506,6 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
14951506 state. num_instruments = audio_source. get_num_instruments ( ) ;
14961507 state. num_patterns = audio_source. get_num_patterns ( ) ;
14971508
1498- let _intrinsic = audio_source. get_num_channels ( ) ;
1499- let _intrinsic = audio_source. get_num_channels ( ) ;
1500-
15011509 if !mic {
15021510 state. tracker_channels = tracker_channels;
15031511 state. tracker_patterns_by_order = audio_source. pre_format_tracker_data ( ) ;
@@ -1561,7 +1569,7 @@ where
15611569 let ( ready_tx, ready_rx) = crossbeam_channel:: bounded :: < AudioChunk > ( pool_size) ;
15621570 let ( free_tx, free_rx) = crossbeam_channel:: bounded :: < AudioChunk > ( pool_size) ;
15631571
1564- let ( video_packet_tx, video_packet_rx) = crossbeam_channel:: bounded :: < ffmpeg_next:: Packet > ( 4096 ) ;
1572+ let ( video_packet_tx, video_packet_rx) = crossbeam_channel:: bounded :: < ( u64 , ffmpeg_next:: Packet ) > ( 4096 ) ;
15651573 audio_source. attach_video_queue ( video_packet_tx) ;
15661574
15671575 if let Some ( ( params, time_base) ) = audio_source. take_video_parameters ( ) {
@@ -1582,6 +1590,7 @@ where
15821590 bit_depth : 8 ,
15831591 color_space : 0 ,
15841592 color_range : 0 ,
1593+ color_trc : 0 ,
15851594 } ) ;
15861595 }
15871596
@@ -1599,14 +1608,18 @@ where
15991608 let mut local_epoch = 0 ;
16001609 let mut fallback_pts_seconds = 0.0 ;
16011610
1602- while let Ok ( packet) = video_packet_rx_for_video. recv ( ) {
1603- let mut track_ended = false ;
1604- if let Ok ( state) = state_for_video. try_lock ( ) {
1605- track_ended = state. track_ended ;
1611+ while let Ok ( ( packet_epoch, packet) ) = video_packet_rx_for_video. recv ( ) {
1612+ let mut track_ended = {
1613+ let state = state_for_video. lock ( ) . unwrap ( ) ;
16061614 if state. seek_epoch > local_epoch {
16071615 decoder. flush ( ) ;
16081616 local_epoch = state. seek_epoch ;
16091617 }
1618+ state. track_ended
1619+ } ;
1620+
1621+ if packet_epoch < local_epoch {
1622+ continue ;
16101623 }
16111624
16121625 if track_ended { return ; }
@@ -1625,17 +1638,21 @@ where
16251638 fallback_pts_seconds = pts + ( 1.0 / 30.0 ) ;
16261639 }
16271640
1641+ let mut skip_push = false ;
16281642 loop {
16291643 let ( cached_seconds, current_epoch) = {
1630- if let Ok ( state) = state_for_video. try_lock ( ) {
1631- track_ended = state. track_ended ;
1632- ( state. current_seconds , state. seek_epoch )
1633- } else {
1634- continue ;
1635- }
1644+ let state = state_for_video. lock ( ) . unwrap ( ) ;
1645+ track_ended = state. track_ended ;
1646+ ( state. current_seconds , state. seek_epoch )
16361647 } ;
16371648
16381649 if track_ended || current_epoch > local_epoch {
1650+ skip_push = true ;
1651+ break ;
1652+ }
1653+
1654+ if pts < cached_seconds - 0.05 {
1655+ skip_push = true ;
16391656 break ;
16401657 }
16411658
@@ -1645,6 +1662,10 @@ where
16451662 std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 2 ) ) ;
16461663 }
16471664
1665+ if skip_push {
1666+ continue ;
1667+ }
1668+
16481669 if let Ok ( mut frame) = free_video_frame_rx. recv ( ) {
16491670 frame. pts = pts;
16501671 frame. width = decoded. width ( ) ;
@@ -1654,6 +1675,7 @@ where
16541675 frame. bit_depth = if format_name. contains ( "10LE" ) { 10 } else if format_name. contains ( "12LE" ) { 12 } else { 8 } ;
16551676 frame. color_space = decoded. color_space ( ) as u32 ;
16561677 frame. color_range = decoded. color_range ( ) as u32 ;
1678+ frame. color_trc = decoded. color_transfer_characteristic ( ) as u32 ;
16571679
16581680 frame. y_stride = decoded. stride ( 0 ) ;
16591681 frame. u_stride = decoded. stride ( 1 ) ;
@@ -1723,6 +1745,7 @@ where
17231745 if let Ok ( mut state) = state_for_decoder. try_lock ( ) {
17241746 if let Some ( pos) = state. seek_request . take ( ) {
17251747 audio_source. set_position_seconds ( pos) ;
1748+ state. current_seconds = pos;
17261749 state. seek_epoch += 1 ;
17271750 while let Ok ( chunk) = ready_rx_for_decoder. try_recv ( ) {
17281751 let _ = free_tx_for_decoder. try_send ( chunk) ;
@@ -1789,7 +1812,7 @@ where
17891812 state. stats . decode_us = state. stats . decode_us * 0.9 + decode_elapsed * 0.1 ;
17901813 let fill_pct = ( ready_rx_for_decoder. len ( ) as f32 / pool_size as f32 ) * 100.0 ;
17911814 state. stats . audio_buffer_fill_pct = fill_pct;
1792- let video_pct = ( video_rx_for_decoder. len ( ) as f32 / 256 .0) * 100.0 ;
1815+ let video_pct = ( video_rx_for_decoder. len ( ) as f32 / 4096 .0) * 100.0 ;
17931816 state. stats . video_buffer_fill_pct = video_pct;
17941817 state. stats . clipping_events += clips;
17951818 }
@@ -1963,6 +1986,8 @@ where
19631986 let mut fft_buffer: Vec < f32 > = vec ! [ 0.0 ; window_size] ;
19641987 let mut channel_fft_buffers: Vec < Vec < f32 > > = vec ! [ vec![ 0.0 ; window_size] ; channels] ;
19651988 let mut windowed_buffer: Vec < f32 > = vec ! [ 0.0 ; window_size] ;
1989+ let mut windowed_channels: Vec < Vec < f32 > > = vec ! [ vec![ 0.0 ; window_size] ; channels] ;
1990+ let mut spare_channels: Vec < Vec < f32 > > = vec ! [ vec![ 0.0 ; window_size] ; channels] ;
19661991 let mut fft_index = 0 ;
19671992
19681993 let mut was_paused = false ;
@@ -2000,7 +2025,6 @@ where
20002025 fft_index = ( fft_index + 1 ) % window_size;
20012026 }
20022027
2003- let mut windowed_channels = vec ! [ vec![ 0.0 ; window_size] ; channels] ;
20042028 for i in 0 ..window_size {
20052029 let idx = ( fft_index + i) % window_size;
20062030 windowed_buffer[ i] = fft_buffer[ idx] ;
@@ -2009,6 +2033,9 @@ where
20092033 }
20102034 }
20112035
2036+ // Swap filled buffers with spare set — zero allocations in the hot path
2037+ std:: mem:: swap ( & mut windowed_channels, & mut spare_channels) ;
2038+
20122039 let mut channel_vus = Vec :: new ( ) ;
20132040 if channels >= 1 { channel_vus. push ( left_peak) ; }
20142041 if channels >= 2 { channel_vus. push ( right_peak) ; }
@@ -2022,7 +2049,7 @@ where
20222049 speed : 0 ,
20232050 current_seconds : 0.0 ,
20242051 current_row_string : String :: new ( ) ,
2025- channel_audio_data : windowed_channels ,
2052+ channel_audio_data : std :: mem :: replace ( & mut spare_channels , vec ! [ vec! [ 0.0 ; window_size ] ; channels ] ) ,
20262053 } ;
20272054
20282055 let _ = tx. try_send ( msg) ;
0 commit comments