
;--- implements IDirectSound

        .386
if ?FLAT        
        .MODEL FLAT, stdcall
else
        .MODEL SMALL, stdcall
endif
        option casemap:none
        option proc:private

        include winbase.inc
        include wincon.inc
        include dsound.inc
        include ddsound.inc
        include mmsystem.inc
        include macros.inc

?MINSAMPLERATE	equ 100        
?MAXSAMPLERATE	equ 44100        
?PRIMARYBUFFERS	equ 1
?HWBUFFERSIZE	equ 4000h

?MAXBUFFERS		equ 64	;max number of buffers a ds object can handle
?DELETEALL		equ 0	;delete all buffers belonging to this ds object

DSOBJ   struct
vft			dd ?
dwCnt		dd ?
hwnd		dd ?
dwFlags		dd ?
dwCoopFlags	dd ?
pBuffers	dd ?
DSOBJ   ends

DSOBJF_INITIALIZED	equ 1

QueryInterface proto pThis:ptr DSOBJ,refiid:dword,pObj:dword
AddRef         proto pThis:ptr DSOBJ
Release        proto pThis:ptr DSOBJ

		.DATA

g_hwo		DWORD 0
g_hwoFlags  DWORD 0	;flags for waveOut device
        
		.CONST
        
IID_IDirectSound	GUID <279AFA83h , 4981h  , 11CEh , <0A5h ,  21h , 00h ,  20h , 0AFh , 0Bh , 0E5h , 60h>>

dsvf    label DSOBJVFT
		dd QueryInterface, AddRef, Release
		dd CreateSoundBuffer
		dd GetCaps
		dd DuplicateSoundBuffer
		dd SetCooperativeLevel
		dd Compact
		dd GetSpeakerConfig
		dd SetSpeakerConfig
		dd Initialize

        .CODE

Initialize proto :ptr DSOBJ, :DWORD

DirectSoundCreate proc public uses ebx pGUID:ptr, pDS:ptr dword, pIUnknown:ptr

		invoke	waveOutGetNumDevs
        and		eax, eax
        jz		error1
        cmp		g_hwo, 0
        jnz		error2
        invoke	LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, sizeof DSOBJ
        and     eax,eax
        jz      error3
        mov		ebx, eax
        mov     [ebx].DSOBJ.vft, offset dsvf
        mov		[ebx].DSOBJ.dwCnt, 1
;        invoke	Initialize, ebx, 0
        mov     ecx,pDS
        mov     [ecx], ebx
        mov     eax,DS_OK
        jmp		exit
error1:
        mov     eax,DSERR_NODRIVER
        jmp		exit
error2:
        mov     eax,DSERR_ALLOCATED
        jmp		exit
error3:
        mov     eax,DSERR_OUTOFMEMORY
exit:  
		@strace	<"DirectSoundCreate(", pGUID, ", ", pDS, ", ", pIUnknown, ")=", eax>
        ret
        align 4
DirectSoundCreate endp

QueryInterface proc uses esi edi ebx pThis:ptr DSOBJ, pIID:dword, pObj:dword

		mov		edx, pThis
        mov     edi,offset IID_IDirectSound
        mov     esi,pIID
        mov     ecx,4
        repz    cmpsd
        jz      found
        mov     ecx,pObj
        mov		dword ptr [ecx],0
        mov     eax,DSERR_NOINTERFACE
        jmp		exit
found:
        mov     ecx, pObj
        mov     [ecx], edx
        invoke	AddRef, edx
        mov     eax,DS_OK
exit:        
		@strace	<"DirectSound::QueryInterface(", pThis, ")=", eax>
        ret
        align 4
QueryInterface endp

AddRef proc pThis:ptr DSOBJ
		mov ecx, pThis
        mov eax, [ecx].DSOBJ.dwCnt
        inc [ecx].DSOBJ.dwCnt
		@strace	<"DirectSound::AddRef(", pThis, ")=", eax>
        ret
        align 4
AddRef endp

Release proc uses ebx pThis:ptr DSOBJ
		mov ebx, pThis
        mov eax, [ebx].DSOBJ.dwCnt
        dec [ebx].DSOBJ.dwCnt
        .if (ZERO?)
        	.if (g_hwo)
            	invoke waveOutClose, g_hwo
                mov g_hwo, 0
            .endif
if ?DELETEALL            
            .if ([ebx].DSOBJ.pBuffers)
            	push esi
                mov esi, [ebx].DSOBJ.pBuffers
            	mov ecx, ?MAXBUFFERS
                .while (ecx)
                	push ecx
                	lodsd
                    .if (eax)
                    	invoke vf(eax, IUnknown, Release)
                    .endif
                    pop ecx
                    dec ecx
                .endw
                pop esi
            .endif
endif            
        	invoke LocalFree, ebx
            xor eax, eax
        .endif
		@strace	<"DirectSound::Release(", pThis, ")=", eax>
        ret
        align 4
Release endp

CreateSoundBuffer proc pThis:ptr DSOBJ, lpDesc:ptr DSBUFFERDESC, lplpDSBuffer:ptr dword, lpUnkOuter:LPUNKNOWN

		invoke Create@DirectSoundBuffer, pThis, lpDesc, lplpDSBuffer, lpUnkOuter
