File: win32\snd_win.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 #include <float.h>
   21 
   22 #include "../client/client.h"
   23 #include "../client/snd_loc.h"
   24 #include "winquake.h"
   25 
   26 #define iDirectSoundCreate(a,b,c)       pDirectSoundCreate(a,b,c)
   27 
   28 HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
   29 
   30 // 64K is > 1 second at 16-bit, 22050 Hz
   31 #define WAV_BUFFERS                             64
   32 #define WAV_MASK                                0x3F
   33 #define WAV_BUFFER_SIZE                 0x0400
   34 #define SECONDARY_BUFFER_SIZE   0x10000
   35 
   36 typedef enum {SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL} sndinitstat;
   37 
   38 cvar_t  *s_wavonly;
   39 
   40 static qboolean dsound_init;
   41 static qboolean wav_init;
   42 static qboolean snd_firsttime = true, snd_isdirect, snd_iswave;
   43 static qboolean primary_format_set;
   44 
   45 // starts at 0 for disabled
   46 static int      snd_buffer_count = 0;
   47 static int      sample16;
   48 static int      snd_sent, snd_completed;
   49 
   50 /* 
   51  * Global variables. Must be visible to window-procedure function 
   52  *  so it can unlock and free the data block after it has been played. 
   53  */ 
   54 
   55 
   56 HANDLE          hData;
   57 HPSTR           lpData, lpData2;
   58 
   59 HGLOBAL         hWaveHdr;
   60 LPWAVEHDR       lpWaveHdr;
   61 
   62 HWAVEOUT    hWaveOut; 
   63 
   64 WAVEOUTCAPS     wavecaps;
   65 
   66 DWORD   gSndBufSize;
   67 
   68 MMTIME          mmstarttime;
   69 
   70 LPDIRECTSOUND pDS;
   71 LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
   72 
   73 HINSTANCE hInstDS;
   74 
   75 qboolean SNDDMA_InitDirect (void);
   76 qboolean SNDDMA_InitWav (void);
   77 
   78 void FreeSound( void );
   79 
   80 static const char *DSoundError( int error )
   81 {
   82         switch ( error )
   83         {
   84         case DSERR_BUFFERLOST:
   85                 return "DSERR_BUFFERLOST";
   86         case DSERR_INVALIDCALL:
   87                 return "DSERR_INVALIDCALLS";
   88         case DSERR_INVALIDPARAM:
   89                 return "DSERR_INVALIDPARAM";
   90         case DSERR_PRIOLEVELNEEDED:
   91                 return "DSERR_PRIOLEVELNEEDED";
   92         }
   93 
   94         return "unknown";
   95 }
   96 
   97 /*
   98 ** DS_CreateBuffers
   99 */
  100 static qboolean DS_CreateBuffers( void )
  101 {
  102         DSBUFFERDESC    dsbuf;
  103         DSBCAPS                 dsbcaps;
  104         WAVEFORMATEX    pformat, format;
  105         DWORD                   dwWrite;
  106 
  107         memset (&format, 0, sizeof(format));
  108         format.wFormatTag = WAVE_FORMAT_PCM;
  109     format.nChannels = dma.channels;
  110     format.wBitsPerSample = dma.samplebits;
  111     format.nSamplesPerSec = dma.speed;
  112     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
  113     format.cbSize = 0;
  114     format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign; 
  115 
  116         Com_Printf( "Creating DS buffers\n" );
  117 
  118         Com_DPrintf("...setting EXCLUSIVE coop level: " );
  119         if ( DS_OK != pDS->lpVtbl->SetCooperativeLevel( pDS, cl_hwnd, DSSCL_EXCLUSIVE ) )
  120         {
  121                 Com_Printf ("failed\n");
  122                 FreeSound ();
  123                 return false;
  124         }
  125         Com_DPrintf("ok\n" );
  126 
  127 // get access to the primary buffer, if possible, so we can set the
  128 // sound hardware format
  129         memset (&dsbuf, 0, sizeof(dsbuf));
  130         dsbuf.dwSize = sizeof(DSBUFFERDESC);
  131         dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
  132         dsbuf.dwBufferBytes = 0;
  133         dsbuf.lpwfxFormat = NULL;
  134 
  135         memset(&dsbcaps, 0, sizeof(dsbcaps));
  136         dsbcaps.dwSize = sizeof(dsbcaps);
  137         primary_format_set = false;
  138 
  139         Com_DPrintf( "...creating primary buffer: " );
  140         if (DS_OK == pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSPBuf, NULL))
  141         {
  142                 pformat = format;
  143 
  144                 Com_DPrintf( "ok\n" );
  145                 if (DS_OK != pDSPBuf->lpVtbl->SetFormat (pDSPBuf, &pformat))
  146                 {
  147                         if (snd_firsttime)
  148                                 Com_DPrintf ("...setting primary sound format: failed\n");
  149                 }
  150                 else
  151                 {
  152                         if (snd_firsttime)
  153                                 Com_DPrintf ("...setting primary sound format: ok\n");
  154 
  155                         primary_format_set = true;
  156                 }
  157         }
  158         else
  159                 Com_Printf( "failed\n" );
  160 
  161         if ( !primary_format_set || !s_primary->value)
  162         {
  163         // create the secondary buffer we'll actually work with
  164                 memset (&dsbuf, 0, sizeof(dsbuf));
  165                 dsbuf.dwSize = sizeof(DSBUFFERDESC);
  166                 dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE;
  167                 dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
  168                 dsbuf.lpwfxFormat = &format;
  169 
  170                 memset(&dsbcaps, 0, sizeof(dsbcaps));
  171                 dsbcaps.dwSize = sizeof(dsbcaps);
  172 
  173                 Com_DPrintf( "...creating secondary buffer: " );
  174                 if (DS_OK != pDS->lpVtbl->CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL))
  175                 {
  176                         Com_Printf( "failed\n" );
  177                         FreeSound ();
  178                         return false;
  179                 }
  180                 Com_DPrintf( "ok\n" );
  181 
  182                 dma.channels = format.nChannels;
  183                 dma.samplebits = format.wBitsPerSample;
  184                 dma.speed = format.nSamplesPerSec;
  185 
  186                 if (DS_OK != pDSBuf->lpVtbl->GetCaps (pDSBuf, &dsbcaps))
  187                 {
  188                         Com_Printf ("*** GetCaps failed ***\n");
  189                         FreeSound ();
  190                         return false;
  191                 }
  192 
  193                 Com_Printf ("...using secondary sound buffer\n");
  194         }
  195         else
  196         {
  197                 Com_Printf( "...using primary buffer\n" );
  198 
  199                 Com_DPrintf( "...setting WRITEPRIMARY coop level: " );
  200                 if (DS_OK != pDS->lpVtbl->SetCooperativeLevel (pDS, cl_hwnd, DSSCL_WRITEPRIMARY))
  201                 {
  202                         Com_Printf( "failed\n" );
  203                         FreeSound ();
  204                         return false;
  205                 }
  206                 Com_DPrintf( "ok\n" );
  207 
  208                 if (DS_OK != pDSPBuf->lpVtbl->GetCaps (pDSPBuf, &dsbcaps))
  209                 {
  210                         Com_Printf ("*** GetCaps failed ***\n");
  211                         return false;
  212                 }
  213 
  214                 pDSBuf = pDSPBuf;
  215         }
  216 
  217         // Make sure mixer is active
  218         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
  219 
  220         if (snd_firsttime)
  221                 Com_Printf("   %d channel(s)\n"
  222                                "   %d bits/sample\n"
  223                                            "   %d bytes/sec\n",
  224                                            dma.channels, dma.samplebits, dma.speed);
  225         
  226         gSndBufSize = dsbcaps.dwBufferBytes;
  227 
  228         /* we don't want anyone to access the buffer directly w/o locking it first. */
  229         lpData = NULL; 
  230 
  231         pDSBuf->lpVtbl->Stop(pDSBuf);
  232         pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite);
  233         pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
  234 
  235         dma.samples = gSndBufSize/(dma.samplebits/8);
  236         dma.samplepos = 0;
  237         dma.submission_chunk = 1;
  238         dma.buffer = (unsigned char *) lpData;
  239         sample16 = (dma.samplebits/8) - 1;
  240 
  241         return true;
  242 }
  243 
  244 /*
  245 ** DS_DestroyBuffers
  246 */
  247 static void DS_DestroyBuffers( void )
  248 {
  249         Com_DPrintf( "Destroying DS buffers\n" );
  250         if ( pDS )
  251         {
  252                 Com_DPrintf( "...setting NORMAL coop level\n" );
  253                 pDS->lpVtbl->SetCooperativeLevel( pDS, cl_hwnd, DSSCL_NORMAL );
  254         }
  255 
  256         if ( pDSBuf )
  257         {
  258                 Com_DPrintf( "...stopping and releasing sound buffer\n" );
  259                 pDSBuf->lpVtbl->Stop( pDSBuf );
  260                 pDSBuf->lpVtbl->Release( pDSBuf );
  261         }
  262 
  263         // only release primary buffer if it's not also the mixing buffer we just released
  264         if ( pDSPBuf && ( pDSBuf != pDSPBuf ) )
  265         {
  266                 Com_DPrintf( "...releasing primary buffer\n" );
  267                 pDSPBuf->lpVtbl->Release( pDSPBuf );
  268         }
  269         pDSBuf = NULL;
  270         pDSPBuf = NULL;
  271 
  272         dma.buffer = NULL;
  273 }
  274 
  275 /*
  276 ==================
  277 FreeSound
  278 ==================
  279 */
  280 void FreeSound (void)
  281 {
  282         int             i;
  283 
  284         Com_DPrintf( "Shutting down sound system\n" );
  285 
  286         if ( pDS )
  287                 DS_DestroyBuffers();
  288 
  289         if ( hWaveOut )
  290         {
  291                 Com_DPrintf( "...resetting waveOut\n" );
  292                 waveOutReset (hWaveOut);
  293 
  294                 if (lpWaveHdr)
  295                 {
  296                         Com_DPrintf( "...unpreparing headers\n" );
  297                         for (i=0 ; i< WAV_BUFFERS ; i++)
  298                                 waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
  299                 }
  300 
  301                 Com_DPrintf( "...closing waveOut\n" );
  302                 waveOutClose (hWaveOut);
  303 
  304                 if (hWaveHdr)
  305                 {
  306                         Com_DPrintf( "...freeing WAV header\n" );
  307                         GlobalUnlock(hWaveHdr);
  308                         GlobalFree(hWaveHdr);
  309                 }
  310 
  311                 if (hData)
  312                 {
  313                         Com_DPrintf( "...freeing WAV buffer\n" );
  314                         GlobalUnlock(hData);
  315                         GlobalFree(hData);
  316                 }
  317 
  318         }
  319 
  320         if ( pDS )
  321         {
  322                 Com_DPrintf( "...releasing DS object\n" );
  323                 pDS->lpVtbl->Release( pDS );
  324         }
  325 
  326         if ( hInstDS )
  327         {
  328                 Com_DPrintf( "...freeing DSOUND.DLL\n" );
  329                 FreeLibrary( hInstDS );
  330                 hInstDS = NULL;
  331         }
  332 
  333         pDS = NULL;
  334         pDSBuf = NULL;
  335         pDSPBuf = NULL;
  336         hWaveOut = 0;
  337         hData = 0;
  338         hWaveHdr = 0;
  339         lpData = NULL;
  340         lpWaveHdr = NULL;
  341         dsound_init = false;
  342         wav_init = false;
  343 }
  344 
  345 /*
  346 ==================
  347 SNDDMA_InitDirect
  348 
  349 Direct-Sound support
  350 ==================
  351 */
  352 sndinitstat SNDDMA_InitDirect (void)
  353 {
  354         DSCAPS                  dscaps;
  355         HRESULT                 hresult;
  356 
  357         dma.channels = 2;
  358         dma.samplebits = 16;
  359 
  360         if (s_khz->value == 44)
  361                 dma.speed = 44100;
  362         if (s_khz->value == 22)
  363                 dma.speed = 22050;
  364         else
  365                 dma.speed = 11025;
  366 
  367         Com_Printf( "Initializing DirectSound\n");
  368 
  369         if ( !hInstDS )
  370         {
  371                 Com_DPrintf( "...loading dsound.dll: " );
  372 
  373                 hInstDS = LoadLibrary("dsound.dll");
  374                 
  375                 if (hInstDS == NULL)
  376                 {
  377                         Com_Printf ("failed\n");
  378                         return SIS_FAILURE;
  379                 }
  380 
  381                 Com_DPrintf ("ok\n");
  382                 pDirectSoundCreate = (void *)GetProcAddress(hInstDS,"DirectSoundCreate");
  383 
  384                 if (!pDirectSoundCreate)
  385                 {
  386                         Com_Printf ("*** couldn't get DS proc addr ***\n");
  387                         return SIS_FAILURE;
  388                 }
  389         }
  390 
  391         Com_DPrintf( "...creating DS object: " );
  392         while ( ( hresult = iDirectSoundCreate( NULL, &pDS, NULL ) ) != DS_OK )
  393         {
  394                 if (hresult != DSERR_ALLOCATED)
  395                 {
  396                         Com_Printf( "failed\n" );
  397                         return SIS_FAILURE;
  398                 }
  399 
  400                 if (MessageBox (NULL,
  401                                                 "The sound hardware is in use by another app.\n\n"
  402                                             "Select Retry to try to start sound again or Cancel to run Quake with no sound.",
  403                                                 "Sound not available",
  404                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
  405                 {
  406                         Com_Printf ("failed, hardware already in use\n" );
  407                         return SIS_NOTAVAIL;
  408                 }
  409         }
  410         Com_DPrintf( "ok\n" );
  411 
  412         dscaps.dwSize = sizeof(dscaps);
  413 
  414         if ( DS_OK != pDS->lpVtbl->GetCaps( pDS, &dscaps ) )
  415         {
  416                 Com_Printf ("*** couldn't get DS caps ***\n");
  417         }
  418 
  419         if ( dscaps.dwFlags & DSCAPS_EMULDRIVER )
  420         {
  421                 Com_DPrintf ("...no DSound driver found\n" );
  422                 FreeSound();
  423                 return SIS_FAILURE;
  424         }
  425 
  426         if ( !DS_CreateBuffers() )
  427                 return SIS_FAILURE;
  428 
  429         dsound_init = true;
  430 
  431         Com_DPrintf("...completed successfully\n" );
  432 
  433         return SIS_SUCCESS;
  434 }
  435 
  436 
  437 /*
  438 ==================
  439 SNDDM_InitWav
  440 
  441 Crappy windows multimedia base
  442 ==================
  443 */
  444 qboolean SNDDMA_InitWav (void)
  445 {
  446         WAVEFORMATEX  format; 
  447         int                             i;
  448         HRESULT                 hr;
  449 
  450         Com_Printf( "Initializing wave sound\n" );
  451         
  452         snd_sent = 0;
  453         snd_completed = 0;
  454 
  455         dma.channels = 2;
  456         dma.samplebits = 16;
  457 
  458         if (s_khz->value == 44)
  459                 dma.speed = 44100;
  460         if (s_khz->value == 22)
  461                 dma.speed = 22050;
  462         else
  463                 dma.speed = 11025;
  464 
  465         memset (&format, 0, sizeof(format));
  466         format.wFormatTag = WAVE_FORMAT_PCM;
  467         format.nChannels = dma.channels;
  468         format.wBitsPerSample = dma.samplebits;
  469         format.nSamplesPerSec = dma.speed;
  470         format.nBlockAlign = format.nChannels
  471                 *format.wBitsPerSample / 8;
  472         format.cbSize = 0;
  473         format.nAvgBytesPerSec = format.nSamplesPerSec
  474                 *format.nBlockAlign; 
  475         
  476         /* Open a waveform device for output using window callback. */ 
  477         Com_DPrintf ("...opening waveform device: ");
  478         while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER, 
  479                                         &format, 
  480                                         0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
  481         {
  482                 if (hr != MMSYSERR_ALLOCATED)
  483                 {
  484                         Com_Printf ("failed\n");
  485                         return false;
  486                 }
  487 
  488                 if (MessageBox (NULL,
  489                                                 "The sound hardware is in use by another app.\n\n"
  490                                             "Select Retry to try to start sound again or Cancel to run Quake 2 with no sound.",
  491                                                 "Sound not available",
  492                                                 MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
  493                 {
  494                         Com_Printf ("hw in use\n" );
  495                         return false;
  496                 }
  497         } 
  498         Com_DPrintf( "ok\n" );
  499 
  500         /* 
  501          * Allocate and lock memory for the waveform data. The memory 
  502          * for waveform data must be globally allocated with 
  503          * GMEM_MOVEABLE and GMEM_SHARE flags. 
  504 
  505         */ 
  506         Com_DPrintf ("...allocating waveform buffer: ");
  507         gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
  508         hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); 
  509         if (!hData) 
  510         { 
  511                 Com_Printf( " failed\n" );
  512                 FreeSound ();
  513                 return false; 
  514         }
  515         Com_DPrintf( "ok\n" );
  516 
  517         Com_DPrintf ("...locking waveform buffer: ");
  518         lpData = GlobalLock(hData);
  519         if (!lpData)
  520         { 
  521                 Com_Printf( " failed\n" );
  522                 FreeSound ();
  523                 return false; 
  524         } 
  525         memset (lpData, 0, gSndBufSize);
  526         Com_DPrintf( "ok\n" );
  527 
  528         /* 
  529          * Allocate and lock memory for the header. This memory must 
  530          * also be globally allocated with GMEM_MOVEABLE and 
  531          * GMEM_SHARE flags. 
  532          */ 
  533         Com_DPrintf ("...allocating waveform header: ");
  534         hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, 
  535                 (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS); 
  536 
  537         if (hWaveHdr == NULL)
  538         { 
  539                 Com_Printf( "failed\n" );
  540                 FreeSound ();
  541                 return false; 
  542         } 
  543         Com_DPrintf( "ok\n" );
  544 
  545         Com_DPrintf ("...locking waveform header: ");
  546         lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr); 
  547 
  548         if (lpWaveHdr == NULL)
  549         { 
  550                 Com_Printf( "failed\n" );
  551                 FreeSound ();
  552                 return false; 
  553         }
  554         memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
  555         Com_DPrintf( "ok\n" );
  556 
  557         /* After allocation, set up and prepare headers. */ 
  558         Com_DPrintf ("...preparing headers: ");
  559         for (i=0 ; i<WAV_BUFFERS ; i++)
  560         {
  561                 lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE; 
  562                 lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
  563 
  564                 if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
  565                                 MMSYSERR_NOERROR)
  566                 {
  567                         Com_Printf ("failed\n");
  568                         FreeSound ();
  569                         return false;
  570                 }
  571         }
  572         Com_DPrintf ("ok\n");
  573 
  574         dma.samples = gSndBufSize/(dma.samplebits/8);
  575         dma.samplepos = 0;
  576         dma.submission_chunk = 512;
  577         dma.buffer = (unsigned char *) lpData;
  578         sample16 = (dma.samplebits/8) - 1;
  579 
  580         wav_init = true;
  581 
  582         return true;
  583 }
  584 
  585 /*
  586 ==================
  587 SNDDMA_Init
  588 
  589 Try to find a sound device to mix for.
  590 Returns false if nothing is found.
  591 ==================
  592 */
  593 int SNDDMA_Init(void)
  594 {
  595         sndinitstat     stat;
  596 
  597         memset ((void *)&dma, 0, sizeof (dma));
  598 
  599         s_wavonly = Cvar_Get ("s_wavonly", "0", 0);
  600 
  601         dsound_init = wav_init = 0;
  602 
  603         stat = SIS_FAILURE;     // assume DirectSound won't initialize
  604 
  605         /* Init DirectSound */
  606         if (!s_wavonly->value)
  607         {
  608                 if (snd_firsttime || snd_isdirect)
  609                 {
  610                         stat = SNDDMA_InitDirect ();
  611 
  612                         if (stat == SIS_SUCCESS)
  613                         {
  614                                 snd_isdirect = true;
  615 
  616                                 if (snd_firsttime)
  617                                         Com_Printf ("dsound init succeeded\n" );
  618                         }
  619                         else
  620                         {
  621                                 snd_isdirect = false;
  622                                 Com_Printf ("*** dsound init failed ***\n");
  623                         }
  624                 }
  625         }
  626 
  627 // if DirectSound didn't succeed in initializing, try to initialize
  628 // waveOut sound, unless DirectSound failed because the hardware is
  629 // already allocated (in which case the user has already chosen not
  630 // to have sound)
  631         if (!dsound_init && (stat != SIS_NOTAVAIL))
  632         {
  633                 if (snd_firsttime || snd_iswave)
  634                 {
  635 
  636                         snd_iswave = SNDDMA_InitWav ();
  637 
  638                         if (snd_iswave)
  639                         {
  640                                 if (snd_firsttime)
  641                                         Com_Printf ("Wave sound init succeeded\n");
  642                         }
  643                         else
  644                         {
  645                                 Com_Printf ("Wave sound init failed\n");
  646                         }
  647                 }
  648         }
  649 
  650         snd_firsttime = false;
  651 
  652         snd_buffer_count = 1;
  653 
  654         if (!dsound_init && !wav_init)
  655         {
  656                 if (snd_firsttime)
  657                         Com_Printf ("*** No sound device initialized ***\n");
  658 
  659                 return 0;
  660         }
  661 
  662         return 1;
  663 }
  664 
  665 /*
  666 ==============
  667 SNDDMA_GetDMAPos
  668 
  669 return the current sample position (in mono samples read)
  670 inside the recirculating dma buffer, so the mixing code will know
  671 how many sample are required to fill it up.
  672 ===============
  673 */
  674 int SNDDMA_GetDMAPos(void)
  675 {
  676         MMTIME  mmtime;
  677         int             s;
  678         DWORD   dwWrite;
  679 
  680         if (dsound_init) 
  681         {
  682                 mmtime.wType = TIME_SAMPLES;
  683                 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite);
  684                 s = mmtime.u.sample - mmstarttime.u.sample;
  685         }
  686         else if (wav_init)
  687         {
  688                 s = snd_sent * WAV_BUFFER_SIZE;
  689         }
  690 
  691 
  692         s >>= sample16;
  693 
  694         s &= (dma.samples-1);
  695 
  696         return s;
  697 }
  698 
  699 /*
  700 ==============
  701 SNDDMA_BeginPainting
  702 
  703 Makes sure dma.buffer is valid
  704 ===============
  705 */
  706 DWORD   locksize;
  707 void SNDDMA_BeginPainting (void)
  708 {
  709         int             reps;
  710         DWORD   dwSize2;
  711         DWORD   *pbuf, *pbuf2;
  712         HRESULT hresult;
  713         DWORD   dwStatus;
  714 
  715         if (!pDSBuf)
  716                 return;
  717 
  718         // if the buffer was lost or stopped, restore it and/or restart it
  719         if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DS_OK)
  720                 Com_Printf ("Couldn't get sound buffer status\n");
  721         
  722         if (dwStatus & DSBSTATUS_BUFFERLOST)
  723                 pDSBuf->lpVtbl->Restore (pDSBuf);
  724         
  725         if (!(dwStatus & DSBSTATUS_PLAYING))
  726                 pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING);
  727 
  728         // lock the dsound buffer
  729 
  730         reps = 0;
  731         dma.buffer = NULL;
  732 
  733         while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &locksize, 
  734                                                                    &pbuf2, &dwSize2, 0)) != DS_OK)
  735         {
  736                 if (hresult != DSERR_BUFFERLOST)
  737                 {
  738                         Com_Printf( "S_TransferStereo16: Lock failed with error '%s'\n", DSoundError( hresult ) );
  739                         S_Shutdown ();
  740                         return;
  741                 }
  742                 else
  743                 {
  744                         pDSBuf->lpVtbl->Restore( pDSBuf );
  745                 }
  746 
  747                 if (++reps > 2)
  748                         return;
  749         }
  750         dma.buffer = (unsigned char *)pbuf;
  751 }
  752 
  753 /*
  754 ==============
  755 SNDDMA_Submit
  756 
  757 Send sound to device if buffer isn't really the dma buffer
  758 Also unlocks the dsound buffer
  759 ===============
  760 */
  761 void SNDDMA_Submit(void)
  762 {
  763         LPWAVEHDR       h;
  764         int                     wResult;
  765 
  766         if (!dma.buffer)
  767                 return;
  768 
  769         // unlock the dsound buffer
  770         if (pDSBuf)
  771                 pDSBuf->lpVtbl->Unlock(pDSBuf, dma.buffer, locksize, NULL, 0);
  772 
  773         if (!wav_init)
  774                 return;
  775 
  776         //
  777         // find which sound blocks have completed
  778         //
  779         while (1)
  780         {
  781                 if ( snd_completed == snd_sent )
  782                 {
  783                         Com_DPrintf ("Sound overrun\n");
  784                         break;
  785                 }
  786 
  787                 if ( ! (lpWaveHdr[ snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
  788                 {
  789                         break;
  790                 }
  791 
  792                 snd_completed++;        // this buffer has been played
  793         }
  794 
  795 //Com_Printf ("completed %i\n", snd_completed);
  796         //
  797         // submit a few new sound blocks
  798         //
  799         while (((snd_sent - snd_completed) >> sample16) < 8)
  800         {
  801                 h = lpWaveHdr + ( snd_sent&WAV_MASK );
  802         if (paintedtime/256 <= snd_sent)
  803                 break;  //      Com_Printf ("submit overrun\n");
  804 //Com_Printf ("send %i\n", snd_sent);
  805                 snd_sent++;
  806                 /* 
  807                  * Now the data block can be sent to the output device. The 
  808                  * waveOutWrite function returns immediately and waveform 
  809                  * data is sent to the output device in the background. 
  810                  */ 
  811                 wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR)); 
  812 
  813                 if (wResult != MMSYSERR_NOERROR)
  814                 { 
  815                         Com_Printf ("Failed to write block to device\n");
  816                         FreeSound ();
  817                         return; 
  818                 } 
  819         }
  820 }
  821 
  822 /*
  823 ==============
  824 SNDDMA_Shutdown
  825 
  826 Reset the sound device for exiting
  827 ===============
  828 */
  829 void SNDDMA_Shutdown(void)
  830 {
  831         FreeSound ();
  832 }
  833 
  834 
  835 /*
  836 ===========
  837 S_Activate
  838 
  839 Called when the main window gains or loses focus.
  840 The window have been destroyed and recreated
  841 between a deactivate and an activate.
  842 ===========
  843 */
  844 void S_Activate (qboolean active)
  845 {
  846         if ( active )
  847         {
  848                 if ( pDS && cl_hwnd && snd_isdirect )
  849                 {
  850                         DS_CreateBuffers();
  851                 }
  852         }
  853         else
  854         {
  855                 if ( pDS && cl_hwnd && snd_isdirect )
  856                 {
  857                         DS_DestroyBuffers();
  858                 }
  859         }
  860 }
  861 
  862