src/audio/windib/SDL_dibaudio.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Aug 2008 15:10:03 +0000
changeset 2735 204be4fc2726
parent 2060 866052b01ee5
child 2859 99210400e8b9
permissions -rw-r--r--
Final merge of Google Summer of Code 2008 work...

Port SDL 1.3 to the Nintendo DS
by Darren Alton, mentored by Sam Lantinga
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@0
    23
slouken@0
    24
/* Allow access to a raw mixing buffer */
slouken@0
    25
slouken@1433
    26
#define WIN32_LEAN_AND_MEAN
slouken@1433
    27
#include <windows.h>
slouken@0
    28
#include <mmsystem.h>
slouken@0
    29
slouken@1358
    30
#include "SDL_timer.h"
slouken@0
    31
#include "SDL_audio.h"
slouken@1361
    32
#include "../SDL_audio_c.h"
slouken@0
    33
#include "SDL_dibaudio.h"
slouken@36
    34
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
slouken@36
    35
#include "win_ce_semaphore.h"
slouken@36
    36
#endif
slouken@0
    37
icculus@2049
    38
#if defined(_WIN32_WCE)
icculus@2049
    39
#define WINDOWS_OS_NAME "Windows CE/PocketPC"
icculus@2049
    40
#elif defined(WIN64)
icculus@2049
    41
#define WINDOWS_OS_NAME "Win64"
icculus@2049
    42
#else
icculus@2049
    43
#define WINDOWS_OS_NAME "Win32"
icculus@2049
    44
#endif
slouken@0
    45
slouken@0
    46
/* The Win32 callback for filling the WAVE device */
slouken@1895
    47
static void CALLBACK
slouken@1895
    48
FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
slouken@1895
    49
          DWORD dwParam1, DWORD dwParam2)
slouken@0
    50
{
slouken@1895
    51
    SDL_AudioDevice *this = (SDL_AudioDevice *) dwInstance;
slouken@0
    52
slouken@1895
    53
    /* Only service "buffer done playing" messages */
slouken@1895
    54
    if (uMsg != WOM_DONE)
slouken@1895
    55
        return;
slouken@0
    56
slouken@1895
    57
    /* Signal that we are done playing a buffer */
slouken@36
    58
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
icculus@2049
    59
    ReleaseSemaphoreCE(this->hidden->audio_sem, 1, NULL);
slouken@36
    60
#else
icculus@2049
    61
    ReleaseSemaphore(this->hidden->audio_sem, 1, NULL);
slouken@36
    62
#endif
slouken@0
    63
}
slouken@0
    64
slouken@1895
    65
static void
slouken@1895
    66
SetMMerror(char *function, MMRESULT code)
slouken@0
    67
{
slouken@1895
    68
    size_t len;
slouken@1895
    69
    char errbuf[MAXERRORLENGTH];
slouken@36
    70
#ifdef _WIN32_WCE
slouken@1895
    71
    wchar_t werrbuf[MAXERRORLENGTH];
slouken@36
    72
#endif
slouken@0
    73
slouken@1895
    74
    SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
slouken@1895
    75
    len = SDL_strlen(errbuf);
slouken@36
    76
slouken@36
    77
#ifdef _WIN32_WCE
slouken@1895
    78
    /* UNICODE version */
slouken@1895
    79
    waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH - len);
slouken@1895
    80
    WideCharToMultiByte(CP_ACP, 0, werrbuf, -1, errbuf + len,
slouken@1895
    81
                        MAXERRORLENGTH - len, NULL, NULL);
slouken@36
    82
#else
slouken@1895
    83
    waveOutGetErrorText(code, errbuf + len, (UINT) (MAXERRORLENGTH - len));
slouken@36
    84
#endif
slouken@36
    85
slouken@1895
    86
    SDL_SetError("%s", errbuf);
slouken@0
    87
}
slouken@0
    88
slouken@0
    89
/* Set high priority for the audio thread */
slouken@1895
    90
static void
icculus@2049
    91
WINWAVEOUT_ThreadInit(_THIS)
slouken@0
    92
{
slouken@1895
    93
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
slouken@0
    94
}
slouken@0
    95
slouken@1895
    96
void
icculus@2049
    97
WINWAVEOUT_WaitDevice(_THIS)
slouken@0
    98
{
slouken@1895
    99
    /* Wait for an audio chunk to finish */
slouken@36
   100
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
icculus@2049
   101
    WaitForSemaphoreCE(this->hidden->audio_sem, INFINITE);
slouken@36
   102
#else
icculus@2049
   103
    WaitForSingleObject(this->hidden->audio_sem, INFINITE);
slouken@36
   104
#endif
slouken@0
   105
}
slouken@0
   106
