Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1845 yogev_ezra 1
; UFMOD.ASM
2
; ---------
3
; uFMOD public source code release. Provided as-is.
4
 
5
SOUND_VERSION     equ 100h ; required Infinity sound driver version
6
FSOUND_Block      equ 10
7
FSOUND_BlockSize  equ 1024 ; 1 << FSOUND_Block
8
 
9
if DEBUG
10
 
11
; Debug messages:
12
sDBGMSG1 db "uFMOD: XM track loaded",13,10,0
13
sDBGMSG2 db "uFMOD: Infinity driver loaded",13,10,0
14
sDBGMSG3 db "uFMOD: Buffer created",13,10,0
15
sDBGMSG4 db "uFMOD: Sound buffer destroyed",13,10,0
16
sDBGMSG5 db "uFMOD: Infinity version: ",0
17
 
18
; DEBUG board: print a string.
19
DBG_print_s:
20
; EDX = msg (trailing 0 is required!)
21
	pushad
22
DBG_print_s_loop:
23
	mov eax,63
24
	mov ebx,1
25
	mov cl,[edx]
26
	test cl,cl
27
	jz DBG_print_s_R
28
	int 40h
29
	inc edx
30
	jmp DBG_print_s_loop
31
DBG_print_s_R:
32
	popad
33
	ret
34
 
35
; DEBUG board: print the hex value in EAX.
36
DBG_print_x:
37
; EAX = val
38
	pushad
39
	mov esi,eax
40
	mov edx,OFFSET MixBuf
41
	mov ecx,7
42
	mov DWORD PTR [edx+8],0A0Dh
43
print_eax_loop:
44
	mov eax,esi
45
	and al,0Fh
46
	cmp al,10
47
	sbb al,69h
48
	das
49
	mov [edx+ecx],al
50
	shr esi,4
51
	dec ecx
52
	jns print_eax_loop
53
	call DBG_print_s
54
	popad
55
	ret
56
endif ; DEBUG
57
 
58
if RAMP_STRONG
59
	volumerampsteps   equ 128
60
	volumeramps_pow   equ 7
61
endif
62
 
63
if RAMP_WEAK
64
	volumerampsteps   equ 16
65
	volumeramps_pow   equ 4
66
endif
67
 
68
if RAMP_NONE
69
	volumerampsteps   equ 64
70
	volumeramps_pow   equ 6
71
endif
72
 
73
XM_MEMORY                 equ 1
74
XM_FILE                   equ 2
75
XM_NOLOOP                 equ 8
76
XM_SUSPENDED              equ 16
77
FMUSIC_ENVELOPE_SUSTAIN   equ 2
78
FMUSIC_ENVELOPE_LOOP      equ 4
79
FMUSIC_FREQ               equ 1
80
FMUSIC_VOLUME             equ 2
81
FMUSIC_PAN                equ 4
82
FMUSIC_TRIGGER            equ 8
83
FMUSIC_VOLUME_OR_FREQ     equ 3
84
FMUSIC_VOLUME_OR_PAN      equ 6
85
FMUSIC_VOL_OR_FREQ_OR_TR  equ 11
86
FMUSIC_FREQ_OR_TRIGGER    equ 9
87
NOT_FMUSIC_TRIGGER        equ 0F7h
88
NOT_FMUSIC_TRIGGER_OR_FRQ equ 0F6h
89
 
90
; FMUSIC_XMCOMMANDS enum:
91
FMUSIC_XM_PORTAUP         equ 1
92
FMUSIC_XM_PORTADOWN       equ 2
93
FMUSIC_XM_PORTATO         equ 3
94
FMUSIC_XM_VIBRATO         equ 4
95
FMUSIC_XM_PORTATOVOLSLIDE equ 5
96
FMUSIC_XM_VIBRATOVOLSLIDE equ 6
97
FMUSIC_XM_TREMOLO         equ 7
98
FMUSIC_XM_SETPANPOSITION  equ 8
99
FMUSIC_XM_SETSAMPLEOFFSET equ 9
100
FMUSIC_XM_VOLUMESLIDE     equ 10
101
FMUSIC_XM_PATTERNJUMP     equ 11
102
FMUSIC_XM_SETVOLUME       equ 12
103
FMUSIC_XM_PATTERNBREAK    equ 13
104
FMUSIC_XM_SPECIAL         equ 14
105
FMUSIC_XM_SETSPEED        equ 15
106
FMUSIC_XM_SETGLOBALVOLUME equ 16
107
FMUSIC_XM_GLOBALVOLSLIDE  equ 17
108
FMUSIC_XM_KEYOFF          equ 20
109
FMUSIC_XM_PANSLIDE        equ 25
110
FMUSIC_XM_MULTIRETRIG     equ 27
111
FMUSIC_XM_TREMOR          equ 29
112
FMUSIC_XM_EXTRAFINEPORTA  equ 33
113
 
