summaryrefslogtreecommitdiff
path: root/src/mplayer/akm/PlayerAkm_SoundEffects.asm
blob: 1e4c3b801ae1d99e917cff5e2f03fc5f8bda7d64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
;       Player of sound effects, for the AKM player.
;       Note that this is the exact same code as for the Lightweight player, as the way to output to the PSG is the same.

        ;Is there a loaded Player Configuration source? If no, use a default configuration.
        IFNDEF PLY_CFG_SFX_ConfigurationIsPresent
                PLY_CFG_UseHardwareSounds = 1
                PLY_CFG_SFX_LoopTo = 1
                PLY_CFG_SFX_NoSoftNoHard = 1
                PLY_CFG_SFX_NoSoftNoHard_Noise = 1
                PLY_CFG_SFX_SoftOnly = 1
                PLY_CFG_SFX_SoftOnly_Noise = 1
                PLY_CFG_SFX_HardOnly = 1
                PLY_CFG_SFX_HardOnly_Noise = 1
                PLY_CFG_SFX_HardOnly_Retrig = 1
                PLY_CFG_SFX_SoftAndHard = 1
                PLY_CFG_SFX_SoftAndHard_Noise = 1
                PLY_CFG_SFX_SoftAndHard_Retrig = 1
        ENDIF
        
;       Agglomerates some Player Configuration flags.
;       --------------------------------------------
;       Mixes the Hardware flags into one.
        IFDEF PLY_CFG_SFX_HardOnly
                PLY_AKM_SE_HardwareSounds = 1
        ENDIF
        IFDEF PLY_CFG_SFX_SoftAndHard
                PLY_AKM_SE_HardwareSounds = 1
        ENDIF
;       Mixes the Hardware Noise flags into one.
        IFDEF PLY_CFG_SFX_HardOnly_Noise
                PLY_AKM_SE_HardwareNoise = 1
        ENDIF
        IFDEF PLY_CFG_SFX_SoftAndHard_Noise
                PLY_AKM_SE_HardwareNoise = 1
        ENDIF
;       Mixes the Noise flags into one.
        IFDEF PLY_AKM_SE_HardwareNoise
                PLY_AKM_SE_Noise = 1
        ENDIF
        IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise
                PLY_AKM_SE_Noise = 1
        ENDIF
        IFDEF PLY_CFG_SFX_SoftOnly
                PLY_AKM_SE_Noise = 1
        ENDIF
;       Noise in Sound Effects? Then noise register code must be compiled.
        IFDEF PLY_AKM_SE_Noise
                PLY_AKM_USE_NoiseRegister = 1
        ENDIF
;       Mixes the Software Volume flags into one.
        IFDEF PLY_CFG_SFX_NoSoftNoHard
                PLY_AKM_SE_VolumeSoft = 1
                PLY_AKM_SE_VolumeSoftOrHard = 1
        ENDIF
        IFDEF PLY_CFG_SFX_SoftOnly
                PLY_AKM_SE_VolumeSoft = 1
                PLY_AKM_SE_VolumeSoftOrHard = 1
        ENDIF
;       Mixes the volume (soft/hard) into one.
        IFDEF PLY_CFG_UseHardwareSounds
                PLY_AKM_SE_VolumeSoftOrHard = 1
        ENDIF
;       Mixes the retrig flags into one.
        IFDEF PLY_CFG_SFX_HardOnly_Retrig
                PLY_AKM_SE_Retrig = 1
        ENDIF
        IFDEF PLY_CFG_SFX_SoftAndHard_Retrig
                PLY_AKM_SE_Retrig = 1
        ENDIF


;Initializes the sound effects. It MUST be called at any times before a first sound effect is triggered.
;It doesn't matter whether the song is playing or not, or if it has been initialized or not.
;IN:    HL = Address to the sound effects data.
PLY_AKM_InitSoundEffectsDisarkGenerateExternalLabel:
PLY_AKM_InitSoundEffects:
        ld (PLY_AKM_PtSoundEffectTable + PLY_AKM_Offset1b),hl
        ret