slouken@1895
   107
Uint8 *
icculus@2049
   108
WINWAVEOUT_GetDeviceBuf(_THIS)
slouken@0
   109
{
slouken@2735
   110
    return (Uint8 *) (this->hidden->
slouken@2735
   111
                      wavebuf[this->hidden->next_buffer].lpData);
slouken@0
   112
}
slouken@0
   113
slouken@1895
   114
void
icculus@2049
   115
WINWAVEOUT_PlayDevice(_THIS)
slouken@0
   116
{
slouken@1895
   117
    /* Queue it up */
icculus@2049
   118
    waveOutWrite(this->hidden->sound,
icculus@2049
   119
                 &this->hidden->wavebuf[this->hidden->next_buffer],
slouken@2060
   120
                 sizeof(this->hidden->wavebuf[0]));
icculus@2049
   121
    this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
slouken@0
   122
}
slouken@0
   123
slouken@1895
   124
void
icculus@2049
   125
WINWAVEOUT_WaitDone(_THIS)
slouken@0
   126
{
slouken@1895
   127
    int i, left;
slouken@0
   128
slouken@1895
   129
    do {
slouken@1895
   130
        left = NUM_BUFFERS;
slouken@1895
   131
        for (i = 0; i < NUM_BUFFERS; ++i) {
icculus@2049
   132
            if (this->hidden->wavebuf[i].dwFlags & WHDR_DONE) {
slouken@1895
   133
                --left;
slouken@1895
   134
            }
slouken@1895
   135
        }
slouken@1895
   136
        if (left > 0) {
slouken@1895
   137
            SDL_Delay(100);
slouken@1895
   138
        }
slouken@2735
   139
    } while (left > 0);
slouken@0
   140
}
slouken@0
   141
slouken@1895
   142
void
icculus@2049
   143
WINWAVEOUT_CloseDevice(_THIS)
slouken@0
   144
{
icculus@2049
   145
    /* Close up audio */
icculus@2049
   146
    if (this->hidden != NULL) {
icculus@2049
   147
        int i;
slouken@0
   148
icculus@2049
   149
        if (this->hidden->audio_sem) {
slouken@36
   150
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
icculus@2049
   151
            CloseSynchHandle(this->hidden->audio_sem);
slouken@36
   152
#else
icculus@2049
   153
            CloseHandle(this->hidden->audio_sem);
slouken@36
   154
#endif
icculus@2049
   155
            this->hidden->audio_sem = 0;
icculus@2049
   156
        }
slouken@0
   157
icculus@2049
   158
        if (this->hidden->sound) {
icculus@2049
   159
            waveOutClose(this->hidden->sound);
icculus@2049
   160
            this->hidden->sound = 0;
slouken@1895
   161
        }
icculus@2049
   162
icculus@2049
   163
        /* Clean up mixing buffers */
icculus@2049
   164
        for (i = 0; i < NUM_BUFFERS; ++i) {
icculus@2049
   165
            if (this->hidden->wavebuf[i].dwUser != 0xFFFF) {
icculus@2049
   166
                waveOutUnprepareHeader(this->hidden->sound,
icculus@2049
   167
                                       &this->hidden->wavebuf[i],
slouken@2060
   168
                                       sizeof(this->hidden->wavebuf[i]));
icculus@2049
   169
                this->hidden->wavebuf[i].dwUser = 0xFFFF;
icculus@2049
   170
            }
icculus@2049
   171
        }
icculus@2049
   172
icculus@2049
   173
        if (this->hidden->mixbuf != NULL) {
icculus@2049
   174
            /* Free raw mixing buffer */
icculus@2049
   175
            SDL_free(this->hidden->mixbuf);
icculus@2049
   176
            this->hidden->mixbuf = NULL;
icculus@2049
   177
        }
icculus@2049
   178
icculus@2049
   179
        SDL_free(this->hidden);
icculus@2049
   180
        this->hidden = NULL;
slouken@1895
   181
    }
slouken@0
   182
}
slouken@0
   183
slouken@1895
   184
int
icculus@2049
   185
