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