Skip to content

Commit d40c5d4

Browse files
committed
nullsound: generic trigger FX
The trigger FX currently implements Furnace's 'delay FX', i.e it allows a note to be registered after a configured number of ticks. The same FX will be used to implement the 'cut FX', i.e stop the playback of a note after a configured number of ticks.
1 parent ee4d0a8 commit d40c5d4

File tree

7 files changed

+303
-31
lines changed

7 files changed

+303
-31
lines changed

nullsound/Makefile.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ all: nullsound.lib linkcheck.map
1919
-include ../Makefile.config
2020

2121
INCLUDE_FILES=helpers ports ym2610
22-
OBJS=entrypoint bios-commands adpcm ym2610 stream timer nss-fm nss-adpcm nss-ssg fx-vibrato fx-slide fx-vol-slide volume
22+
OBJS=entrypoint bios-commands adpcm ym2610 stream timer nss-fm nss-adpcm nss-ssg fx-vibrato fx-slide fx-vol-slide fx-trigger volume
2323
LIB=nullsound.lib
2424

2525
VERSION=@version@

nullsound/fx-trigger.s

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
;;;
2+
;;; nullsound - modular sound driver
3+
;;; Copyright (c) 2024 Damien Ciabrini
4+
;;; This file is part of ngdevkit
5+
;;;
6+
;;; ngdevkit is free software: you can redistribute it and/or modify
7+
;;; it under the terms of the GNU Lesser General Public License as
8+
;;; published by the Free Software Foundation, either version 3 of the
9+
;;; License, or (at your option) any later version.
10+
;;;
11+
;;; ngdevkit is distributed in the hope that it will be useful,
12+
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
;;; GNU Lesser General Public License for more details.
15+
;;;
16+
;;; You should have received a copy of the GNU Lesser General Public License
17+
;;; along with ngdevkit. If not, see <http://www.gnu.org/licenses/>.
18+
19+
;;; Trigger effect (delay, cut...), common functions for FM and SSG
20+
;;;
21+
22+
.module nullsound
23+
24+
.include "ym2610.inc"
25+
.include "struct-fx.inc"
26+
27+
28+
.area CODE
29+
30+
31+
;;; Enable delayed trigger for the next note and volume
32+
;;; (note and volume and played after a number of ticks)
33+
;;; ------
34+
;;; ix : state for channel
35+
;;; [ hl ]: delay
36+
;;; [ hl modified ]
37+
trigger_delay_init::
38+
;; a: delay
39+
ld a, (hl)
40+
inc a
41+
inc hl
42+
43+
;; configure trigger FX for delay
44+
ld TRIGGER_DELAY(ix), a
45+
xor a
46+
set BIT_TRIGGER_ACTION_DELAY, a
47+
ld TRIGGER_ACTION(ix), a
48+
set BIT_FX_TRIGGER, FX(ix)
49+
50+
ret
51+
52+
53+
;;; Call an function from the action lookup table
54+
;;; ------
55+
;;; hl: function lookup table
56+
;;; bc: offset in bytes in the function lookup table
57+
;;; a: input (note, vol...)
58+
;;; [bc, de modified]
59+
trigger_action_function::
60+
push hl
61+
62+
;; bc: function to call
63+
add hl, bc
64+
ld c, (hl)
65+
inc hl
66+
ld b, (hl)
67+
68+
;; call
69+
ld de, #_trigger_post_action
70+
push de
71+
push bc
72+
ret
73+
_trigger_post_action:
74+
pop hl
75+
ret
76+
77+
78+
;;; Update the trigger configuration for the current channel
79+
;;; ------
80+
;;; ix: mirrored state of the current channel
81+
;;; hl: function lookup table for the current channel
82+
;;; [hl, bc, de modified]
83+
eval_trigger_step::
84+
;; is the trigger a delay?
85+
bit BIT_TRIGGER_ACTION_DELAY, TRIGGER_ACTION(ix)
86+
jr z, _trigger_post_delay
87+
;; check whether delay is reached
88+
dec TRIGGER_DELAY(ix)
89+
jr nz, _trigger_end
90+
jr _trigger_load_and_clear
91+
_trigger_post_delay:
92+
_trigger_end:
93+
ret
94+
95+
_trigger_load_and_clear:
96+
;; load new note?
97+
bit BIT_TRIGGER_LOAD_NOTE, TRIGGER_ACTION(ix)
98+
jr z, _trigger_post_load_note
99+
ld a, TRIGGER_NOTE(ix)
100+
ld bc, #TRIGGER_LOAD_NOTE_FUNC
101+
call trigger_action_function
102+
_trigger_post_load_note:
103+
104+
;; load new vol?
105+
bit BIT_TRIGGER_LOAD_VOL, TRIGGER_ACTION(ix)
106+
jr z, _trigger_post_load_vol
107+
ld a, TRIGGER_VOL(ix)
108+
ld bc, #TRIGGER_LOAD_VOL_FUNC
109+
call trigger_action_function
110+
_trigger_post_load_vol:
111+
112+
;; trigger is finished
113+
xor a
114+
ld TRIGGER_ACTION(ix), a
115+
res BIT_FX_TRIGGER, FX(ix)
116+
117+
ret

nullsound/nss-fm.s