WINWAVEOUT_OpenDevice(_THIS, const char *devname, int iscapture)
slouken@0
   186
{
icculus@2049
   187
    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2049
   188
    int valid_datatype = 0;
slouken@1895
   189
    MMRESULT result;
icculus@2049
   190
    WAVEFORMATEX waveformat;
slouken@1895
   191
    int i;
icculus@2049
   192
icculus@2049
   193
    /* Initialize all variables that we clean on shutdown */
icculus@2049
   194
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
   195
        SDL_malloc((sizeof *this->hidden));
icculus@2049
   196
    if (this->hidden == NULL) {
icculus@2049
   197
        SDL_OutOfMemory();
icculus@2049
   198
        return 0;
icculus@2049
   199
    }
icculus@2049
   200
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@0
   201
slouken@1895
   202
    /* Initialize the wavebuf structures for closing */
slouken@1895
   203
    for (i = 0; i < NUM_BUFFERS; ++i)
icculus@2049
   204
        this->hidden->wavebuf[i].dwUser = 0xFFFF;
icculus@2049
   205
icculus@2049
   206
    while ((!valid_datatype) && (test_format)) {
icculus@2049
   207
        valid_datatype = 1;
icculus@2050
   208
        this->spec.format = test_format;
icculus@2049
   209
        switch (test_format) {
slouken@2060
   210
        case AUDIO_U8:
slouken@2060
   211
        case AUDIO_S16:
slouken@2060
   212
        case AUDIO_S32:
slouken@2060
   213
            break;              /* valid. */
icculus@2049
   214
slouken@2060
   215
        default:
slouken@2060
   216
            valid_datatype = 0;
slouken@2060
   217
            test_format = SDL_NextAudioFormat();
slouken@2060
   218
            break;
icculus@2049
   219
        }
icculus@2049
   220
    }
icculus@2049
   221
icculus@2049
   222
    if (!valid_datatype) {
icculus@2049
   223
        WINWAVEOUT_CloseDevice(this);
icculus@2049
   224
        SDL_SetError("Unsupported audio format");
icculus@2049
   225
        return 0;
icculus@2049
   226
    }
slouken@0
   227
slouken@1895
   228
    /* Set basic WAVE format parameters */
slouken@2060
   229
    SDL_memset(&waveformat, '\0', sizeof(waveformat));
slouken@1895
   230
    waveformat.wFormatTag = WAVE_FORMAT_PCM;
icculus@2049
   231
    waveformat.wBitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
slouken@0
   232
icculus@2049
   233
    if (this->spec.channels > 2)
slouken@2060
   234
        this->spec.channels = 2;        /* !!! FIXME: is this right? */
icculus@2049
   235
icculus@2049
   236
    waveformat.nChannels = this->spec.channels;
icculus@2049
   237
    waveformat.nSamplesPerSec = this->spec.freq;
slouken@1895
   238
    waveformat.nBlockAlign =
slouken@1895
   239
        waveformat.nChannels * (waveformat.wBitsPerSample / 8);
slouken@1895
   240
    waveformat.nAvgBytesPerSec =
slouken@1895
   241
        waveformat.nSamplesPerSec * waveformat.nBlockAlign;
slouken@0
   242
slouken@1895
   243
    /* Check the buffer size -- minimum of 1/4 second (word aligned) */
icculus@2049
   244
    if (this->spec.samples < (this->spec.freq / 4))
icculus@2049
   245
        this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
slouken@0
   246
slouken@1895
   247
    /* Update the fragment size as size in bytes */
icculus@2049
   248
    SDL_CalculateAudioSpec(&this->spec);
slouken@0
   249
slouken@1895
   250
    /* Open the audio device */
icculus@2049
   251
    result = waveOutOpen(&this->hidden->sound, WAVE_MAPPER, &waveformat,
slouken@1895
   252
                         (DWORD_PTR) FillSound, (DWORD_PTR) this,
slouken@1895
   253
                         CALLBACK_FUNCTION);
slouken@1895
   254
    if (result != MMSYSERR_NOERROR) {
icculus@2049
   255
        WINWAVEOUT_CloseDevice(this);
slouken@1895
   256
        SetMMerror("waveOutOpen()", result);
icculus@2049
   257
        return 0;
slouken@1895
   258
    }
slouken@1895
   259
#ifdef SOUND_DEBUG
slouken@1895
   260
    /* Check the sound device we retrieved */
slouken@1895
   261
    {
slouken@1895
   262
        WAVEOUTCAPS caps;
slouken@0
   263
icculus@2049
   264
        result = waveOutGetDevCaps((UINT) this->hidden->sound,
icculus@2049
   265
                                   &caps, sizeof(caps));
slouken@1895
   266
        if (result != MMSYSERR_NOERROR) {
icculus@2049
   267
            WINWAVEOUT_CloseDevice(this);
slouken@1895
   268
            SetMMerror("waveOutGetDevCaps()", result);
icculus@2049
   269
            return 0;
slouken@1895
   270
        }
slouken@1895
   271
        printf("Audio device: %s\n", caps.szPname);
slouken@1895
   272
    }
slouken@0
   273
#endif
slouken@0
   274
slouken@1895
   275
    /* Create the audio buffer semaphore */
slouken@2060
   276
    this->hidden->audio_sem =
slouken@36
   277
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
slouken@2060
   278
        CreateSemaphoreCE(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
slouken@36
   279
#else
slouken@2060
   280
        CreateSemaphore(NULL, NUM_BUFFERS - 1, NUM_BUFFERS, NULL);
slouken@36
   281
#endif
icculus@2049
   282
    if (this->hidden->audio_sem == NULL) {
icculus@2049
   283
        WINWAVEOUT_CloseDevice(this);
slouken@1895
   284
        SDL_SetError("Couldn't create semaphore");
icculus@2049
   285
        return 0;
slouken@1895
   286
    }
slouken@0
   287
slouken@1895
   288
    /* Create the sound buffers */
slouken@2060
   289
    this->hidden->mixbuf =
slouken@2060
   290
        (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
icculus@2050
   291
    if (this->hidden->mixbuf == NULL) {
icculus@2049
   292
        WINWAVEOUT_CloseDevice(this);
icculus@2049
   293
        SDL_OutOfMemory();
icculus@2049
   294
        return 0;
slouken@1895
   295
    }
slouken@1895
   296
    for (i = 0; i < NUM_BUFFERS; ++i) {
icculus@2049
   297
        SDL_memset(&this->hidden->wavebuf[i], '\0',
slouken@2060
   298
                   sizeof(this->hidden->wavebuf[i]));
icculus@2049
   299
        this->hidden->wavebuf[i].dwBufferLength = this->spec.size;
icculus@2049
   300
        this->hidden->wavebuf[i].dwFlags = WHDR_DONE;
icculus@2049
   301
        this->hidden->wavebuf[i].lpData =
slouken@2060
   302
            (LPSTR) & this->hidden->mixbuf[i * this->spec.size];
icculus@2049
   303
        result = waveOutPrepareHeader(this->hidden->sound,
icculus@2049
   304
                                      &this->hidden->wavebuf[i],
slouken@2060
   305
                                      sizeof(this->hidden->wavebuf[i]));
slouken@1895
   306
        if (result != MMSYSERR_NOERROR) {
icculus@2049
   307
            WINWAVEOUT_CloseDevice(this);
slouken@1895
   308
            SetMMerror("waveOutPrepareHeader()", result);
icculus@2049
   309
            return 0;
slouken@1895
   310
        }
slouken@1895
   311
    }
slouken@0
   312
slouken@2060
   313
    return 1;                   /* Ready to go! */
slouken@0
   314
}
slouken@1895
   315
icculus@2049
   316
icculus@2049
   317
static int
slouken@2060
   318
WINWAVEOUT_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   319
{
icculus@2049
   320
    /* Set the function pointers */
icculus@2049
   321
    impl->OpenDevice = WINWAVEOUT_OpenDevice;
icculus@2049
   322
    impl->ThreadInit = WINWAVEOUT_ThreadInit;
icculus@2049
   323
    impl->PlayDevice = WINWAVEOUT_PlayDevice;
icculus@2049
   324
    impl->WaitDevice = WINWAVEOUT_WaitDevice;
icculus@2049
   325
    impl->WaitDone = WINWAVEOUT_WaitDone;
icculus@2049
   326
    impl->GetDeviceBuf = WINWAVEOUT_GetDeviceBuf;
icculus@2049
   327
    impl->CloseDevice = WINWAVEOUT_CloseDevice;
slouken@2060
   328
    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: Is this true? */
icculus@2049
   329
icculus@2049
   330
    return 1;
icculus@2049
   331
}
icculus@2049
   332
icculus@2049
   333
AudioBootStrap WINWAVEOUT_bootstrap = {
icculus@2049
   334
    "waveout", WINDOWS_OS_NAME " WaveOut", WINWAVEOUT_Init, 0
icculus@2049
   335
};
icculus@2049
   336
slouken@1895
   337
/* vi: set ts=4 sw=4 expandtab: */