114
; FMUSIC_XMCOMMANDSSPECIAL enum:
115
FMUSIC_XM_FINEPORTAUP      equ 1
116
FMUSIC_XM_FINEPORTADOWN    equ 2
117
FMUSIC_XM_SETGLISSANDO     equ 3
118
FMUSIC_XM_SETVIBRATOWAVE   equ 4
119
FMUSIC_XM_SETFINETUNE      equ 5
120
FMUSIC_XM_PATTERNLOOP      equ 6
121
FMUSIC_XM_SETTREMOLOWAVE   equ 7
122
FMUSIC_XM_SETPANPOSITION16 equ 8
123
FMUSIC_XM_RETRIG           equ 9
124
FMUSIC_XM_NOTECUT          equ 12
125
FMUSIC_XM_NOTEDELAY        equ 13
126
FMUSIC_XM_PATTERNDELAY     equ 14
127
 
128
if AC97SND_ON
129
 
130
	file_read:
131
	; buf  in EAX
132
	; size in EDX
133
		push ebx
134
		push esi
135
		push edi
136
		push ebp
137
		xchg eax,edi
138
	file_read_begin:
139
		test edx,edx
140
		jg file_read_chk_cache
141
	file_read_done:
142
		pop ebp
143
		pop edi
144
		pop esi
145
		pop ebx
146
		ret
147
		; *** CHECK IN THE CACHE
148
	file_read_chk_cache:
149
		mov ebp,OFFSET file_struct
150
		mov esi,[ebp-24]
151
		sub esi,[ebp+28] ; cache_offset
152
		js file_read_cache_done
153
		mov ecx,8192
154
		sub ecx,esi
155
		jle file_read_cache_done
156
		add esi,OFFSET MixBuf
157
		sub edx,ecx
158
		jns file_read_do_cache
159
		add ecx,edx
160
	file_read_do_cache:
161
		add [ebp-24],ecx
162
		rep movsb
163
		test edx,edx
164
		jle file_read_done ; data read from the cache (no need to access the FS)
165
	file_read_cache_done:
166
		; *** FS BATCH READ OPERATION
167
		mov eax,[ebp-24]
168
		mov ecx,edx
169
		add ecx,eax
170
		and ecx,0FFFFE000h
171
		sub ecx,eax
172
		jle file_read_fs_done ; Too few data requested for a FS batch operation
173
		sub edx,ecx
174
		mov [ebp+4],eax  ; file offset
175
		mov [ebp+12],ecx ; NumberOfBytesToRead
176
		mov [ebp+16],edi ; lpBuffer
177
		mov ebx,ebp
178
		add edi,ecx
179
		push 70
180
		add [ebp-24],ecx
181
		pop eax
182
		int 40h
183
	file_read_fs_done:
184
		; *** UPDATE THE CACHE
185
		mov ecx,[ebp-24]
186
		and ecx,0FFFFE000h
187
		mov [ebp+4],ecx           ; file offset
188
		mov [ebp+28],ecx          ; cache_offset
189
		mov DWORD PTR [ebp+12],8192 ; NumberOfBytesToRead
190
		mov DWORD PTR [ebp+16],OFFSET MixBuf ; lpBuffer
191
		mov ebx,ebp
192
		push 70
193
		pop eax
194
		int 40h
195
		jmp file_read_begin
196
 
197
	if INFO_API_ON
198
		PUBLIC _uFMOD_GetTitle
199
		_uFMOD_GetTitle:
200
			mov eax,OFFSET szTtl
201
			ret
202
	endif
203
 
204
else
205
		uFMOD_mem dd mem_open, mem_read
206
	if XM_FILE_ON
207
		uFMOD_fs  dd file_open, file_read
208
	endif
209
 
210
	szInfinity db "INFINITY",0
211
 
212
	if JUMP_TO_PAT_ON
213
 
214
		; Jump to the given pattern
215
	PUBLIC _uFMOD_Jump2Pattern
216
	_uFMOD_Jump2Pattern:
217
		mov eax,[esp+4]
218
		mov ecx,OFFSET _mod+36
219
		movzx eax,ax
220
		and DWORD PTR [ecx+FMUSIC_MODULE.nextrow-36],0
221
		cmp ax,[ecx+FMUSIC_MODULE.numorders-36]
222
		sbb edx,edx
223
		and eax,edx
224
		mov [ecx+FMUSIC_MODULE.nextorder-36],eax
225
		ret
226
	endif
227
 
228
	if VOL_CONTROL_ON
229
 
230
		; Set global volume [0: silence, 25: max. volume]
231
		vol_scale dw 1     ; -45 dB
232
	        	  dw 130   ; -24
233
			  dw 164   ; -23
234
		          dw 207   ; -22
235
		          dw 260   ; -21
236
		          dw 328   ; -20
237
	        	  dw 413   ; -19
238
		          dw 519   ; -18
239
		          dw 654   ; -17
240
	        	  dw 823   ; -16
241
		          dw 1036  ; -15
242
		          dw 1305  ; -14
243
	        	  dw 1642  ; -13
244
		          dw 2068  ; -12
245
		          dw 2603  ; -11
246
	        	  dw 3277  ; -10
247
		          dw 4125  ; -9
248
		          dw 5193  ; -8
249
		          dw 6538  ; -7
250
		          dw 8231  ; -6
