Skip to content

Commit a6006b7

Browse files
committed
nullsound: rework exclusive ADPCM playback semantics
Update the ADPCM SFX playback functions: . snd_X_play now plays a ADPCM sample only if the current channel is not locked for uninterruptible playback. . snd_X_play_exclusive locks a channel to be uninterruptible, except if another snd_X_play_exclusive is run on the same channel.
1 parent efb28fa commit a6006b7

2 files changed

Lines changed: 98 additions & 33 deletions

File tree

nullsound/adpcm.s

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,18 @@
3737
state_adpcm_busy:
3838
.blkb 1
3939

40+
;;; ADPCM channels currently held by an exclusive play action
41+
;;; | B | __ | A6 | A5 | A4 | A3 | A2 | A1 |
42+
state_adpcm_exclusive:
43+
.blkb 1
44+
4045

4146
.area CODE
4247

4348
init_adpcm_state_tracker::
4449
ld a, #0
4550
ld (state_adpcm_busy), a
51+
ld (state_adpcm_exclusive), a
4652
ret
4753

4854
update_adpcm_state_tracker::
@@ -58,12 +64,15 @@ update_adpcm_state_tracker::
5864
ld b, #REG_ADPCM_PLAYBACK_MASK
5965
ld c, #0x00
6066
call ym2610_write_port_a
61-
;; a <- busy state clear mask
67+
;; a <- busy/exclusive states clear mask
6268
cpl
6369
ld b, a
6470
ld a, (state_adpcm_busy)
6571
and b
6672
ld (state_adpcm_busy), a
73+
ld a, (state_adpcm_exclusive)
74+
and b
75+
ld (state_adpcm_exclusive), a
6776
_adpcm_no_state_update:
6877
ret
6978

@@ -97,7 +106,7 @@ _adpcm_no_state_update:
97106
;;; ------
98107
;;; ix: sample play config
99108
;;; [a modified - other registers saved]
100-
snd_adpcm_a_play::
109+
snd_adpcm_a_play_common::
101110
push bc
102111
;; stop playback
103112
ld b, #REG_ADPCM_A_START_STOP
@@ -152,23 +161,44 @@ _snd_adpcm_a_end:
152161
ret
153162

154163

155-
;;; Play a ROM sample on a ADPCM-A channel only if it's not in use
164+
;;; Play a ROM sample on a ADPCM-A channel
165+
;;; If the channel is currently locked by an ongoing play,
166+
;;; no action is taken and the ongoing play continues.
156167
;;; ------
157168
;;; ix: sample play config
158169
;;; [a modified - other registers saved]
159-
snd_adpcm_a_play_exclusive::
170+
snd_adpcm_a_play::
160171
push bc
161-
ld a, (state_adpcm_busy)
172+
ld a, (state_adpcm_exclusive)
162173
ld b, A_CHANNEL_BIT(ix)
163174
and b
164175
jp nz, _snd_adpcm_a_busy
165176
;; play sample (will mark the channel as busy)
166-
call snd_adpcm_a_play
177+
call snd_adpcm_a_play_common
167178
_snd_adpcm_a_busy:
168179
pop bc
169180
ret
170181

171182

183+
;;; Play a ROM sample on a ADPCM-A channel
184+
;;; If the channel is currently locked by an ongoing play,
185+
;;; replace it and play the new sample while keeping this channel locked.
186+
;;; ------
187+
;;; ix: sample play config
188+
;;; [a modified - other registers saved]
189+
snd_adpcm_a_play_exclusive::
190+
push bc
191+
;; lock the channel
192+
ld a, (state_adpcm_exclusive)
193+
ld b, A_CHANNEL_BIT(ix)
194+
or b
195+
ld (state_adpcm_exclusive), a
196+
;; play sample (will mark the channel as busy)
197+
call snd_adpcm_a_play_common
198+
pop bc
199+
ret
200+
201+
172202
;;; Configure a ROM sample on the ADPCM-B channel for playback
173203
;;; ------
174204
;;; ix: sample play config
@@ -216,78 +246,99 @@ snd_adpcm_b_play_common::
216246
ld b, #REG_ADPCM_B_VOLUME
217247
ld c, B_VOLUME(ix)
218248
call ym2610_write_port_a
249+
;; mark the channel as busy
250+
;; playback will be started by the caller
251+
ld a, (state_adpcm_busy)
252+
ld c, #0x80
253+
or c
254+
ld (state_adpcm_busy), a
219255
ret
220256

221257

222258
;;; Play a ROM sample on the ADPCM-B channel
259+
;;; If the channel is currently locked by an ongoing play,
260+
;;; replace it and play the new sample while keeping this channel locked.
223261
;;; ------
224262
;;; ix: sample play config
225263
;;; [a modified - other registers saved]
226-
snd_adpcm_b_play::
264+
snd_adpcm_b_play_exclusive::
227265
push bc
228-
call snd_adpcm_b_play_common
266+
;; lock the channel
267+
ld a, (state_adpcm_exclusive)
268+
ld b, #0x80
269+
or b
270+
ld (state_adpcm_exclusive), a
229271
;; play channel
272+
call snd_adpcm_b_play_common
230273
ld b, #REG_ADPCM_B_START_STOP
231274
ld c, #0x80
232275
call ym2610_write_port_a
233-
;; mark the channel as busy
234-
ld a, (state_adpcm_busy)
235-
ld c, #0x80
236-
or c
237-
ld (state_adpcm_busy), a
238276
pop bc
239277
ret
240278

241279

