Skip to content

Commit 8561fa3

Browse files
authored
Merge pull request #7 from lukaslihotzki/no-alloc
Avoid allocations
2 parents f677334 + 0e86d22 commit 8561fa3

File tree

6 files changed

+83
-74
lines changed

6 files changed

+83
-74
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ name = "resid"
1515

1616
[features]
1717
default = ["std"]
18-
std = []
18+
std = ["alloc"]
19+
alloc = []
1920

2021
[dependencies]
2122
bit_field = "0.10"

src/filter.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
77

8-
use alloc::vec::Vec;
98
use core::f64;
109

1110
use super::spline;
@@ -518,18 +517,8 @@ impl Filter {
518517
}
519518

520519
fn set_f0(&mut self) {
521-
let points = self
522-
.f0_points
523-
.iter()
524-
.map(|&pt| spline::Point {
525-
x: pt.0 as f64,
526-
y: pt.1 as f64,
527-
})
528-
.collect::<Vec<spline::Point>>();
529-
let mut plotter = spline::PointPlotter::new(2048);
530-
spline::interpolate(&points, &mut plotter, 1.0);
531-
let output = plotter.output();
532-
self.f0[..2048].clone_from_slice(&output[..2048]);
520+
let mut plotter = spline::PointPlotter::new(&mut self.f0[..2048]);
521+
spline::interpolate(self.f0_points, &mut plotter, 1.0);
533522
}
534523

