From ea2812f50fb9f74d0a2656ff667ea35758c47569 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 3 Jul 2022 10:58:57 +0200 Subject: [PATCH] add max volume to psg envelope --- compiler/res/prog8lib/cx16/psg.p8 | 68 ++++++++++++++++++------------- docs/source/todo.rst | 1 + examples/cx16/bdmusic.p8 | 8 ++-- examples/cx16/tehtriz.p8 | 14 +++---- 4 files changed, 51 insertions(+), 40 deletions(-) diff --git a/compiler/res/prog8lib/cx16/psg.p8 b/compiler/res/prog8lib/cx16/psg.p8 index 67742219c..4d1afad05 100644 --- a/compiler/res/prog8lib/cx16/psg.p8 +++ b/compiler/res/prog8lib/cx16/psg.p8 @@ -19,6 +19,7 @@ psg { cx16.vpoke(1, $f9c2 + voice_num * 4, channel | volume) cx16.vpoke(1, $f9c3 + voice_num * 4, waveform | pulsewidth) envelope_volumes[voice_num] = mkword(volume, 0) + envelope_maxvolumes[voice_num] = volume } ; sub freq_hz(ubyte voice_num, float hertz) { @@ -37,6 +38,7 @@ psg { uword reg = $f9c2 + voice_num * 4 cx16.vpoke(1, reg, cx16.vpeek(1, reg) & %11000000 | vol) envelope_volumes[voice_num] = mkword(vol, 0) + envelope_maxvolumes[voice_num] = vol } sub pulse_width(ubyte voice_num, ubyte pw) { @@ -44,61 +46,68 @@ psg { cx16.vpoke(1, reg, cx16.vpeek(1, reg) & %11000000 | pw) } - sub envelope(ubyte voice_num, ubyte attack, ubyte sustain, ubyte release) { + sub envelope(ubyte voice_num, ubyte maxvolume, ubyte attack, ubyte sustain, ubyte release) { envelope_states[voice_num] = 255 - envelope_attacks[voice_num] = attack * $0040 + envelope_attacks[voice_num] = attack envelope_sustains[voice_num] = sustain - envelope_releases[voice_num] = release * $0040 + envelope_releases[voice_num] = release if attack attack = 0 else - attack = 63 ; max volume when no attack is set + attack = maxvolume ; max volume when no attack is set envelope_volumes[voice_num] = mkword(attack, 0) + envelope_maxvolumes[voice_num] = maxvolume envelope_states[voice_num] = 0 } sub silent() { - for cx16.r15L in 0 to 15 { - envelope_states[cx16.r15L] = 255 - envelope_volumes[cx16.r15L] = 0 - volume(cx16.r15L, 0) + for cx16.r1L in 0 to 15 { + envelope_states[cx16.r1L] = 255 + envelope_volumes[cx16.r1L] = 0 + volume(cx16.r1L, 0) } } sub envelopes_irq() { + ; If you want to use real-time volume envelopes (Attack-Sustain-Release), + ; you have to call this routine every 1/60th second, for example from your vsync irq handler, + ; or just install this routine as the only irq handler if you don't have to do other things there. + ; Example: cx16.set_irq(&psg.envelopes_irq, true) + ; cx16.r0 = the volume word (volume scaled by 256) - ; cx16.r15L = the voice number - ; the other virtual registers are used to backup vera registers. + ; cx16.r1L = the voice number + ; cx16.r2L = attack value ; calculate new volumes - for cx16.r15L in 0 to 15 { - when envelope_states[cx16.r15L] { + for cx16.r1L in 0 to 15 { + when envelope_states[cx16.r1L] { 0 -> { ; attack - cx16.r0 = envelope_volumes[cx16.r15L] + envelope_attacks[cx16.r15L] - if msb(cx16.r0) & %11000000 or envelope_attacks[cx16.r15L]==0 { - cx16.r0 = mkword(63, 0) - envelope_attacks[cx16.r15L] = 0 - envelope_states[cx16.r15L] = 1 ; start sustain + cx16.r2L = envelope_maxvolumes[cx16.r1L] + cx16.r0 = envelope_volumes[cx16.r1L] + envelope_attacks[cx16.r1L] * $0040 + if msb(cx16.r0) > cx16.r2L or envelope_attacks[cx16.r1L]==0 { + cx16.r0 = mkword(cx16.r2L, 0) + envelope_attacks[cx16.r1L] = 0 + envelope_states[cx16.r1L] = 1 ; start sustain } - envelope_volumes[cx16.r15L] = cx16.r0 + envelope_volumes[cx16.r1L] = cx16.r0 } 1 -> { ; sustain - if envelope_sustains[cx16.r15L] { - envelope_sustains[cx16.r15L]-- + if envelope_sustains[cx16.r1L] { + envelope_sustains[cx16.r1L]-- } else { - envelope_states[cx16.r15L] = 2 ; start release + envelope_states[cx16.r1L] = 2 ; start release } } 2 -> { ; release - cx16.r0 = envelope_volumes[cx16.r15L] - envelope_releases[cx16.r15L] + cx16.r0 = envelope_volumes[cx16.r1L] - envelope_releases[cx16.r1L] * $0040 if msb(cx16.r0) & %11000000 { cx16.r0 = 0 - envelope_releases[cx16.r15L] = 0 + envelope_releases[cx16.r1L] = 0 } - envelope_volumes[cx16.r15L] = cx16.r0 + envelope_volumes[cx16.r1L] = cx16.r0 } } } @@ -113,15 +122,16 @@ psg { cx16.VERA_ADDR_L = $c2 cx16.VERA_ADDR_M = $f9 cx16.VERA_ADDR_H = 1 | %00110000 - for cx16.r15L in 0 to 15 { - cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r15L]) + for cx16.r1L in 0 to 15 { + cx16.VERA_DATA0 = cx16.VERA_DATA1 & %11000000 | msb(envelope_volumes[cx16.r1L]) } cx16.pop_vera_context() } ubyte[16] envelope_states - uword[16] envelope_volumes - uword[16] envelope_attacks + uword[16] envelope_volumes ; scaled by 256 + ubyte[16] envelope_attacks ubyte[16] envelope_sustains - uword[16] envelope_releases + ubyte[16] envelope_releases + ubyte[16] envelope_maxvolumes } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e174b356a..134a7d9f1 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -5,6 +5,7 @@ For next release ^^^^^^^^^^^^^^^^ ... + Need help with ^^^^^^^^^^^^^^ - c128 target: various machine specific things (free zp locations, how banking works, getting the floating point routines working, ...) diff --git a/examples/cx16/bdmusic.p8 b/examples/cx16/bdmusic.p8 index e2a4fc9f5..72406b627 100644 --- a/examples/cx16/bdmusic.p8 +++ b/examples/cx16/bdmusic.p8 @@ -12,8 +12,8 @@ main { psg.voice(1, psg.RIGHT, 0, psg.NOISE, 0) psg.freq(0, 1000) psg.freq(1, 2000) - psg.envelope(0, 50, 0, 5) - psg.envelope(1, 80, 0, 6) + psg.envelope(0, 63, 50, 0, 5) + psg.envelope(1, 63, 80, 0, 6) sys.wait(100) psg.silent() cx16.restore_irq() @@ -68,8 +68,8 @@ main { ubyte note1 = msb(note) psg.freq(0, vera_freqs[note0]) psg.freq(1, vera_freqs[note1]) - psg.envelope(0, 255, 0, 6) - psg.envelope(1, 255, 0, 6) + psg.envelope(0, 63, 255, 0, 6) + psg.envelope(1, 63, 255, 0, 6) print_notes(note0, note1) sys.wait(10) } diff --git a/examples/cx16/tehtriz.p8 b/examples/cx16/tehtriz.p8 index c8377ce4b..cf9a87b04 100644 --- a/examples/cx16/tehtriz.p8 +++ b/examples/cx16/tehtriz.p8 @@ -602,35 +602,35 @@ sound { ; soft click/"tschk" sound psg.freq(0, 15600) psg.voice(0, psg.LEFT | psg.RIGHT, 32, psg.NOISE, 0) - psg.envelope(0, 200, 1, 100) + psg.envelope(0, 32, 200, 1, 100) } sub blockdrop() { ; swish psg.freq(1, 4600) psg.voice(1, psg.LEFT | psg.RIGHT, 32, psg.NOISE, 0) - psg.envelope(1, 200, 5, 20) + psg.envelope(1, 32, 200, 5, 20) } sub swapping() { ; beep psg.freq(2, 1500) psg.voice(2, psg.LEFT | psg.RIGHT, 32, psg.TRIANGLE, 0) - psg.envelope(2, 100, 6, 10) + psg.envelope(2, 40, 100, 6, 10) } sub lineclear() { ; explosion psg.freq(3, 1400) psg.voice(3, psg.LEFT | psg.RIGHT, 63, psg.NOISE, 0) - psg.envelope(3, 100, 8, 10) + psg.envelope(3, 63, 100, 8, 10) } sub lineclear_big() { ; big explosion psg.freq(4, 2500) psg.voice(4, psg.LEFT | psg.RIGHT, 63, psg.NOISE, 0) - psg.envelope(4, 100, 20, 10) + psg.envelope(4, 63, 100, 20, 10) } sub gameover() { @@ -639,7 +639,7 @@ sound { psg.freq(6, 600) psg.voice(5, psg.LEFT | psg.RIGHT, 0, psg.SAWTOOTH, 0) psg.voice(6, psg.LEFT | psg.RIGHT, 0, psg.TRIANGLE, 0) - psg.envelope(5, 100, 30, 10) - psg.envelope(6, 100, 30, 10) + psg.envelope(5, 50, 100, 30, 10) + psg.envelope(6, 50, 100, 30, 10) } }