| /* DirectSound |
| * |
| * Copyright 1998 Marcus Meissner |
| */ |
| /* |
| * Note: This file requires multithread ability. It is not possible to |
| * implement the stuff in a single thread anyway. And most DirectX apps |
| * require threading themselves. |
| * |
| * FIXME: This file is full of race conditions and unlocked variable access |
| * from two threads. But we usually don't need to bother. |
| * |
| * Tested with a Soundblaster clone and a Gravis UltraSound Classic. |
| * |
| * Status: |
| * - Wing Commander 4/W95: |
| * The intromovie plays without problems. Nearly lipsynchron. |
| * - DiscWorld 2 |
| * The sound works, but noticeable chunks are left out (from the sound and |
| * the animation). Don't know why yet. |
| * - Diablo: |
| * Sound works, but slows down the movieplayer. |
| * - XvT: |
| * Doesn't sound yet. |
| * - Monkey Island 3: |
| * The background sound of the startscreen works ;) |
| * - WingCommander Prophecy Demo: |
| * Sound works for the intromovie. |
| */ |
| |
| #include "config.h" |
| #include <assert.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/time.h> |
| #include <sys/fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <math.h> /* Insomnia - pow() function */ |
| #include "windows.h" |
| #include "winerror.h" |
| #include "interfaces.h" |
| #include "mmsystem.h" |
| #include "dsound.h" |
| #include "thread.h" |
| #include "debug.h" |
| |
| #ifdef HAVE_OSS |
| # include <sys/ioctl.h> |
| # ifdef HAVE_MACHINE_SOUNDCARD_H |
| # include <machine/soundcard.h> |
| # endif |
| # ifdef HAVE_SYS_SOUNDCARD_H |
| # include <sys/soundcard.h> |
| # endif |
| |
| static int audiofd = -1; |
| static LPDIRECTSOUND dsound = NULL; |
| |
| static short playbuf[2048]; |
| |
| #endif |
| |
| HRESULT WINAPI DirectSoundEnumerate32A(LPDSENUMCALLBACK32A enumcb,LPVOID context) { |
| #ifdef HAVE_OSS |
| enumcb(NULL,"WINE DirectSound using Open Sound System","sound",context); |
| #endif |
| return 0; |
| } |
| |
| #ifdef HAVE_OSS |
| static void _dump_DSBCAPS(DWORD xmask) { |
| struct { |
| DWORD mask; |
| char *name; |
| } flags[] = { |
| #define FE(x) { x, #x }, |
| FE(DSBCAPS_PRIMARYBUFFER) |
| FE(DSBCAPS_STATIC) |
| FE(DSBCAPS_LOCHARDWARE) |
| FE(DSBCAPS_LOCSOFTWARE) |
| FE(DSBCAPS_CTRLFREQUENCY) |
| FE(DSBCAPS_CTRLPAN) |
| FE(DSBCAPS_CTRLVOLUME) |
| FE(DSBCAPS_CTRLDEFAULT) |
| FE(DSBCAPS_CTRLALL) |
| FE(DSBCAPS_STICKYFOCUS) |
| FE(DSBCAPS_GETCURRENTPOSITION2) |
| }; |
| int i; |
| |
| for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++) |
| if (flags[i].mask & xmask) |
| fprintf(stderr,"%s ",flags[i].name); |
| } |
| |
| /******************************************************************************* |
| * IDirectSoundNotify |
| */ |
| static HRESULT WINAPI IDirectSoundNotify_QueryInterface( |
| LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj |
| ) { |
| char xbuf[50]; |
| |
| WINE_StringFromCLSID(riid,xbuf); |
| TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj); |
| return E_FAIL; |
| } |
| |
| static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) { |
| return ++(this->ref); |
| } |
| |
| static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) { |
| this->ref--; |
| if (!this->ref) { |
| this->dsb->lpvtbl->fnRelease(this->dsb); |
| HeapFree(GetProcessHeap(),0,this); |
| return 0; |
| } |
| return this->ref; |
| } |
| |
| static int _sort_notifies(const void *a,const void *b) { |
| LPDSBPOSITIONNOTIFY na = (LPDSBPOSITIONNOTIFY)a; |
| LPDSBPOSITIONNOTIFY nb = (LPDSBPOSITIONNOTIFY)b; |
| |
| return na->dwOffset-nb->dwOffset; |
| } |
| |
| static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions( |
| LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify |
| ) { |
| int i; |
| |
| if (TRACE_ON(dsound)) { |
| TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify); |
| for (i=0;i<howmuch;i++) |
| TRACE(dsound,"notify at %ld to 0x%08lx\n", |
| notify[i].dwOffset,(DWORD)notify[i].hEventNotify); |
| } |
| this->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,this->dsb->notifies,(this->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY)); |
| memcpy( this->dsb->notifies+this->dsb->nrofnotifies, |
| notify, |
| howmuch*sizeof(DSBPOSITIONNOTIFY) |
| ); |
| this->dsb->nrofnotifies+=howmuch; |
| qsort(this->dsb->notifies,this->dsb->nrofnotifies,sizeof(DSBPOSITIONNOTIFY),_sort_notifies); |
| return 0; |
| } |
| |
| IDirectSoundNotify_VTable dsnvt = { |
| IDirectSoundNotify_QueryInterface, |
| IDirectSoundNotify_AddRef, |
| IDirectSoundNotify_Release, |
| IDirectSoundNotify_SetNotificationPositions, |
| }; |
| |
| /******************************************************************************* |
| * IDirectSoundBuffer |
| */ |
| static HRESULT WINAPI IDirectSoundBuffer_SetFormat( |
| LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex |
| ) { |
| |
| memcpy(&(this->wfx),wfex,sizeof(this->wfx)); |
| TRACE(dsound,"(%p,%p)\n", this,wfex); |
| TRACE(dsound,"(formattag=0x%04x,chans=%d,samplerate=%ld" |
| "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", |
| wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec, |
| wfex->nAvgBytesPerSec, wfex->nBlockAlign, |
| wfex->wBitsPerSample, wfex->cbSize); |
| |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_SetVolume( |
| LPDIRECTSOUNDBUFFER this,LONG vol |
| ) { |
| TRACE(dsound,"(%p,%ld)\n",this,vol); |
| this->volume = vol; |
| this->volfac = ((double)vol+10000.0)/10000.0; |
| |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetVolume( |
| LPDIRECTSOUNDBUFFER this,LPLONG vol |
| ) { |
| TRACE(dsound,"(%p,%p)\n",this,vol); |
| *vol = this->volume; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_SetFrequency( |
| LPDIRECTSOUNDBUFFER this,DWORD freq |
| ) { |
| TRACE(dsound,"(%p,%ld)\n",this,freq); |
| this->wfx.nSamplesPerSec = freq; |
| this->wfx.nAvgBytesPerSec = freq*this->wfx.nChannels*(this->wfx.wBitsPerSample/8); |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_Play( |
| LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags |
| ) { |
| TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n", |
| this,reserved1,reserved2,flags |
| ); |
| this->playpos = 0; |
| this->playflags = flags; |
| this->playing = 1; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this) { |
| TRACE(dsound,"(%p)\n",this); |
| this->playing = 0; |
| this->writepos = 0; /* hmm */ |
| return 0; |
| } |
| |
| static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) { |
| return ++(this->ref); |
| } |
| static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) { |
| int i; |
| |
| if (--this->ref) |
| return this->ref; |
| for (i=0;i<this->dsound->nrofbuffers;i++) |
| if (this->dsound->buffers[i] == this) |
| break; |
| if (i < this->dsound->nrofbuffers) { |
| memcpy( |
| this->dsound->buffers+i, |
| this->dsound->buffers+i+1, |
| sizeof(LPDIRECTSOUNDBUFFER)*(this->dsound->nrofbuffers-i-1) |
| ); |
| this->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,this->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*this->dsound->nrofbuffers); |
| this->dsound->nrofbuffers--; |
| this->dsound->lpvtbl->fnRelease(this->dsound); |
| } |
| HeapFree(GetProcessHeap(),0,this->buffer); |
| HeapFree(GetProcessHeap(),0,this); |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition( |
| LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos |
| ) { |
| TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos); |
| if (playpos) *playpos = this->playpos; |
| if (writepos) *writepos = this->writepos; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetStatus( |
| LPDIRECTSOUNDBUFFER this,LPDWORD status |
| ) { |
| TRACE(dsound,"(%p,%p)\n",this,status); |
| *status = 0; |
| if (this->playing) |
| *status |= DSBSTATUS_PLAYING; |
| if (this->playflags & DSBPLAY_LOOPING) |
| *status |= DSBSTATUS_LOOPING; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetFormat( |
| LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten |
| ) { |
| TRACE(dsound,"(%p,%p,%ld,%p)\n",this,lpwf,wfsize,wfwritten); |
| if (wfsize>sizeof(this->wfx)) wfsize = sizeof(this->wfx); |
| memcpy(lpwf,&(this->wfx),wfsize); |
| if (wfwritten) *wfwritten = wfsize; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_Lock( |
| LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags |
| ) { |
| |
| TRACE(dsound,"(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n", |
| this, |
| writecursor, |
| writebytes, |
| lplpaudioptr1, |
| audiobytes1, |
| lplpaudioptr2, |
| audiobytes2, |
| flags |
| ); |
| if (flags & DSBLOCK_FROMWRITECURSOR) |
| writecursor = this->writepos; |
| assert(audiobytes1!=audiobytes2); |
| assert(lplpaudioptr1!=lplpaudioptr2); |
| if (writecursor+writebytes <= this->buflen) { |
| *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor; |
| *audiobytes1 = writebytes; |
| if (lplpaudioptr2) |
| *(LPBYTE*)lplpaudioptr2 = NULL; |
| if (audiobytes2) |
| *audiobytes2 = 0; |
| TRACE(dsound,"->%ld.0\n",writebytes); |
| } else { |
| *(LPBYTE*)lplpaudioptr1 = this->buffer+writecursor; |
| *audiobytes1 = this->buflen-writecursor; |
| if (lplpaudioptr2) |
| *(LPBYTE*)lplpaudioptr2 = this->buffer; |
| if (audiobytes2) |
| *audiobytes2 = writebytes-(this->buflen-writecursor); |
| TRACE(dsound,"->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0); |
| } |
| this->writepos=(writecursor+writebytes)%this->buflen; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition( |
| LPDIRECTSOUNDBUFFER this,DWORD newpos |
| ) { |
| TRACE(dsound,"(%p,%ld)\n",this,newpos); |
| this->playpos = newpos; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_SetPan( |
| LPDIRECTSOUNDBUFFER this,LONG newpan |
| ) { |
| TRACE(dsound,"(%p,%ld)\n",this,newpan); |
| this->pan = newpan; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetPan( |
| LPDIRECTSOUNDBUFFER this,LPLONG pan |
| ) { |
| TRACE(dsound,"(%p,%p)\n",this,pan); |
| *pan = this->pan; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_Unlock( |
| LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2 |
| ) { |
| /* FIXME(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2); */ |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetFrequency( |
| LPDIRECTSOUNDBUFFER this,LPDWORD freq |
| ) { |
| TRACE(dsound,"(%p,%p)\n",this,freq); |
| *freq = this->wfx.nSamplesPerSec; |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_Initialize( |
| LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd |
| ) { |
| FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd); |
| printf("Re-Init!!!\n"); |
| return DSERR_ALREADYINITIALIZED; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_GetCaps( |
| LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps |
| ) { |
| caps->dwSize = sizeof(*caps); |
| caps->dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_STATIC|DSBCAPS_CTRLALL|DSBCAPS_LOCSOFTWARE; |
| caps->dwBufferBytes = 0; |
| caps->dwUnlockTransferRate = 0; |
| caps->dwPlayCpuOverhead = 0; |
| return DS_OK; |
| } |
| |
| static HRESULT WINAPI IDirectSoundBuffer_QueryInterface( |
| LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj |
| ) { |
| char xbuf[50]; |
| |
| if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) { |
| IDirectSoundNotify *dsn; |
| |
| dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn)); |
| dsn->ref = 1; |
| dsn->dsb = this; |
| this->lpvtbl->fnAddRef(this); |
| dsn->lpvtbl = &dsnvt; |
| *ppobj = (LPVOID)dsn; |
| return 0; |
| } |
| WINE_StringFromCLSID(riid,xbuf); |
| TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj); |
| return E_FAIL; |
| } |
| |
| static struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = { |
| IDirectSoundBuffer_QueryInterface, |
| IDirectSoundBuffer_AddRef, |
| IDirectSoundBuffer_Release, |
| IDirectSoundBuffer_GetCaps, |
| IDirectSoundBuffer_GetCurrentPosition, |
| IDirectSoundBuffer_GetFormat, |
| IDirectSoundBuffer_GetVolume, |
| IDirectSoundBuffer_GetPan, |
| IDirectSoundBuffer_GetFrequency, |
| IDirectSoundBuffer_GetStatus, |
| IDirectSoundBuffer_Initialize, |
| IDirectSoundBuffer_Lock, |
| IDirectSoundBuffer_Play, |
| IDirectSoundBuffer_SetCurrentPosition, |
| IDirectSoundBuffer_SetFormat, |
| IDirectSoundBuffer_SetVolume, |
| IDirectSoundBuffer_SetPan, |
| IDirectSoundBuffer_SetFrequency, |
| IDirectSoundBuffer_Stop, |
| IDirectSoundBuffer_Unlock |
| }; |
| |
| /******************************************************************************* |
| * IDirectSound |
| */ |
| |
| static HRESULT WINAPI IDirectSound_SetCooperativeLevel( |
| LPDIRECTSOUND this,HWND32 hwnd,DWORD level |
| ) { |
| FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level); |
| return 0; |
| } |
| |
| |
| static HRESULT WINAPI IDirectSound_CreateSoundBuffer( |
| LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk |
| ) { |
| if (TRACE_ON(dsound)) { |
| TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk); |
| TRACE(dsound,"(size=%ld)\n",dsbd->dwSize); |
| TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags); |
| _dump_DSBCAPS(dsbd->dwFlags); |
| TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes); |
| TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat); |
| } |
| *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer)); |
| (*ppdsb)->ref =1; |
| (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsbd->dwBufferBytes); |
| (*ppdsb)->buflen = dsbd->dwBufferBytes; |
| (*ppdsb)->playpos = 0; |
| (*ppdsb)->writepos = 0; |
| (*ppdsb)->lpvtbl = &dsbvt; |
| (*ppdsb)->dsound = this; |
| (*ppdsb)->playing = 0; |
| (*ppdsb)->volfac = 1.0; |
| memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd)); |
| |
| /* register buffer */ |
| this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1)); |
| this->buffers[this->nrofbuffers] = *ppdsb; |
| this->nrofbuffers++; |
| this->lpvtbl->fnAddRef(this); |
| |
| if (dsbd->lpwfxFormat) dsbvt.fnSetFormat(*ppdsb,dsbd->lpwfxFormat); |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSound_DuplicateSoundBuffer( |
| LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb |
| ) { |
| TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb); |
| |
| *ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer)); |
| (*ppdsb)->ref =1; |
| (*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,pdsb->buflen); |
| memcpy((*ppdsb)->buffer,pdsb->buffer,pdsb->buflen); |
| (*ppdsb)->buflen = pdsb->buflen; |
| (*ppdsb)->playpos = 0; |
| (*ppdsb)->writepos = 0; |
| (*ppdsb)->lpvtbl = &dsbvt; |
| (*ppdsb)->dsound = this; |
| dsbvt.fnSetFormat(*ppdsb,&(pdsb->wfx)); |
| /* register buffer */ |
| this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1)); |
| this->buffers[this->nrofbuffers] = *ppdsb; |
| this->nrofbuffers++; |
| this->lpvtbl->fnAddRef(this); |
| return 0; |
| } |
| |
| |
| static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) { |
| TRACE(dsound,"(%p,%p)\n",this,caps); |
| TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags); |
| |
| caps->dwSize = sizeof(*caps); |
| caps->dwFlags = DSCAPS_PRIMARYSTEREO|DSCAPS_PRIMARY16BIT|DSCAPS_EMULDRIVER|DSCAPS_SECONDARYSTEREO|DSCAPS_SECONDARY16BIT; |
| /* FIXME: query OSS */ |
| caps->dwMinSecondarySampleRate = 22050; |
| caps->dwMaxSecondarySampleRate = 48000; |
| caps->dwPrimaryBuffers = 1; |
| /* FIXME: set the rest... hmm */ |
| return 0; |
| } |
| |
| static ULONG WINAPI IDirectSound_AddRef(LPDIRECTSOUND this) { |
| return ++(this->ref); |
| } |
| |
| static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) { |
| if (!--(this->ref)) { |
| HeapFree(GetProcessHeap(),0,this); |
| dsound = NULL; |
| close(audiofd);audiofd = -1; |
| return 0; |
| } |
| return this->ref; |
| } |
| |
| static HRESULT WINAPI IDirectSound_SetSpeakerConfig( |
| LPDIRECTSOUND this,DWORD config |
| ) { |
| FIXME(dsound,"(%p,0x%08lx):stub\n",this,config); |
| return 0; |
| } |
| |
| static HRESULT WINAPI IDirectSound_QueryInterface( |
| LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj |
| ) { |
| char xbuf[50]; |
| |
| WINE_StringFromCLSID(riid,xbuf); |
| TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj); |
| return E_FAIL; |
| } |
| |
| static struct tagLPDIRECTSOUND_VTABLE dsvt = { |
| IDirectSound_QueryInterface, |
| IDirectSound_AddRef, |
| IDirectSound_Release, |
| IDirectSound_CreateSoundBuffer, |
| IDirectSound_GetCaps, |
| IDirectSound_DuplicateSoundBuffer, |
| IDirectSound_SetCooperativeLevel, |
| (void *)8, |
| (void *)9, |
| IDirectSound_SetSpeakerConfig, |
| (void *)11 |
| }; |
| |
| static int |
| DSOUND_setformat(LPWAVEFORMATEX wfex) { |
| int xx,channels,speed,format,nformat; |
| |
| |
| switch (wfex->wFormatTag) { |
| default: |
| WARN(dsound,"unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag); |
| return DSERR_BADFORMAT; |
| case WAVE_FORMAT_PCM: |
| break; |
| } |
| if (wfex->wBitsPerSample==8) |
| format = AFMT_U8; |
| else |
| format = AFMT_S16_LE; |
| |
| if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) { |
| perror("ioctl SNDCTL_DSP_GETFMTS"); |
| return -1; |
| } |
| if ((xx&format)!=format) {/* format unsupported */ |
| WARN(dsound,"SNDCTL_DSP_GETFMTS: format not supported\n"); |
| return -1; |
| } |
| nformat = format; |
| if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) { |
| perror("ioctl SNDCTL_DSP_SETFMT"); |
| return -1; |
| } |
| if (nformat!=format) {/* didn't work */ |
| WARN(dsound,"SNDCTL_DSP_GETFMTS: format not set\n"); |
| return -1; |
| } |
| |
| channels = wfex->nChannels-1; |
| if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) { |
| perror("ioctl SNDCTL_DSP_STEREO"); |
| return -1; |
| } |
| speed = wfex->nSamplesPerSec; |
| if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) { |
| perror("ioctl SNDCTL_DSP_SPEED"); |
| return -1; |
| } |
| TRACE(dsound,"(freq=%ld,channels=%d,bits=%d)\n", |
| wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample |
| ); |
| return 0; |
| } |
| |
| static LPDSBPOSITIONNOTIFY |
| DSOUND_nextevent(IDirectSoundBuffer *dsb) { |
| int i; |
| |
| if (dsb->nrofnotifies) { |
| for (i=0;i<dsb->nrofnotifies;i++) { |
| if (dsb->playpos<dsb->notifies[i].dwOffset) |
| break; |
| } |
| if (i==dsb->nrofnotifies) |
| i=0; |
| return dsb->notifies+i; |
| } |
| return NULL; |
| } |
| |
| #define CHECK_EVENT \ |
| if (nextevent && (dsb->playpos == nextevent->dwOffset)) { \ |
| SetEvent(nextevent->hEventNotify); \ |
| TRACE(dsound,"signalled event %d\n",nextevent->hEventNotify);\ |
| nextevent = DSOUND_nextevent(dsb); \ |
| } |
| |
| |
| static void |
| DSOUND_MixInBuffer(IDirectSoundBuffer *dsb) { |
| int j,buflen = dsb->buflen; |
| LPDSBPOSITIONNOTIFY nextevent; |
| int xdiff = dsb->wfx.nSamplesPerSec-dsound->wfx.nSamplesPerSec; |
| |
| /* Insomnia - Going along with REAL author's style */ |
| long Rvoldec, Lvoldec; |
| long pan = dsb->pan; |
| long samp; /* temporary sample workspace */ |
| |
| { |
| double tmpr=dsb->volume-500; |
| double tmpl=tmpr; |
| if(pan>0) tmpl -= (double)pan; |
| else tmpr += (double)pan; |
| tmpl /= 1000.0; |
| tmpr /= 1000.0; |
| tmpl = pow(2.0, tmpl); |
| tmpr = pow(2.0, tmpr); |
| tmpl *= 65536; /* Set to the correct multiple times */ |
| tmpr *= 65536; /* 65536 to be convenient for bit shifting */ |
| tmpl += 0.5; /* Add .5 for rounding accuracy */ |
| tmpr += 0.5; |
| Lvoldec = (long)tmpl; |
| Rvoldec = (long)tmpr; |
| } |
| /* End Insomnia's mod */ |
| |
| if (xdiff<0) xdiff=-xdiff; |
| if (xdiff>1500) { |
| WARN(dsound,"mixing in buffer of different frequency (%ld vs %ld), argh!\n", |
| dsb->wfx.nSamplesPerSec,dsound->wfx.nSamplesPerSec); |
| } |
| nextevent = DSOUND_nextevent(dsb); |
| /* TRACE(dsound,"(%d.%d.%d.%d)\n",dsound->wfx.wBitsPerSample,dsb->wfx.wBitsPerSample,dsound->wfx.nChannels,dsb->wfx.nChannels);*/ |
| |
| if (dsound->wfx.wBitsPerSample == 8) { |
| char *playbuf8 = (char*)playbuf; |
| |
| if (dsb->wfx.wBitsPerSample == 8) { |
| unsigned char *xbuf = (unsigned char*)(dsb->buffer); |
| if (dsb->wfx.nChannels == 1) { |
| for (j=0;j<sizeof(playbuf)/2;j++) { |
| |
| dsb->playpos=(dsb->playpos+1)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| /* Left Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Lvoldec; |
| samp >>= 16; |
| samp += playbuf8[(j<<1)]; |
| if(samp > 127L) samp = 127L; |
| else if(samp < -128L) samp = -128L; |
| playbuf8[(j<<1)] = (short)samp; |
| |
| /* Right Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Rvoldec; |
| samp >>= 16; |
| samp += playbuf8[(j<<1)+1]; |
| if(samp > 127L) samp = 127L; |
| else if(samp < -128L) samp = -128L; |
| playbuf8[(j<<1)+1] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } else { |
| for (j=0;j<sizeof(playbuf);j++) { |
| dsb->playpos=(dsb->playpos+1)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| samp = xbuf[dsb->playpos>>1]; |
| |
| /* Right Channel */ |
| if(j&1) samp *= Rvoldec; |
| /* Left Channel */ |
| else samp *= Lvoldec; |
| |
| samp >>= 16; |
| samp += playbuf8[j]; |
| if(samp > 127L) samp = 127L; |
| else if(samp < -128L) samp = -128L; |
| playbuf8[j] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } |
| } else { /* 16 */ |
| short *xbuf = (short*)(dsb->buffer); |
| if (dsb->wfx.nChannels == 1) { |
| for (j=0;j<sizeof(playbuf)/2;j++) { |
| dsb->playpos=(dsb->playpos+2)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| /* Left Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Lvoldec; |
| samp >>= 24; |
| samp += playbuf8[(j<<1)]; |
| if(samp > 127L) samp = 127L; |
| else if(samp < -128L) samp = -128L; |
| playbuf8[(j<<1)] = (short)samp; |
| |
| /* Right Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Rvoldec; |
| samp >>= 24; |
| samp += playbuf8[(j<<1)+1]; |
| if(samp > 127L) samp = 127L; |
| else if(samp < -128L) samp = -128L; |
| playbuf8[(j<<1)+1] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } else { |
| for (j=0;j<sizeof(playbuf);j++) { |
| dsb->playpos=(dsb->playpos+2)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| samp = xbuf[dsb->playpos>>1]; |
| |
| /* Right Channel */ |
| if(j&1) samp *= Rvoldec; |
| /* Left Channel */ |
| else samp *= Lvoldec; |
| |
| samp >>= 24; |
| samp += playbuf8[j]; |
| if(samp > 127L) samp = 127L; |
| else if(samp < -128L) samp = -128L; |
| playbuf8[j] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } |
| } |
| } else { /* 16 bit */ |
| if (dsb->wfx.wBitsPerSample == 8) { |
| /* unsigned char *xbuf = (unsigned char*)(dsb->buffer); */ |
| char *xbuf = dsb->buffer; |
| if (dsb->wfx.nChannels == 1) { |
| WARN(dsound,"Mixing 8-bit stereo into 16!!\n"); |
| for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) { |
| dsb->playpos=(dsb->playpos+1)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| /* Left Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Lvoldec; |
| samp >>= 8; |
| samp += playbuf[(j<<1)]; |
| if(samp > 32767L) samp = 32767L; |
| else if(samp < -32768L) samp = -32768L; |
| playbuf[(j<<1)] = (short)samp; |
| |
| /* Right Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Rvoldec; |
| samp >>= 8; |
| samp += playbuf[(j<<1)+1]; |
| if(samp > 32767L) samp = 32767L; |
| else if(samp < -32768L) samp = -32768L; |
| playbuf[(j<<1)+1] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } else { |
| for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) { |
| dsb->playpos=(dsb->playpos+1)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| samp = xbuf[dsb->playpos>>1]; |
| |
| /* Right Channel */ |
| if(j&1) samp *= Rvoldec; |
| /* Left Channel */ |
| else samp *= Lvoldec; |
| |
| samp >>= 8; |
| samp += playbuf[j]; |
| if(samp > 32767L) samp = 32767L; |
| else if(samp < -32768L) samp = -32768L; |
| playbuf[j] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } |
| } else { /* 16 */ |
| short *xbuf = (short*)(dsb->buffer); |
| if (dsb->wfx.nChannels == 1) { |
| for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0])/2;j++) { |
| dsb->playpos=(dsb->playpos+2)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| /* Left Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Lvoldec; |
| samp >>= 16; |
| samp += playbuf[(j<<1)]; |
| if(samp > 32767L) samp = 32767L; |
| else if(samp < -32768L) samp = -32768L; |
| playbuf[(j<<1)] = (short)samp; |
| |
| /* Right Channel */ |
| samp = xbuf[dsb->playpos>>1]; |
| samp *= Rvoldec; |
| samp >>= 16; |
| samp += playbuf[(j<<1)+1]; |
| if(samp > 32767L) samp = 32767L; |
| else if(samp < -32768L) samp = -32768L; |
| playbuf[(j<<1)+1] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } else { |
| for (j=0;j<sizeof(playbuf)/sizeof(playbuf[0]);j++) { |
| dsb->playpos=(dsb->playpos+2)%buflen; |
| if (!dsb->playpos && !(dsb->playflags&DSBPLAY_LOOPING)) { |
| dsb->playing = 0; |
| dsb->playpos = buflen; |
| return; |
| } |
| /* Insomnia- volume, panning, and correcting against wrap */ |
| samp = xbuf[dsb->playpos>>1]; |
| |
| /* Right Channel */ |
| if(j&1) samp *= Rvoldec; |
| /* Left Channel */ |
| else samp *= Lvoldec; |
| |
| samp >>= 16; |
| samp += playbuf[j]; |
| if(samp > 32767L) samp = 32767L; |
| else if(samp < -32768L) samp = -32768L; |
| playbuf[j] = (short)samp; |
| /* End Insomnia's mod */ |
| |
| CHECK_EVENT |
| } |
| } |
| } |
| } |
| } |
| |
| static DWORD WINAPI |
| DSOUND_thread(LPVOID arg) { |
| int res,i,curleft,playing,haveprimary = 0; |
| |
| TRACE(dsound,"dsound is at pid %d\n",getpid()); |
| while (1) { |
| if (!dsound) { |
| WARN(dsound,"DSOUND thread giving up.\n"); |
| ExitThread(0); |
| } |
| if (getppid()==1) { |
| WARN(dsound,"DSOUND father died? Giving up.\n"); |
| ExitThread(0); |
| } |
| /* RACE: dsound could be deleted */ |
| dsound->lpvtbl->fnAddRef(dsound); |
| if (!dsound->nrofbuffers) { |
| /* no soundbuffer yet... wait. */ |
| Sleep(1000); |
| continue; |
| } |
| memset(playbuf,0,sizeof(playbuf)); |
| playing = 0; |
| dsound->lpvtbl->fnAddRef(dsound); |
| haveprimary = 0; |
| for (i=dsound->nrofbuffers;i--;) { |
| IDirectSoundBuffer *dsb = dsound->buffers[i]; |
| |
| if (!dsb || !dsb->lpvtbl) |
| continue; |
| dsb->lpvtbl->fnAddRef(dsb); |
| if (dsb->playing && dsb->buflen) |
| playing++; |
| if (dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) { |
| haveprimary = 1; |
| if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) { |
| DSOUND_setformat(&(dsb->wfx)); |
| memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx)); |
| } |
| } |
| dsb->lpvtbl->fnRelease(dsb); |
| } |
| /* We have just one playbuffer, so use its format */ |
| if ((playing==1) && !haveprimary) { |
| for (i=dsound->nrofbuffers;i--;) { |
| IDirectSoundBuffer *dsb = dsound->buffers[i]; |
| |
| dsb->lpvtbl->fnAddRef(dsb); |
| if (dsb->playing && dsb->buflen) { |
| if (memcmp(&dsound->wfx,&(dsb->wfx),sizeof(dsound->wfx))) { |
| DSOUND_setformat(&(dsb->wfx)); |
| memcpy(&dsound->wfx,&(dsb->wfx),sizeof(dsb->wfx)); |
| } |
| } |
| dsb->lpvtbl->fnRelease(dsb); |
| } |
| } |
| for (i=dsound->nrofbuffers;i--;) { |
| IDirectSoundBuffer *dsb = dsound->buffers[i]; |
| |
| if (!dsb || !dsb->lpvtbl) |
| continue; |
| dsb->lpvtbl->fnAddRef(dsb); |
| if (dsb->buflen && dsb->playing) { |
| playing++; |
| DSOUND_MixInBuffer(dsb); |
| } |
| dsb->lpvtbl->fnRelease(dsb); |
| } |
| dsound->lpvtbl->fnRelease(dsound); |
| |
| /*fputc('0'+playing,stderr);*/ |
| curleft = 0; |
| while (curleft < sizeof(playbuf)) { |
| res = write(audiofd,(LPBYTE)playbuf+curleft,sizeof(playbuf)-curleft); |
| if (res==-1) { |
| if (errno==EINTR) |
| continue; |
| perror("write audiofd"); |
| ExitThread(0); |
| break; |
| } |
| curleft+=res; |
| } |
| } |
| ExitThread(0); |
| } |
| |
| #endif /* HAVE_OSS */ |
| |
| HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter ) { |
| int xx; |
| if (lpGUID) |
| TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter); |
| #ifdef HAVE_OSS |
| if (audiofd>=0) |
| return DSERR_ALLOCATED; |
| audiofd = open("/dev/audio",O_WRONLY); |
| if (audiofd==-1) { |
| perror("open /dev/audio"); |
| audiofd=0; |
| return DSERR_NODRIVER; |
| } |
| xx=0x0002000c; |
| if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&xx)) |
| perror("ioctl SETFRAGMENT"); |
| /* |
| TRACE(dsound,"SETFRAGMENT. count is now %d, fragsize is %d\n", |
| (xx>>16)+1,xx&0xffff |
| ); |
| */ |
| |
| *ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound)); |
| (*ppDS)->ref = 1; |
| (*ppDS)->lpvtbl = &dsvt; |
| (*ppDS)->buffers = NULL; |
| (*ppDS)->nrofbuffers = 0; |
| |
| (*ppDS)->wfx.wFormatTag = 1; |
| (*ppDS)->wfx.nChannels = 2; |
| (*ppDS)->wfx.nSamplesPerSec = 22050; |
| (*ppDS)->wfx.nAvgBytesPerSec = 44100; |
| (*ppDS)->wfx.nBlockAlign = 2; |
| (*ppDS)->wfx.wBitsPerSample = 8; |
| |
| DSOUND_setformat(&((*ppDS)->wfx)); |
| |
| if (!dsound) { |
| HANDLE32 hnd; |
| DWORD xid; |
| |
| dsound = (*ppDS); |
| hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid); |
| } |
| return 0; |
| #else |
| MessageBox32A(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP); |
| return DSERR_NODRIVER; |
| #endif |
| } |
| |
| |
| /******************************************************************************* |
| * DllGetClassObject [DSOUND.4] |
| * Retrieves class object from a DLL object |
| * |
| * NOTES |
| * Docs say returns STDAPI |
| * |
| * PARAMS |
| * rclsid [I] CLSID for the class object |
| * riid [I] Reference to identifier of interface for class object |
| * ppv [O] Address of variable to receive interface pointer for riid |
| * |
| * RETURNS |
| * Success: S_OK |
| * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG, |
| * E_UNEXPECTED |
| */ |
| DWORD WINAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID *ppv ) |
| { |
| FIXME(dsound, "(%p,%p,%p): stub\n", rclsid, riid, ppv); |
| return S_OK; |
| } |
| |
| |
| /******************************************************************************* |
| * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use. |
| * |
| * RETURNS |
| * Success: S_OK |
| * Failure: S_FALSE |
| */ |
| DWORD WINAPI DllCanUnloadNow(void) |
| { |
| FIXME(dsound, "(void): stub\n"); |
| return S_FALSE; |
| } |