251
	        	  dw 10362 ; -5
252
		          dw 13045 ; -4
253
		          dw 16423 ; -3
254
	        	  dw 20675 ; -2
255
		          dw 26029 ; -1
256
		          dw 32768 ; 0 dB
257
		PUBLIC _uFMOD_SetVolume
258
		_uFMOD_SetVolume:
259
			mov eax,[esp+4]
260
			cmp eax,25
261
			jna get_vol_scale
262
			push 25
263
			pop eax
264
		get_vol_scale:
265
			mov ax,[vol_scale+eax*2]
266
			mov [ufmod_vol],eax
267
			ret
268
	endif
269
 
270
	if PAUSE_RESUME_ON
271
 
272
			; Pause the currently playing song.
273
		PUBLIC _uFMOD_Pause
274
		_uFMOD_Pause:
275
			mov al,1
276
			jmp $+4
277
 
278
			; Resume the currently paused song.
279
		PUBLIC _uFMOD_Resume
280
		_uFMOD_Resume:
281
			xor eax,eax
282
			mov BYTE PTR [ufmod_pause_],al
283
			ret
284
	endif
285
 
286
	if INFO_API_ON
287
 
288
		; Return currently playing signal stats:
289
		;    lo word : RMS volume in R channel
290
		;    hi word : RMS volume in L channel
291
		PUBLIC _uFMOD_GetStats
292
		_uFMOD_GetStats:
293
			push 4
294
			jmp _uFMOD_InfoApi
295
 
296
		; Return currently playing row and order:
297
		;    lo word : row
298
		;    hi word : order
299
		PUBLIC _uFMOD_GetRowOrder
300
		_uFMOD_GetRowOrder:
301
			push 8
302
			jmp _uFMOD_InfoApi
303
 
304
		; Return the time in milliseconds since the song was started.
305
		PUBLIC _uFMOD_GetTime
306
		_uFMOD_GetTime:
307
			push 0
308
		_uFMOD_InfoApi:
309
			pop edx
310
			mov eax,[time_ms+edx]
311
			ret
312
	endif
313
 
314
	; ***********************
315
	; * XM_MEMORY CALLBACKS *
316
	; ***********************
317
	mem_read:
318
	; buf in EAX
319
	; size in EDX
320
		push edi
321
		push esi
322
		xchg eax,edi ; buf
323
		mov esi,OFFSET mmf
324
		lodsd
325
		mov ecx,edx
326
		add edx,[esi]
327
		cmp edx,eax
328
		jl copy
329
		sub eax,[esi]
330
		xchg eax,ecx
331
	copy:
332
		test ecx,ecx
333
		jle mem_read_R
334
		lodsd
335
		add eax,[esi]
336
		mov [esi-4],edx
337
	mem_do_copy:
338
		mov dl,[eax]
339
		mov [edi],dl
340
		inc eax
341
		inc edi
342
		dec ecx
343
		jnz mem_do_copy
344
	mem_read_R:
345
		pop esi
346
		pop edi
347
	if INFO_API_ON
348
		PUBLIC _uFMOD_GetTitle
349
		_uFMOD_GetTitle:
350
			mov eax,OFFSET szTtl
351
	endif
352
	mem_open:
353
		ret
354
 
355
	; *********************
356
	; * XM_FILE CALLBACKS *
357
	; *********************
358
	if XM_FILE_ON
359
		file_open:
360
		; pszName in ESI
361
			; Prepare the FILE struct for subsecuent I/O:
362
			lea eax,[ebp+8]  ; file_struct
363
			xor edx,edx
364
			mov [eax],edx    ;  +0 subfunction: 0 = read
365
			mov [eax+8],edx  ;  +8 Reserved
366
			                 ; +12 NumberOfBytesToRead
367
			                 ; +16 lpBuffer
368
			push -1
369
			push 1
370
			mov [eax+20],dl  ; +20 db 0
371
			mov [eax+21],esi ; +21 lpFileName
372
			pop DWORD PTR [eax+28] ; cache_offset
373
			pop DWORD PTR [eax-28] ; [mmf] = maximum size
374
			ret
375
 
376
		file_read:
377
		; buf  in EAX
378
		; size in EDX
379
			push ebx
380
			push esi
381
			push edi
382
			push ebp
383
			xchg eax,edi
384
		file_read_begin:
385
			test edx,edx
386
			jg file_read_chk_cache
387
		file_read_done:
388
			pop ebp
389
			pop edi
390
			pop esi
391
			pop ebx
392
			ret
393
			; *** CHECK IN THE CACHE
394
		file_read_chk_cache:
395
			mov ebp,OFFSET file_struct
396
			mov esi,[ebp-24]
397
			sub esi,[ebp+28] ; cache_offset
398
			js file_read_cache_done
399
			mov ecx,8192
400
			sub ecx,esi
401
			jle file_read_cache_done
402
			add esi,OFFSET MixBuf
403
			sub edx,ecx
404
			jns file_read_do_cache
405
			add ecx,edx
406
		file_read_do_cache:
407
			add [ebp-24],ecx
408
			rep movsb
409
			test edx,edx