;Pl²ys a sound effect. If a previous one was already playing on the same channel, it is replaced.
;This does not actually plays the sound effect, but programs its playing.
;The music player, when called, will call the PLY_AKM_PlaySoundEffectsStream method below.
;IN:    A = Sound effect number (>0!).
;       C = The channel where to play the sound effect (0, 1, 2).
;       B = Inverted volume (0 = full volume, 16 = no sound). Hardware sounds are also lowered.
PLY_AKM_PlaySoundEffectDisarkGenerateExternalLabel:
PLY_AKM_PlaySoundEffect:
        ;Gets the address to the sound effect.
        dec a                   ;The 0th is not encoded.
        IFNDEF PLY_AKM_Rom
dknr3 (void):
PLY_AKM_PtSoundEffectTable: ld hl,0
        ELSE
        ld hl,(PLY_AKM_PtSoundEffectTable)
        ENDIF
        ld e,a
        ld d,0
        add hl,de
        add hl,de
        ld e,(hl)
        inc hl
        ld d,(hl)
        ;Reads the header of the sound effect to get the speed.
        ld a,(de)
        inc de
        ex af,af'

        ld a,b

        ;Finds the pointer to the sound effect of the desired channel.
        ld hl,PLY_AKM_Channel1_SoundEffectData
        ld b,0
        sla c
        sla c
        sla c
        add hl,bc
        ld (hl),e
        inc hl
        ld (hl),d
        inc hl        
        
        ;Now stores the inverted volume.
        ld (hl),a
        inc hl
        
        ;Resets the current speed, stores the instrument speed.
        ld (hl),0
        inc hl
        ex af,af'
        ld (hl),a
        
        ret

;Stops a sound effect. Nothing happens if there was no sound effect.
;IN:    A = The channel where to stop the sound effect (0, 1, 2).
PLY_AKM_StopSoundEffectFromChannelDisarkGenerateExternalLabel:
PLY_AKM_StopSoundEffectFromChannel:
        ;Puts 0 to the pointer of the sound effect.
        add a,a
        add a,a
        add a,a
        ld e,a
        ld d,0
        ld hl,PLY_AKM_Channel1_SoundEffectData
        add hl,de
        ld (hl),d               ;0 means "no sound".
        inc hl
        ld (hl),d
        ret

;Plays the sound effects, if any has been triggered by the user.
;This does not actually send registers to the PSG, it only overwrite the required values of the registers of the player.
;The sound effects initialization method must have been called before!
;As R7 is required, this must be called after the music has been played, but BEFORE the registers are sent to the PSG.
;IN:    A = R7.
PLY_AKM_PlaySoundEffectsStream:
        ;Shifts the R7 to the left twice, so that bit 2 and 5 only can be set for each track, below.
        rla
        rla

        ;Plays the sound effects on every track.
        ld ix,PLY_AKM_Channel1_SoundEffectData
        ld iy,PLY_AKM_Track1_Registers
        ld c,a
        call PLY_AKM_PSES_Play
        ld ix,PLY_AKM_Channel2_SoundEffectData
        ld iy,PLY_AKM_Track2_Registers
        srl c                                   ;Not RR, to make sure bit 6 is 0 (else, no more keyboard on CPC!).
                                                ;Also, on MSX, bit 6 must be 0.
        call PLY_AKM_PSES_Play
        ld ix,PLY_AKM_Channel3_SoundEffectData
        ld iy,PLY_AKM_Track3_Registers
        IFDEF PLY_AKM_HARDWARE_MSX
                scf             ;On MSX, bit 7 must be 1.
                rr c
        ELSE
                rr c            ;On other platforms, we don't care about b7.
        ENDIF
        call PLY_AKM_PSES_Play

        ld a,c
        ld (PLY_AKM_MixerRegister),a
        ret


;Plays the sound stream from the given pointer to the sound effect. If 0, no sound is played.
;The given R7 is given shift twice to the left, so that this code MUST set/reset the bit 2 (sound), and set/reset bit 5 (noise).
;This code MUST overwrite these bits because sound effects have priority over the music.
;IN:    IX = Points on the sound effect pointer. If the sound effect pointer is 0, nothing must be played.
;       IY = Points at the beginning of the register structure related to the channel.
;       C = R7, shifted twice to the left.
;OUT:   The pointed pointer by IX may be modified as the sound advances.
;       C = R7, MUST be modified if there is a sound effect.
PLY_AKM_PSES_Play:
        ;Reads the pointer pointed by IX.
        ld l,(ix + 0)
        ld h,(ix + 1)
        ld a,l
        or h
        ret z           ;No sound to be played? Returns immediately.

        ;Reads the first byte. What type of sound is it?
