buffer.c

来自「一个类似windows」· C语言 代码 · 共 1,512 行 · 第 1/3 页

C
1,512
字号
/*  			DirectSound * * Copyright 1998 Marcus Meissner * Copyright 1998 Rob Riggs * Copyright 2000-2002 TransGaming Technologies, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <stdarg.h>#define NONAMELESSSTRUCT#define NONAMELESSUNION#include "windef.h"#include "winbase.h"#include "mmsystem.h"#include "winreg.h"#include "winternl.h"#include "wine/debug.h"#include "dsound.h"#include "dsdriver.h"#include "dsound_private.h"WINE_DEFAULT_DEBUG_CHANNEL(dsound);/******************************************************************************* *		IDirectSoundNotify */static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(	LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj) {	IDirectSoundNotifyImpl *This = (IDirectSoundNotifyImpl *)iface;	TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);	if (This->dsb == NULL) {		WARN("invalid parameter\n");		return E_INVALIDARG;	}	return IDirectSoundBuffer_QueryInterface((LPDIRECTSOUNDBUFFER)This->dsb, riid, ppobj);}static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface){    IDirectSoundNotifyImpl *This = (IDirectSoundNotifyImpl *)iface;    ULONG ref = InterlockedIncrement(&(This->ref));    TRACE("(%p) ref was %ld\n", This, ref - 1);    return ref;}static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface){    IDirectSoundNotifyImpl *This = (IDirectSoundNotifyImpl *)iface;    ULONG ref = InterlockedDecrement(&(This->ref));    TRACE("(%p) ref was %ld\n", This, ref + 1);    if (!ref) {        IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);        This->dsb->notify = NULL;        HeapFree(GetProcessHeap(), 0, This);        TRACE("(%p) released\n", This);    }    return ref;}static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(	LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify) {	IDirectSoundNotifyImpl *This = (IDirectSoundNotifyImpl *)iface;	TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);        if (howmuch > 0 && notify == NULL) {	    WARN("invalid parameter: notify == NULL\n");	    return DSERR_INVALIDPARAM;	}	if (TRACE_ON(dsound)) {	    unsigned int	i;	    for (i=0;i<howmuch;i++)		TRACE("notify at %ld to %p\n",		    notify[i].dwOffset,notify[i].hEventNotify);	}	if (This->dsb->hwnotify) {	    HRESULT hres;	    hres = IDsDriverNotify_SetNotificationPositions(This->dsb->hwnotify, howmuch, notify);	    if (hres != DS_OK)		    WARN("IDsDriverNotify_SetNotificationPositions failed\n");	    return hres;        } else if (howmuch > 0) {	    /* Make an internal copy of the caller-supplied array.	     * Replace the existing copy if one is already present. */	    if (This->dsb->notifies)		    This->dsb->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,			This->dsb->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));	    else		    This->dsb->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,			howmuch * sizeof(DSBPOSITIONNOTIFY));	    if (This->dsb->notifies == NULL) {		    WARN("out of memory\n");		    return DSERR_OUTOFMEMORY;	    }	    CopyMemory(This->dsb->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));	    This->dsb->nrofnotifies = howmuch;        } else {           HeapFree(GetProcessHeap(), 0, This->dsb->notifies);           This->dsb->notifies = NULL;           This->dsb->nrofnotifies = 0;        }	return S_OK;}static const IDirectSoundNotifyVtbl dsnvt ={    IDirectSoundNotifyImpl_QueryInterface,    IDirectSoundNotifyImpl_AddRef,    IDirectSoundNotifyImpl_Release,    IDirectSoundNotifyImpl_SetNotificationPositions,};HRESULT WINAPI IDirectSoundNotifyImpl_Create(    IDirectSoundBufferImpl * dsb,    IDirectSoundNotifyImpl **pdsn){    IDirectSoundNotifyImpl * dsn;    TRACE("(%p,%p)\n",dsb,pdsn);    dsn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsn));    if (dsn == NULL) {        WARN("out of memory\n");        return DSERR_OUTOFMEMORY;    }    dsn->ref = 0;    dsn->lpVtbl = &dsnvt;    dsn->dsb = dsb;    dsb->notify = dsn;    IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)dsb);    *pdsn = dsn;    return DS_OK;}HRESULT WINAPI IDirectSoundNotifyImpl_Destroy(    IDirectSoundNotifyImpl *pdsn){    TRACE("(%p)\n",pdsn);    while (IDirectSoundNotifyImpl_Release((LPDIRECTSOUNDNOTIFY)pdsn) > 0);    return DS_OK;}/******************************************************************************* *		IDirectSoundBuffer */static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(	LPDIRECTSOUNDBUFFER8 iface,LPCWAVEFORMATEX wfex) {	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	TRACE("(%p,%p)\n",This,wfex);	/* This method is not available on secondary buffers */	WARN("invalid call\n");	return DSERR_INVALIDCALL;}static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(	LPDIRECTSOUNDBUFFER8 iface,LONG vol) {	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	LONG oldVol;	HRESULT hres = DS_OK;	TRACE("(%p,%ld)\n",This,vol);	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {		WARN("control unavailable: This->dsbd.dwFlags = 0x%08lx\n", This->dsbd.dwFlags);		return DSERR_CONTROLUNAVAIL;	}	if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {		WARN("invalid parameter: vol = %ld\n", vol);		return DSERR_INVALIDPARAM;	}	/* **** */	EnterCriticalSection(&(This->lock));	if (This->dsbd.dwFlags & DSBCAPS_CTRL3D) {		oldVol = This->ds3db_lVolume;		This->ds3db_lVolume = vol;	} else {		oldVol = This->volpan.lVolume;		This->volpan.lVolume = vol;		if (vol != oldVol)			DSOUND_RecalcVolPan(&(This->volpan));	}	if (vol != oldVol) {		if (This->hwbuf) {			hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));	    		if (hres != DS_OK)		    		WARN("IDsDriverBuffer_SetVolumePan failed\n");		} else			DSOUND_ForceRemix(This);	}	LeaveCriticalSection(&(This->lock));	/* **** */	return hres;}static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(	LPDIRECTSOUNDBUFFER8 iface,LPLONG vol) {	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	TRACE("(%p,%p)\n",This,vol);	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {		WARN("control unavailable\n");		return DSERR_CONTROLUNAVAIL;	}	if (vol == NULL) {		WARN("invalid parameter: vol == NULL\n");		return DSERR_INVALIDPARAM;	}	*vol = This->volpan.lVolume;	return DS_OK;}static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(	LPDIRECTSOUNDBUFFER8 iface,DWORD freq) {	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	DWORD oldFreq;	TRACE("(%p,%ld)\n",This,freq);	if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {		WARN("control unavailable\n");		return DSERR_CONTROLUNAVAIL;	}	if (freq == DSBFREQUENCY_ORIGINAL)		freq = This->pwfx->nSamplesPerSec;	if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX)) {		WARN("invalid parameter: freq = %ld\n", freq);		return DSERR_INVALIDPARAM;	}	/* **** */	EnterCriticalSection(&(This->lock));	oldFreq = This->freq;	This->freq = freq;	if (freq != oldFreq) {		This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->dsound->device->pwfx->nSamplesPerSec;		This->nAvgBytesPerSec = freq * This->pwfx->nBlockAlign;		DSOUND_RecalcFormat(This);		if (!This->hwbuf)			DSOUND_ForceRemix(This);	}	LeaveCriticalSection(&(This->lock));	/* **** */	return DS_OK;}static HRESULT WINAPI IDirectSoundBufferImpl_Play(	LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags) {	HRESULT hres = DS_OK;	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	TRACE("(%p,%08lx,%08lx,%08lx)\n",This,reserved1,reserved2,flags);	/* **** */	EnterCriticalSection(&(This->lock));	This->playflags = flags;	if (This->state == STATE_STOPPED) {		This->leadin = TRUE;		This->startpos = This->buf_mixpos;		This->state = STATE_STARTING;	} else if (This->state == STATE_STOPPING)		This->state = STATE_PLAYING;	if (This->hwbuf) {		hres = IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);		if (hres != DS_OK)			WARN("IDsDriverBuffer_Play failed\n");		else			This->state = STATE_PLAYING;	}	LeaveCriticalSection(&(This->lock));	/* **** */	return hres;}static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface){	HRESULT hres = DS_OK;	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	TRACE("(%p)\n",This);	/* **** */	EnterCriticalSection(&(This->lock));	if (This->state == STATE_PLAYING)		This->state = STATE_STOPPING;	else if (This->state == STATE_STARTING)		This->state = STATE_STOPPED;	if (This->hwbuf) {		hres = IDsDriverBuffer_Stop(This->hwbuf);		if (hres != DS_OK)			WARN("IDsDriverBuffer_Stop failed\n");		else			This->state = STATE_STOPPED;	}	DSOUND_CheckEvent(This, 0);	LeaveCriticalSection(&(This->lock));	/* **** */	return hres;}static ULONG WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface){    IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;    ULONG ref = InterlockedIncrement(&(This->ref));    TRACE("(%p) ref was %ld\n", This, ref - 1);    return ref;}static ULONG WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface){    IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;    ULONG ref = InterlockedDecrement(&(This->ref));    TRACE("(%p) ref was %ld\n", This, ref + 1);    if (!ref) {	DSOUND_RemoveBuffer(This->dsound, This);	This->lock.DebugInfo->Spare[0] = 0;	DeleteCriticalSection(&(This->lock));	if (This->hwbuf) {		IDsDriverBuffer_Release(This->hwbuf);		if (This->dsound->device->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) {			This->buffer->ref--;			if (This->buffer->ref==0) {				HeapFree(GetProcessHeap(),0,This->buffer->memory);				HeapFree(GetProcessHeap(),0,This->buffer);			}		}	} else {		This->buffer->ref--;		if (This->buffer->ref==0) {			HeapFree(GetProcessHeap(),0,This->buffer->memory);			HeapFree(GetProcessHeap(),0,This->buffer);		}	}	HeapFree(GetProcessHeap(), 0, This->notifies);	HeapFree(GetProcessHeap(), 0, This->pwfx);	HeapFree(GetProcessHeap(), 0, This);	TRACE("(%p) released\n", This);    }    return ref;}DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This, DWORD pplay, DWORD pwrite){	DWORD bplay = This->buf_mixpos;	DWORD pmix = This->primary_mixpos;	TRACE("(%p, pplay=%lu, pwrite=%lu)\n", This, pplay, pwrite);	/* the actual primary play position (pplay) is always behind last mixed (pmix),	 * unless the computer is too slow or something */	/* we need to know how far away we are from there */	if (pmix < pplay) pmix += This->dsound->device->buflen; /* wraparound */	pmix -= pplay;	/* detect buffer underrun */	if (pwrite < pplay) pwrite += This->dsound->device->buflen; /* wraparound */	pwrite -= pplay;	if (pmix > (ds_snd_queue_max * This->dsound->device->fraglen + pwrite + This->dsound->device->writelead)) {		WARN("detected an underrun: primary queue was %ld\n",pmix);		pmix = 0;	}	/* divide the offset by its sample size */	pmix /= This->dsound->device->pwfx->nBlockAlign;	TRACE("primary back-samples=%ld\n",pmix);	/* adjust for our frequency */	pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;	/* multiply by our own sample size */	pmix *= This->pwfx->nBlockAlign;	TRACE("this back-offset=%ld\n", pmix);	/* subtract from our last mixed position */	while (bplay < pmix) bplay += This->buflen; /* wraparound */	bplay -= pmix;	if (This->leadin && ((bplay < This->startpos) || (bplay > This->buf_mixpos))) {		/* seems we haven't started playing yet */		TRACE("this still in lead-in phase\n");		bplay = This->startpos;	}	/* return the result */	return bplay;}static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(	LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos) {	HRESULT	hres;	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	TRACE("(%p,%p,%p)\n",This,playpos,writepos);	if (This->hwbuf) {		hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);		if (hres != DS_OK) {		    WARN("IDsDriverBuffer_GetPosition failed\n");		    return hres;		}	} else {		if (playpos && (This->state != STATE_PLAYING)) {			/* we haven't been merged into the primary buffer (yet) */			*playpos = This->buf_mixpos;		} else if (playpos) {			DWORD pplay, pwrite;			/* let's get this exact; first, recursively call GetPosition on the primary */			EnterCriticalSection(&(This->dsound->device->mixlock));			if (DSOUND_PrimaryGetPosition(This->dsound->device, &pplay, &pwrite) != DS_OK)				WARN("DSOUND_PrimaryGetPosition failed\n");			/* detect HEL mode underrun */			if (!(This->dsound->device->hwbuf || This->dsound->device->pwqueue))				TRACE("detected an underrun\n");			if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->dsound->device->hwbuf) {				/* calculate play position using this */				*playpos = DSOUND_CalcPlayPosition(This, pplay, pwrite);			} else {				/* (unless the app isn't using GETCURRENTPOSITION2) */				/* don't know exactly how this should be handled...				 * the docs says that play cursor is reported as directly				 * behind write cursor, hmm... */				/* let's just do what might work for Half-Life */				DWORD wp;				wp = (This->dsound->device->pwplay + ds_hel_margin) * This->dsound->device->fraglen;				wp %= This->dsound->device->buflen;				*playpos = DSOUND_CalcPlayPosition(This, wp, pwrite);			}			LeaveCriticalSection(&(This->dsound->device->mixlock));		}		if (writepos)                    *writepos = This->buf_mixpos;	}	if (writepos) {		if (This->state != STATE_STOPPED) {			/* apply the documented 10ms lead to writepos */			*writepos += This->writelead;		}		*writepos %= This->buflen;	}	if (playpos)            This->last_playpos = *playpos;	TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());	return DS_OK;}static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(	LPDIRECTSOUNDBUFFER8 iface,LPDWORD status) {	IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;	TRACE("(%p,%p), thread is %04lx\n",This,status,GetCurrentThreadId());	if (status == NULL) {		WARN("invalid parameter: status = NULL\n");		return DSERR_INVALIDPARAM;	}	*status = 0;	if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?