@@ -116,6 +116,12 @@ pub fn get_default_audio_device_name(mic: bool) -> Option<String> {
116116 default_device. and_then ( |d| d. description ( ) . ok ( ) . map ( |desc| desc. name ( ) . to_string ( ) ) )
117117}
118118
119+ pub fn calculate_power_of_two_window_size ( sample_rate : u32 ) -> usize {
120+ let target = ( sample_rate as f32 * 0.185 ) . round ( ) as usize ;
121+ let window_size = 1 << ( target as f32 ) . log2 ( ) . round ( ) as usize ;
122+ window_size. max ( 2048 ) . min ( 65536 )
123+ }
124+
119125pub fn spawn_dsp_thread (
120126 rx : Receiver < DspMessage > ,
121127 shared_state : Arc < Mutex < AppState > > ,
@@ -161,6 +167,7 @@ pub fn spawn_dsp_thread(
161167 log_bin_mappings. push ( BinMapping { i0, i1, frac } ) ;
162168 }
163169
170+ let n_sqrt = ( window_size as f32 ) . sqrt ( ) ;
164171 let mut last_waveform_push = Instant :: now ( ) ;
165172
166173 while let Ok ( msg) = rx. recv ( ) {
@@ -176,7 +183,6 @@ pub fn spawn_dsp_thread(
176183 fft. process ( & mut complex_buf) ;
177184
178185 // Compute magnitudes (only first half — up to Nyquist)
179- let n_sqrt = ( window_size as f32 ) . sqrt ( ) ;
180186 for i in 0 ..window_size / 2 {
181187 magnitudes[ i] = complex_buf[ i] . norm ( ) / n_sqrt;
182188 }
@@ -1569,43 +1575,50 @@ pub fn load_audio_source(file_path: &str) -> Result<Box<dyn AudioSource>> {
15691575
15701576 // 3. Try MIDI
15711577 if ext == "mid" || ext == "midi" {
1572- // Look for soundfont in project dir, then fallback to executable-relative paths and macOS Resources bundle
1578+ // Look for soundfont in project dir, then fallback to executable-relative paths, system-wide paths, and macOS Resources bundle
15731579 let mut sf_path = "assets/soundfont.sf2" . to_string ( ) ;
15741580 if !std:: path:: Path :: new ( & sf_path) . exists ( ) {
15751581 if std:: path:: Path :: new ( "soundfont.sf2" ) . exists ( ) {
15761582 sf_path = "soundfont.sf2" . to_string ( ) ;
1577- } else if let Ok ( exe_path) = std:: env:: current_exe ( ) {
1578- if let Some ( exe_dir) = exe_path. parent ( ) {
1579- let test_path = exe_dir. join ( "assets/soundfont.sf2" ) ;
1580- if test_path. exists ( ) {
1581- sf_path = test_path. to_string_lossy ( ) . into_owned ( ) ;
1582- } else {
1583- let test_path = exe_dir. join ( "soundfont.sf2" ) ;
1584- if test_path. exists ( ) {
1585- sf_path = test_path. to_string_lossy ( ) . into_owned ( ) ;
1586- } else {
1587- let test_path = exe_dir. join ( "../share/rusttracker/assets/soundfont.sf2" ) ;
1588- if test_path. exists ( ) {
1589- sf_path = test_path. to_string_lossy ( ) . into_owned ( ) ;
1590- } else {
1591- let test_path = exe_dir. join ( "../share/rusttracker/soundfont.sf2" ) ;
1592- if test_path. exists ( ) {
1593- sf_path = test_path. to_string_lossy ( ) . into_owned ( ) ;
1594- } else {
1595- let test_path = exe_dir. join ( "../Resources/soundfont.sf2" ) ;
1596- if test_path. exists ( ) {
1597- sf_path = test_path. to_string_lossy ( ) . into_owned ( ) ;
1598- } else {
1599- let test_path = exe_dir. join ( "../Resources/assets/soundfont.sf2" ) ;
1600- if test_path. exists ( ) {
1601- sf_path = test_path. to_string_lossy ( ) . into_owned ( ) ;
1602- }
1603- }
1604- }
1583+ } else {
1584+ let mut found = false ;
1585+ if let Ok ( exe_path) = std:: env:: current_exe ( ) {
1586+ if let Some ( exe_dir) = exe_path. parent ( ) {
1587+ let test_paths = vec ! [
1588+ exe_dir. join( "assets/soundfont.sf2" ) ,
1589+ exe_dir. join( "soundfont.sf2" ) ,
1590+ exe_dir. join( "../share/rusttracker/assets/soundfont.sf2" ) ,
1591+ exe_dir. join( "../share/rusttracker/soundfont.sf2" ) ,
1592+ exe_dir. join( "../Resources/soundfont.sf2" ) ,
1593+ exe_dir. join( "../Resources/assets/soundfont.sf2" ) ,
1594+ ] ;
1595+ for tp in test_paths {
1596+ if tp. exists ( ) {
1597+ sf_path = tp. to_string_lossy ( ) . into_owned ( ) ;
1598+ found = true ;
1599+ break ;
16051600 }
16061601 }
16071602 }
16081603 }
1604+
1605+ // Fallback to system-wide paths on Linux/BSD if not found yet
1606+ if !found {
1607+ let system_paths = vec ! [
1608+ "/usr/share/sounds/sf2/FluidR3_GM.sf2" ,
1609+ "/usr/share/sounds/sf2/default.sf2" ,
1610+ "/usr/share/soundfonts/FluidR3_GM.sf2" ,
1611+ "/usr/share/soundfonts/default.sf2" ,
1612+ "/usr/share/soundfonts/FluidR3_GM.sf3" ,
1613+ "/usr/share/soundfonts/freepats-general-midi.sf2" ,
1614+ ] ;
1615+ for sp in system_paths {
1616+ if std:: path:: Path :: new ( sp) . exists ( ) {
1617+ sf_path = sp. to_string ( ) ;
1618+ break ;
1619+ }
1620+ }
1621+ }
16091622 }
16101623 }
16111624 if let Ok ( source) = MidiSource :: new ( file_path, & sf_path, 48000 ) {
@@ -1636,8 +1649,7 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
16361649 if let Ok ( ( handle, decoder_rate, codec_name, has_video) ) = crate :: bitstream:: start_bitstream_thread ( file_path, shared_state. clone ( ) , tx. clone ( ) , stop_token. clone ( ) ) {
16371650 let max_frequency = shared_state. lock ( ) . unwrap ( ) . max_frequency ;
16381651 let sample_rate = decoder_rate;
1639- let window_size = ( ( ( sample_rate as f32 * 0.185 ) . round ( ) as usize ) / 2 ) * 2 ;
1640- let window_size = window_size. max ( 2048 ) . min ( 65536 ) ;
1652+ let window_size = calculate_power_of_two_window_size ( sample_rate) ;
16411653
16421654 let display_name = match codec_name. as_str ( ) {
16431655 "truehd" => "TrueHD / Dolby Atmos" ,
@@ -1738,8 +1750,7 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
17381750 }
17391751
17401752 let max_frequency = { shared_state. lock ( ) . unwrap ( ) . max_frequency } ;
1741- let window_size = ( ( ( config. sample_rate as f32 * 0.185 ) . round ( ) as usize ) / 2 ) * 2 ;
1742- let window_size = window_size. max ( 2048 ) . min ( 65536 ) ;
1753+ let window_size = calculate_power_of_two_window_size ( config. sample_rate ) ;
17431754 spawn_dsp_thread ( rx, shared_state. clone ( ) , config. sample_rate , max_frequency, window_size) ;
17441755
17451756 let stream = match supported_config. sample_format ( ) {
@@ -1894,8 +1905,7 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
18941905 }
18951906
18961907 let max_frequency = { shared_state_closure. lock ( ) . unwrap ( ) . max_frequency } ;
1897- let window_size = ( ( ( config. sample_rate as f32 * 0.185 ) . round ( ) as usize ) / 2 ) * 2 ;
1898- let window_size = window_size. max ( 2048 ) . min ( 65536 ) ;
1908+ let window_size = calculate_power_of_two_window_size ( config. sample_rate ) ;
18991909
19001910 spawn_dsp_thread ( rx, shared_state_closure. clone ( ) , config. sample_rate , max_frequency, window_size) ;
19011911
@@ -1934,8 +1944,7 @@ pub fn start_audio_thread(file_path: &str, mic: bool, shared_state: Arc<Mutex<Ap
19341944
19351945 if let Some ( mut audio_source) = audio_source_opt. take ( ) {
19361946 let sample_rate = target_rate;
1937- let window_size = ( ( ( sample_rate as f32 * 0.185 ) . round ( ) as usize ) / 2 ) * 2 ;
1938- let window_size = window_size. max ( 2048 ) . min ( 65536 ) ;
1947+ let window_size = calculate_power_of_two_window_size ( sample_rate) ;
19391948 let ( tx, rx) = bounded :: < DspMessage > ( 32 ) ;
19401949
19411950 {
@@ -2166,20 +2175,26 @@ fn run_dummy(
21662175
21672176 frame. y_plane . clear ( ) ;
21682177 let y_plane = decoded. data ( 0 ) ;
2169- if y_len <= y_plane. len ( ) {
2170- frame. y_plane . extend_from_slice ( & y_plane[ ..y_len] ) ;
2178+ let y_copy_len = y_len. min ( y_plane. len ( ) ) ;
2179+ frame. y_plane . extend_from_slice ( & y_plane[ ..y_copy_len] ) ;
2180+ if y_copy_len < y_len {
2181+ frame. y_plane . resize ( y_len, 0 ) ;
21712182 }
21722183
21732184 frame. u_plane . clear ( ) ;
21742185 let u_plane = decoded. data ( 1 ) ;
2175- if u_len <= u_plane. len ( ) {
2176- frame. u_plane . extend_from_slice ( & u_plane[ ..u_len] ) ;
2186+ let u_copy_len = u_len. min ( u_plane. len ( ) ) ;
2187+ frame. u_plane . extend_from_slice ( & u_plane[ ..u_copy_len] ) ;
2188+ if u_copy_len < u_len {
2189+ frame. u_plane . resize ( u_len, 0 ) ;
21772190 }
21782191
21792192 frame. v_plane . clear ( ) ;
21802193 let v_plane = decoded. data ( 2 ) ;
2181- if v_len <= v_plane. len ( ) {
2182- frame. v_plane . extend_from_slice ( & v_plane[ ..v_len] ) ;
2194+ let v_copy_len = v_len. min ( v_plane. len ( ) ) ;
2195+ frame. v_plane . extend_from_slice ( & v_plane[ ..v_copy_len] ) ;
2196+ if v_copy_len < v_len {
2197+ frame. v_plane . resize ( v_len, 0 ) ;
21832198 }
21842199
21852200 if let Err ( crossbeam_channel:: TrySendError :: Full ( f) ) = video_frame_tx. try_send ( frame) {
@@ -2813,20 +2828,26 @@ where
28132828
28142829 frame. y_plane . clear ( ) ;
28152830 let y_plane = decoded. data ( 0 ) ;
2816- if y_len <= y_plane. len ( ) {
2817- frame. y_plane . extend_from_slice ( & y_plane[ ..y_len] ) ;
2831+ let y_copy_len = y_len. min ( y_plane. len ( ) ) ;
2832+ frame. y_plane . extend_from_slice ( & y_plane[ ..y_copy_len] ) ;
2833+ if y_copy_len < y_len {
2834+ frame. y_plane . resize ( y_len, 0 ) ;
28182835 }
28192836
28202837 frame. u_plane . clear ( ) ;
28212838 let u_plane = decoded. data ( 1 ) ;
2822- if u_len <= u_plane. len ( ) {
2823- frame. u_plane . extend_from_slice ( & u_plane[ ..u_len] ) ;
2839+ let u_copy_len = u_len. min ( u_plane. len ( ) ) ;
2840+ frame. u_plane . extend_from_slice ( & u_plane[ ..u_copy_len] ) ;
2841+ if u_copy_len < u_len {
2842+ frame. u_plane . resize ( u_len, 0 ) ;
28242843 }
28252844
28262845 frame. v_plane . clear ( ) ;
28272846 let v_plane = decoded. data ( 2 ) ;
2828- if v_len <= v_plane. len ( ) {
2829- frame. v_plane . extend_from_slice ( & v_plane[ ..v_len] ) ;
2847+ let v_copy_len = v_len. min ( v_plane. len ( ) ) ;
2848+ frame. v_plane . extend_from_slice ( & v_plane[ ..v_copy_len] ) ;
2849+ if v_copy_len < v_len {
2850+ frame. v_plane . resize ( v_len, 0 ) ;
28302851 }
28312852
28322853 if let Err ( crossbeam_channel:: TrySendError :: Full ( f) ) = video_frame_tx. try_send ( frame) {
@@ -3019,8 +3040,7 @@ where
30193040 T : cpal:: Sample + cpal:: FromSample < f32 > + cpal:: SizedSample + Into < f32 > ,
30203041{
30213042 let channels = config. channels as usize ;
3022- let window_size = ( ( ( config. sample_rate as f32 * 0.185 ) . round ( ) as usize ) / 2 ) * 2 ;
3023- let window_size = window_size. max ( 2048 ) . min ( 65536 ) ;
3043+ let window_size = calculate_power_of_two_window_size ( config. sample_rate ) ;
30243044
30253045 let mut fft_buffer: Vec < f32 > = vec ! [ 0.0 ; window_size] ;
30263046 let mut channel_fft_buffers: Vec < Vec < f32 > > = vec ! [ vec![ 0.0 ; window_size] ; channels] ;
0 commit comments