PLY_AKM_PSES_ReadFirstByte:
        ld a,(hl)
        inc hl
        ld b,a
        rra
        jr c,PLY_AKM_PSES_SoftwareOrSoftwareAndHardware
        rra
                        IFDEF PLY_CFG_SFX_HardOnly              ;CONFIG SPECIFIC
        jr c,PLY_AKM_PSES_HardwareOnly
                        ENDIF ;PLY_CFG_SFX_HardOnly

        ;No software, no hardware, or end/loop.
        ;-------------------------------------------
        ;End or loop?
        rra
                        IFDEF PLY_CFG_SFX_NoSoftNoHard         ;CONFIG SPECIFIC. If not present, the jump is not needed, the method is just below.
        jr c,PLY_AKM_PSES_S_EndOrLoop
                        
        ;No software, no hardware.
        ;-------------------------------------------
        ;Gets the volume.
        call PLY_AKM_PSES_ManageVolumeFromA_Filter4Bits

        ;Noise?
                                IFDEF PLY_CFG_SFX_NoSoftNoHard_Noise                ;CONFIG SPECIFIC
        rl b
        call PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel
                                ELSE
        set 5,c                                                 ;No noise in compilation, so stops the noise.
                                ENDIF ;PLY_CFG_SFX_NoSoftNoHard_Noise

        ;Cuts the sound.
        set 2,c
        
        jr PLY_AKM_PSES_SavePointerAndExit
                        ENDIF ;PLY_CFG_SFX_NoSoftNoHard

        ;**Warning!** Do not put any instruction between EndOrLoop and NoSoftNoHard.

PLY_AKM_PSES_S_EndOrLoop:
                        IFDEF PLY_CFG_SFX_LoopTo                ;CONFIG SPECIFIC. If no "loop to", the sounds always end, no need to test.
        ;Is it an end?
        rra
        jr c,PLY_AKM_PSES_S_Loop
                        ENDIF ;PLY_CFG_SFX_LoopTo
        ;End of the sound. Marks the sound pointer with 0, meaning "no sound".
        xor a
        ld (ix + 0),a
        ld (ix + 1),a
        ret
                        IFDEF PLY_CFG_SFX_LoopTo                ;CONFIG SPECIFIC.
PLY_AKM_PSES_S_Loop:
        ;Loops. Reads the pointer and directly uses it.
        ld a,(hl)
        inc hl
        ld h,(hl)
        ld l,a
        jr PLY_AKM_PSES_ReadFirstByte
                        ENDIF ;PLY_CFG_SFX_LoopTo


;Saves HL into IX, and exits. This must be called at the end of each Cell.
;If the speed has not been reached, it is not saved.
PLY_AKM_PSES_SavePointerAndExit:
        ;Speed reached?
        ld a,(ix + PLY_AKM_SoundEffectData_OffsetCurrentStep)
        cp (ix + PLY_AKM_SoundEffectData_OffsetSpeed)
        jr c,PLY_AKM_PSES_NotReached
        ;The speed has been reached, so resets it and saves the pointer to the next cell to read.
        ld (ix + PLY_AKM_SoundEffectData_OffsetCurrentStep),0
        ld (ix + 0),l
        ld (ix + 1),h
        ret
PLY_AKM_PSES_NotReached:
        ;Speed not reached. Increases it, that's all. The same cell will be read next time.
        inc (ix + PLY_AKM_SoundEffectData_OffsetCurrentStep)
        ret

                        IFDEF PLY_CFG_SFX_HardOnly         ;CONFIG SPECIFIC
        ;Hardware only.
        ;-------------------------------------------
PLY_AKM_PSES_HardwareOnly:
        ;Calls the shared code that manages everything.
        call PLY_AKM_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise
        ;Cuts the sound.
        set 2,c

        jr PLY_AKM_PSES_SavePointerAndExit
                        ENDIF ;PLY_CFG_SFX_HardOnly



