Skip to content

Instantly share code, notes, and snippets.

@mkalte666
Created November 17, 2023 09:59
Show Gist options
  • Save mkalte666/9797eb7e0eb31cdbe208fce994d3e45b to your computer and use it in GitHub Desktop.
Save mkalte666/9797eb7e0eb31cdbe208fce994d3e45b to your computer and use it in GitHub Desktop.
/// Recursive function for mipmap creation
/// makes some assumptions about the size relationship of in_samp and out_samp
/// that is, in_samp should be == out_samp except in the recursion abort case (in_samp<2); out_samp should then be zero
/// Then performs two things:
/// IIR filtering with f_c = 0.5 of in_samp
/// And then downsamples it (every second part) into out samp
/// Then, it splits out samp in half:
/// First half becomes the new input, second half becomes new output, and recursion happens
fn make_mipmap_slice(in_samp: &[f32], out_samp: &mut [f32]) {
// done?
if in_samp.len() < 2 || out_samp.is_empty() {
return;
}
// single pole low pass
let x = (-2.0 * std::f32::consts::PI * 0.5).exp();
let a0 = 1.0 - x;
let b1 = x;
let mut last = 0.0;
for i in 0..in_samp.len() / 2 {
let first = in_samp[i * 2] * a0 + last * b1;
let second = in_samp[i * 2 + 1] * a0 + first * b1;
out_samp[i] = second;
last = second;
}
let (new_input, new_output) = out_samp.split_at_mut(in_samp.len() / 2);
make_mipmap_slice(new_input, new_output);
}
/// take a bunch of input data and make a mipmap for it
fn make_mipmap(samples: Vec<f32>) -> Vec<f32> {
let mut res = samples;
res.resize(res.len() * 2, 0.0);
let mid = res.len() / 2;
let (inp, outp) = res.split_at_mut(mid);
make_mipmap_slice(inp, outp);
res
}
/// Get `count` samples out of the mipmap+
/// The mipmap is just a slice of len()/2 points, followed by len()/4 points with half the resolution, and so on
/// This function goes deeper and deeper, until the requested range (start_top..end_top) is smaller or equal to the requested samples
/// Then, because im lazy, it does just nearest pick to fill the output vector. I should probably interpolate here with the next mip level or something
/// But whatever, it works well enough
fn sample_mipmap(mipmap: &[f32], count: usize, start_top: usize, end_top: usize) -> Vec<f32> {
if (end_top - start_top) > count {
sample_mipmap(
&mipmap[mipmap.len() / 2..],
count,
start_top / 2,
end_top / 2,
)
} else {
let mut res = vec![0.0; count];
for i in 0..count {
// sample nearest for now?
let data_i = i * (end_top - start_top) / count + start_top;
if data_i >= mipmap.len() / 2 {
res[i] = 0.0;
} else {
res[i] = mipmap[data_i];
}
}
res
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment