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