PLY_AKM_PSES_SoftwareOrSoftwareAndHardware:
        ;Software only?
        rra
                        IFDEF PLY_CFG_SFX_SoftAndHard         ;CONFIG SPECIFIC
        jr c,PLY_AKM_PSES_SoftwareAndHardware
                        ENDIF ;PLY_CFG_SFX_SoftAndHard

        ;Software.
        ;-------------------------------------------
                        IFDEF PLY_CFG_SFX_SoftOnly          ;CONFIG SPECIFIC
        ;Volume.
        call PLY_AKM_PSES_ManageVolumeFromA_Filter4Bits

        ;Noise?
                                IFDEF PLY_CFG_SFX_SoftOnly_Noise                ;CONFIG SPECIFIC
        rl b
        call PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel
                                ELSE
        set 5,c                                                 ;No noise in compilation, so stops the noise.
                                ENDIF ;PLY_CFG_SFX_SoftOnly_Noise

        ;Opens the "sound" channel.
        res 2,c

        ;Reads the software period.
        call PLY_AKM_PSES_ReadSoftwarePeriod

        jr PLY_AKM_PSES_SavePointerAndExit
                        ENDIF ;PLY_CFG_SFX_SoftOnly


        ;Software and Hardware.
        ;-------------------------------------------
                        IFDEF PLY_AKM_SE_HardwareSounds         ;CONFIG SPECIFIC
PLY_AKM_PSES_SoftwareAndHardware:
        ;Calls the shared code that manages everything.
        call PLY_AKM_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise

        ;Reads the software period.
        call PLY_AKM_PSES_ReadSoftwarePeriod

        ;Opens the sound.
        res 2,c

        jr PLY_AKM_PSES_SavePointerAndExit
                        ENDIF ;PLY_AKM_SE_HardwareSounds


                        IFDEF PLY_CFG_UseHardwareSounds         ;CONFIG SPECIFIC
        ;Shared code used by the "hardware only" and "software and hardware" part.
        ;Reads the Retrig flag, the Hardware Envelope, the possible noise, the hardware period,
        ;and sets the volume to 16. The R7 sound channel is NOT modified.
PLY_AKM_PSES_Shared_ReadRetrigHardwareEnvPeriodNoise:
        ;Retrig?
        rra
                                IFDEF PLY_AKM_SE_Retrig                  ;CONFIG SPECIFIC
        jr nc,PLY_AKM_PSES_H_AfterRetrig
        ld d,a
        ld a,255
        ld (PLY_AKM_SetReg13Old + PLY_AKM_Offset1b),a
        ld a,d
PLY_AKM_PSES_H_AfterRetrig:
                                ENDIF ;PLY_AKM_SE_Retrig

        ;The hardware envelope can be set (8-15).
        and %111
        add a,8
        ld (PLY_AKM_SetReg13 + PLY_AKM_Offset1b),a

        ;Noise?
                                IFDEF PLY_AKM_SE_HardwareNoise           ;CONFIG SPECIFIC. B not needed after, we can put it in the condition too.
        rl b
        call PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel
                                ELSE
        set 5,c                                                 ;No noise in compilation, so stops the noise.
                                ENDIF ;PLY_AKM_SE_HardwareNoise

        ;Reads the hardware period.
        call PLY_AKM_PSES_ReadHardwarePeriod

        ;Sets the volume to "hardware". It still may be decreased.
        ld a,16
        jp PLY_AKM_PSES_ManageVolumeFromA_Hard
                        ENDIF ;PLY_CFG_UseHardwareSounds


                        IFDEF PLY_AKM_SE_Noise
;If asked to, reads the noise pointed by HL, increases HL, and opens the noise channel. If no noise, closes the noise channel.
;IN:    Carry = true if noise to read. False if no noise, so the noise channel must be closed.
;       HL = the data.
PLY_AKM_PSES_ReadNoiseIfNeededAndOpenOrCloseNoiseChannel:
        jr c,PLY_AKM_PSES_ReadNoiseAndOpenNoiseChannel_OpenNoise
        ;No noise, so closes the noise channel.
        set 5,c
        ret
PLY_AKM_PSES_ReadNoiseAndOpenNoiseChannel_OpenNoise:
        ;Reads the noise.
        ld a,(hl)
        ld (PLY_AKM_NoiseRegister),a
        inc hl

        ;Opens noise channel.
        res 5,c
        ret
                        ENDIF ;PLY_AKM_SE_Noise

                        IFDEF PLY_CFG_UseHardwareSounds         ;CONFIG SPECIFIC
