Skip to content

Commit d70fc7c

Browse files
committed
various tweaks and K3 testing
1 parent 04b7632 commit d70fc7c

23 files changed

Lines changed: 334 additions & 167 deletions

RustTracker-Setup.exe

-54.3 MB
Binary file not shown.

src/audio.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,36 @@ pub fn spawn_dsp_thread(
137137
let mut complex_buf = vec![Complex { re: 0.0f32, im: 0.0f32 }; window_size];
138138
let mut magnitudes = vec![0.0f32; window_size / 2];
139139

140+
// Precompute log-spaced bin interpolation mapping for GPU spectrum
141+
let resolution = sample_rate as f32 / window_size as f32; // Hz per FFT bin
142+
let min_freq = 20.0f32;
143+
let max_f = max_frequency.max(min_freq * 2.0);
144+
let num_bins = 1024;
145+
let nyquist = window_size / 2;
146+
147+
struct BinMapping {
148+
i0: usize,
149+
i1: usize,
150+
frac: f32,
151+
}
152+
let mut log_bin_mappings = Vec::with_capacity(num_bins);
153+
for i in 0..num_bins {
154+
let t = i as f32 / num_bins as f32;
155+
let freq = min_freq * (max_f / min_freq).powf(t);
156+
let idx = freq / resolution;
157+
let idx_clamped = idx.clamp(0.0, (nyquist - 1) as f32);
158+
let i0 = idx_clamped.floor() as usize;
159+
let i1 = (i0 + 1).min(nyquist - 1);
160+
let frac = idx_clamped - i0 as f32;
161+
log_bin_mappings.push(BinMapping { i0, i1, frac });
162+
}
163+
140164
let mut last_waveform_push = Instant::now();
141165

142166
while let Ok(msg) = rx.recv() {
143167
let fft_start = Instant::now();
144168

145-
// Apply Hann window and prepare complex input
169+
// 1. Apply Hann window and prepare complex input for mono channel
146170
for i in 0..window_size {
147171
let sample = *msg.audio_data.get(i).unwrap_or(&0.0);
148172
complex_buf[i] = Complex { re: sample * hann_window[i], im: 0.0 };
@@ -157,13 +181,7 @@ pub fn spawn_dsp_thread(
157181
magnitudes[i] = complex_buf[i].norm() / n_sqrt;
158182
}
159183

160-
// Logarithmic binning into 1024 display bins
161-
let resolution = sample_rate as f32 / window_size as f32; // Hz per FFT bin
162-
let min_freq = 20.0f32;
163-
let max_f = max_frequency.max(min_freq * 2.0);
164-
let num_bins = 1024;
165-
let nyquist = window_size / 2;
166-
184+
// Logarithmic binning into 1024 display bins (mono)
167185
binned_data.fill(0.0);
168186
for i in 0..num_bins {
169187
let freq_start = min_freq * (max_f / min_freq).powf(i as f32 / num_bins as f32);
@@ -191,6 +209,33 @@ pub fn spawn_dsp_thread(
191209

192210
binned_data[i] = (max_val * 100.0).clamp(0.0, 100.0);
193211
}
212+
213+
// 2. Process multi-channel FFT for GPU visualizers (always run on background thread)
214+
let mut gpu_spectrum = vec![0.0f32; 32 * 1024 * 2];
215+
let num_channels = msg.channel_audio_data.len().min(32);
216+
for c in 0..num_channels {
217+
let channel_data = &msg.channel_audio_data[c];
218+
for i in 0..window_size {
219+
let sample = *channel_data.get(i).unwrap_or(&0.0);
220+
complex_buf[i] = Complex { re: sample * hann_window[i], im: 0.0 };
221+
}
222+
fft.process(&mut complex_buf);
223+
224+
for (i, mapping) in log_bin_mappings.iter().enumerate() {
225+
let c0 = complex_buf[mapping.i0];
226+
let c1 = complex_buf[mapping.i1];
227+
let re = c0.re * (1.0 - mapping.frac) + c1.re * mapping.frac;
228+
let im = c0.im * (1.0 - mapping.frac) + c1.im * mapping.frac;
229+
230+
let norm_re = re / n_sqrt;
231+
let norm_im = im / n_sqrt;
232+
233+
let out_idx = c * 1024 * 2 + i * 2;
234+
gpu_spectrum[out_idx] = norm_re;
235+
gpu_spectrum[out_idx + 1] = norm_im;
236+
}
237+
}
238+
194239
let fft_elapsed = fft_start.elapsed().as_micros() as f32;
195240

196241
// Sync to UI state
@@ -210,6 +255,7 @@ pub fn spawn_dsp_thread(
210255
}
211256

212257
state.raw_spectrum_data.copy_from_slice(&binned_data);
258+
state.gpu_spectrum_data = gpu_spectrum;
213259
state.raw_audio_channels = msg.channel_audio_data;
214260

215261
// --- Waveform extraction (Zero-Crossing Edge Trigger) ---

0 commit comments

Comments
 (0)