+76-13
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ state_fm1:
9696
state_fm_pipeline: .blkb 1 ; actions to run at every tick (load note, vol, other regs)
9797
state_fm_fx: .blkb 1 ; enabled FX for this channel
9898
;;; FX state trackers
99+
state_fm_trigger: .blkb TRIGGER_SIZE
99100
state_fm_fx_vol_slide: .blkb VOL_SLIDE_SIZE
100101
state_fm_fx_slide: .blkb SLIDE_SIZE
101102
state_fm_fx_vibrato: .blkb VIBRATO_SIZE
@@ -156,6 +157,12 @@ _state_fm_end:
156157
.area CODE
157158

158159

160+
;;; context: channel action functions for FM
161+
state_fm_action_funcs:
162+
.dw fm_configure_note_on
163+
.dw fm_configure_vol
164+
165+
159166
;;; Reset FM playback state.
160167
;;; Called before playing a stream
161168
;;; ------
@@ -597,6 +604,11 @@ _fm_update_loop:
597604

598605
;; Pipeline action: evaluate one FX step for each enabled FX
599606

607+
bit BIT_FX_TRIGGER, FX(ix)
608+
jr z, _fm_post_fx_trigger
609+
ld hl, #state_fm_action_funcs
610+
call eval_trigger_step
611+
_fm_post_fx_trigger:
600612
bit BIT_FX_VIBRATO, FX(ix)
601613
jr z, _fm_post_fx_vibrato
602614
call eval_fm_vibrato_step
@@ -705,10 +717,18 @@ fm_vol::
705717
sub (hl)
706718
inc hl
707719

708-
;; register pending volume configuration for channel
709-
ld VOL(ix), a
710-
set BIT_LOAD_VOL, PIPELINE(ix)
720+
;; delay load via the trigger FX?
721+
bit BIT_TRIGGER_ACTION_DELAY, TRIGGER_ACTION(ix)
722+
jr z, _fm_vol_immediate
723+
ld TRIGGER_VOL(ix), a
724+
set BIT_TRIGGER_LOAD_VOL, TRIGGER_ACTION(ix)
725+
jr _fm_vol_end
726+
727+
_fm_vol_immediate:
728+
;; else load vol immediately
729+
call fm_configure_vol
711730

731+
_fm_vol_end:
712732
ld a, #1
713733
ret
714734

@@ -996,32 +1016,63 @@ _end_fm_slide_load_fnum2:
9961016
ret
9971017

9981018

999-
;;; FM_NOTE_ON
1000-
;;; Emit a specific note (frequency) on an FM channel
1019+
;;; Configure state for new note and trigger a load in the pipeline
10011020
;;; ------
1002-
;;; [ hl ]: note (0xAB: A=octave B=semitone)
1003-
fm_note_on::
1021+
fm_configure_note_on:
10041022
push bc
10051023

1024+
ld NOTE(ix), a
1025+
10061026
;; stop current FM channel (disable all OPs)
10071027
;; CHECK: do it in the pipeline instead?
1028+
10081029
ld a, (state_fm_ym2610_channel)
10091030
ld c, a
10101031
ld b, #REG_FM_KEY_ON_OFF_OPS
10111032
call ym2610_write_port_a
10121033

1013-
;; record note, block and freq to FM state
1014-
;; b: note (0xAB: A=octave B=semitone)
1015-
ld b, (hl)
1016-
inc hl
1017-
ld NOTE_SEMITONE(ix), b
1018-
10191034
ld a, PIPELINE(ix)
10201035
or #(STATE_PLAYING|STATE_EVAL_MACRO|STATE_LOAD_NOTE)
10211036
ld PIPELINE(ix), a
10221037

10231038
pop bc
10241039

1040+
ret
1041+
1042+
1043+
;;; Configure state for new volume and trigger a load in the pipeline
1044+
;;; ------
1045+
fm_configure_vol:
1046+
ld VOL(ix), a
1047+
1048+
;; reload configured vol at the next pipeline run
1049+
set BIT_LOAD_VOL, PIPELINE(ix)
1050+
1051+
ret
1052+
1053+
1054+
;;; FM_NOTE_ON
1055+
;;; Emit a specific note (frequency) on an FM channel
1056+
;;; ------
1057+
;;; [ hl ]: note (0xAB: A=octave B=semitone)
1058+
fm_note_on::
1059+
;; a: note (0xAB: A=octave B=semitone)
1060+
ld a, (hl)
1061+
inc hl
1062+
1063+
;; delay load via the trigger FX?
1064+
bit BIT_TRIGGER_ACTION_DELAY, TRIGGER_ACTION(ix)
1065+
jr z, _fm_note_on_immediate
1066+
ld TRIGGER_NOTE(ix), a
1067+
set BIT_TRIGGER_LOAD_NOTE, TRIGGER_ACTION(ix)
1068+
jr _fm_note_on_end
1069+
1070+
_fm_note_on_immediate:
1071+
;; else load note immediately
1072+
ld NOTE_SEMITONE(ix), a
1073+
call fm_configure_note_on
1074+
1075+
_fm_note_on_end:
10251076
;; fm context will now target the next channel
10261077
ld a, (state_fm_channel)
10271078
inc a
@@ -1231,3 +1282,15 @@ fm_vol_slide_down::
12311282

12321283
ld a, #1
12331284
ret
1285+
1286+
1287+
;;; FM_DELAY
1288+
;;; Enable delayed trigger for the next note and volume
1289+
;;; (note and volume and played after a number of ticks)
1290+
;;; ------
1291+
;;; [ hl ]: delay
1292+
fm_delay::
1293+
call trigger_delay_init
1294+
1295+
ld a, #1
1296+
ret

0 commit comments

Comments
 (0)