Skip to content

Commit 2201fd7

Browse files
committed
nullsound: global music volume support
Add functions to control the global output volume for a playing NSS stream. Add volume fade out function for both music and SFX. This commit introduces a change in how nsstool generates stream data, and nullsound now expects this new format, so it is not backward-compatible with previously generated NSS data.
1 parent 2502a9d commit 2201fd7

10 files changed

+869
-116
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
22+
OBJS=entrypoint bios-commands adpcm ym2610 stream timer nss-fm nss-adpcm nss-ssg fx-vibrato fx-slide volume
2323
LIB=nullsound.lib
2424

2525
VERSION=@version@

nullsound/entrypoint.s

+2-1
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ snd_start_driver::
135135
call init_timer_state_tracker
136136
call init_adpcm_state_tracker
137137
call init_stream_state_tracker
138+
call init_volume_state_tracker
138139

139140
;; reset the pending commands buffer
140141
xor a
@@ -148,7 +149,7 @@ snd_mainloop:
148149
;; Update the state trackers
149150
call update_stream_state_tracker
150151
call update_adpcm_state_tracker
151-
;; TODO: master volume state tracker
152+
call update_volume_state_tracker
152153
;; TODO: CD state tracker
153154

154155
jp snd_mainloop

nullsound/nss-adpcm.s

+140-12
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ state_adpcm_a_vol:: .blkb 6
5050
;;; current volumes for ADPCM-B channel
5151
state_adpcm_b_vol:: .blkb 1
5252

53+
;;; Global volume attenuation for all ADPCM-A channels
54+
state_adpcm_a_volume_attenuation:: .blkb 1
55+
;;; Global volume attenuation for ADPCM-B channel
56+
state_adpcm_b_volume_attenuation:: .blkb 1
57+
58+
5359
_state_adpcm_end:
5460

5561
.area CODE
@@ -73,6 +79,7 @@ init_nss_adpcm_state_tracker::
7379
ld (state_adpcm_a_vol+5), a
7480
ld a, #0xff
7581
ld (state_adpcm_b_vol), a
82+
;; global ADPCM volumes are initialized in the volume state tracker
7683
ret
7784

7885
;;; Reset ADPCM-A playback state.
@@ -224,20 +231,26 @@ _adpcm_a_loop:
224231
ld a, (state_adpcm_a_channel)
225232
ld d, a
226233

227-
;; b: volume register for this channel
228-
ld a, #REG_ADPCM_A1_PAN_VOLUME
229-
add d
230-
ld b, a
231-
232-
;; c: current channel volume (8bit add)
234+
;; a: current channel volume (8bit add)
233235
ld hl, #state_adpcm_a_vol
234236
ld a, l
235237
add d
236238
ld l, a
237239
ld a, (hl)
238-
or #0xc0 ; default pan (L+R)
240+
241+
;; a: volume + default pan (L/R)
242+
;; ld a, c
243+
or #0xc0
244+
245+
;; c: attenuation to match the configured ADPCM-A output level
246+
call adpcm_a_scale_output
239247
ld c, a
240248

249+
;; set volume for channel in the YM2610
250+
;; b: volume register for this channel
251+
ld a, #REG_ADPCM_A1_PAN_VOLUME
252+
add d
253+
ld b, a
241254
call ym2610_write_port_b
242255

243256
pop de
@@ -345,6 +358,117 @@ _off_bit:
345358
ret
346359

347360