410
			jle file_read_done ; data read from the cache (no need to access the FS)
411
		file_read_cache_done:
412
			; *** FS BATCH READ OPERATION
413
			mov eax,[ebp-24]
414
			mov ecx,edx
415
			add ecx,eax
416
			and ecx,0FFFFE000h
417
			sub ecx,eax
418
			jle file_read_fs_done ; Too few data requested for a FS batch operation
419
			sub edx,ecx
420
			mov [ebp+4],eax  ; file offset
421
			mov [ebp+12],ecx ; NumberOfBytesToRead
422
			mov [ebp+16],edi ; lpBuffer
423
			mov ebx,ebp
424
			add edi,ecx
425
			push 70
426
			add [ebp-24],ecx
427
			pop eax
428
			int 40h
429
		file_read_fs_done:
430
			; *** UPDATE THE CACHE
431
			mov ecx,[ebp-24]
432
			and ecx,0FFFFE000h
433
			mov [ebp+4],ecx           ; file offset
434
			mov [ebp+28],ecx          ; cache_offset
435
			mov DWORD PTR [ebp+12],8192 ; NumberOfBytesToRead
436
			mov DWORD PTR [ebp+16],OFFSET MixBuf ; lpBuffer
437
			mov ebx,ebp
438
			push 70
439
			pop eax
440
			int 40h
441
			jmp file_read_begin
442
	endif
443
 
444
endif ; AC97SND_ON = 0
445
 
446
uFMOD_lseek:
447
; pos  in EAX
448
; org  in ECX
449
; !org in Z
450
	mov edx,OFFSET mmf+4
451
	jz mem_ok
452
	add eax,[edx]
453
mem_ok:
454
	test eax,eax
455
	js mem_seek_R
456
	cmp eax,[edx-4]
457
	ja mem_seek_R
458
	mov [edx],eax
459
mem_seek_R:
460
	ret
461
 
462
; Dynamic memory allocation
463
alloc:
464
; EAX: how many bytes to allocate
465
	add eax,3
466
	and eax,-4
467
	jle alloc_error2
468
	mov ecx,OFFSET ufmod_heap
469
alloc_lookup:
470
	cmp DWORD PTR [ecx],0
471
	je do_alloc
472
	mov ecx,[ecx]
473
	cmp [ecx+4],eax
474
	jl alloc_lookup
475
	sub [ecx+4],eax
476
	mov eax,[ecx+4]
477
	lea eax,[eax+ecx+8]
478
	ret
479
do_alloc:
480
	add eax,8
481
	push ebx
482
	push edi
483
	mov ebx,eax
484
	add ebx,8191
485
	neg eax
486
	and ebx,-8192
487
	push ecx
488
	add eax,ebx
489
	xchg eax,edi
490
	push 12
491
	push 68
492
	mov ecx,ebx
493
	pop eax
494
	pop ebx
495
	int 40h
496
	; Test for error condition
497
	test eax,eax
498
	pop ebx
499
	mov edx,edi ; free space
500
	jz alloc_error1
501
	mov [ebx],eax
502
	mov edi,eax
503
	lea eax,[eax+edx+8]
504
	mov [edi+4],edx
505
	pop edi
506
	pop ebx
507
	ret
508
alloc_error1:
509
	pop edi
510
	pop ebx
511
alloc_error2:
512
	xor eax,eax
513
	pop edx ; EIP
514
	pop ebx
515
	leave
516
_alloc_R:
517
	ret
518
 
519
; Starts playing a song.
520
PUBLIC _uFMOD_LoadSong
521
_uFMOD_LoadSong:
522
 
523
	; *** FREE PREVIOUS TRACK, IF ANY
524
	call _uFMOD_StopSong
525
 
526
	if AC97SND_ON
527
		mov ecx,[esp+4]
528
		push ebx
529
		push esi
530
		push edi
531
		push ebp
532
		mov ebp,OFFSET uFMOD_fopen
533
		; Prepare the FILE struct for subsecuent I/O:
534
		lea eax,[ebp+8]  ; file_struct
535
		xor edx,edx
536
		mov [eax],edx    ;  +0 subfunction: 0 = read
537
		mov [eax+8],edx  ;  +8 Reserved
538
		                 ; +12 NumberOfBytesToRead
539
		                 ; +16 lpBuffer
540
		mov [eax+20],dl  ; +20 db 0
541
		mov [eax+21],ecx ; +21 lpFileName
542
		push -1
543
		push 1
544
		mov DWORD PTR [ebp+4],OFFSET file_read ; uFMOD_fread
545
		pop DWORD PTR [eax+28]          ; cache_offset
546
		mov [eax-24],edx
547
		pop DWORD PTR [eax-28]          ; [mmf] = maximum size
548
	else
549
		mov eax,[esp+8]  ; param
550
		mov ecx,[esp+12] ; fdwSong
551
		mov edx,[esp+4]  ; lpXM
552
		test edx,edx
553
		jz _alloc_R
554
		; *** SET I/O CALLBACKS
555
		push ebx
556
		push esi
557
		push edi
558
		push ebp
