diff --git a/examples/curlywas/tim_ges.cwa b/examples/curlywas/tim_ges.cwa index 0f033ec..c5bb7df 100644 --- a/examples/curlywas/tim_ges.cwa +++ b/examples/curlywas/tim_ges.cwa @@ -1,7 +1,7 @@ include "../include/microw8-api.cwa" export fn upd() { - let T = 32!32 / 120; + let T = 32!32 / 116; let inline riff_pos = abs(((T&31) - 16) as f32) as i32; let lazy shift = ((1-((T>>5)&3))%2-1) * 2; @@ -11,35 +11,112 @@ export fn upd() { let inline riff_note = 5514 >> (riff_pos % note_count * 4) & 15; let inline melody_note = shift + octave - riff_note; - 80?1 = melody_note + 64; + 80?0 = ((T&1) << 1); // note trigger + 80?4 = 0xa; // attack, decay + 80?5 = 0xa; // sustain, release + 80?3 = melody_note + 76; } export fn snd(t: i32) -> f32 { gesSample(t) } -const gesStateOffset = 112; -const gesBufferOffset = 120; +const GesChannelState.Trigger = 0; +const GesChannelState.EnvState = 1; +const GesChannelState.EnvVol = 2; +const GesChannelState.Phase = 4; +const GesChannelState.Size = 6; + +const GesState.Size = GesChannelState.Size * 4; + +const GesStateOffset = 112; +const GesBufferOffset = 112 + GesState.Size; fn gesSample(t: i32) -> f32 { if !(t & 127) { let i: i32; loop clearLoop { - i!gesBufferOffset = 0; - branch_if (i := i + 4) < 128*4: clearLoop; + i!GesBufferOffset = 0; + branch_if (i := i + 4) < 128*8: clearLoop; } - let phase = 0!gesStateOffset; - let inline note = 80?1; - let inline freq = 440 as f32 * pow(2.0, (note - 69) as f32 / 12 as f32); - let phaseInc = (freq * (65536.0 / 88200.0)) as i32; - i = 0; - loop osciLoop { - i!gesBufferOffset = (phase & 65535) - 32768; - phase = phase + phaseInc; - branch_if (i := i + 4) < 128*4: osciLoop; + let ch: i32; + loop channelLoop { + let channelState = GesStateOffset + ch * GesChannelState.Size; + let channelReg = 80 + ch * 6; + let envState = channelState?GesChannelState.EnvState; + let envVol = i32.load16_u(channelState, GesChannelState.EnvVol); + + let oldTrigger = channelState?GesChannelState.Trigger; + let ctrl = channelReg?0; + channelState?GesChannelState.Trigger = ctrl; + if (((oldTrigger ^ ctrl) >> 1) & 1) | (ctrl & !(oldTrigger & 1)) { + envState = 0; + envVol = 0; + } + + if envState == 0 { + envVol = envVol + (16384 / ((channelReg?4 >> 4) + 1)); + if envVol >= 65535 { + envVol = 65535; + envState = 1; + } + } else { + if envState == 1 & ctrl { + envVol = envVol - (16 - (channelReg?4 & 15)) * 48; + let sustain = (channelReg?5 >> 4) * 4096; + if envVol < sustain { + envVol = sustain; + } + } else { + envVol = envVol - (16 - (channelReg?5 & 15)) * 48; + if envVol < 0 { + envVol = 0; + } + } + } + channelState?GesChannelState.EnvState = envState; + + i32.store16(envVol, channelState, GesChannelState.EnvVol); + + let inline note = channelReg?3; + let inline freq = 440 as f32 * pow(2.0, (note - 69) as f32 / 12 as f32); + let phaseInc = (freq * (65536.0 / 88200.0)) as i32; + + let phase = i32.load16_u(channelState, GesChannelState.Phase); + + i = 0; + let wave = ctrl >> 6; + if wave < 2 { + if wave == 0 { + loop rectLoop { + i!(GesBufferOffset + 128*4) = select(phase & 32768, -32768, 32767); + phase = phase + phaseInc; + branch_if (i := i + 4) < 64*4: rectLoop; + } + } + else + { + loop sawLoop { + i!(GesBufferOffset + 128*4) = (phase & 65535) - 32768; + phase = phase + phaseInc; + branch_if (i := i + 4) < 64*4: sawLoop; + } + } + } + + i32.store16(phase, channelState, GesChannelState.Phase); + + i = 0; + loop mixLoop { + let sample = (i!(GesBufferOffset + 128*4) * envVol) >> 18; + (i * 2)!GesBufferOffset = (i * 2)!GesBufferOffset + sample; + (i * 2)!(GesBufferOffset + 4) = (i * 2)!(GesBufferOffset + 4) + sample; + branch_if (i := i + 4) < 64*4: mixLoop; + } + + branch_if (ch := ch + 1) < 4: channelLoop; } - 0!gesStateOffset = phase; } - (((t & 127) * 4)!gesBufferOffset) as f32 / 32768 as f32 + (((t & 127) * 4)!GesBufferOffset) as f32 / (32768 * 10) as f32 } diff --git a/site/content/docs.md b/site/content/docs.md index 012e6ae..fdd3df0 100644 --- a/site/content/docs.md +++ b/site/content/docs.md @@ -18,7 +18,9 @@ The memory has to be imported as `env` `memory` and has a maximum size of 256kb 00000-00040: user memory 00040-00044: time since module start in ms 00044-0004c: gamepad state -0004c-00078: reserved +0004c-00050: reserved +00050-00070: sound registers +00070-00078: reserved 00078-12c78: frame buffer 12c78-13000: reserved 13000-13400: palette @@ -269,6 +271,38 @@ Sets the background color. Sets the cursor position. In normal mode `x` and `y` are multiplied by 8 to get the pixel position, in graphics mode they are used as is. +## Sound + +``` +Per channel: + +00 : CTRL - wave form, ring, sync, filter send, trigger + bit 0: note on flag + bit 1: note trigger + bit 2,3: filter 0,1 send + bit 6,7: wave form (rect, saw, tri, noise) +01 : PULS - pulse width +02 : FINE - fine tuning +03 : NOTE - note +04 : ENVA - attack, decay +05 : ENVR - sustain, release + +50-56: channel 0 +56-5b: channel 1 +5c-61: channel 2 +62-67: channel 3 + +68: VO01 - volumes channel 0&1 +69: VO23 - volumes channel 2&3 + +6a : FCTR 0 - type, resonance +6b : FCTR 1 - type, resonance +6c : FFIN 0 - cutoff fine tuning +6d : FNOT 0 - cutoff note +6e : FFIN 1 - cutoff fine tuning +6f : FNOT 1 - cutoff note +``` + # The `uw8` tool The `uw8` tool included in the MicroW8 download includes a number of useful tools for developing MicroW8 carts. For small productions written in