;Reads the hardware period from HL and sets the R11/R12 registers. HL is incremented of 2.
PLY_AKM_PSES_ReadHardwarePeriod:
        ld a,(hl)
        ld (PLY_AKM_Reg11),a
        inc hl
        ld a,(hl)
        ld (PLY_AKM_Reg12),a
        inc hl
        ret
                        ENDIF ;PLY_CFG_UseHardwareSounds

;Reads the software period from HL and sets the period registers thanks to IY. HL is incremented of 2.
PLY_AKM_PSES_ReadSoftwarePeriod:
        ld a,(hl)
        ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodLSB),a
        inc hl
        ld a,(hl)
        ld (iy + PLY_AKM_Registers_OffsetSoftwarePeriodMSB),a
        inc hl
        ret

                        IFDEF PLY_AKM_SE_VolumeSoft      ;CONFIG SPECIFIC
;Reads the volume in A, decreases it from the inverted volume of the channel, and sets the volume via IY.
;IN:    A = volume, from 0 to 15 (no hardware envelope).
PLY_AKM_PSES_ManageVolumeFromA_Filter4Bits:
        and %1111
                        ENDIF ;PLY_AKM_SE_VolumeSoft
                        IFDEF PLY_AKM_SE_VolumeSoftOrHard        ;CONFIG SPECIFIC
;After the filtering. Useful for hardware sound (volume has been forced to 16).
PLY_AKM_PSES_ManageVolumeFromA_Hard:
        ;Decreases the volume, checks the limit.
        sub (ix + PLY_AKM_SoundEffectData_OffsetInvertedVolume)
        jr nc,PLY_AKM_PSES_MVFA_NoOverflow
        xor a
PLY_AKM_PSES_MVFA_NoOverflow:
        ld (iy + PLY_AKM_Registers_OffsetVolume),a
        ret
                        ENDIF ;PLY_AKM_SE_VolumeSoftOrHard


        ;The data for RAM player. For ROM player, it is declared in the main player.
        IFNDEF PLY_AKM_Rom
;The data of the Channels MUST be consecutive.
PLY_AKM_Channel1_SoundEffectData:
dkws (void):
        dw 0                                            ;Points to the sound effect for the track 1, or 0 if not playing.
PLY_AKM_Channel1_SoundEffectInvertedVolume:
dkwe (void):
dkbs (void):
        db 0                                            ;Inverted volume.
PLY_AKM_Channel1_SoundEffectCurrentStep:
        db 0                                            ;Current step (>=0).
PLY_AKM_Channel1_SoundEffectSpeed:
        db 0                                            ;Speed (>=0).
        ds 3,0                                          ;Padding.
dkbe (void):
PLY_AKM_Channel_SoundEffectDataSize: equ $ - PLY_AKM_Channel1_SoundEffectData
    
dkbs (void):    
PLY_AKM_Channel2_SoundEffectData:
        ds PLY_AKM_Channel_SoundEffectDataSize, 0
PLY_AKM_Channel3_SoundEffectData:
        ds PLY_AKM_Channel_SoundEffectDataSize, 0
dkbe (void):

        ;Checks that the pointers are consecutive.
        assert (PLY_AKM_Channel1_SoundEffectData + PLY_AKM_Channel_SoundEffectDataSize) == PLY_AKM_Channel2_SoundEffectData
        assert (PLY_AKM_Channel2_SoundEffectData + PLY_AKM_Channel_SoundEffectDataSize) == PLY_AKM_Channel3_SoundEffectData

        ENDIF ;PLY_AKM_Rom
        
;Offset from the beginning of the data, to reach the inverted volume.
PLY_AKM_SoundEffectData_OffsetInvertedVolume: equ PLY_AKM_Channel1_SoundEffectInvertedVolume - PLY_AKM_Channel1_SoundEffectData
PLY_AKM_SoundEffectData_OffsetCurrentStep: equ PLY_AKM_Channel1_SoundEffectCurrentStep - PLY_AKM_Channel1_SoundEffectData
PLY_AKM_SoundEffectData_OffsetSpeed: equ PLY_AKM_Channel1_SoundEffectSpeed - PLY_AKM_Channel1_SoundEffectData