559
		mov ebp,OFFSET uFMOD_fopen
560
		mov [ebp-20],eax ; mmf
561
		xor eax,eax
562
		mov [ebp-16],eax ; mmf+4
563
		test cl,XM_MEMORY
564
		mov esi,OFFSET uFMOD_mem
565
	if XM_FILE_ON
566
		jnz set_callbacks
567
		test cl,XM_FILE
568
		lea esi,[esi+(uFMOD_fs-uFMOD_mem)]
569
	endif
570
		jz goto_StopSong
571
	set_callbacks:
572
	if NOLOOP_ON
573
		test cl,XM_NOLOOP
574
		setnz [ebp-24] ; ufmod_noloop
575
	endif
576
	if PAUSE_RESUME_ON
577
		and cl,XM_SUSPENDED
578
		mov [ebp-23],cl ; ufmod_pause_
579
	endif
580
		mov edi,ebp ; uFMOD_fopen
581
		movsd
582
		movsd
583
		mov esi,edx ; uFMOD_fopen:lpXM <= ESI
584
	if VOL_CONTROL_ON
585
		cmp [ebp-4],eax ; ufmod_vol
586
		jne play_vol_ok
587
		mov WORD PTR [ebp-4],32768
588
	play_vol_ok:
589
	endif
590
		xor edi,edi
591
		; *** INIT THE INFINITY DRIVER
592
		lea eax,[edi+68]
593
		lea ebx,[edi+16]
594
		mov ecx,OFFSET szInfinity
595
		int 40h
596
		test eax,eax
597
		mov [hSound],eax
598
		jz goto_StopSong
599
	if DEBUG
600
		mov edx,OFFSET sDBGMSG2
601
		call DBG_print_s
602
	endif
603
		; *** CHECK THE DRIVER VERSION
604
		push edi ; ver = 0
605
		push esp ; &ver
606
		mov edx,esp
607
		push 4   ; .out_size
608
		push edx ; .output = &&ver
609
		push edi ; .inp_size
610
		push edi ; .input
611
		push edi ; .code   = SRV_GETVERSION
612
		push eax ; .handle = [hSound]
613
		lea ebx,[edi+17]
614
		lea eax,[edi+68]
615
		mov ecx,esp
616
		int 40h
617
		add esp,28
618
		pop eax ; ver
619
	if DEBUG
620
		mov edx,OFFSET sDBGMSG5
621
		call DBG_print_s
622
		call DBG_print_x
623
	endif
624
		shr eax,16
625
		cmp eax,SOUND_VERSION
626
		ja _uFMOD_StopSong+4 ; obsolete program version (Hint: try adjusting SOUND_VERSION!)
627
		; *** ALLOCATE A HEAP OBJECT
628
		lea eax,[edi+68]
629
		lea ebx,[edi+11]
630
		int 40h
631
		test eax,eax
632
		jz goto_StopSong
633
		; *** LOAD THE TRACK
634
		mov [ebp-12],esi ; mmf+8 <= pMem
635
	if XM_FILE_ON
636
		call DWORD PTR [ebp] ; uFMOD_fopen
637
	endif
638
 
639
	endif ; AC97SND_ON = 0
640
 
641
	call LoadXM
642
	test eax,eax
643
goto_StopSong:
644
	jz _uFMOD_StopSong+4
645
if DEBUG
646
	mov edx,OFFSET sDBGMSG1
647
	call DBG_print_s
648
endif
649
 
650
	if AC97SND_ON
651
	else
652
		xor edi,edi
653
		; *** CREATE THE PCM BUFFER
654
		push edi        ; size (default is 64Kb)
655
		push PCM_format ; format: 16-bit / stereo / sampling rate
656
		mov edx,esp
657
		push OFFSET hBuff
658
		mov eax,esp
659
		push 4              ; .out_size
660
		push eax            ; .output = &&hBuff
661
		push 8              ; .inp_size
662
		push edx            ; .input
663
		push 1              ; .code   = SND_CREATE_BUFF
664
		push DWORD PTR [hSound] ; .handle
665
		lea eax,[edi+68]
666
		lea ebx,[edi+17]
667
		mov ecx,esp
668
		int 40h
669
		pop esi ; <- hSound
670
		add esp,32
671
		test eax,eax
672
		jnz _uFMOD_StopSong+4 ; buffer not created
673
	if DEBUG
674
		mov edx,OFFSET sDBGMSG3
675
		call DBG_print_s
676
	endif
677
		xchg eax,esi ; return the driver handle
678
	endif ; AC97SND_ON = 0
679
 
680
	; *** ENABLE PCM OUTPUT
681
	mov [SW_Exit],eax
682
	pop ebp
683
	pop edi
684
	pop esi
685
	pop ebx
686
	ret
687
 
688
; Stop the currently playing song, if any, and free all resources allocated for that song.
689
PUBLIC _uFMOD_StopSong
690
_uFMOD_StopSong:
691
	push ebx
692
	push esi
693
	push edi
694
	push ebp
695
; _uFMOD_StopSong+4
696
	xor edi,edi
697
	mov ebp,OFFSET ufmod_heap