535524
fn set_q(&mut self) {

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
#![no_std]
77

8-
#[cfg(not(feature = "std"))]
8+
#[cfg(all(feature = "alloc", not(feature = "std")))]
99
extern crate alloc;
10-
#[cfg(feature = "std")]
10+
#[cfg(all(feature = "alloc", feature = "std"))]
1111
extern crate std as alloc;
1212

1313
mod data;

src/sampler.rs

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
77
#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
88

9-
use alloc::vec::Vec;
109
use core::f64;
10+
11+
#[cfg(feature = "alloc")]
12+
use alloc::vec::Vec;
13+
1114
#[cfg(not(feature = "std"))]
1215
use libm::F64Ext;
1316

@@ -34,19 +37,27 @@ const FIXP_MASK: i32 = 0xffff;
3437
pub enum SamplingMethod {
3538
Fast,
3639
Interpolate,
40+
#[cfg(feature = "alloc")]
3741
Resample,
42+
#[cfg(feature = "alloc")]
3843
ResampleFast,
3944
}
4045

46+
#[derive(Clone)]
47+
struct Fir {
48+
data: Vec<i16>,
49+
n: i32,
50+
res: i32,
51+
}
52+
4153
#[derive(Clone)]
4254
pub struct Sampler {
4355
// Dependencies
4456
pub synth: Synth,
4557
// Configuration
4658
cycles_per_sample: u32,
47-
fir: Vec<i16>,
48-
fir_n: i32,
49-
fir_res: i32,
59+
#[cfg(feature = "alloc")]
60+
fir: Fir,
5061
sampling_method: SamplingMethod,
5162
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
5263
use_sse42: bool,
@@ -64,9 +75,12 @@ impl Sampler {
6475
Sampler {
6576
synth,
6677
cycles_per_sample: 0,
67-
fir: Vec::new(),
68-
fir_n: 0,
69-
fir_res: 0,
78+
#[cfg(feature = "alloc")]
79+
fir: Fir {
80+
data: Vec::new(),
81+
n: 0,
82+
res: 0,
83+
},
7084
sampling_method: SamplingMethod::Fast,
7185
#[cfg(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))]
7286
use_avx2: alloc::is_x86_feature_detected!("avx2"),
@@ -83,6 +97,8 @@ impl Sampler {
8397
self.cycles_per_sample =
8498
(clock_freq as f64 / sample_freq as f64 * (1 << FIXP_SHIFT) as f64 + 0.5) as u32;
8599
self.sampling_method = method;
100+
101+
#[cfg(feature = "alloc")]
86102
if self.sampling_method == SamplingMethod::Resample
87103
|| self.sampling_method == SamplingMethod::ResampleFast
88104
{
@@ -109,7 +125,9 @@ impl Sampler {
109125
match self.sampling_method {
110126
SamplingMethod::Fast => self.clock_fast(delta, buffer, interleave),
111127
SamplingMethod::Interpolate => self.clock_interpolate(delta, buffer, interleave),
128+
#[cfg(feature = "alloc")]
112129
SamplingMethod::Resample => self.clock_resample_interpolate(delta, buffer, interleave),
130+
#[cfg(feature = "alloc")]
113131
SamplingMethod::ResampleFast => self.clock_resample_fast(delta, buffer, interleave),
114132
}
115133
}
@@ -215,6 +233,7 @@ impl Sampler {
215233
///
216234
/// NB! the result of right shifting negative numbers is really
217235
/// implementation dependent in the C++ standard.
236+
#[cfg(feature = "alloc")]
218237
#[inline]
219238
fn clock_resample_interpolate(
220239
&mut self,
@@ -242,34 +261,34 @@ impl Sampler {
242261
delta -= delta_sample;
243262
self.update_sample_offset2(next_sample_offset);
244263

245-
let fir_offset_1 = (self.offset * self.fir_res) >> FIXP_SHIFT;
246-
let fir_offset_rmd = (self.offset * self.fir_res) & FIXP_MASK;
247-
let fir_start_1 = (fir_offset_1 * self.fir_n) as usize;
248-
let fir_end_1 = fir_start_1 + self.fir_n as usize;
249-
let sample_start_1 = (self.index as i32 - self.fir_n + RING_SIZE as i32) as usize;
250-
let sample_end_1 = sample_start_1 + self.fir_n as usize;
264+
let fir_offset_1 = (self.offset * self.fir.res) >> FIXP_SHIFT;
265+
let fir_offset_rmd = (self.offset * self.fir.res) & FIXP_MASK;
266+
let fir_start_1 = (fir_offset_1 * self.fir.n) as usize;
267+
let fir_end_1 = fir_start_1 + self.fir.n as usize;
268+
let sample_start_1 = (self.index as i32 - self.fir.n + RING_SIZE as i32) as usize;
269+
let sample_end_1 = sample_start_1 + self.fir.n as usize;
251270

252271
// Convolution with filter impulse response.
253272
let v1 = self.compute_convolution_fir(
254273
&self.buffer[sample_start_1..sample_end_1],
255-
&self.fir[fir_start_1..fir_end_1],
274+
&self.fir.data[fir_start_1..fir_end_1],
256275
);
257276

258277
// Use next FIR table, wrap around to first FIR table using
259278
// previous sample.
260279
let mut fir_offset_2 = fir_offset_1 + 1;
261280
let mut sample_start_2 = sample_start_1;
262-
if fir_offset_2 == self.fir_res {
281+
if fir_offset_2 == self.fir.res {
263282
fir_offset_2 = 0;
264283
sample_start_2 -= 1;
265284
}
266-
let fir_start_2 = (fir_offset_2 * self.fir_n) as usize;
267-
let fir_end_2 = fir_start_2 + self.fir_n as usize;
268-
let sample_end_2 = sample_start_2 + self.fir_n as usize;
285+
let fir_start_2 = (fir_offset_2 * self.fir.n) as usize;
286+
let fir_end_2 = fir_start_2 + self.fir.n as usize;
287+
let sample_end_2 = sample_start_2 + self.fir.n as usize;
269288

270289
let v2 = self.compute_convolution_fir(
271290
&self.buffer[sample_start_2..sample_end_2],
272-
&self.fir[fir_start_2..fir_end_2],
291+
&self.fir.data[fir_start_2..fir_end_2],
273292
);
274293

275294
// Linear interpolation.
@@ -305,6 +324,7 @@ impl Sampler {
305324
}
306325

307326
/// SID clocking with audio sampling - cycle based with audio resampling.
327+
#[cfg(feature = "alloc")]
308328
#[inline]
309329
fn clock_resample_fast(
310330
&mut self,
@@ -332,16 +352,16 @@ impl Sampler {
332352
delta -= delta_sample;
333353
self.update_sample_offset2(next_sample_offset);
334354

335-
let fir_offset = (self.offset * self.fir_res) >> FIXP_SHIFT;
336-
let fir_start = (fir_offset * self.fir_n) as usize;
337-
let fir_end = fir_start + self.fir_n as usize;
338-
let sample_start = (self.index as i32 - self.fir_n + RING_SIZE as i32) as usize;
339-
let sample_end = sample_start + self.fir_n as usize;
355+
let fir_offset = (self.offset * self.fir.res) >> FIXP_SHIFT;
356+
let fir_start = (fir_offset * self.fir.n) as usize;
357+
let fir_end = fir_start + self.fir.n as usize;
358+
let sample_start = (self.index as i32 - self.fir.n + RING_SIZE as i32) as usize;
359+
let sample_end = sample_start + self.fir.n as usize;
340360

341361
// Convolution with filter impulse response.
342362
let mut v = self.compute_convolution_fir(
343363
&self.buffer[sample_start..sample_end],
344-
&self.fir[fir_start..fir_end],
364+
&self.fir.data[fir_start..fir_end],
345365
);
346366
v >>= FIR_SHIFT;
347367

@@ -515,6 +535,7 @@ impl Sampler {
515535
self.offset = next_sample_offset & FIXP_MASK;
516536
}
517537

538+
#[cfg(feature = "alloc")]
518539
fn init_fir(
519540
&mut self,
520541
clock_freq: f64,
@@ -557,8 +578,8 @@ impl Sampler {
557578

558579
// The filter length is equal to the filter order + 1.
559580
// The filter length must be an odd number (sinc is symmetric about x = 0).
560-
self.fir_n = (n_cap as f64 * cycles_per_sample) as i32 + 1;
561-
self.fir_n |= 1;
581+
self.fir.n = (n_cap as f64 * cycles_per_sample) as i32 + 1;
582+
self.fir.n |= 1;
562583

563584
// We clamp the filter table resolution to 2^n, making the fixpoint
564585
// sample_offset a whole multiple of the filter table resolution.
@@ -568,18 +589,20 @@ impl Sampler {
568589
FIR_RES_FAST
569590
};
570591
let n = ((res as f64 / cycles_per_sample).ln() / (2.0f64).ln()).ceil() as i32;
571-
self.fir_res = 1 << n;
592+
self.fir.res = 1 << n;
572593

573-
self.fir.clear();
574-
self.fir.resize((self.fir_n * self.fir_res) as usize, 0);
594+
self.fir.data.clear();
595+
self.fir
596+
.data
597+
.resize((self.fir.n * self.fir.res) as usize, 0);
575598

576599
// Calculate fir_RES FIR tables for linear interpolation.
577-
for i in 0..self.fir_res {
578-
let fir_offset = i * self.fir_n + self.fir_n / 2;
579-
let j_offset = i as f64 / self.fir_res as f64;
600+
for i in 0..self.fir.res {
601+
let fir_offset = i * self.fir.n + self.fir.n / 2;
602+
let j_offset = i as f64 / self.fir.res as f64;
580603
// Calculate FIR table. This is the sinc function, weighted by the
581604
// Kaiser window.
582-
let fir_n_div2 = self.fir_n / 2;
605+
let fir_n_div2 = self.fir.n / 2;
583606
for j in -fir_n_div2..=fir_n_div2 {
584607
let jx = j as f64 - j_offset;
585608
let wt = wc * jx / cycles_per_sample;
@@ -593,7 +616,7 @@ impl Sampler {
593616
let val = (1i32 << FIR_SHIFT) as f64 * filter_scale * samples_per_cycle * wc / pi
594617
* sincwt
595618
* kaiser;
596-
self.fir[(fir_offset + j) as usize] = (val + 0.5) as i16;
619+
self.fir.data[(fir_offset + j) as usize] = (val + 0.5) as i16;
597620
}
598621
}
599622
}

src/spline.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -99,28 +99,28 @@
9999
#![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))]
100100
#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
101101

102-
use alloc::vec;
103-
use alloc::vec::Vec;
104-
105102
#[derive(Clone, Copy, PartialEq)]
106103
pub struct Point {
107104
pub x: f64,
108105
pub y: f64,
109106
}
110107

111-
pub struct PointPlotter {
112-
output: Vec<i32>,
113-
}
114-
115-
impl PointPlotter {
116-
pub fn new(capacity: usize) -> Self {
117-
PointPlotter {
118-
output: vec![0; capacity],
108+
impl From<(i32, i32)> for Point {
109+
fn from((x, y): (i32, i32)) -> Point {
110+
Point {
111+
x: x as f64,
112+
y: y as f64,
119113
}
120114
}
115+
}
116+
117+
pub struct PointPlotter<'a> {
118+
output: &'a mut [i32],
119+
}
121120

122-
pub fn output(&self) -> &Vec<i32> {
123-
&self.output
121+
impl<'a> PointPlotter<'a> {
122+
pub fn new(output: &'a mut [i32]) -> Self {
123+
PointPlotter { output }
124124
}
125125

126126
pub fn plot(&mut self, x: f64, y: f64) {
@@ -202,14 +202,14 @@ fn interpolate_forward_difference(
202202
/// desirable, the end points can simply be repeated to ensure interpolation.
203203
/// Note also that points of non-differentiability and discontinuity can be
204204
/// introduced by repeating points.
205-
pub fn interpolate(points: &[Point], plotter: &mut PointPlotter, res: f64) {
205+
pub fn interpolate<P: Into<Point> + Copy>(points: &[P], plotter: &mut PointPlotter, res: f64) {
206206
let last_index = points.len() - 4;
207207
let mut i = 0;
208208
while i <= last_index {
209-
let p0 = points[i];
210-
let p1 = points[i + 1];
211-
let p2 = points[i + 2];
212-
let p3 = points[i + 3];
209+
let p0 = points[i].into();
210+
let p1 = points[i + 1].into();
211+
let p2 = points[i + 2].into();
212+
let p3 = points[i + 3].into();
213213
// p1 and p2 equal; single point.
214214
if p1.x != p2.x {
215215
let k1;

tests/spline_test.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,8 @@ fn set_f0(f0: &mut [i32; 2048]) {
4646
y: pt.1 as f64,
4747
})
4848
.collect::<Vec<spline::Point>>();
49-
let mut plotter = spline::PointPlotter::new(2048);
49+
let mut plotter = spline::PointPlotter::new(f0);
5050
spline::interpolate(&points, &mut plotter, 1.0);
51-
let output = plotter.output();
52-
for i in 0..2048 {
53-
f0[i] = output[i];
54-
}
5551
}
5652

5753
#[test]

0 commit comments

Comments
 (0)