@@ -50,6 +50,12 @@ state_adpcm_a_vol:: .blkb 6
50
50
;;; current volumes for ADPCM-B channel
51
51
state_adpcm_b_vol: : .blkb 1
52
52
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
+
53
59
_state_adpcm_end:
54
60
55
61
.area CODE
@@ -73,6 +79,7 @@ init_nss_adpcm_state_tracker::
73
79
ld (state_adpcm_a_vol+5 ), a
74
80
ld a, #0xff
75
81
ld (state_adpcm_b_vol), a
82
+ ;; global ADPCM volumes are initialized in the volume state tracker
76
83
ret
77
84
78
85
;;; Reset ADPCM-A playback state.
@@ -224,20 +231,26 @@ _adpcm_a_loop:
224
231
ld a, (state_adpcm_a_channel)
225
232
ld d, a
226
233
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)
233
235
ld hl, #state_adpcm_a_vol
234
236
ld a, l
235
237
add d
236
238
ld l, a
237
239
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
239
247
ld c, a
240
248
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
241
254
call ym2610_write_port_b
242
255
243
256
pop de
@@ -345,6 +358,117 @@ _off_bit:
345
358
ret
346
359
347
360
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
+
348
472
;;; ADPCM_A_VOL
349
473
;;; Set playback volume of the current ADPCM-A channel
350
474
;;; ------
@@ -366,17 +490,19 @@ adpcm_a_vol::
366
490
ld a, c
367
491
ld (hl), a
368
492
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
-
374
493
;; c: volume + default pan (L/R)
375
494
ld a, c
376
495
or #0xc0
496
+
497
+ ;; c: last attenuation to match the configured ADPCM-A output level
498
+ call adpcm_a_scale_output
377
499
ld c, a
378
500
379
501
;; 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
380
506
call ym2610_write_port_b
381
507
382
508
pop hl
@@ -443,6 +569,7 @@ _adpcm_b_post_loop_chk:
443
569
;; current volume
444
570
ld b, #REG_ADPCM_B_VOLUME
445
571
ld a, (state_adpcm_b_vol)
572
+ call adpcm_b_scale_output
446
573
ld c, a
447
574
call ym2610_write_port_a
448
575
@@ -585,6 +712,7 @@ adpcm_b_vol::
585
712
586
713
;; new configured volume for ADPCM-B
587
714
ld (state_adpcm_b_vol), a
715
+ call adpcm_b_scale_output
588
716
589
717
;; set volume in the YM2610
590
718
ld b, #REG_ADPCM_B_VOLUME
0 commit comments