698
	; *** DISABLE PCM OUTPUT
699
	mov [ebp+16],edi ; SW_Exit
700
 
701
	if AC97SND_ON
702
	else
703
		; *** STOP AND DESTROY THE PCM BUFFER
704
		mov eax,[ebp+12] ; hBuff
705
		test eax,eax
706
		jz SND_buffer_free
707
		push eax             ; buffer
708
		mov edx,esp
709
		push edi             ; .out_size
710
		push edi             ; .output
711
		push 4               ; .inp_size
712
		push edx             ; .input
713
		push 11              ; .code   = SND_STOP
714
		push DWORD PTR [ebp+8]   ; .handle = [hSound]
715
		lea eax,[edi+68]
716
		lea ebx,[edi+17]
717
		mov ecx,esp
718
		int 40h
719
		mov DWORD PTR [esp+4],2  ; .code = SND_DESTROY_BUFF
720
		lea eax,[edi+68]
721
		int 40h
722
		add esp,28
723
	if DEBUG
724
		mov edx,OFFSET sDBGMSG4
725
		call DBG_print_s
726
	endif
727
	SND_buffer_free:
728
		mov [ebp+12],edi ; hBuff
729
	endif ; AC97SND_ON = 0
730
 
731
	; *** FREE THE HEAP
732
	mov esi,[ebp]    ; ufmod_heap
733
heapfree:
734
	test esi,esi
735
	jz free_R
736
	mov ecx,esi
737
	mov esi,[esi]
738
	lea eax,[edi+68]
739
	lea ebx,[edi+13]
740
	int 40h
741
	jmp heapfree
742
free_R:
743
	xor eax,eax
744
 
745
	if AC97SND_ON
746
	else
747
		if INFO_API_ON
748
			; *** CLEAR THE INFO API DATA
749
			lea ecx,[eax+4]
750
			mov edi,OFFSET time_ms
751
			rep stosd
752
		endif
753
	endif ; AC97SND_ON = 0
754
 
755
	mov DWORD PTR [ebp],eax ; ufmod_heap
756
	pop ebp
757
	pop edi
758
	pop esi
759
	pop ebx
760
	ret
761
 
762
PUBLIC _uFMOD_WaveOut
763
_uFMOD_WaveOut:
764
	push edi
765
	push ebp
766
	xor edi,edi
767
 
768
	if AC97SND_ON
769
		; *** PCM OUTPUT ENABLED?
770
		cmp DWORD PTR [SW_Exit],edi
771
		lea eax,[edi+1] ; return error if output not enabled
772
		je _uFMOD_WaveOut_R
773
		; *** COMPUTE THE NUMBER OF FREE BLOCKS AVAILABLE
774
		lea ecx,[esp+12]    ; &hBuff
775
		push edi            ; space = 0
776
		mov edx,esp
777
		push 4              ; .out_size
778
		push edx            ; .output = &space
779
		push 4              ; .inp_size
780
		push ecx            ; .input
781
		push 17             ; .code   = SND_GETFREESPACE
782
		mov ecx,[ecx]
783
		push DWORD PTR [hSound] ; .handle
784
		mov [hBuff],ecx
785
	else
786
		mov ebp,OFFSET hSound
787
		; *** PCM OUTPUT ENABLED?
788
		cmp [ebp+8],edi ; SW_Exit
789
		lea eax,[edi+1] ; return error if output not enabled
790
		je _uFMOD_WaveOut_R
791
		; *** COMPUTE THE NUMBER OF FREE BLOCKS AVAILABLE
792
		push edi         ; space = 0
793
		mov edx,esp
794
		push 4           ; .out_size
795
		lea ecx,[ebp+4]  ; &hBuff
796
		push edx         ; .output = &space
797
		push 4           ; .inp_size
798
		push ecx         ; .input
799
		push 17          ; .code   = SND_GETFREESPACE
800
		push DWORD PTR [ebp] ; .handle = [hSound]
801
	endif ; AC97SND_ON = 0
802
 
803
	lea ebx,[edi+17]
804
	lea eax,[edi+68]
805
	mov ecx,esp
806
	int 40h
807
	add esp,24
808
	pop edi ; <- space
809
	shr edi,FSOUND_Block+2  ; / (FSOUND_BlockSize * 4)
810
	jz _uFMOD_WaveOut_R     ; no free blocks available
811
_uFMOD_WaveOut_loop:
812
	call uFMOD_do_WaveOut
813
	neg eax
814
	dec edi
815
	ja _uFMOD_WaveOut_loop
816
_uFMOD_WaveOut_R:
817
	pop ebp
818
	pop edi
819
	ret
820
 
821
uFMOD_do_WaveOut:
822
	mov ecx,FSOUND_BlockSize*2
823
	push ebx
824
	push esi
825
	push edi
826
	mov edi,OFFSET MixBuf
827
	xor eax,eax
828
	push edi ; mixbuffer <= MixBuf
829
	push edi ; <- MixPtr
830
	; MIXBUFFER CLEAR
831
	mov esi,OFFSET _mod+36
832
	rep stosd
833
 
834
	if AC97SND_ON
