#include #include #include #undef WINNT #define NONAMELESSUNION #define DIRECTSOUND_VERSION 0x0300 #include #include "../sexyal.h" typedef struct { LPDIRECTSOUND ppDS; /* DirectSound interface object. */ LPDIRECTSOUNDBUFFER ppbuf; /* Primary buffer. */ LPDIRECTSOUNDBUFFER ppbufsec; /* Secondary buffer. */ LPDIRECTSOUNDBUFFER ppbufw; /* Buffer to do writes to. */ WAVEFORMATEX wf; /* Format of the primary and secondary buffers. */ long DSBufferSize; /* The size of the buffer that we can write to, in bytes. */ long BufHowMuch; /* How many bytes we should try to buffer. */ DWORD ToWritePos; /* Position which the next write to the buffer should write to. */ } DSFobby; static void CheckStatus(DSFobby *tmp) { DWORD status=0; IDirectSoundBuffer_GetStatus(tmp->ppbufw, &status); if(status&DSBSTATUS_BUFFERLOST) IDirectSoundBuffer_Restore(tmp->ppbufw); if(!(status&DSBSTATUS_PLAYING)) { tmp->ToWritePos=0; IDirectSoundBuffer_SetCurrentPosition(tmp->ppbufsec,0); IDirectSoundBuffer_SetFormat(tmp->ppbufw,&tmp->wf); IDirectSoundBuffer_Play(tmp->ppbufw,0,0,DSBPLAY_LOOPING); } } SexyAL_device *SexyALI_DSound_Open(uint64_t id, SexyAL_format *format, SexyAL_buffering *buffering) { SexyAL_device *dev; DSFobby *fobby; DSBUFFERDESC DSBufferDesc; DSCAPS dscaps; DSBCAPS dsbcaps; dev=malloc(sizeof(SexyAL_device)); fobby=malloc(sizeof(DSFobby)); memset(fobby,0,sizeof(DSFobby)); memset(&fobby->wf,0,sizeof(WAVEFORMATEX)); fobby->wf.wFormatTag = WAVE_FORMAT_PCM; fobby->wf.nChannels = format->channels; fobby->wf.nSamplesPerSec = format->rate; if(DirectSoundCreate(0,&fobby->ppDS,0) != DS_OK) { free(dev); free(fobby); return(0); } { //HWND hWnd = GetForegroundWindow(); // Ugly. //if(!hWnd) //{ hWnd=GetDesktopWindow(); exit(1); } HWND hWnd; hWnd=GetDesktopWindow(); IDirectSound_SetCooperativeLevel(fobby->ppDS,hWnd,DSSCL_PRIORITY); } memset(&dscaps,0x00,sizeof(dscaps)); dscaps.dwSize=sizeof(dscaps); IDirectSound_GetCaps(fobby->ppDS,&dscaps); IDirectSound_Compact(fobby->ppDS); /* Create primary buffer */ memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC)); DSBufferDesc.dwSize=sizeof(DSBufferDesc); DSBufferDesc.dwFlags=DSBCAPS_PRIMARYBUFFER; if(IDirectSound_CreateSoundBuffer(fobby->ppDS,&DSBufferDesc,&fobby->ppbuf,0) != DS_OK) { IDirectSound_Release(fobby->ppDS); free(dev); free(fobby); return(0); } /* Set primary buffer format. */ if(format->sampformat == SEXYAL_FMT_PCMU8) fobby->wf.wBitsPerSample=8; else // if(format->sampformat == SEXYAL_FMT_PCMS16) { fobby->wf.wBitsPerSample=16; format->sampformat=SEXYAL_FMT_PCMS16; } fobby->wf.nBlockAlign=fobby->wf.wBitsPerSample>>3; fobby->wf.nAvgBytesPerSec=fobby->wf.nSamplesPerSec*fobby->wf.nBlockAlign; if(IDirectSoundBuffer_SetFormat(fobby->ppbuf,&fobby->wf) != DS_OK) { IDirectSound_Release(fobby->ppbuf); IDirectSound_Release(fobby->ppDS); free(dev); free(fobby); return(0); } /* Create secondary sound buffer */ IDirectSoundBuffer_GetFormat(fobby->ppbuf,&fobby->wf,sizeof(WAVEFORMATEX),0); memset(&DSBufferDesc,0x00,sizeof(DSBUFFERDESC)); DSBufferDesc.dwSize=sizeof(DSBufferDesc); DSBufferDesc.dwFlags=DSBCAPS_GETCURRENTPOSITION2; DSBufferDesc.dwFlags|=DSBCAPS_GLOBALFOCUS; DSBufferDesc.dwBufferBytes=65536; DSBufferDesc.lpwfxFormat=&fobby->wf; if(IDirectSound_CreateSoundBuffer(fobby->ppDS, &DSBufferDesc, &fobby->ppbufsec, 0) != DS_OK) { IDirectSound_Release(fobby->ppbuf); IDirectSound_Release(fobby->ppDS); free(dev); free(fobby); return(0); } fobby->DSBufferSize=65536; IDirectSoundBuffer_SetCurrentPosition(fobby->ppbufsec,0); fobby->ppbufw=fobby->ppbufsec; memcpy(&dev->format,format,sizeof(SexyAL_format)); if(!buffering->ms) buffering->ms=53; buffering->totalsize=(int64_t)format->rate*buffering->ms/1000; fobby->BufHowMuch=buffering->totalsize* format->channels * (format->sampformat>>4); //printf("%d\n",fobby->BufHowMuch); //fflush(stdout); dev->private=fobby; timeBeginPeriod(1); return(dev); } uint32_t SexyALI_DSound_RawCanWrite(SexyAL_device *device) { DSFobby *tmp=device->private; DWORD CurWritePos,CurPlayPos=0; CheckStatus(tmp); CurWritePos=0; if(IDirectSoundBuffer_GetCurrentPosition(tmp->ppbufw,&CurPlayPos,&CurWritePos)==DS_OK) { //FCEU_DispMessage("%d",CurWritePos-CurPlayPos); } CurWritePos=(CurPlayPos+tmp->BufHowMuch)%tmp->DSBufferSize; /* If the current write pos is >= half the buffer size less than the to write pos, assume DirectSound has wrapped around. */ if(((int32_t)tmp->ToWritePos-(int32_t)CurWritePos) >= (tmp->DSBufferSize/2)) { CurWritePos+=tmp->DSBufferSize; //printf("Fixit: %d,%d,%d\n",tmp->ToWritePos,CurWritePos,CurWritePos-tmp->DSBufferSize); } if(tmp->ToWritePosToWritePos; if(howmuch > tmp->BufHowMuch) /* Oopsie. Severe buffer overflow... */ { tmp->ToWritePos=CurWritePos%tmp->DSBufferSize; //IDirectSoundBuffer_Stop(tmp->ppbufsec); //IDirectSoundBuffer_SetCurrentPosition(tmp->ppbufsec,tmp->ToWritePos); //puts("Oops"); //fflush(stdout); //return(0); } return(CurWritePos-tmp->ToWritePos); } else return(0); } int SexyALI_DSound_RawWrite(SexyAL_device *device, void *data, uint32_t len) { DSFobby *tmp=device->private; // uint32_t cw; //printf("Pre: %d\n",SexyALI_DSound_RawCanWrite(device)); //fflush(stdout); CheckStatus(tmp); /* In this block, we write as much data as we can, then we write the rest of it in >=1ms chunks. */ while(len) { VOID *LockPtr[2]={0,0}; DWORD LockLen[2]={0,0}; int32_t curlen; while(!(curlen=SexyALI_DSound_RawCanWrite(device))) { Sleep(1); } if(curlen>len) curlen=len; if(DS_OK == IDirectSoundBuffer_Lock(tmp->ppbufw,tmp->ToWritePos,curlen,&LockPtr[0],&LockLen[0],&LockPtr[1],&LockLen[1],0)) { } if(LockPtr[1] != 0 && LockPtr[1] != LockPtr[0]) { memcpy(LockPtr[0],data,LockLen[0]); memcpy(LockPtr[1],data+LockLen[0],len-LockLen[0]); } else if(LockPtr[0]) { memcpy(LockPtr[0],data,curlen); } IDirectSoundBuffer_Unlock(tmp->ppbufw,LockPtr[0],LockLen[0],LockPtr[1],LockLen[1]); tmp->ToWritePos=(tmp->ToWritePos+curlen)%tmp->DSBufferSize; len-=curlen; (uint8_t *) data+=curlen; if(len) Sleep(1); } // end while(len) loop return(1); } int SexyALI_DSound_Close(SexyAL_device *device) { if(device) { if(device->private) { DSFobby *tmp=device->private; if(tmp->ppbufsec) { IDirectSoundBuffer_Stop(tmp->ppbufsec); IDirectSoundBuffer_Release(tmp->ppbufsec); } if(tmp->ppbuf) { IDirectSoundBuffer_Stop(tmp->ppbuf); IDirectSoundBuffer_Release(tmp->ppbuf); } if(tmp->ppDS) { IDirectSound_Release(tmp->ppDS); } free(device->private); } free(device); timeEndPeriod(1); return(1); } return(0); }