if ?DELETEALL        
        .if (eax == DS_OK)
        	pushad
            mov ebx, pThis
            .if (![ebx].DSOBJ.pBuffers)
            	invoke LocalAlloc, LMEM_FIXED or LMEM_ZEROINIT, ?MAXBUFFERS*4
                mov [ebx].DSOBJ.pBuffers, eax
            .endif
            .if ([ebx].DSOBJ.pBuffers)
            	mov edi, [ebx].DSOBJ.pBuffers
                xor eax, eax
                mov ecx, ?MAXBUFFERS
                repnz scasd
                .if (ZERO?)
                	mov ecx, lplpDSBuffer
                    mov edx, [ecx]
                    mov [edi-4],edx
                .endif
            .endif
            popad
        .endif
endif        
		@strace	<"DirectSound::CreateSoundBuffer(", pThis, ", ",  lpDesc, ", ", lplpDSBuffer, ", ", lpUnkOuter, ")=", eax>
		ret
        align 4
CreateSoundBuffer endp

;--- since destroying a DS object should also destroy all
;--- buffers, a linked list of buffers is required

_UnlinkBuffer proc public uses edi pThis:ptr DSOBJ, pBuffer:ptr
if ?DELETEALL
        mov ecx, pThis
        mov edi, [ecx].DSOBJ.pBuffers
        .if (edi)
        	mov eax, pBuffer
            mov ecx, ?MAXBUFFERS
            repnz scasd
            .if (ZERO?)
            	mov dword ptr [edi-4], 0
            .endif
        .endif
endif        
		ret
        align 4
_UnlinkBuffer endp

GetCaps proc uses edi pThis:ptr DSOBJ, lpCaps:LPDSCAPS

local	woc:WAVEOUTCAPSA

		invoke waveOutGetDevCapsA, 0, addr woc, sizeof woc
        
		mov edx, lpCaps
        mov ecx, [edx].DSCAPS.dwSize
        .if (ecx < sizeof DSCAPS)
        	mov eax, DSERR_INVALIDPARAM
            jmp exit
        .endif
        shr ecx, 2
        dec ecx
        lea edi, [edx].DSCAPS.dwFlags
        xor eax, eax
        rep stosd
;;        mov [edx].DSCAPS.dwFlags, DSCAPS_EMULDRIVER or DSCAPS_PRIMARY16BIT or DSCAPS_PRIMARY8BIT or DSCAPS_PRIMARYMONO or DSCAPS_PRIMARYSTEREO
        mov [edx].DSCAPS.dwFlags,\
        	DSCAPS_PRIMARY16BIT or DSCAPS_PRIMARY8BIT or DSCAPS_PRIMARYMONO or DSCAPS_PRIMARYSTEREO
        mov [edx].DSCAPS.dwMinSecondarySampleRate, ?MINSAMPLERATE
        mov [edx].DSCAPS.dwMaxSecondarySampleRate, ?MAXSAMPLERATE
        mov [edx].DSCAPS.dwPrimaryBuffers, ?PRIMARYBUFFERS
        mov [edx].DSCAPS.dwTotalHwMemBytes, ?HWBUFFERSIZE
        mov [edx].DSCAPS.dwFreeHwMemBytes, ?HWBUFFERSIZE
        mov [edx].DSCAPS.dwMaxContigFreeHwMemBytes, ?HWBUFFERSIZE
		mov eax, DS_OK
exit:        
		@strace	<"DirectSound::GetCaps(", pThis, ", ",  lpCaps, ")=", eax>
		ret
        align 4
GetCaps endp

DuplicateSoundBuffer proc pThis:ptr DSOBJ, lpDSBuffer:ptr, lplpDSBuffer:ptr dword

		mov eax, DSERR_UNINITIALIZED
		@strace	<"DirectSound::DuplicateSoundBuffer(", pThis, ", ",  lpDSBuffer, ", ", lplpDSBuffer, ")=", eax>
		ret
        align 4
DuplicateSoundBuffer endp

SetCooperativeLevel proc uses ebx pThis:ptr DSOBJ, hwnd:dword, dwFlags:dword

		mov ebx, pThis
        mov ecx, hwnd
        mov edx, dwFlags
        mov [ebx].DSOBJ.hwnd, ecx
        mov [ebx].DSOBJ.dwCoopFlags, edx
		mov eax, DS_OK
		@strace	<"DirectSound::SetCooperativeLevel(", pThis, ", ",  hwnd, ", ", dwFlags, ")=", eax>
		ret
        align 4
SetCooperativeLevel endp

Compact proc pThis:ptr DSOBJ

		mov eax, DSERR_UNINITIALIZED
		@strace	<"DirectSound::Compact(", pThis, ")=", eax>
		ret
        align 4
Compact endp

GetSpeakerConfig proc pThis:ptr DSOBJ, lpdw1:ptr dword

		mov eax, DSERR_UNINITIALIZED
		@strace	<"DirectSound::GetSpeakerConfig(", pThis, ", ",  lpdw1, ")=", eax>
		ret
        align 4
GetSpeakerConfig endp

SetSpeakerConfig proc pThis:ptr DSOBJ, dw1:dword

		mov eax, DSERR_UNINITIALIZED
		@strace	<"DirectSound::SetSpeakerConfig(", pThis, ", ",  dw1, ")=", eax>
		ret
        align 4
SetSpeakerConfig endp

Initialize proc pThis:ptr DSOBJ, refguid:dword

		mov ecx, pThis
        .if ([ecx].DSOBJ.dwFlags & DSOBJF_INITIALIZED)
			mov eax, DSERR_ALREADYINITIALIZED
        .else
	        or [ecx].DSOBJ.dwFlags, DSOBJF_INITIALIZED
			mov eax, DS_OK
        .endif
		@strace	<"DirectSound::Initialize(", pThis, ", ",  refguid, ")=", eax>
		ret
        align 4
Initialize endp

        END