835
	else
836
		if PAUSE_RESUME_ON
837
			cmp [ufmod_pause_],al
838
			xchg eax,ebp
839
			jne do_swfill
840
		endif
841
	endif ; AC97SND_ON = 0
842
 
843
	mov ebp,FSOUND_BlockSize
844
	; UPDATE MUSIC
845
	mov ebx,[esi+FMUSIC_MODULE.mixer_samplesleft-36]
846
fill_loop_1:
847
	test ebx,ebx
848
	jnz mixedleft_nz
849
	; UPDATE XM EFFECTS
850
	cmp [esi+FMUSIC_MODULE.tick-36],ebx ; new note
851
	mov ecx,[esi+FMUSIC_MODULE.pattern-36]
852
	jne update_effects
853
	dec ebx
854
	; process any rows commands to set the next order/row
855
	mov edx,[esi+FMUSIC_MODULE.nextorder-36]
856
	mov eax,[esi+FMUSIC_MODULE.nextrow-36]
857
	mov [esi+FMUSIC_MODULE.nextorder-36],ebx
858
	test edx,edx
859
	mov [esi+FMUSIC_MODULE.nextrow-36],ebx
860
	jl fill_nextrow
861
	mov [esi+FMUSIC_MODULE.order-36],edx
862
fill_nextrow:
863
	test eax,eax
864
	jl update_note
865
	mov [esi+FMUSIC_MODULE.row-36],eax
866
update_note:
867
	; mod+36 : ESI
868
	call DoNote
869
if ROWCOMMANDS_ON
870
	cmp DWORD PTR [esi+FMUSIC_MODULE.nextrow-36],-1
871
	jne inc_tick
872
endif
873
	mov eax,[esi+FMUSIC_MODULE.row-36]
874
	inc eax
875
	; if end of pattern
876
	; "if(mod->nextrow >= mod->pattern[mod->orderlist[mod->order]].rows)"
877
	cmp ax,[ebx]
878
	jl set_nextrow
879
	mov edx,[esi+FMUSIC_MODULE.order-36]
880
	movzx ecx,WORD PTR [esi+FMUSIC_MODULE.numorders-36]
881
	inc edx
882
	xor eax,eax
883
	cmp edx,ecx
884
	jl set_nextorder
885
 
886
	if AC97SND_ON
887
	else
888
		; We've reached the end of the order list. So, stop playing, unless looping is enabled.
889
		if NOLOOP_ON
890
			cmp [ufmod_noloop],al
891
			je set_restart
892
			pop eax ; skip MixPtr
893
			pop edx ; skip mixbuffer
894
			pop edi
895
			inc eax ; end of loop reached while XM_NOLOOP flag is enabled
896
			pop esi
897
			pop ebx
898
			ret
899
		set_restart:
900
		endif
901
	endif ; AC97SND_ON = 0
902
 
903
	movzx edx,WORD PTR [esi+FMUSIC_MODULE.restart-36]
904
	cmp edx,ecx
905
	sbb ecx,ecx
906
	and edx,ecx
907
set_nextorder:
908
	mov [esi+FMUSIC_MODULE.nextorder-36],edx
909
set_nextrow:
910
	mov [esi+FMUSIC_MODULE.nextrow-36],eax
911
	jmp inc_tick
912
update_effects:
913
	; mod+36 : ESI
914
	call DoEffs
915
inc_tick:
916
	mov eax,[esi+FMUSIC_MODULE.speed-36]
917
	mov ebx,[esi+FMUSIC_MODULE.mixer_samplespertick-36]
918
	inc DWORD PTR [esi+FMUSIC_MODULE.tick-36]
919
if PATTERNDELAY_ON
920
	add eax,[esi+FMUSIC_MODULE.patterndelay-36]
921
endif
922
	cmp [esi+FMUSIC_MODULE.tick-36],eax
923
	jl mixedleft_nz
924
if PATTERNDELAY_ON
925
	and DWORD PTR [esi+FMUSIC_MODULE.patterndelay-36],0
926
endif
927
	and DWORD PTR [esi+FMUSIC_MODULE.tick-36],0
928
mixedleft_nz:
929
	mov edi,ebp
930
	cmp ebx,edi
931
	jae fill_ramp
932
	mov edi,ebx
933
fill_ramp:
934
	pop edx  ; <- MixPtr
935
	sub ebp,edi
936
	lea eax,[edx+edi*8]
937
	push eax ; MixPtr += (SamplesToMix<<3)
938
	; tail    : [arg0]
939
	; len     : EDI
940
	; mixptr  : EDX
941
	; _mod+36 : ESI
942
	call Ramp
943
 
944
	if AC97SND_ON
945
	else
946
		if INFO_API_ON
947
			lea eax,[edi+edi*4]
948
			cdq
949
			shl eax,2
950
			mov ecx,FSOUND_MixRate/50
951
			div ecx
952
			; time_ms += SamplesToMix*FSOUND_OOMixRate*1000
953
			add [time_ms],eax
954
		endif
955
	endif ; AC97SND_ON = 0
956
 
957
	sub ebx,edi ; MixedLeft -= SamplesToMix
