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