361+
;;; adpcm_a_scale_output
362+
;;; adjust a channel volume to match configured ADPCM-A output level
363+
;;; the YM2610's ADPCM-A output level ramp follows an exponential
364+
;;; curve, so we implement this output level attenuation via a basic
365+
;;; substraction, clamped to 0.
366+
;;; ------
367+
;;; a: input level [0x00..0x1f]
368+
;;; modified: bc
369+
adpcm_a_scale_output::
370+
;; b: pan info
371+
ld c, a
372+
and #0xc0
373+
ld b, a
374+
375+
;; c: volume info
376+
ld a, c
377+
and #0x1f
378+
ld c, a
379+
380+
;; attenuation to match the configured ADPCM-A output level
381+
ld a, (state_adpcm_a_volume_attenuation)
382+
neg
383+
add c
384+
bit 7, a
385+
jr nz, _adpcm_a_clamp_level
386+
;; restore pan info
387+
add b
388+
ret
389+
_adpcm_a_clamp_level:
390+
;; NOTE: ADPCM-A oddity: it seems that channels with volume set to 0
391+
;; still outputs something? For now, reset the pan to force mute
392+
ld a, #0
393+
ret
394+
395+
396+
;;; adpcm_b_scale_output
397+
;;; adjust ADPCM-B volume to match configured ADPCM-B output level
398+
;;; output volume = [0..1] * input volume, where the scale factor
399+
;;; is the currently configured ADPCM-B output level [0x00..0xff]
400+
;;; ------
401+
;;; a: input level [0x00..0x1f]
402+
;;; modified: bc
403+
adpcm_b_scale_output::
404+
push hl
405+
406+
;; bc: note volume fraction 000000fff fffff00
407+
ld l, a
408+
ld h, #0
409+
add hl, hl
410+
add hl, hl
411+
ld c, l
412+
ld b, h
413+
414+
;; init result
415+
ld hl, #0
416+
417+
;; e: attenuation factor -> volume factor
418+
ld a, (state_adpcm_b_volume_attenuation)
419+
neg
420+
add #64
421+
ld e, a
422+
423+
_b_level_bit0:
424+
bit 0, e
425+
jr z, _b_level_bit1
426+
;; add this bit's value to the result
427+
add hl, bc
428+
_b_level_bit1:
429+
;; bc: bc * 2
430+
sla c
431+
rl b
432+
bit 1, e
433+
jr z, _b_level_bit2
434+
add hl, bc
435+
_b_level_bit2:
436+
sla c
437+
rl b
438+
bit 2, e
439+
jr z, _b_level_bit3
440+
add hl, bc
441+
_b_level_bit3:
442+
sla c
443+
rl b
444+
bit 3, e
445+
jr z, _b_level_bit4
446+
add hl, bc
447+
_b_level_bit4:
448+
sla c
449+
rl b
450+
bit 4, e
451+
jr z, _b_level_bit5
452+
add hl, bc
453+
_b_level_bit5:
454+
sla c
455+
rl b
456+
bit 5, e
457+
jr z, _b_level_bit6
458+
add hl, bc
459+
_b_level_bit6:
460+
sla c
461+
rl b
462+
bit 6, e
463+
jr z, _b_level_post
464+
add hl, bc
465+
_b_level_post:
466+
;; keep the 8 MSB from hl, this is the scaled volume
467+
ld a, h
468+
pop hl
469+
ret
470+
471+
348472
;;; ADPCM_A_VOL
349473
;;; Set playback volume of the current ADPCM-A channel
350474
;;; ------
@@ -366,17 +490,19 @@ adpcm_a_vol::
366490
ld a, c
367491
ld (hl), a
368492

369-
;; b: ADPCM-A channel
370-
ld a, (state_adpcm_a_channel)
371-
add a, #REG_ADPCM_A1_PAN_VOLUME
372-
ld b, a
373-
374493
;; c: volume + default pan (L/R)
375494
ld a, c
376495
or #0xc0
496+
497+
;; c: last attenuation to match the configured ADPCM-A output level
498+
call adpcm_a_scale_output
377499
ld c, a
378500

379501
;; set volume for channel in the YM2610
502+
;; b: ADPCM-A channel
503+
ld a, (state_adpcm_a_channel)
504+
add a, #REG_ADPCM_A1_PAN_VOLUME
505+
ld b, a
380506
call ym2610_write_port_b
381507

382508
pop hl
@@ -443,6 +569,7 @@ _adpcm_b_post_loop_chk:
443569
;; current volume
444570
ld b, #REG_ADPCM_B_VOLUME
445571
ld a, (state_adpcm_b_vol)
572+
call adpcm_b_scale_output
446573
ld c, a
447574
call ym2610_write_port_a
448575

@@ -585,6 +712,7 @@ adpcm_b_vol::
585712

586713
;; new configured volume for ADPCM-B
587714
ld (state_adpcm_b_vol), a
715+
call adpcm_b_scale_output
588716

589717
;; set volume in the YM2610
590718
ld b, #REG_ADPCM_B_VOLUME

0 commit comments

Comments
 (0)