|  | /*  			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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #define NONAMELESSSTRUCT | 
|  | #define NONAMELESSUNION | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winuser.h" | 
|  | #include "mmsystem.h" | 
|  | #include "winreg.h" | 
|  | #include "winternl.h" | 
|  | #include "mmddk.h" | 
|  | #include "wine/debug.h" | 
|  | #include "dsound.h" | 
|  | #include "dsdriver.h" | 
|  | #include "dsound_private.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(dsound); | 
|  |  | 
|  | static void DSOUND_RecalcPrimary(DirectSoundDevice *device) | 
|  | { | 
|  | DWORD nBlockAlign; | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | nBlockAlign = device->pwfx->nBlockAlign; | 
|  | if (device->hwbuf) { | 
|  | DWORD fraglen; | 
|  | /* let fragment size approximate the timer delay */ | 
|  | fraglen = (device->pwfx->nSamplesPerSec * DS_TIME_DEL / 1000) * nBlockAlign; | 
|  | /* reduce fragment size until an integer number of them fits in the buffer */ | 
|  | /* (FIXME: this may or may not be a good idea) */ | 
|  | while (device->buflen % fraglen) fraglen -= nBlockAlign; | 
|  | device->fraglen = fraglen; | 
|  | TRACE("fraglen=%d\n", device->fraglen); | 
|  | } | 
|  | /* calculate the 10ms write lead */ | 
|  | device->writelead = (device->pwfx->nSamplesPerSec / 100) * nBlockAlign; | 
|  | } | 
|  |  | 
|  | static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) | 
|  | { | 
|  | HRESULT err = DS_OK; | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | /* are we using waveOut stuff? */ | 
|  | if (!device->driver) { | 
|  | LPBYTE newbuf; | 
|  | DWORD buflen; | 
|  | HRESULT merr = DS_OK; | 
|  | /* Start in pause mode, to allow buffers to get filled */ | 
|  | waveOutPause(device->hwo); | 
|  | if (device->state == STATE_PLAYING) device->state = STATE_STARTING; | 
|  | else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED; | 
|  | /* use fragments of 10ms (1/100s) each (which should get us within | 
|  | * the documented write cursor lead of 10-15ms) */ | 
|  | buflen = ((device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign) * DS_HEL_FRAGS; | 
|  | TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer); | 
|  | /* reallocate emulated primary buffer */ | 
|  |  | 
|  | if (device->buffer) | 
|  | newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer,buflen); | 
|  | else | 
|  | newbuf = HeapAlloc(GetProcessHeap(),0,buflen); | 
|  |  | 
|  | if (newbuf == NULL) { | 
|  | ERR("failed to allocate primary buffer\n"); | 
|  | merr = DSERR_OUTOFMEMORY; | 
|  | /* but the old buffer might still exist and must be re-prepared */ | 
|  | } else { | 
|  | device->buffer = newbuf; | 
|  | device->buflen = buflen; | 
|  | } | 
|  | if (device->buffer) { | 
|  | unsigned c; | 
|  |  | 
|  | device->fraglen = device->buflen / DS_HEL_FRAGS; | 
|  |  | 
|  | /* prepare fragment headers */ | 
|  | for (c=0; c<DS_HEL_FRAGS; c++) { | 
|  | device->pwave[c]->lpData = (char*)device->buffer + c*device->fraglen; | 
|  | device->pwave[c]->dwBufferLength = device->fraglen; | 
|  | device->pwave[c]->dwUser = (DWORD)device; | 
|  | device->pwave[c]->dwFlags = 0; | 
|  | device->pwave[c]->dwLoops = 0; | 
|  | err = mmErr(waveOutPrepareHeader(device->hwo,device->pwave[c],sizeof(WAVEHDR))); | 
|  | if (err != DS_OK) { | 
|  | while (c--) | 
|  | waveOutUnprepareHeader(device->hwo,device->pwave[c],sizeof(WAVEHDR)); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | device->pwplay = 0; | 
|  | device->pwwrite = 0; | 
|  | device->pwqueue = 0; | 
|  | device->playpos = 0; | 
|  | device->mixpos = 0; | 
|  | FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0); | 
|  | TRACE("fraglen=%d\n", device->fraglen); | 
|  | DSOUND_WaveQueue(device, (DWORD)-1); | 
|  | } | 
|  | if ((err == DS_OK) && (merr != DS_OK)) | 
|  | err = merr; | 
|  | } else if (!device->hwbuf) { | 
|  | err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx, | 
|  | DSBCAPS_PRIMARYBUFFER,0, | 
|  | &(device->buflen),&(device->buffer), | 
|  | (LPVOID*)&(device->hwbuf)); | 
|  | if (err != DS_OK) { | 
|  | WARN("IDsDriver_CreateSoundBuffer failed\n"); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | if (device->state == STATE_PLAYING) device->state = STATE_STARTING; | 
|  | else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED; | 
|  | device->playpos = 0; | 
|  | device->mixpos = 0; | 
|  | FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0); | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void DSOUND_PrimaryClose(DirectSoundDevice *device) | 
|  | { | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | /* are we using waveOut stuff? */ | 
|  | if (!device->hwbuf) { | 
|  | unsigned c; | 
|  |  | 
|  | device->pwqueue = (DWORD)-1; /* resetting queues */ | 
|  | waveOutReset(device->hwo); | 
|  | for (c=0; c<DS_HEL_FRAGS; c++) | 
|  | waveOutUnprepareHeader(device->hwo, device->pwave[c], sizeof(WAVEHDR)); | 
|  | device->pwqueue = 0; | 
|  | } else { | 
|  | if (IDsDriverBuffer_Release(device->hwbuf) == 0) | 
|  | device->hwbuf = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device) | 
|  | { | 
|  | HRESULT err = DS_OK; | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | device->buflen = device->pwfx->nAvgBytesPerSec; | 
|  |  | 
|  | /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */ | 
|  |  | 
|  | if (device->driver) { | 
|  | err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx, | 
|  | DSBCAPS_PRIMARYBUFFER,0, | 
|  | &(device->buflen),&(device->buffer), | 
|  | (LPVOID*)&(device->hwbuf)); | 
|  | if (err != DS_OK) { | 
|  | WARN("IDsDriver_CreateSoundBuffer failed\n"); | 
|  | return err; | 
|  | } | 
|  | } | 
|  | if (!device->hwbuf) { | 
|  | /* Allocate memory for HEL buffer headers */ | 
|  | unsigned c; | 
|  | for (c=0; c<DS_HEL_FRAGS; c++) { | 
|  | device->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR)); | 
|  | if (!device->pwave[c]) { | 
|  | /* Argh, out of memory */ | 
|  | while (c--) { | 
|  | HeapFree(GetProcessHeap(),0,device->pwave[c]); | 
|  | } | 
|  | WARN("out of memory\n"); | 
|  | return DSERR_OUTOFMEMORY; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | err = DSOUND_PrimaryOpen(device); | 
|  |  | 
|  | if (err != DS_OK) { | 
|  | WARN("DSOUND_PrimaryOpen failed\n"); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* calculate fragment size and write lead */ | 
|  | DSOUND_RecalcPrimary(device); | 
|  | device->state = STATE_STOPPED; | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) | 
|  | { | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | DSOUND_PrimaryClose(device); | 
|  | if (device->driver) { | 
|  | if (device->hwbuf) { | 
|  | if (IDsDriverBuffer_Release(device->hwbuf) == 0) | 
|  | device->hwbuf = 0; | 
|  | } | 
|  | } else { | 
|  | unsigned c; | 
|  | for (c=0; c<DS_HEL_FRAGS; c++) { | 
|  | HeapFree(GetProcessHeap(),0,device->pwave[c]); | 
|  | } | 
|  | } | 
|  | HeapFree(GetProcessHeap(),0,device->pwfx); | 
|  | device->pwfx=NULL; | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) | 
|  | { | 
|  | HRESULT err = DS_OK; | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | if (device->hwbuf) { | 
|  | err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING); | 
|  | if (err != DS_OK) | 
|  | WARN("IDsDriverBuffer_Play failed\n"); | 
|  | } else { | 
|  | err = mmErr(waveOutRestart(device->hwo)); | 
|  | if (err != DS_OK) | 
|  | WARN("waveOutRestart failed\n"); | 
|  | } | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) | 
|  | { | 
|  | HRESULT err = DS_OK; | 
|  | TRACE("(%p)\n", device); | 
|  |  | 
|  | if (device->hwbuf) { | 
|  | err = IDsDriverBuffer_Stop(device->hwbuf); | 
|  | if (err == DSERR_BUFFERLOST) { | 
|  | DWORD flags = CALLBACK_FUNCTION; | 
|  | if (ds_hw_accel != DS_HW_ACCEL_EMULATION) | 
|  | flags |= WAVE_DIRECTSOUND; | 
|  | /* Wine-only: the driver wants us to reopen the device */ | 
|  | /* FIXME: check for errors */ | 
|  | IDsDriverBuffer_Release(device->hwbuf); | 
|  | waveOutClose(device->hwo); | 
|  | device->hwo = 0; | 
|  | err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, | 
|  | device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, | 
|  | flags)); | 
|  | if (err == DS_OK) { | 
|  | err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx, | 
|  | DSBCAPS_PRIMARYBUFFER,0, | 
|  | &(device->buflen),&(device->buffer), | 
|  | (LPVOID)&(device->hwbuf)); | 
|  | if (err != DS_OK) | 
|  | WARN("IDsDriver_CreateSoundBuffer failed\n"); | 
|  | } else { | 
|  | WARN("waveOutOpen failed\n"); | 
|  | } | 
|  | } else if (err != DS_OK) { | 
|  | WARN("IDsDriverBuffer_Stop failed\n"); | 
|  | } | 
|  | } else { | 
|  | err = mmErr(waveOutPause(device->hwo)); | 
|  | if (err != DS_OK) | 
|  | WARN("waveOutPause failed\n"); | 
|  | } | 
|  | return err; | 
|  | } | 
|  |  | 
|  | HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos) | 
|  | { | 
|  | TRACE("(%p,%p,%p)\n", device, playpos, writepos); | 
|  |  | 
|  | if (device->hwbuf) { | 
|  | HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos); | 
|  | if (err) { | 
|  | WARN("IDsDriverBuffer_GetPosition failed\n"); | 
|  | return err; | 
|  | } | 
|  | } else { | 
|  | if (playpos) { | 
|  | MMTIME mtime; | 
|  | mtime.wType = TIME_BYTES; | 
|  | waveOutGetPosition(device->hwo, &mtime, sizeof(mtime)); | 
|  | mtime.u.cb = mtime.u.cb % device->buflen; | 
|  | *playpos = mtime.u.cb; | 
|  | } | 
|  | if (writepos) { | 
|  | /* the writepos should only be used by apps with WRITEPRIMARY priority, | 
|  | * in which case our software mixer is disabled anyway */ | 
|  | *writepos = (device->pwplay + ds_hel_margin) * device->fraglen; | 
|  | while (*writepos >= device->buflen) | 
|  | *writepos -= device->buflen; | 
|  | } | 
|  | } | 
|  | TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount()); | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex) | 
|  | { | 
|  | HRESULT err = DS_OK; | 
|  | int i, alloc_size, cp_size; | 
|  | DWORD nSamplesPerSec; | 
|  | TRACE("(%p,%p)\n", device, wfex); | 
|  |  | 
|  | if (device->priolevel == DSSCL_NORMAL) { | 
|  | WARN("failed priority check!\n"); | 
|  | return DSERR_PRIOLEVELNEEDED; | 
|  | } | 
|  |  | 
|  | /* Let's be pedantic! */ | 
|  | if (wfex == NULL) { | 
|  | WARN("invalid parameter: wfex==NULL!\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  | TRACE("(formattag=0x%04x,chans=%d,samplerate=%d," | 
|  | "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", | 
|  | wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec, | 
|  | wfex->nAvgBytesPerSec, wfex->nBlockAlign, | 
|  | wfex->wBitsPerSample, wfex->cbSize); | 
|  |  | 
|  | /* **** */ | 
|  | RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE); | 
|  | EnterCriticalSection(&(device->mixlock)); | 
|  |  | 
|  | if (wfex->wFormatTag == WAVE_FORMAT_PCM) { | 
|  | alloc_size = sizeof(WAVEFORMATEX); | 
|  | cp_size = sizeof(PCMWAVEFORMAT); | 
|  | } else | 
|  | alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize; | 
|  |  | 
|  | device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size); | 
|  |  | 
|  | nSamplesPerSec = device->pwfx->nSamplesPerSec; | 
|  |  | 
|  | CopyMemory(device->pwfx, wfex, cp_size); | 
|  |  | 
|  | if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) { | 
|  | DWORD flags = CALLBACK_FUNCTION; | 
|  | if (ds_hw_accel != DS_HW_ACCEL_EMULATION) | 
|  | flags |= WAVE_DIRECTSOUND; | 
|  | /* FIXME: check for errors */ | 
|  | DSOUND_PrimaryClose(device); | 
|  | waveOutClose(device->hwo); | 
|  | device->hwo = 0; | 
|  | err = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, | 
|  | device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, | 
|  | flags)); | 
|  | if (err == DS_OK) { | 
|  | err = DSOUND_PrimaryOpen(device); | 
|  | if (err != DS_OK) { | 
|  | WARN("DSOUND_PrimaryOpen failed\n"); | 
|  | goto done; | 
|  | } | 
|  | } else { | 
|  | WARN("waveOutOpen failed\n"); | 
|  | goto done; | 
|  | } | 
|  | } else if (device->hwbuf) { | 
|  | err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx); | 
|  | if (err == DSERR_BUFFERLOST) { | 
|  | /* Wine-only: the driver wants us to recreate the HW buffer */ | 
|  | IDsDriverBuffer_Release(device->hwbuf); | 
|  | err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx, | 
|  | DSBCAPS_PRIMARYBUFFER,0, | 
|  | &(device->buflen),&(device->buffer), | 
|  | (LPVOID)&(device->hwbuf)); | 
|  | if (err != DS_OK) { | 
|  | WARN("IDsDriver_CreateSoundBuffer failed\n"); | 
|  | goto done; | 
|  | } | 
|  | if (device->state == STATE_PLAYING) device->state = STATE_STARTING; | 
|  | else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED; | 
|  | } else { | 
|  | WARN("IDsDriverBuffer_SetFormat failed\n"); | 
|  | goto done; | 
|  | } | 
|  | /* FIXME: should we set err back to DS_OK in all cases ? */ | 
|  | } | 
|  | DSOUND_RecalcPrimary(device); | 
|  |  | 
|  | if (nSamplesPerSec != device->pwfx->nSamplesPerSec) { | 
|  | IDirectSoundBufferImpl** dsb = device->buffers; | 
|  | for (i = 0; i < device->nrofbuffers; i++, dsb++) { | 
|  | /* **** */ | 
|  | EnterCriticalSection(&((*dsb)->lock)); | 
|  |  | 
|  | (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) / | 
|  | wfex->nSamplesPerSec; | 
|  |  | 
|  | LeaveCriticalSection(&((*dsb)->lock)); | 
|  | /* **** */ | 
|  | } | 
|  | } | 
|  |  | 
|  | done: | 
|  | LeaveCriticalSection(&(device->mixlock)); | 
|  | RtlReleaseResource(&(device->buffer_list_lock)); | 
|  | /* **** */ | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /******************************************************************************* | 
|  | *		PrimaryBuffer | 
|  | */ | 
|  | /* This sets this format for the <em>Primary Buffer Only</em> */ | 
|  | /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */ | 
|  | static HRESULT WINAPI PrimaryBufferImpl_SetFormat( | 
|  | LPDIRECTSOUNDBUFFER8 iface, | 
|  | LPCWAVEFORMATEX wfex) | 
|  | { | 
|  | TRACE("(%p,%p)\n", iface, wfex); | 
|  | return DSOUND_PrimarySetFormat(((PrimaryBufferImpl *)iface)->device, wfex); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_SetVolume( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LONG vol | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | DWORD ampfactors; | 
|  | DSVOLUMEPAN volpan; | 
|  | HRESULT hres = DS_OK; | 
|  | TRACE("(%p,%d)\n", iface, vol); | 
|  |  | 
|  | if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) { | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) { | 
|  | WARN("invalid parameter: vol = %d\n", vol); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | /* **** */ | 
|  | EnterCriticalSection(&(device->mixlock)); | 
|  |  | 
|  | waveOutGetVolume(device->hwo, &factors); | 
|  | volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; | 
|  | volpan.dwTotalRightAmpFactor=ampfactors >> 16; | 
|  | DSOUND_AmpFactorToVolPan(&volpan); | 
|  | if (vol != volpan.lVolume) { | 
|  | volpan.lVolume=vol; | 
|  | DSOUND_RecalcVolPan(&volpan); | 
|  | if (device->hwbuf) { | 
|  | hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan); | 
|  | if (hres != DS_OK) | 
|  | WARN("IDsDriverBuffer_SetVolumePan failed\n"); | 
|  | } else { | 
|  | ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16); | 
|  | waveOutSetVolume(device->hwo, ampfactors); | 
|  | } | 
|  | } | 
|  |  | 
|  | LeaveCriticalSection(&(device->mixlock)); | 
|  | /* **** */ | 
|  |  | 
|  | return hres; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetVolume( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPLONG vol | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | DWORD ampfactors; | 
|  | DSVOLUMEPAN volpan; | 
|  | TRACE("(%p,%p)\n", iface, vol); | 
|  |  | 
|  | if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) { | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | if (vol == NULL) { | 
|  | WARN("invalid parameter: vol = NULL\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | waveOutGetVolume(device->hwo, &factors); | 
|  | volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; | 
|  | volpan.dwTotalRightAmpFactor=ampfactors >> 16; | 
|  | DSOUND_AmpFactorToVolPan(&volpan); | 
|  | *vol = volpan.lVolume; | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_SetFrequency( | 
|  | LPDIRECTSOUNDBUFFER8 iface,DWORD freq | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | TRACE("(%p,%d)\n",This,freq); | 
|  |  | 
|  | /* You cannot set the frequency of the primary buffer */ | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_Play( | 
|  | LPDIRECTSOUNDBUFFER8 iface,DWORD reserved1,DWORD reserved2,DWORD flags | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags); | 
|  |  | 
|  | if (!(flags & DSBPLAY_LOOPING)) { | 
|  | WARN("invalid parameter: flags = %08x\n", flags); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | /* **** */ | 
|  | EnterCriticalSection(&(device->mixlock)); | 
|  |  | 
|  | if (device->state == STATE_STOPPED) | 
|  | device->state = STATE_STARTING; | 
|  | else if (device->state == STATE_STOPPING) | 
|  | device->state = STATE_PLAYING; | 
|  |  | 
|  | LeaveCriticalSection(&(device->mixlock)); | 
|  | /* **** */ | 
|  |  | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface) | 
|  | { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p)\n", iface); | 
|  |  | 
|  | /* **** */ | 
|  | EnterCriticalSection(&(device->mixlock)); | 
|  |  | 
|  | if (device->state == STATE_PLAYING) | 
|  | device->state = STATE_STOPPING; | 
|  | else if (device->state == STATE_STARTING) | 
|  | device->state = STATE_STOPPED; | 
|  |  | 
|  | LeaveCriticalSection(&(device->mixlock)); | 
|  | /* **** */ | 
|  |  | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER8 iface) | 
|  | { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | ULONG ref = InterlockedIncrement(&(This->ref)); | 
|  | TRACE("(%p) ref was %d\n", This, ref - 1); | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) | 
|  | { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | DWORD ref = InterlockedDecrement(&(This->ref)); | 
|  | TRACE("(%p) ref was %d\n", This, ref + 1); | 
|  |  | 
|  | if (!ref) { | 
|  | This->device->primary = NULL; | 
|  | HeapFree(GetProcessHeap(), 0, This); | 
|  | TRACE("(%p) released\n", This); | 
|  | } | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos | 
|  | ) { | 
|  | HRESULT	hres; | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%p,%p)\n", iface, playpos, writepos); | 
|  |  | 
|  | hres = DSOUND_PrimaryGetPosition(device, playpos, writepos); | 
|  | if (hres != DS_OK) { | 
|  | WARN("DSOUND_PrimaryGetPosition failed\n"); | 
|  | return hres; | 
|  | } | 
|  | if (writepos) { | 
|  | if (device->state != STATE_STOPPED) | 
|  | /* apply the documented 10ms lead to writepos */ | 
|  | *writepos += device->writelead; | 
|  | while (*writepos >= device->buflen) *writepos -= device->buflen; | 
|  | } | 
|  | TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount()); | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetStatus( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPDWORD status | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%p)\n", iface, status); | 
|  |  | 
|  | if (status == NULL) { | 
|  | WARN("invalid parameter: status == NULL\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | *status = 0; | 
|  | if ((device->state == STATE_STARTING) || | 
|  | (device->state == STATE_PLAYING)) | 
|  | *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING; | 
|  |  | 
|  | TRACE("status=%x\n", *status); | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetFormat( | 
|  | LPDIRECTSOUNDBUFFER8 iface, | 
|  | LPWAVEFORMATEX lpwf, | 
|  | DWORD wfsize, | 
|  | LPDWORD wfwritten) | 
|  | { | 
|  | DWORD size; | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten); | 
|  |  | 
|  | size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize; | 
|  |  | 
|  | if (lpwf) {	/* NULL is valid */ | 
|  | if (wfsize >= size) { | 
|  | CopyMemory(lpwf,device->pwfx,size); | 
|  | if (wfwritten) | 
|  | *wfwritten = size; | 
|  | } else { | 
|  | WARN("invalid parameter: wfsize too small\n"); | 
|  | if (wfwritten) | 
|  | *wfwritten = 0; | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  | } else { | 
|  | if (wfwritten) | 
|  | *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize; | 
|  | else { | 
|  | WARN("invalid parameter: wfwritten == NULL\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  | } | 
|  |  | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_Lock( | 
|  | LPDIRECTSOUNDBUFFER8 iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags | 
|  | ) { | 
|  | HRESULT hres; | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n", | 
|  | iface, | 
|  | writecursor, | 
|  | writebytes, | 
|  | lplpaudioptr1, | 
|  | audiobytes1, | 
|  | lplpaudioptr2, | 
|  | audiobytes2, | 
|  | flags, | 
|  | GetTickCount() | 
|  | ); | 
|  |  | 
|  | if (device->priolevel != DSSCL_WRITEPRIMARY) { | 
|  | WARN("failed priority check!\n"); | 
|  | return DSERR_PRIOLEVELNEEDED; | 
|  | } | 
|  |  | 
|  | /* when this flag is set, writecursor is meaningless and must be calculated */ | 
|  | if (flags & DSBLOCK_FROMWRITECURSOR) { | 
|  | /* GetCurrentPosition does too much magic to duplicate here */ | 
|  | hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor); | 
|  | if (hres != DS_OK) { | 
|  | WARN("IDirectSoundBuffer_GetCurrentPosition failed\n"); | 
|  | return hres; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* when this flag is set, writebytes is meaningless and must be set */ | 
|  | if (flags & DSBLOCK_ENTIREBUFFER) | 
|  | writebytes = device->buflen; | 
|  |  | 
|  | if (writecursor >= device->buflen) { | 
|  | WARN("Invalid parameter, writecursor: %u >= buflen: %u\n", | 
|  | writecursor, device->buflen); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | if (writebytes > device->buflen) { | 
|  | WARN("Invalid parameter, writebytes: %u > buflen: %u\n", | 
|  | writebytes, device->buflen); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) { | 
|  | hres = IDsDriverBuffer_Lock(device->hwbuf, | 
|  | lplpaudioptr1, audiobytes1, | 
|  | lplpaudioptr2, audiobytes2, | 
|  | writecursor, writebytes, | 
|  | 0); | 
|  | if (hres != DS_OK) { | 
|  | WARN("IDsDriverBuffer_Lock failed\n"); | 
|  | return hres; | 
|  | } | 
|  | } else { | 
|  | if (writecursor+writebytes <= device->buflen) { | 
|  | *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor; | 
|  | *audiobytes1 = writebytes; | 
|  | if (lplpaudioptr2) | 
|  | *(LPBYTE*)lplpaudioptr2 = NULL; | 
|  | if (audiobytes2) | 
|  | *audiobytes2 = 0; | 
|  | TRACE("->%d.0\n",writebytes); | 
|  | } else { | 
|  | *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor; | 
|  | *audiobytes1 = device->buflen-writecursor; | 
|  | if (lplpaudioptr2) | 
|  | *(LPBYTE*)lplpaudioptr2 = device->buffer; | 
|  | if (audiobytes2) | 
|  | *audiobytes2 = writebytes-(device->buflen-writecursor); | 
|  | TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0); | 
|  | } | 
|  | } | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition( | 
|  | LPDIRECTSOUNDBUFFER8 iface,DWORD newpos | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | TRACE("(%p,%d)\n",This,newpos); | 
|  |  | 
|  | /* You cannot set the position of the primary buffer */ | 
|  | WARN("invalid call\n"); | 
|  | return DSERR_INVALIDCALL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_SetPan( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LONG pan | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | DWORD ampfactors; | 
|  | DSVOLUMEPAN volpan; | 
|  | HRESULT hres = DS_OK; | 
|  | TRACE("(%p,%d)\n", iface, pan); | 
|  |  | 
|  | if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) { | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) { | 
|  | WARN("invalid parameter: pan = %d\n", pan); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | /* **** */ | 
|  | EnterCriticalSection(&(device->mixlock)); | 
|  |  | 
|  | waveOutGetVolume(device->hwo, &factors); | 
|  | volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; | 
|  | volpan.dwTotalRightAmpFactor=ampfactors >> 16; | 
|  | DSOUND_AmpFactorToVolPan(&volpan); | 
|  | if (pan != volpan.lPan) { | 
|  | volpan.lPan=pan; | 
|  | DSOUND_RecalcVolPan(&volpan); | 
|  | if (device->hwbuf) { | 
|  | hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan); | 
|  | if (hres != DS_OK) | 
|  | WARN("IDsDriverBuffer_SetVolumePan failed\n"); | 
|  | } else { | 
|  | ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16); | 
|  | waveOutSetVolume(device->hwo, ampfactors); | 
|  | } | 
|  | } | 
|  |  | 
|  | LeaveCriticalSection(&(device->mixlock)); | 
|  | /* **** */ | 
|  |  | 
|  | return hres; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetPan( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPLONG pan | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | DWORD ampfactors; | 
|  | DSVOLUMEPAN volpan; | 
|  | TRACE("(%p,%p)\n", iface, pan); | 
|  |  | 
|  | if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) { | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | if (pan == NULL) { | 
|  | WARN("invalid parameter: pan == NULL\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | waveOutGetVolume(device->hwo, &factors); | 
|  | volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; | 
|  | volpan.dwTotalRightAmpFactor=ampfactors >> 16; | 
|  | DSOUND_AmpFactorToVolPan(&volpan); | 
|  | *pan = volpan.lPan; | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_Unlock( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2 | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2); | 
|  |  | 
|  | if (device->priolevel != DSSCL_WRITEPRIMARY) { | 
|  | WARN("failed priority check!\n"); | 
|  | return DSERR_PRIOLEVELNEEDED; | 
|  | } | 
|  |  | 
|  | if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) { | 
|  | HRESULT	hres; | 
|  |  | 
|  | hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2); | 
|  | if (hres != DS_OK) { | 
|  | WARN("IDsDriverBuffer_Unlock failed\n"); | 
|  | return hres; | 
|  | } | 
|  | } | 
|  |  | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_Restore( | 
|  | LPDIRECTSOUNDBUFFER8 iface | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | FIXME("(%p):stub\n",This); | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetFrequency( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPDWORD freq | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%p)\n", iface, freq); | 
|  |  | 
|  | if (freq == NULL) { | 
|  | WARN("invalid parameter: freq == NULL\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) { | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | *freq = device->pwfx->nSamplesPerSec; | 
|  | TRACE("-> %d\n", *freq); | 
|  |  | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_SetFX( | 
|  | LPDIRECTSOUNDBUFFER8 iface,DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | DWORD u; | 
|  | FIXME("(%p,%u,%p,%p): stub\n",This,dwEffectsCount,pDSFXDesc,pdwResultCodes); | 
|  |  | 
|  | if (pdwResultCodes) | 
|  | for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN; | 
|  |  | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_AcquireResources( | 
|  | LPDIRECTSOUNDBUFFER8 iface,DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | DWORD u; | 
|  | FIXME("(%p,%08u,%u,%p): stub\n",This,dwFlags,dwEffectsCount,pdwResultCodes); | 
|  |  | 
|  | if (pdwResultCodes) | 
|  | for (u=0; u<dwEffectsCount; u++) pdwResultCodes[u] = DSFXR_UNKNOWN; | 
|  |  | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetObjectInPath( | 
|  | LPDIRECTSOUNDBUFFER8 iface,REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID* ppObject | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | FIXME("(%p,%s,%u,%s,%p): stub\n",This,debugstr_guid(rguidObject),dwIndex,debugstr_guid(rguidInterface),ppObject); | 
|  |  | 
|  | WARN("control unavailable\n"); | 
|  | return DSERR_CONTROLUNAVAIL; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_Initialize( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd); | 
|  | DPRINTF("Re-Init!!!\n"); | 
|  | WARN("already initialized\n"); | 
|  | return DSERR_ALREADYINITIALIZED; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_GetCaps( | 
|  | LPDIRECTSOUNDBUFFER8 iface,LPDSBCAPS caps | 
|  | ) { | 
|  | DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device; | 
|  | TRACE("(%p,%p)\n", iface, caps); | 
|  |  | 
|  | if (caps == NULL) { | 
|  | WARN("invalid parameter: caps == NULL\n"); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | if (caps->dwSize < sizeof(*caps)) { | 
|  | WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize); | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | caps->dwFlags = device->dsbd.dwFlags; | 
|  | if (device->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE; | 
|  | else caps->dwFlags |= DSBCAPS_LOCSOFTWARE; | 
|  |  | 
|  | caps->dwBufferBytes = device->buflen; | 
|  |  | 
|  | /* This value represents the speed of the "unlock" command. | 
|  | As unlock is quite fast (it does not do anything), I put | 
|  | 4096 ko/s = 4 Mo / s */ | 
|  | /* FIXME: hwbuf speed */ | 
|  | caps->dwUnlockTransferRate = 4096; | 
|  | caps->dwPlayCpuOverhead = 0; | 
|  |  | 
|  | return DS_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI PrimaryBufferImpl_QueryInterface( | 
|  | LPDIRECTSOUNDBUFFER8 iface,REFIID riid,LPVOID *ppobj | 
|  | ) { | 
|  | PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface; | 
|  | DirectSoundDevice *device = This->device; | 
|  | TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj); | 
|  |  | 
|  | if (ppobj == NULL) { | 
|  | WARN("invalid parameter\n"); | 
|  | return E_INVALIDARG; | 
|  | } | 
|  |  | 
|  | *ppobj = NULL;	/* assume failure */ | 
|  |  | 
|  | if ( IsEqualGUID(riid, &IID_IUnknown) || | 
|  | IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) { | 
|  | IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This); | 
|  | *ppobj = This; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /* DirectSoundBuffer and DirectSoundBuffer8 are different and */ | 
|  | /* a primary buffer can't have a DirectSoundBuffer8 interface */ | 
|  | if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) { | 
|  | WARN("app requested DirectSoundBuffer8 on primary buffer\n"); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) { | 
|  | ERR("app requested IDirectSoundNotify on primary buffer\n"); | 
|  | /* FIXME: should we support this? */ | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) { | 
|  | ERR("app requested IDirectSound3DBuffer on primary buffer\n"); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) { | 
|  | if (!device->listener) | 
|  | IDirectSound3DListenerImpl_Create(device, &device->listener); | 
|  | if (device->listener) { | 
|  | *ppobj = device->listener; | 
|  | IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | WARN("IID_IDirectSound3DListener failed\n"); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) { | 
|  | FIXME("app requested IKsPropertySet on primary buffer\n"); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | FIXME( "Unknown IID %s\n", debugstr_guid( riid ) ); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | static const IDirectSoundBuffer8Vtbl dspbvt = | 
|  | { | 
|  | PrimaryBufferImpl_QueryInterface, | 
|  | PrimaryBufferImpl_AddRef, | 
|  | PrimaryBufferImpl_Release, | 
|  | PrimaryBufferImpl_GetCaps, | 
|  | PrimaryBufferImpl_GetCurrentPosition, | 
|  | PrimaryBufferImpl_GetFormat, | 
|  | PrimaryBufferImpl_GetVolume, | 
|  | PrimaryBufferImpl_GetPan, | 
|  | PrimaryBufferImpl_GetFrequency, | 
|  | PrimaryBufferImpl_GetStatus, | 
|  | PrimaryBufferImpl_Initialize, | 
|  | PrimaryBufferImpl_Lock, | 
|  | PrimaryBufferImpl_Play, | 
|  | PrimaryBufferImpl_SetCurrentPosition, | 
|  | PrimaryBufferImpl_SetFormat, | 
|  | PrimaryBufferImpl_SetVolume, | 
|  | PrimaryBufferImpl_SetPan, | 
|  | PrimaryBufferImpl_SetFrequency, | 
|  | PrimaryBufferImpl_Stop, | 
|  | PrimaryBufferImpl_Unlock, | 
|  | PrimaryBufferImpl_Restore, | 
|  | PrimaryBufferImpl_SetFX, | 
|  | PrimaryBufferImpl_AcquireResources, | 
|  | PrimaryBufferImpl_GetObjectInPath | 
|  | }; | 
|  |  | 
|  | HRESULT PrimaryBufferImpl_Create( | 
|  | DirectSoundDevice * device, | 
|  | PrimaryBufferImpl ** ppdsb, | 
|  | LPCDSBUFFERDESC dsbd) | 
|  | { | 
|  | PrimaryBufferImpl *dsb; | 
|  | TRACE("%p,%p,%p)\n",device,ppdsb,dsbd); | 
|  |  | 
|  | if (dsbd->lpwfxFormat) { | 
|  | WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n"); | 
|  | *ppdsb = NULL; | 
|  | return DSERR_INVALIDPARAM; | 
|  | } | 
|  |  | 
|  | dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb)); | 
|  |  | 
|  | if (dsb == NULL) { | 
|  | WARN("out of memory\n"); | 
|  | *ppdsb = NULL; | 
|  | return DSERR_OUTOFMEMORY; | 
|  | } | 
|  |  | 
|  | dsb->ref = 0; | 
|  | dsb->device = device; | 
|  | dsb->lpVtbl = &dspbvt; | 
|  |  | 
|  | CopyMemory(&device->dsbd, dsbd, sizeof(*dsbd)); | 
|  |  | 
|  | TRACE("Created primary buffer at %p\n", dsb); | 
|  | TRACE("(formattag=0x%04x,chans=%d,samplerate=%d," | 
|  | "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", | 
|  | device->pwfx->wFormatTag, device->pwfx->nChannels, | 
|  | device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec, | 
|  | device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample, | 
|  | device->pwfx->cbSize); | 
|  |  | 
|  | *ppdsb = dsb; | 
|  | return S_OK; | 
|  | } |