src/audio/winmm/SDL_winmm.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8093 b43765095a6f
child 8585 2d08b56cbfa9
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

Is it that time already??
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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_internal.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 
    35 #ifndef WAVE_FORMAT_IEEE_FLOAT
    36 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
    37 #endif
    38 
    39 #define DETECT_DEV_IMPL(typ, capstyp) \
    40 static void DetectWave##typ##Devs(SDL_AddAudioDevice addfn) { \
    41     const UINT devcount = wave##typ##GetNumDevs(); \
    42     capstyp caps; \
    43     UINT i; \
    44     for (i = 0; i < devcount; i++) { \
    45         if (wave##typ##GetDevCaps(i,&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
    46             char *name = WIN_StringToUTF8(caps.szPname); \
    47             if (name != NULL) { \
    48                 addfn(name); \
    49                 SDL_free(name); \
    50             } \
    51         } \
    52     } \
    53 }
    54 
    55 DETECT_DEV_IMPL(Out, WAVEOUTCAPS)
    56 DETECT_DEV_IMPL(In, WAVEINCAPS)
    57 
    58 static void
    59 WINMM_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
    60 {
    61     if (iscapture) {
    62         DetectWaveInDevs(addfn);
    63     } else {
    64         DetectWaveOutDevs(addfn);
    65     }
    66 }
    67 
    68 static void CALLBACK
    69 CaptureSound(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
    70           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    71 {
    72     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    73 
    74     /* Only service "buffer is filled" messages */
    75     if (uMsg != WIM_DATA)
    76         return;
    77 
    78     /* Signal that we have a new buffer of data */
    79     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    80 }
    81 
    82 
    83 /* The Win32 callback for filling the WAVE device */
    84 static void CALLBACK
    85 FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
    86           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    87 {
    88     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    89 
    90     /* Only service "buffer done playing" messages */
    91     if (uMsg != WOM_DONE)
    92         return;
    93 
    94     /* Signal that we are done playing a buffer */
    95     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    96 }
    97 
    98 static int
    99 SetMMerror(char *function, MMRESULT code)
   100 {
   101     size_t len;
   102     char errbuf[MAXERRORLENGTH];
   103     wchar_t werrbuf[MAXERRORLENGTH];
   104 
   105     SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
   106     len = SDL_strlen(errbuf);
   107 
   108     waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
   109     WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
   110                         MAXERRORLENGTH - len, NULL, NULL);
   111 
   112     return SDL_SetError("%s", errbuf);
   113 }
   114 
   115 static void
   116 WINMM_WaitDevice(_THIS)
   117 {
   118     /* Wait for an audio chunk to finish */
   119     WaitForSingleObject(this->hidden->audio_sem, INFINITE);
   120 }
   121 
   122 static Uint8 *
   123 WINMM_GetDeviceBuf(_THIS)
   124 {
   125     return (Uint8 *) (this->hidden->
   126                       wavebuf[this->hidden->next_buffer].lpData);
   127 }
   128 
   129 static void
   130 WINMM_PlayDevice(_THIS)
   131 {
   132     /* Queue it up */
   133     waveOutWrite(this->hidden->hout,
   134                  &this->hidden->wavebuf[this->hidden->next_buffer],
   135                  sizeof(this->hidden->wavebuf[0]));
   136     this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
   137 }
   138 
   139 static void
   140 WINMM_WaitDone(_THIS)
   141 {
   142     int i, left;
   143 
   144     do {
   145         left = NUM_BUFFERS;
   146         for (i = 0; i < NUM_BUFFERS; ++i) {
   147             if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
   148                 --left;
   149             }
   150         }
   151         if (left > 0) {
   152             SDL_Delay(100);
   153         }
   154     } while (left > 0);
   155 }
   156 
   157 static void
   158 WINMM_CloseDevice(_THIS)
   159 {
   160     /* Close up audio */
   161     if (this->hidden != NULL) {
   162         int i;
   163 
   164         if (this->hidden->audio_sem) {
   165             CloseHandle(this->hidden->audio_sem);
   166             this->hidden->audio_sem = 0;
   167         }
   168 
   169         /* Clean up mixing buffers */
   170         for (i = 0; i < NUM_BUFFERS; ++i) {
   171             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
   172                 waveOutUnprepareHeader(this->hidden->hout,
   173                                        &this->hidden->wavebuf[i],
   174                                        sizeof(this->hidden->wavebuf[i]));
   175                 this->hidden->wavebuf[i].dwUser = 0xFFFF;
   176             }
   177         }
   178 
   179         /* Free raw mixing buffer */
   180         SDL_free(this->hidden->mixbuf);
   181         this->hidden->mixbuf = NULL;
   182 
   183         if (this->hidden->hin) {
   184             waveInClose(this->hidden->hin);
   185             this->hidden->hin = 0;
   186         }
   187 
   188         if (this->hidden->hout) {
   189             waveOutClose(this->hidden->hout);
   190             this->hidden->hout = 0;
   191         }
   192 
   193         SDL_free(this->hidden);
   194         this->hidden = NULL;
   195     }
   196 }
   197 
   198 static SDL_bool
   199 PrepWaveFormat(_THIS, UINT_PTR devId, WAVEFORMATEX *pfmt, const int iscapture)
   200 {
   201     SDL_zerop(pfmt);
   202 
   203     if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
   204         pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   205     } else {
   206         pfmt->wFormatTag = WAVE_FORMAT_PCM;
   207     }
   208     pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   209 
   210     pfmt->nChannels = this->spec.channels;
   211     pfmt->nSamplesPerSec = this->spec.freq;
   212     pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
   213     pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
   214 
   215     if (iscapture) {
   216         return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
   217     } else {
   218         return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
   219     }
   220 }
   221 
   222 static int
   223 WINMM_OpenDevice(_THIS, const char *devname, int iscapture)
   224 {
   225     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   226     int valid_datatype = 0;
   227     MMRESULT result;
   228     WAVEFORMATEX waveformat;
   229     UINT_PTR devId = WAVE_MAPPER;  /* WAVE_MAPPER == choose system's default */
   230     char *utf8 = NULL;
   231     int i;
   232 
   233     if (devname != NULL) {  /* specific device requested? */
   234         if (iscapture) {
   235             const int devcount = (int) waveInGetNumDevs();
   236             WAVEINCAPS caps;
   237             for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
   238                 result = waveInGetDevCaps(i, &caps, sizeof (caps));
   239                 if (result != MMSYSERR_NOERROR)
   240                     continue;
   241                 else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
   242                     continue;
   243                 else if (SDL_strcmp(devname, utf8) == 0)
   244                     devId = (UINT_PTR) i;
   245                 SDL_free(utf8);
   246             }
   247         } else {
   248             const int devcount = (int) waveOutGetNumDevs();
   249             WAVEOUTCAPS caps;
   250             for (i = 0; (i < devcount) && (devId == WAVE_MAPPER); i++) {
   251                 result = waveOutGetDevCaps(i, &caps, sizeof (caps));
   252                 if (result != MMSYSERR_NOERROR)
   253                     continue;
   254                 else if ((utf8 = WIN_StringToUTF8(caps.szPname)) == NULL)
   255                     continue;
   256                 else if (SDL_strcmp(devname, utf8) == 0)
   257                     devId = (UINT_PTR) i;
   258                 SDL_free(utf8);
   259             }
   260         }
   261 
   262         if (devId == WAVE_MAPPER) {
   263             return SDL_SetError("Requested device not found");
   264         }
   265     }
   266 
   267     /* Initialize all variables that we clean on shutdown */
   268     this->hidden = (struct SDL_PrivateAudioData *)
   269         SDL_malloc((sizeof *this->hidden));
   270     if (this->hidden == NULL) {
   271         return SDL_OutOfMemory();
   272     }
   273     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   274 
   275     /* Initialize the wavebuf structures for closing */
   276     for (i = 0; i < NUM_BUFFERS; ++i)
   277         this->hidden->wavebuf[i].dwUser = 0xFFFF;
   278 
   279     if (this->spec.channels > 2)
   280         this->spec.channels = 2;        /* !!! FIXME: is this right? */
   281 
   282     /* Check the buffer size -- minimum of 1/4 second (word aligned) */
   283     if (this->spec.samples < (this->spec.freq / 4))
   284         this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
   285 
   286     while ((!valid_datatype) && (test_format)) {
   287         switch (test_format) {
   288         case AUDIO_U8:
   289         case AUDIO_S16:
   290         case AUDIO_S32:
   291         case AUDIO_F32:
   292             this->spec.format = test_format;
   293             if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
   294                 valid_datatype = 1;
   295             } else {
   296                 test_format = SDL_NextAudioFormat();
   297             }
   298             break;
   299 
   300         default:
   301             test_format = SDL_NextAudioFormat();
   302             break;
   303         }
   304     }
   305 
   306     if (!valid_datatype) {
   307         WINMM_CloseDevice(this);
   308         return SDL_SetError("Unsupported audio format");
   309     }
   310 
   311     /* Update the fragment size as size in bytes */
   312     SDL_CalculateAudioSpec(&this->spec);
   313 
   314     /* Open the audio device */
   315     if (iscapture) {
   316         result = waveInOpen(&this->hidden->hin, devId, &waveformat,
   317                              (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
   318                              CALLBACK_FUNCTION);
   319     } else {
   320         result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
   321                              (DWORD_PTR) FillSound, (DWORD_PTR) this,
   322                              CALLBACK_FUNCTION);
   323     }
   324 
   325     if (result != MMSYSERR_NOERROR) {
   326         WINMM_CloseDevice(this);
   327         return SetMMerror("waveOutOpen()", result);
   328     }
   329 #ifdef SOUND_DEBUG
   330     /* Check the sound device we retrieved */
   331     {
   332         WAVEOUTCAPS caps;
   333 
   334         result = waveOutGetDevCaps((UINT) this->hidden->hout,
   335                                    &caps, sizeof(caps));
   336         if (result != MMSYSERR_NOERROR) {
   337             WINMM_CloseDevice(this);
   338             return SetMMerror("waveOutGetDevCaps()", result);
   339         }
   340         printf("Audio device: %s\n", caps.szPname);
   341     }
   342 #endif
   343 
   344     /* Create the audio buffer semaphore */
   345     this->hidden->audio_sem =
   346         CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
   347     if (this->hidden->audio_sem == NULL) {
   348         WINMM_CloseDevice(this);
   349         return SDL_SetError("Couldn't create semaphore");
   350     }
   351 
   352     /* Create the sound buffers */
   353     this->hidden->mixbuf =
   354         (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
   355     if (this->hidden->mixbuf == NULL) {
   356         WINMM_CloseDevice(this);
   357         return SDL_OutOfMemory();
   358     }
   359     for (i = 0; i < NUM_BUFFERS; ++i) {
   360         SDL_memset(&this->hidden->wavebuf[i], 0,
   361                    sizeof(this->hidden->wavebuf[i]));
   362         this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
   363         this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
   364         this->hidden->wavebuf[i].lpData =
   365             (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
   366         result = waveOutPrepareHeader(this->hidden->hout,
   367                                       &this->hidden->wavebuf[i],
   368                                       sizeof(this->hidden->wavebuf[i]));
   369         if (result != MMSYSERR_NOERROR) {
   370             WINMM_CloseDevice(this);
   371             return SetMMerror("waveOutPrepareHeader()", result);
   372         }
   373     }
   374 
   375     return 0;                   /* Ready to go! */
   376 }
   377 
   378 
   379 static int
   380 WINMM_Init(SDL_AudioDriverImpl * impl)
   381 {
   382     /* Set the function pointers */
   383     impl->DetectDevices = WINMM_DetectDevices;
   384     impl->OpenDevice = WINMM_OpenDevice;
   385     impl->PlayDevice = WINMM_PlayDevice;
   386     impl->WaitDevice = WINMM_WaitDevice;
   387     impl->WaitDone = WINMM_WaitDone;
   388     impl->GetDeviceBuf = WINMM_GetDeviceBuf;
   389     impl->CloseDevice = WINMM_CloseDevice;
   390 
   391     return 1;   /* this audio target is available. */
   392 }
   393 
   394 AudioBootStrap WINMM_bootstrap = {
   395     "winmm", "Windows Waveform Audio", WINMM_Init, 0
   396 };
   397 
   398 #endif /* SDL_AUDIO_DRIVER_WINMM */
   399 
   400 /* vi: set ts=4 sw=4 expandtab: */