242280
;;; Play and loop a ROM sample on the ADPCM-B channel
243-
;;; ------
281+
;;; If the channel is currently locked by an ongoing play,
282+
;;; replace it and play the new sample while keeping this channel locked.
283+
;; ------
244284
;;; ix: sample play config
245285
;;; [a modified - other registers saved]
246-
snd_adpcm_b_play_loop::
286+
snd_adpcm_b_play_loop_exclusive::
247287
push bc
248-
call snd_adpcm_b_play_common
288+
;; lock the channel
289+
ld a, (state_adpcm_exclusive)
290+
ld b, #0x80
291+
or b
292+
ld (state_adpcm_exclusive), a
249293
;; play channel
294+
call snd_adpcm_b_play_common
250295
ld b, #REG_ADPCM_B_START_STOP
251296
ld c, #0x90
252297
call ym2610_write_port_a
253-
;; mark the channel as busy
254-
ld a, (state_adpcm_busy)
255-
ld c, #0x80
256-
or c
257-
ld (state_adpcm_busy), a
258298
pop bc
259299
ret
260300

261301

262-
;;; Play a ROM sample on the ADPCM-B channel only if it's not in use
302+
;;; Play a ROM sample on the ADPCM-B channel
303+
;;; If the channel is currently locked by an ongoing play,
304+
;;; no action is taken and the ongoing play continues.
263305
;;; ------
264306
;;; ix: sample play config
265307
;;; [a modified - other registers saved]
266-
snd_adpcm_b_play_exclusive::
308+
snd_adpcm_b_play::
267309
push bc
268-
ld a, (state_adpcm_busy)
310+
ld a, (state_adpcm_exclusive)
269311
ld b, #0x80
270312
and b
271313
jp nz, _snd_adpcm_b_busy
272314
;; play sample (will mark the channel as busy)
273-
call snd_adpcm_b_play
315+
call snd_adpcm_b_play_common
316+
ld b, #REG_ADPCM_B_START_STOP
317+
ld c, #0x80
318+
call ym2610_write_port_a
274319
_snd_adpcm_b_busy:
275320
pop bc
276321
ret
277322

278323

279-
;;; Play and loop a ROM sample on the ADPCM-B channel only if it's not in use
324+
;;; Play and loop a ROM sample on the ADPCM-B channel
325+
;;; If the channel is currently locked by an ongoing play,
326+
;;; no action is taken and the ongoing play continues.
280327
;;; ------
281328
;;; ix: sample play config
282329
;;; [a modified - other registers saved]
283-
snd_adpcm_b_play_loop_exclusive::
330+
snd_adpcm_b_play_loop::
284331
push bc
285-
ld a, (state_adpcm_busy)
332+
ld a, (state_adpcm_exclusive)
286333
ld b, #0x80
287334
and b
288335
jp nz, _snd_adpcm_b_loop_busy
289336
;; play sample (will mark the channel as busy)
290-
call snd_adpcm_b_play_loop
337+
call snd_adpcm_b_play_common
338+
call snd_adpcm_b_play_common
339+
ld b, #REG_ADPCM_B_START_STOP
340+
ld c, #0x90
341+
call ym2610_write_port_a
291342
_snd_adpcm_b_loop_busy:
292343
pop bc
293344
ret

tools/soundtool.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import glob
2424
import os
2525
import wave
26+
from operator import itemgetter
2627

2728
# Special handling for YAML: try to import ruamel.yaml first,
2829
# because it's better at keeping comments in YAML files, and
@@ -131,8 +132,11 @@ def detect_adpcm_type(f):
131132
name=mkid(os.path.splitext(os.path.basename(f))[0])
132133
sfx = {'name': name, 'uri': uri}
133134
sfxdir=os.path.basename(os.path.dirname(f))
134-
if re.match(r'^[1-6]$',sfxdir):
135-
sfx['channel']=int(sfxdir)
135+
m = re.match(r'^([1-6])(_x)?$',sfxdir)
136+
if m:
137+
sfx['channel']=int(m[1])
138+
if m[2]:
139+
sfx['exclusive']=True
136140
read_data.append([name, {sfxtype: sfx}])
137141

138142
result_data = []
@@ -143,9 +147,15 @@ def detect_adpcm_type(f):
143147
else:
144148
result_data.append(data)
145149

150+
# sort result_data to get a consistent output on update
151+
result_keys = [next(iter(x.keys())) for x in result_data]
152+
decorated_data = [((type, data[type]['channel'], data[type]['name']), data) for type, data in zip(result_keys, result_data)]
153+
decorated_data.sort(key=itemgetter(0))
154+
sorted_data = [data for key, data in decorated_data]
155+
146156
# save sound map description
147157
print(header, end='', file=output)
148-
print(yaml_dump(result_data, end='', f=output))
158+
yaml_dump(sorted_data, end='', f=output)
149159

150160

151161

@@ -234,10 +244,14 @@ def print_music_cmd(stype, data, bank):
234244
def print_sfx_cmd(stype, data, bank):
235245
name=data['name']
236246
channel=data.get('channel', 6)-1
247+
snd_func_name='snd_%s_play'%stype
248+
exclusive=data.get('exclusive', False)
249+
if exclusive:
250+
snd_func_name+='_exclusive'
237251
ucname=name.upper()
238252
print('sfx_%s::'%name, file=f)
239253
print(' ld ix, #sfx_%s_data'%name, file=f)
240-
print(' call snd_%s_play'%stype, file=f)
254+
print(' call %s'%snd_func_name, file=f)
241255
print(' ret', file=f)
242256
print('sfx_%s_data:'%name, file=f)
243257
print(' .db %s_START_LSB, %s_START_MSB ; start>>8 in VROM'%(ucname,ucname), file=f)

0 commit comments

Comments
 (0)