Commit 652f52fc authored by Aske Simon Christensen's avatar Aske Simon Christensen
Browse files

Additive synth core in assembly

parent b3627b7b
......@@ -2,12 +2,16 @@
name = "Oidos"
version = "0.1.0"
authors = ["Aske Simon Christensen <blueberry@loonies.dk>"]
build = "build.rs"
[dependencies]
vst2 = { path = "../../rust-vst2" }
#vst2 = { git = "https://github.com/overdrivenpotato/rust-vst2.git" }
rand = "0.3"
[build-dependencies]
nasm-rs = { git = "https://github.com/askeksa/nasm-rs" }
[lib]
name = "Oidos"
crate-type = ["dylib"]
extern crate nasm_rs;
fn main() {
nasm_rs::compile_library("additive.lib", &["src/additive.asm"]);
println!("cargo:rustc-link-lib=static=additive");
}
%if __BITS__ == 32
%define NAME _additive_core
%define r(n) e%+n
%define PSIZE 4
%define STACK_OFFSET (4*4 + 4)
%else
%define NAME additive_core
%define r(n) r%+n
%define PSIZE 8
%define STACK_OFFSET (4*8 + 2*16 + 8)
%endif
global NAME
section sec text align=1
NAME:
; Disable denormals
push r(ax)
vstmxcsr [r(sp)]
or dword [r(sp)], 0x8040
vldmxcsr [r(sp)]
pop r(ax)
%if __BITS__ == 64
; Save register arguments to stack
mov [rsp + 8], rcx
mov [rsp + 16], rdx
mov [rsp + 24], r8
mov [rsp + 32], r9
; Save callee-save registers
sub rsp, 2*16
vmovupd [rsp + 0*16], xmm6
vmovupd [rsp + 1*16], xmm7
%endif
push r(bx)
push r(bp)
push r(si)
push r(di)
; Initialize
vxorpd ymm0, ymm0
mov eax, 1
vcvtsi2sd xmm1, eax
vbroadcastsd ymm1, xmm1
vbroadcastsd ymm6, [r(sp) + STACK_OFFSET + 6*PSIZE + 0*8]
vbroadcastsd ymm7, [r(sp) + STACK_OFFSET + 6*PSIZE + 1*8]
; Pointers
mov r(ax), [r(sp) + STACK_OFFSET + 0*PSIZE] ; state_re
mov r(dx), [r(sp) + STACK_OFFSET + 1*PSIZE] ; state_im
mov r(bx), [r(sp) + STACK_OFFSET + 2*PSIZE] ; step_re
mov r(bp), [r(sp) + STACK_OFFSET + 3*PSIZE] ; step_im
mov r(si), [r(sp) + STACK_OFFSET + 4*PSIZE] ; filter_low
mov r(di), [r(sp) + STACK_OFFSET + 5*PSIZE] ; filter_high
; Count
mov r(cx), [r(sp) + STACK_OFFSET + 6*PSIZE + 2*8]
add r(cx), 3
shr r(cx), 2
.loop:
; Update oscillator
vmovupd ymm2, [r(ax)]
vmovupd ymm3, [r(dx)]
vmulpd ymm4, ymm2, [r(bx)]
vmulpd ymm5, ymm2, [r(bp)]
vmulpd ymm2, ymm3, [r(bp)]
vmulpd ymm3, ymm3, [r(bx)]
vsubpd ymm2, ymm4, ymm2
vaddpd ymm3, ymm3, ymm5
vmovupd [r(ax)], ymm2
vmovupd [r(dx)], ymm3
; Update filter
vmovupd ymm4, [r(si)]
vmovupd ymm5, [r(di)]
vminpd ymm3, ymm4, ymm5
vaddpd ymm4, ymm4, ymm6
vaddpd ymm5, ymm5, ymm7
vmovupd [r(si)], ymm4
vmovupd [r(di)], ymm5
vxorpd ymm4, ymm4
vminpd ymm3, ymm3, ymm1
vmaxpd ymm3, ymm3, ymm4
; Accumulate filtered oscillator
vmulpd ymm2, ymm2, ymm3
vaddpd ymm0, ymm0, ymm2
; Advance pointers
add r(ax), 32
add r(dx), 32
add r(bx), 32
add r(bp), 32
add r(si), 32
add r(di), 32
loop .loop
; Final summation
vextractf128 xmm1, ymm0, 1
vaddpd xmm0, xmm0, xmm1
vhaddpd xmm0, xmm0, xmm0
; Restore callee-save registers
pop r(di)
pop r(si)
pop r(bp)
pop r(bx)
%if __BITS__ == 64
vmovupd xmm6, [rsp + 0*16]
vmovupd xmm7, [rsp + 1*16]
add rsp, 2*16
%else
; Return result on FP stack
sub esp, 8
vmovsd [esp], xmm0
fld qword [esp]
add esp, 8
%endif
ret
......@@ -191,15 +191,16 @@ impl SoundGenerator for OidosSoundGenerator {
fn new(param: &OidosSoundParameters, tone: u8, time: usize, random: &OidosRandomData) -> OidosSoundGenerator {
let n_partials = param.modes as usize * param.fat as usize;
let n_partials_in_array = (n_partials + 3) & !3;
let mut gen = OidosSoundGenerator {
n_partials: n_partials,
state_re: Vec::with_capacity(n_partials),
state_im: Vec::with_capacity(n_partials),
step_re: Vec::with_capacity(n_partials),
step_im: Vec::with_capacity(n_partials),
filter_low: Vec::with_capacity(n_partials),
filter_high: Vec::with_capacity(n_partials),
state_re: Vec::with_capacity(n_partials_in_array),
state_im: Vec::with_capacity(n_partials_in_array),
step_re: Vec::with_capacity(n_partials_in_array),
step_im: Vec::with_capacity(n_partials_in_array),
filter_low: Vec::with_capacity(n_partials_in_array),
filter_high: Vec::with_capacity(n_partials_in_array),
f_add_low: (-param.f_sweeplow * param.f_slopelow) as f64,
f_add_high: (param.f_sweephigh * param.f_slopehigh) as f64,
......@@ -248,41 +249,30 @@ impl SoundGenerator for OidosSoundGenerator {
}
}
for _ in n_partials..n_partials_in_array {
gen.state_re.push(0.0);
gen.state_im.push(0.0);
gen.step_re.push(0.0);
gen.step_im.push(0.0);
gen.filter_low.push(0.0);
gen.filter_high.push(0.0);
}
gen
}
fn produce_sample(&mut self) -> f32 {
let s = self.oscillator_step() / (self.n_partials as f64).sqrt();
self.softclip(s) as f32
let s = unsafe {
additive_core(self.state_re.as_mut_ptr(), self.state_im.as_mut_ptr(),
self.step_re.as_ptr(), self.step_im.as_ptr(),
self.filter_low.as_mut_ptr(), self.filter_high.as_mut_ptr(),
self.f_add_low, self.f_add_high, self.n_partials)
};
(s * (self.gain / (self.n_partials as f64 + (self.gain - 1.0) * s * s)).sqrt()) as f32
}
}
impl OidosSoundGenerator {
fn oscillator_step(&mut self) -> f64 {
let mut s: f64 = 0.0;
for i in 0..self.n_partials {
let state_re = self.state_re[i];
let state_im = self.state_im[i];
let step_re = self.step_re[i];
let step_im = self.step_im[i];
let newstate_re = state_re * step_re - state_im * step_im;
let newstate_im = state_re * step_im + state_im * step_re;
self.state_re[i] = newstate_re;
self.state_im[i] = newstate_im;
let f_low = self.filter_low[i];
let f_high = self.filter_high[i];
self.filter_low[i] = f_low + self.f_add_low;
self.filter_high[i] = f_high + self.f_add_high;
let f = f_low.min(f_high).min(1.0).max(0.0);
s += newstate_re * f;
}
s
}
fn softclip(&self, v: f64) -> f64 {
v * (self.gain / (1.0 + (self.gain - 1.0) * v * v)).sqrt()
}
extern "cdecl" {
fn additive_core(state_re: *mut f64, state_im: *mut f64, step_re: *const f64, step_im: *const f64,
filter_low: *mut f64, filter_high: *mut f64, f_add_low: f64, f_add_high: f64, n: usize) -> f64;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment