src/audio/winmm/SDL_winmm.c
author Ryan C. Gordon
Sat, 15 Sep 2012 10:59:39 -0400
changeset 6430 48d519500f7e
parent 6352 a9bcd26e7105
child 6885 700f1b25f77f
permissions -rwxr-xr-x
Removed Windows CE support from SDL 2.0.

It's a long-dead platform, and we don't have any way to build for, test, or
maintain it, so there's no sense in doing acrobatics to support it.

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