src/audio/winmm/SDL_winmm.c
author Sam Lantinga
Thu, 05 Jul 2012 12:16:44 -0400
changeset 6352 a9bcd26e7105
parent 6138 4c64952a58fb
child 6430 48d519500f7e
permissions -rwxr-xr-x
Removed unneeded audio buffer memset() for consistent behavior on all platforms.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 #if SDL_AUDIO_DRIVER_WINMM
    24 
    25 /* Allow access to a raw mixing buffer */
    26 
    27 #include "../../core/windows/SDL_windows.h"
    28 #include <mmsystem.h>
    29 
    30 #include "SDL_timer.h"
    31 #include "SDL_audio.h"
    32 #include "../SDL_audio_c.h"
    33 #include "SDL_winmm.h"
    34 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
    35 #include "win_ce_semaphore.h"
    36 #endif
    37 
    38 #define DETECT_DEV_IMPL(typ, capstyp) \
    39 static void DetectWave##typ##Devs(SDL_AddAudioDevice addfn) { \
    40     const UINT devcount = wave##typ##GetNumDevs(); \
    41     capstyp caps; \
    42     UINT i; \
    43     for (i = 0; i < devcount; i++) { \
    44         if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
    45             char *name = WIN_StringToUTF8(caps.szPname); \
    46             if (name != NULL) { \
    47                 addfn(name); \
    48                 SDL_free(name); \
    49             } \
    50         } \
    51     } \
    52 }
    53 
    54 DETECT_DEV_IMPL(Out, WAVEOUTCAPS)
    55 DETECT_DEV_IMPL(In, WAVEINCAPS)
    56 
    57 static void
    58 WINMM_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
    59 {
    60     if (iscapture) {
    61         DetectWaveInDevs(addfn);
    62     } else {
    63         DetectWaveOutDevs(addfn);
    64     }
    65 }
    66 
    67 static void CALLBACK
    68 CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
    69           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    70 {
    71     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    72 
    73     /* Only service "buffer is filled" messages */
    74     if (uMsg != WIM_DATA)
    75         return;
    76 
    77     /* Signal that we have a new buffer of data */
    78 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
    79     ReleaseSemaphoreCE(this->hidden->audio_sem, 1, NULL);
    80 #else
    81     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    82 #endif
    83 }
    84 
    85 
    86 /* The Win32 callback for filling the WAVE device */
    87 static void CALLBACK
    88 FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
    89           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    90 {
    91     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    92 
    93     /* Only service "buffer done playing" messages */
    94     if (uMsg != WOM_DONE)
    95         return;
    96 
    97     /* Signal that we are done playing a buffer */
    98 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
    99     ReleaseSemaphoreCE(this->hidden->audio_sem, 1, NULL);
   100 #else
   101     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
   102 #endif
   103 }
   104 
   105 static void
   106 SetMMerror(char *function, MMRESULT code)
   107 {
   108     size_t len;
   109     char errbuf[MAXERRORLENGTH];
   110     wchar_t werrbuf[MAXERRORLENGTH];
   111 
   112     SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
   113     len = SDL_strlen(errbuf);
   114 
   115     waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
   116     WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
   117                         MAXERRORLENGTH - len, NULL, NULL);
   118 
   119     SDL_SetError("%s", errbuf);
   120 }
   121 
   122 static void
   123 WINMM_WaitDevice(_THIS)
   124 {
   125     /* Wait for an audio chunk to finish */
   126 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   127     WaitForSemaphoreCE(this->hidden->audio_sem, INFINITE);
   128 #else
   129     WaitForSingleObject(this->hidden->audio_sem, INFINITE);
   130 #endif
   131 }
   132 
   133 static Uint8 *
   134 WINMM_GetDeviceBuf(_THIS)
   135 {
   136     return (Uint8 *) (this->hidden->
   137                       wavebuf[this->hidden->next_buffer].lpData);
   138 }
   139 
   140 static void
   141 WINMM_PlayDevice(_THIS)
   142 {
   143     /* Queue it up */
   144     waveOutWrite(this->hidden->hout,
   145                  &this->hidden->wavebuf[this->hidden->next_buffer],
   146                  sizeof(this->hidden->wavebuf[0]));
   147     this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
   148 }
   149 
   150 static void
   151 WINMM_WaitDone(_THIS)
   152 {
   153     int i, left;
   154 
   155     do {
   156         left = NUM_BUFFERS;
   157         for (i = 0; i < NUM_BUFFERS; ++i) {
   158             if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
   159                 --left;
   160             }
   161         }
   162         if (left > 0) {
   163             SDL_Delay(100);
   164         }
   165     } while (left > 0);
   166 }
   167 
   168 static void
   169 WINMM_CloseDevice(_THIS)
   170 {
   171     /* Close up audio */
   172     if (this->hidden != NULL) {
   173         int i;
   174 
   175         if (this->hidden->audio_sem) {
   176 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   177             CloseSynchHandle(this->hidden->audio_sem);
   178 #else
   179             CloseHandle(this->hidden->audio_sem);
   180 #endif
   181             this->hidden->audio_sem = 0;
   182         }
   183 
   184         if (this->hidden->hin) {
   185             waveInClose(this->hidden->hin);
   186             this->hidden->hin = 0;
   187         }
   188 
   189         if (this->hidden->hout) {
   190             waveOutClose(this->hidden->hout);
   191             this->hidden->hout = 0;
   192         }
   193 
   194         /* Clean up mixing buffers */
   195         for (i = 0; i < NUM_BUFFERS; ++i) {
   196             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
   197                 waveOutUnprepareHeader(this->hidden->hout,
   198                                        &this->hidden->wavebuf[i],
   199                                        sizeof(this->hidden->wavebuf[i]));
   200                 this->hidden->wavebuf[i].dwUser = 0xFFFF;
   201             }
   202         }
   203 
   204         if (this->hidden->mixbuf != NULL) {
   205             /* Free raw mixing buffer */
   206             SDL_free(this->hidden->mixbuf);
   207             this->hidden->mixbuf = NULL;
   208         }
   209 
   210         SDL_free(this->hidden);
   211         this->hidden = NULL;
   212     }
   213 }
   214 
   215 static int
   216 WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
   217 {
   218     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   219     int valid_datatype = 0;
   220     MMRESULT result;
   221     WAVEFORMATEX waveformat;
   222     UINT_PTR devId = WAVE_MAPPER;  /* WAVE_MAPPER == choose system's default */
   223     char *utf8 = NULL;
   224     int i;
   225 
   226     if (devname != NULL) {  /* specific device requested? */
   227         if (iscapture) {
   228             const int devcount = (int) waveInGetNumDevs();
   229             WAVEINCAPS caps;
   230             for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
   231                 result = waveInGetDevCaps(i, &caps, sizeof (caps));
   232                 if (result != MMSYSERR_NOERROR)
   233                     continue;
   234                 else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
   235                     continue;
   236                 else if (SDL_strcmp(devname, utf8) == 0)
   237                     devId = (UINT_PTR) i;
   238                 SDL_free(utf8);
   239             }
   240         } else {
   241             const int devcount = (int) waveOutGetNumDevs();
   242             WAVEOUTCAPS caps;
   243             for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
   244                 result = waveOutGetDevCaps(i, &caps, sizeof (caps));
   245                 if (result != MMSYSERR_NOERROR)
   246                     continue;
   247                 else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
   248                     continue;
   249                 else if (SDL_strcmp(devname, utf8) == 0)
   250                     devId = (UINT_PTR) i;
   251                 SDL_free(utf8);
   252             }
   253         }
   254 
   255         if (devId == WAVE_MAPPER) {
   256             SDL_SetError("Requested device not found");
   257             return 0;
   258         }
   259     }
   260 
   261     /* Initialize all variables that we clean on shutdown */
   262     this->hidden = (struct SDL_PrivateAudioData *)
   263         SDL_malloc((sizeof *this->hidden));
   264     if (this->hidden == NULL) {
   265         SDL_OutOfMemory();
   266         return 0;
   267     }
   268     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   269 
   270     /* Initialize the wavebuf structures for closing */
   271     for (i = 0; i < NUM_BUFFERS; ++i)
   272         this->hidden->wavebuf[i].dwUser = 0xFFFF;
   273 
   274     while ((!valid_datatype) && (test_format)) {
   275         valid_datatype = 1;
   276         this->spec.format = test_format;
   277         switch (test_format) {
   278         case AUDIO_U8:
   279         case AUDIO_S16:
   280         case AUDIO_S32:
   281             break;              /* valid. */
   282 
   283         default:
   284             valid_datatype = 0;
   285             test_format = SDL_NextAudioFormat();
   286             break;
   287         }
   288     }
   289 
   290     if (!valid_datatype) {
   291         WINMM_CloseDevice(this);
   292         SDL_SetError("Unsupported audio format");
   293         return 0;
   294     }
   295 
   296     /* Set basic WAVE format parameters */
   297     SDL_memset(&waveformat, '\0', sizeof(waveformat));
   298     waveformat.wFormatTag = WAVE_FORMAT_PCM;
   299     waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   300 
   301     if (this->spec.channels > 2)
   302         this->spec.channels = 2;        /* !!! FIXME: is this right? */
   303 
   304     waveformat.nChannels = this->spec.channels;
   305     waveformat.nSamplesPerSec = this->spec.freq;
   306     waveformat.nBlockAlign =
   307         waveformat.nChannels * (waveformat.wBitsPerSample / 8);
   308     waveformat.nAvgBytesPerSec =
   309         waveformat.nSamplesPerSec * waveformat.nBlockAlign;
   310 
   311     /* Check the buffer size -- minimum of 1/4 second (word aligned) */
   312     if (this->spec.samples < (this->spec.freq / 4))
   313         this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
   314 
   315     /* Update the fragment size as size in bytes */
   316     SDL_CalculateAudioSpec(&this->spec);
   317 
   318     /* Open the audio device */
   319     if (iscapture) {
   320         result = waveInOpen(&this->hidden->hin, devId, &waveformat,
   321                              (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
   322                              CALLBACK_FUNCTION);
   323     } else {
   324         result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
   325                              (DWORD_PTR) FillSound, (DWORD_PTR) this,
   326                              CALLBACK_FUNCTION);
   327     }
   328 
   329     if (result != MMSYSERR_NOERROR) {
   330         WINMM_CloseDevice(this);
   331         SetMMerror("waveOutOpen()", result);
   332         return 0;
   333     }
   334 #ifdef SOUND_DEBUG
   335     /* Check the sound device we retrieved */
   336     {
   337         WAVEOUTCAPS caps;
   338 
   339         result = waveOutGetDevCaps((UINT) this->hidden->hout,
   340                                    &caps, sizeof(caps));
   341         if (result != MMSYSERR_NOERROR) {
   342             WINMM_CloseDevice(this);
   343             SetMMerror("waveOutGetDevCaps()", result);
   344             return 0;
   345         }
   346         printf("Audio device: %s\n", caps.szPname);
   347     }
   348 #endif
   349 
   350     /* Create the audio buffer semaphore */
   351     this->hidden->audio_sem =
   352 #if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
   353         CreateSemaphoreCE(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
   354 #else
   355         CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
   356 #endif
   357     if (this->hidden->audio_sem == NULL) {
   358         WINMM_CloseDevice(this);
   359         SDL_SetError("Couldn't create semaphore");
   360         return 0;
   361     }
   362 
   363     /* Create the sound buffers */
   364     this->hidden->mixbuf =
   365         (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
   366     if (this->hidden->mixbuf == NULL) {
   367         WINMM_CloseDevice(this);
   368         SDL_OutOfMemory();
   369         return 0;
   370     }
   371     for (i = 0; i < NUM_BUFFERS; ++i) {
   372         SDL_memset(&this->hidden->wavebuf[i], 0,
   373                    sizeof(this->hidden->wavebuf[i]));
   374         this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
   375         this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
   376         this->hidden->wavebuf[i].lpData =
   377             (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
   378         result = waveOutPrepareHeader(this->hidden->hout,
   379                                       &this->hidden->wavebuf[i],
   380                                       sizeof(this->hidden->wavebuf[i]));
   381         if (result != MMSYSERR_NOERROR) {
   382             WINMM_CloseDevice(this);
   383             SetMMerror("waveOutPrepareHeader()", result);
   384             return 0;
   385         }
   386     }
   387 
   388     return 1;                   /* Ready to go! */
   389 }
   390 
   391 
   392 static int
   393 WINMM_Init(SDL_AudioDriverImpl * impl)
   394 {
   395     /* Set the function pointers */
   396     impl->DetectDevices = WINMM_DetectDevices;
   397     impl->OpenDevice = WINMM_OpenDevice;
   398     impl->PlayDevice = WINMM_PlayDevice;
   399     impl->WaitDevice = WINMM_WaitDevice;
   400     impl->WaitDone = WINMM_WaitDone;
   401     impl->GetDeviceBuf = WINMM_GetDeviceBuf;
   402     impl->CloseDevice = WINMM_CloseDevice;
   403 
   404     return 1;   /* this audio target is available. */
   405 }
   406 
   407 AudioBootStrap WINMM_bootstrap = {
   408     "winmm", "Windows Waveform Audio", WINMM_Init, 0
   409 };
   410 
   411 #endif /* SDL_AUDIO_DRIVER_WINMM */
   412 
   413 /* vi: set ts=4 sw=4 expandtab: */