src/audio/winmm/SDL_winmm.c
author Sam Lantinga
Mon, 09 Jan 2017 11:58:01 -0800
changeset 10802 6afc9b833867
parent 10737 3406a0f8b041
child 10818 7e06b0e4dbe0
permissions -rw-r--r--
We only need the first few keymaps corresponding to the following constants:
K_NORMTAB, K_SHIFTTAB, K_ALTTAB, K_ALTSHIFTTAB

In the normal case we'll load all the keymaps from the kernel, but this reduces the size of the SDL library for the fallback case when we can't get to the tty.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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_assert.h"
    31 #include "SDL_timer.h"
    32 #include "SDL_audio.h"
    33 #include "../SDL_audio_c.h"
    34 #include "SDL_winmm.h"
    35 
    36 #ifndef WAVE_FORMAT_IEEE_FLOAT
    37 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
    38 #endif
    39 
    40 #define DETECT_DEV_IMPL(iscap, typ, capstyp) \
    41 static void DetectWave##typ##Devs(void) { \
    42     const UINT iscapture = iscap ? 1 : 0; \
    43     const UINT devcount = wave##typ##GetNumDevs(); \
    44     capstyp##2W caps; \
    45     UINT i; \
    46     for (i = 0; i < devcount; i++) { \
    47 	if (wave##typ##GetDevCaps(i,(LP##capstyp##W)&caps,sizeof(caps))==MMSYSERR_NOERROR) { \
    48             char *name = WIN_LookupAudioDeviceName(caps.szPname,&caps.NameGuid); \
    49             if (name != NULL) { \
    50                 SDL_AddAudioDevice((int) iscapture, name, (void *) ((size_t) i+1)); \
    51                 SDL_free(name); \
    52             } \
    53         } \
    54     } \
    55 }
    56 
    57 DETECT_DEV_IMPL(SDL_FALSE, Out, WAVEOUTCAPS)
    58 DETECT_DEV_IMPL(SDL_TRUE, In, WAVEINCAPS)
    59 
    60 static void
    61 WINMM_DetectDevices(void)
    62 {
    63     DetectWaveInDevs();
    64     DetectWaveOutDevs();
    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     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    79 }
    80 
    81 
    82 /* The Win32 callback for filling the WAVE device */
    83 static void CALLBACK
    84 FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
    85           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    86 {
    87     SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
    88 
    89     /* Only service "buffer done playing" messages */
    90     if (uMsg != WOM_DONE)
    91         return;
    92 
    93     /* Signal that we are done playing a buffer */
    94     ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
    95 }
    96 
    97 static int
    98 SetMMerror(char *function, MMRESULT code)
    99 {
   100     int len;
   101     char errbuf[MAXERRORLENGTH];
   102     wchar_t werrbuf[MAXERRORLENGTH];
   103 
   104     SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
   105     len = SDL_static_cast(int, SDL_strlen(errbuf));
   106 
   107     waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
   108     WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
   109                         MAXERRORLENGTH - len, NULL, NULL);
   110 
   111     return SDL_SetError("%s", errbuf);
   112 }
   113 
   114 static void
   115 WINMM_WaitDevice(_THIS)
   116 {
   117     /* Wait for an audio chunk to finish */
   118     WaitForSingleObject(this->hidden->audio_sem, INFINITE);
   119 }
   120 
   121 static Uint8 *
   122 WINMM_GetDeviceBuf(_THIS)
   123 {
   124     return (Uint8 *) (this->hidden->
   125                       wavebuf[this->hidden->next_buffer].lpData);
   126 }
   127 
   128 static void
   129 WINMM_PlayDevice(_THIS)
   130 {
   131     /* Queue it up */
   132     waveOutWrite(this->hidden->hout,
   133                  &this->hidden->wavebuf[this->hidden->next_buffer],
   134                  sizeof(this->hidden->wavebuf[0]));
   135     this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
   136 }
   137 
   138 static int
   139 WINMM_CaptureFromDevice(_THIS, void *buffer, int buflen)
   140 {
   141     const int nextbuf = this->hidden->next_buffer;
   142     MMRESULT result;
   143 
   144     SDL_assert(buflen == this->spec.size);
   145 
   146     /* Wait for an audio chunk to finish */
   147     WaitForSingleObject(this->hidden->audio_sem, INFINITE);
   148 
   149     /* Copy it to caller's buffer... */
   150     SDL_memcpy(buffer, this->hidden->wavebuf[nextbuf].lpData, this->spec.size);
   151 
   152     /* requeue the buffer that just finished. */
   153     result = waveInAddBuffer(this->hidden->hin,
   154                              &this->hidden->wavebuf[nextbuf],
   155                              sizeof (this->hidden->wavebuf[nextbuf]));
   156     if (result != MMSYSERR_NOERROR) {
   157         return -1;  /* uhoh! Disable the device. */
   158     }
   159 
   160     /* queue the next buffer in sequence, next time. */
   161     this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
   162     return this->spec.size;
   163 }
   164 
   165 static void
   166 WINMM_FlushCapture(_THIS)
   167 {
   168     /* Wait for an audio chunk to finish */
   169     if (WaitForSingleObject(this->hidden->audio_sem, 0) == WAIT_OBJECT_0) {
   170         const int nextbuf = this->hidden->next_buffer;
   171         /* requeue the buffer that just finished without reading from it. */
   172         waveInAddBuffer(this->hidden->hin,
   173                         &this->hidden->wavebuf[nextbuf],
   174                         sizeof (this->hidden->wavebuf[nextbuf]));
   175         this->hidden->next_buffer = (nextbuf + 1) % NUM_BUFFERS;
   176     }
   177 }
   178 
   179 static void
   180 WINMM_CloseDevice(_THIS)
   181 {
   182     int i;
   183 
   184     if (this->hidden->hout) {
   185         waveOutReset(this->hidden->hout);
   186 
   187         /* Clean up mixing buffers */
   188         for (i = 0; i < NUM_BUFFERS; ++i) {
   189             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
   190                 waveOutUnprepareHeader(this->hidden->hout,
   191                                        &this->hidden->wavebuf[i],
   192                                        sizeof (this->hidden->wavebuf[i]));
   193             }
   194         }
   195 
   196         waveOutClose(this->hidden->hout);
   197     }
   198 
   199     if (this->hidden->hin) {
   200         waveInReset(this->hidden->hin);
   201 
   202         /* Clean up mixing buffers */
   203         for (i = 0; i < NUM_BUFFERS; ++i) {
   204             if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
   205                 waveInUnprepareHeader(this->hidden->hin,
   206                                        &this->hidden->wavebuf[i],
   207                                        sizeof (this->hidden->wavebuf[i]));
   208             }
   209         }
   210         waveInClose(this->hidden->hin);
   211     }
   212 
   213     if (this->hidden->audio_sem) {
   214         CloseHandle(this->hidden->audio_sem);
   215     }
   216 
   217     SDL_free(this->hidden->mixbuf);
   218     SDL_free(this->hidden);
   219 }
   220 
   221 static SDL_bool
   222 PrepWaveFormat(_THIS, UINT devId, WAVEFORMATEX *pfmt, const int iscapture)
   223 {
   224     SDL_zerop(pfmt);
   225 
   226     if (SDL_AUDIO_ISFLOAT(this->spec.format)) {
   227         pfmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
   228     } else {
   229         pfmt->wFormatTag = WAVE_FORMAT_PCM;
   230     }
   231     pfmt->wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
   232 
   233     pfmt->nChannels = this->spec.channels;
   234     pfmt->nSamplesPerSec = this->spec.freq;
   235     pfmt->nBlockAlign = pfmt->nChannels * (pfmt->wBitsPerSample / 8);
   236     pfmt->nAvgBytesPerSec = pfmt->nSamplesPerSec * pfmt->nBlockAlign;
   237 
   238     if (iscapture) {
   239         return (waveInOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
   240     } else {
   241         return (waveOutOpen(0, devId, pfmt, 0, 0, WAVE_FORMAT_QUERY) == 0);
   242     }
   243 }
   244 
   245 static int
   246 WINMM_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   247 {
   248     SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
   249     int valid_datatype = 0;
   250     MMRESULT result;
   251     WAVEFORMATEX waveformat;
   252     UINT devId = WAVE_MAPPER;  /* WAVE_MAPPER == choose system's default */
   253     UINT i;
   254 
   255     if (handle != NULL) {  /* specific device requested? */
   256         /* -1 because we increment the original value to avoid NULL. */
   257         const size_t val = ((size_t) handle) - 1;
   258         devId = (UINT) val;
   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         return SDL_OutOfMemory();
   266     }
   267     SDL_zerop(this->hidden);
   268 
   269     /* Initialize the wavebuf structures for closing */
   270     for (i = 0; i < NUM_BUFFERS; ++i)
   271         this->hidden->wavebuf[i].dwUser = 0xFFFF;
   272 
   273     if (this->spec.channels > 2)
   274         this->spec.channels = 2;        /* !!! FIXME: is this right? */
   275 
   276     while ((!valid_datatype) && (test_format)) {
   277         switch (test_format) {
   278         case AUDIO_U8:
   279         case AUDIO_S16:
   280         case AUDIO_S32:
   281         case AUDIO_F32:
   282             this->spec.format = test_format;
   283             if (PrepWaveFormat(this, devId, &waveformat, iscapture)) {
   284                 valid_datatype = 1;
   285             } else {
   286                 test_format = SDL_NextAudioFormat();
   287             }
   288             break;
   289 
   290         default:
   291             test_format = SDL_NextAudioFormat();
   292             break;
   293         }
   294     }
   295 
   296     if (!valid_datatype) {
   297         return SDL_SetError("Unsupported audio format");
   298     }
   299 
   300     /* Update the fragment size as size in bytes */
   301     SDL_CalculateAudioSpec(&this->spec);
   302 
   303     /* Open the audio device */
   304     if (iscapture) {
   305         result = waveInOpen(&this->hidden->hin, devId, &waveformat,
   306                              (DWORD_PTR) CaptureSound, (DWORD_PTR) this,
   307                              CALLBACK_FUNCTION);
   308         if (result != MMSYSERR_NOERROR) {
   309             return SetMMerror("waveInOpen()", result);
   310         }
   311     } else {
   312         result = waveOutOpen(&this->hidden->hout, devId, &waveformat,
   313                              (DWORD_PTR) FillSound, (DWORD_PTR) this,
   314                              CALLBACK_FUNCTION);
   315         if (result != MMSYSERR_NOERROR) {
   316             return SetMMerror("waveOutOpen()", result);
   317         }
   318     }
   319 
   320 #ifdef SOUND_DEBUG
   321     /* Check the sound device we retrieved */
   322     {
   323         if (iscapture) {
   324             WAVEINCAPS caps;
   325             result = waveInGetDevCaps((UINT) this->hidden->hout,
   326                                       &caps, sizeof (caps));
   327             if (result != MMSYSERR_NOERROR) {
   328                 return SetMMerror("waveInGetDevCaps()", result);
   329             }
   330             printf("Audio device: %s\n", caps.szPname);
   331         } else {
   332             WAVEOUTCAPS caps;
   333             result = waveOutGetDevCaps((UINT) this->hidden->hout,
   334                                        &caps, sizeof(caps));
   335             if (result != MMSYSERR_NOERROR) {
   336                 return SetMMerror("waveOutGetDevCaps()", result);
   337             }
   338             printf("Audio device: %s\n", caps.szPname);
   339         }
   340     }
   341 #endif
   342 
   343     /* Create the audio buffer semaphore */
   344     this->hidden->audio_sem =
   345 		CreateSemaphore(NULL, iscapture ? 0 : NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
   346     if (this->hidden->audio_sem == NULL) {
   347         return SDL_SetError("Couldn't create semaphore");
   348     }
   349 
   350     /* Create the sound buffers */
   351     this->hidden->mixbuf =
   352         (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
   353     if (this->hidden->mixbuf == NULL) {
   354         return SDL_OutOfMemory();
   355     }
   356 
   357     SDL_zero(this->hidden->wavebuf);
   358     for (i = 0; i < NUM_BUFFERS; ++i) {
   359         this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
   360         this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
   361         this->hidden->wavebuf[i].lpData =
   362             (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
   363 
   364         if (iscapture) {
   365             result = waveInPrepareHeader(this->hidden->hin,
   366                                           &this->hidden->wavebuf[i],
   367                                           sizeof(this->hidden->wavebuf[i]));
   368             if (result != MMSYSERR_NOERROR) {
   369                 return SetMMerror("waveInPrepareHeader()", result);
   370             }
   371 
   372             result = waveInAddBuffer(this->hidden->hin,
   373                                      &this->hidden->wavebuf[i],
   374                                      sizeof(this->hidden->wavebuf[i]));
   375             if (result != MMSYSERR_NOERROR) {
   376                 return SetMMerror("waveInAddBuffer()", result);
   377             }
   378         } else {
   379             result = waveOutPrepareHeader(this->hidden->hout,
   380                                           &this->hidden->wavebuf[i],
   381                                           sizeof(this->hidden->wavebuf[i]));
   382             if (result != MMSYSERR_NOERROR) {
   383                 return SetMMerror("waveOutPrepareHeader()", result);
   384             }
   385         }
   386     }
   387 
   388     if (iscapture) {
   389         result = waveInStart(this->hidden->hin);
   390         if (result != MMSYSERR_NOERROR) {
   391             return SetMMerror("waveInStart()", result);
   392         }
   393     }
   394 
   395     return 0;                   /* Ready to go! */
   396 }
   397 
   398 
   399 static int
   400 WINMM_Init(SDL_AudioDriverImpl * impl)
   401 {
   402     /* Set the function pointers */
   403     impl->DetectDevices = WINMM_DetectDevices;
   404     impl->OpenDevice = WINMM_OpenDevice;
   405     impl->PlayDevice = WINMM_PlayDevice;
   406     impl->WaitDevice = WINMM_WaitDevice;
   407     impl->GetDeviceBuf = WINMM_GetDeviceBuf;
   408     impl->CaptureFromDevice = WINMM_CaptureFromDevice;
   409     impl->FlushCapture = WINMM_FlushCapture;
   410     impl->CloseDevice = WINMM_CloseDevice;
   411 
   412     impl->HasCaptureSupport = SDL_TRUE;
   413 
   414     return 1;   /* this audio target is available. */
   415 }
   416 
   417 AudioBootStrap WINMM_bootstrap = {
   418     "winmm", "Windows Waveform Audio", WINMM_Init, 0
   419 };
   420 
   421 #endif /* SDL_AUDIO_DRIVER_WINMM */
   422 
   423 /* vi: set ts=4 sw=4 expandtab: */