958
	test ebp,ebp
959
	jnz fill_loop_1
960
	mov [esi+FMUSIC_MODULE.mixer_samplesleft-36],ebx ; <= MixedLeft
961
 
962
	if AC97SND_ON
963
	else
964
		if INFO_API_ON
965
			mov ecx,[esi + FMUSIC_MODULE.row-36]
966
			or ecx,[esi + FMUSIC_MODULE.order-2-36]
967
			mov DWORD PTR [s_row],ecx
968
		endif
969
	endif ; AC97SND_ON = 0
970
 
971
do_swfill:
972
	; *** CLIP AND COPY BLOCK TO OUTPUT BUFFER
973
	pop eax ; skip MixPtr
974
	pop esi ; <- mixbuffer
975
 
976
	if AC97SND_ON
977
	else
978
		if INFO_API_ON
979
			; ebx : L channel RMS volume
980
			; ebp : R channel RMS volume
981
			xor ebx,ebx
982
		endif
983
	endif ; AC97SND_ON = 0
984
 
985
	mov edi,esi
986
	mov ecx,FSOUND_BlockSize*2
987
	align 4
988
fill_loop_2:
989
	lodsd
990
 
991
	if AC97SND_ON
992
		mov ebx,eax
993
		cdq
994
		xor eax,edx
995
		sub eax,edx
996
		mov ebp,255*volumerampsteps/2
997
		xor edx,edx
998
		div ebp
999
		cmp edx,255*volumerampsteps/4
1000
		sbb eax,-1
1001
		cmp eax,8000h
1002
		sbb edx,edx
1003
		not edx
1004
		or eax,edx
1005
		sar ebx,31
1006
		and eax,7FFFh
1007
		xor eax,ebx
1008
		sub eax,ebx
1009
	else
1010
		if INFO_API_ON
1011
			push edi
1012
			cdq
1013
			mov edi,eax
1014
			push esi
1015
			xor eax,edx
1016
			mov esi,255*volumerampsteps/2
1017
			sub eax,edx
1018
			xor edx,edx
1019
			div esi
1020
			cmp edx,255*volumerampsteps/4
1021
			pop esi
1022
			sbb eax,-1
1023
			cmp eax,8000h
1024
			sbb edx,edx
1025
			not edx
1026
			or eax,edx
1027
			sar edi,31
1028
			and eax,7FFFh
1029
		if VOL_CONTROL_ON
1030
			mul DWORD PTR [ufmod_vol]
1031
			shr eax,15
1032
		endif
1033
			; sum. the L and R channel RMS volume
1034
			ror ecx,1
1035
			sbb edx,edx
1036
			and edx,eax
1037
			add ebp,edx ; += |vol|
1038
			rol ecx,1
1039
			sbb edx,edx
1040
			not edx
1041
			and edx,eax
1042
			add ebx,edx ; += |vol|
1043
			xor eax,edi
1044
			sub eax,edi
1045
			pop edi
1046
		else
1047
			mov ebx,eax
1048
			cdq
1049
			xor eax,edx
1050
			sub eax,edx
1051
			mov ebp,255*volumerampsteps/2
1052
			xor edx,edx
1053
			div ebp
1054
			cmp edx,255*volumerampsteps/4
1055
			sbb eax,-1
1056
			cmp eax,8000h
1057
			sbb edx,edx
1058
			not edx
1059
			or eax,edx
1060
			sar ebx,31
1061
			and eax,7FFFh
1062
		if VOL_CONTROL_ON
1063
			mul DWORD PTR [ufmod_vol]
1064
			shr eax,15
1065
		endif
1066
			xor eax,ebx
1067
			sub eax,ebx
1068
		endif
1069
	endif ; AC97SND_ON = 0
1070
 
1071
	dec ecx
1072
	stosw
1073
	jnz fill_loop_2
1074
 
1075
	if AC97SND_ON
1076
	else
1077
		if INFO_API_ON
1078
			shr ebp,FSOUND_Block      ; R_vol / FSOUND_BlockSize
1079
			shl ebx,16-FSOUND_Block   ; (L_vol / FSOUND_BlockSize) << 16
1080
			mov bx,bp
1081
			mov DWORD PTR [L_vol],ebx
1082
		endif
1083
	endif ; AC97SND_ON = 0
1084
 
1085
	; *** DISPATCH DATA TO THE AC97 DRIVER
1086
	push FSOUND_BlockSize*4 ; size
1087
	push OFFSET MixBuf      ; &src
1088
	push DWORD PTR [hBuff]  ; buffer
1089
	mov edx,esp
1090
	push ecx                ; .out_size
1091
	push ecx                ; .output
1092
	push 12                 ; .inp_size
1093
	push edx                ; .input
1094
	push 9                  ; .code = SND_OUT
1095
	push DWORD PTR [hSound] ; .handle
1096
	lea eax,[ecx+68]
1097
	lea ebx,[ecx+17]
1098
	mov ecx,esp
1099
	int 40h
1100
	add esp,36
1101
	pop edi
1102
	pop esi
1103
	pop ebx
1104
	ret