mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-21 19:46:41 +01:00
180 lines
7.6 KiB
Plaintext
180 lines
7.6 KiB
Plaintext
const GesChannelState.Trigger = 0;
|
|
const GesChannelState.EnvState = 1;
|
|
const GesChannelState.EnvVol = 2;
|
|
const GesChannelState.Phase = 4;
|
|
const GesChannelState.Size = 6;
|
|
|
|
const GesState.Filter = GesChannelState.Size * 4;
|
|
const GesState.Size = GesState.Filter + 8*4;
|
|
|
|
const GesStateOffset = 112;
|
|
const GesBufferOffset = 112 + GesState.Size;
|
|
|
|
export fn gesSnd(t: i32) -> f32 {
|
|
if !(t & 127) {
|
|
let i: i32;
|
|
loop clearLoop {
|
|
i!GesBufferOffset = 0;
|
|
branch_if (i := i + 4) < 128*8: clearLoop;
|
|
}
|
|
|
|
let ch: i32;
|
|
loop channelLoop {
|
|
let lazy channelState = GesStateOffset + ch * GesChannelState.Size;
|
|
let lazy channelReg = 80 + ch * 6;
|
|
let envState = channelState?GesChannelState.EnvState;
|
|
let envVol = i32.load16_u(channelState, GesChannelState.EnvVol);
|
|
|
|
let lazy oldTrigger = channelState?GesChannelState.Trigger;
|
|
let lazy ctrl = channelReg?0;
|
|
if (oldTrigger ^ ctrl) & (ctrl | 2) & 3 {
|
|
envState = 1;
|
|
envVol = 0;
|
|
}
|
|
channelState?GesChannelState.Trigger = ctrl;
|
|
|
|
if envState {
|
|
let lazy attack = channelReg?4 & 15;
|
|
envVol = envVol + 12 * pow(1.5625, (15 - attack) as f32) as i32;
|
|
if envVol >= 65535 | !attack {
|
|
envVol = 65535;
|
|
envState = 0;
|
|
}
|
|
} else {
|
|
let inline decay = (channelReg - (ctrl & 1))?5 >> 4;
|
|
let inline dec = 8 * pow(1.5625, (15 - decay) as f32) as i32;
|
|
envVol = envVol - ((dec * (envVol + 8192)) >> 16);
|
|
let inline sustain = (channelReg?5 & 15) << 12;
|
|
let lazy targetVol = (ctrl & 1) * sustain;
|
|
if envVol < targetVol {
|
|
envVol = targetVol;
|
|
}
|
|
}
|
|
channelState?GesChannelState.EnvState = envState;
|
|
|
|
i32.store16(envVol, channelState, GesChannelState.EnvVol);
|
|
|
|
let inline note = i32.load16_u(channelReg, 2);
|
|
let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
|
|
let phaseInc = (freq * (65536.0 / 44100.0)) as i32;
|
|
|
|
let phase = i32.load16_u(channelState, GesChannelState.Phase) << 8;
|
|
|
|
let inline pulseWidth = channelReg?1;
|
|
let phaseShift = (pulseWidth - 128) * 255;
|
|
let invPhaseInc = 1 as f32 / phaseInc as f32;
|
|
|
|
i = 0;
|
|
let wave = ctrl >> 6;
|
|
if wave < 2 {
|
|
if wave {
|
|
let pulsePhase1 = pulseWidth << 23;
|
|
let pulsePhase2 = (511 - pulseWidth) << 23;
|
|
loop sawLoop {
|
|
let p = (phase ^ 32768) << 16;
|
|
let saw = (p >> 16) - polyBlep(phase, invPhaseInc, -32767);
|
|
let saw2 = select(p #>= pulsePhase1 & p #< pulsePhase2, -saw, saw);
|
|
let saw2 = saw2 -
|
|
polyBlep((p - pulsePhase1) >> 16, invPhaseInc, -saw) -
|
|
polyBlep((p - pulsePhase2) >> 16, invPhaseInc, saw);
|
|
i!(GesBufferOffset + 128*4) = saw2;
|
|
phase = phase + phaseInc;
|
|
branch_if (i := i + 4) < 64*4: sawLoop;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
let pulsePhase = 32768 + pulseWidth * 128;
|
|
loop rectLoop {
|
|
i!(GesBufferOffset + 128*4) = select((phase & 65535) < pulsePhase, -32768, 32767) -
|
|
polyBlep(phase, invPhaseInc, -32767) -
|
|
polyBlep(phase - pulsePhase, invPhaseInc, 32767);
|
|
phase = phase + phaseInc;
|
|
branch_if (i := i + 4) < 64*4: rectLoop;
|
|
}
|
|
}
|
|
} else {
|
|
if wave == 2 {
|
|
let scale = pulseWidth + 256;
|
|
loop triLoop {
|
|
let s = phase << 16;
|
|
s = (s ^ (s >> 31));
|
|
s = (s >> 8) * scale;
|
|
s = (s ^ (s >> 31));
|
|
i!(GesBufferOffset + 128*4) = (s >> 15) - 32768;
|
|
phase = phase + phaseInc;
|
|
branch_if (i := i + 4) < 64*4: triLoop;
|
|
}
|
|
} else {
|
|
loop noiseLoop {
|
|
let s = (phase >> 12) & 4095;
|
|
let pulse = ((phase >> 8) & 255) >= pulseWidth;
|
|
s = s * 0x6746ba73;
|
|
s = s ^ (s >> 15) * pulse;
|
|
i!(GesBufferOffset + 128*4) = (s * 0x83567a92 + (phase << 16)) >> 16;
|
|
phase = phase + phaseInc;
|
|
branch_if (i := i + 4) < 64*4: noiseLoop;
|
|
}
|
|
}
|
|
}
|
|
|
|
i32.store16(phase >> 8, channelState, GesChannelState.Phase);
|
|
|
|
let channelVol = ((ch >> 1)?0x68 >> ((ch & 1) * 4)) & 15;
|
|
envVol = envVol * channelVol / 15;
|
|
|
|
let leftVol = (0x4c6a >> (ch * 4)) & 15;
|
|
let rightVol = 16 - leftVol;
|
|
|
|
let lazy filter = ((ctrl >> 2) & 3) - 1;
|
|
|
|
i = 0;
|
|
if filter #> 1 {
|
|
loop mixLoop {
|
|
let sample = (i!(GesBufferOffset + 128*4) * envVol) >> 18;
|
|
(i * 2)!GesBufferOffset = (i * 2)!GesBufferOffset + ((sample * leftVol) >> 4);
|
|
(i * 2)!(GesBufferOffset + 4) = (i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4);
|
|
branch_if (i := i + 4) < 64*4: mixLoop;
|
|
}
|
|
} else {
|
|
let ctrl = filter?0x6a;
|
|
let note = i32.load16_u(filter * 2, 0x6c);
|
|
let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
|
|
let F = (8192 as f32 * sin(min(0.25, freq / 44100 as f32) * 3.1415)) as i32;
|
|
let Q = 8192 - (ctrl >> 4) * (7000/15);
|
|
let Qlimit = (8192*4096/F - F/2) * 3 / 4;
|
|
if Q > Qlimit {
|
|
Q = Qlimit;
|
|
}
|
|
let low_out = ctrl & 1;
|
|
let high_out = (ctrl >> 1) & 1;
|
|
let band_out = (ctrl >> 2) & 1;
|
|
let low = (ch * 8)!(GesStateOffset + GesState.Filter);
|
|
let band = (ch * 8)!(GesStateOffset + GesState.Filter + 4);
|
|
loop filterLoop {
|
|
let in = (i!(GesBufferOffset + 128*4) * envVol) >> 18;
|
|
|
|
let high = in - low - ((band * Q) >> 12);
|
|
band = band + ((F * high) >> 12);
|
|
low = low + ((F * band) >> 12);
|
|
|
|
let sample = low * low_out + high * high_out + band * band_out;
|
|
(i * 2)!GesBufferOffset = (i * 2)!GesBufferOffset + ((sample * leftVol) >> 4);
|
|
(i * 2)!(GesBufferOffset + 4) = (i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4);
|
|
branch_if (i := i + 4) < 64*4: filterLoop;
|
|
}
|
|
(ch * 8)!(GesStateOffset + GesState.Filter) = low;
|
|
(ch * 8)!(GesStateOffset + GesState.Filter + 4) = band;
|
|
}
|
|
|
|
branch_if (ch := ch + 1) < 4: channelLoop;
|
|
}
|
|
}
|
|
(((t & 127) * 4)!GesBufferOffset) as f32 / 32768 as f32
|
|
}
|
|
|
|
fn polyBlep(transientPhase: i32, invPhaseInc: f32, magnitude: i32) -> i32 {
|
|
let lazy t = ((transientPhase << 16) >> 16) as f32 * invPhaseInc;
|
|
let lazy x = max(0 as f32, 1 as f32 - abs(t));
|
|
(f32.copysign(x * x, t) * magnitude as f32) as i32
|
|
} |