src/audio/mme/SDL_mmeaudio.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Aug 2009 18:39:57 +0000
changeset 3227 458e53d8662c
parent 2859 99210400e8b9
child 3697 f7b03b6838cb
permissions -rw-r--r--
Clarified API documentation
slouken@654
     1
/*
slouken@654
     2
    SDL - Simple DirectMedia Layer
slouken@2859
     3
    Copyright (C) 1997-2009 Sam Lantinga
slouken@654
     4
slouken@654
     5
    This library is free software; you can redistribute it and/or
slouken@654
     6
    modify it under the terms of the GNU Library General Public
slouken@654
     7
    License as published by the Free Software Foundation; either
slouken@654
     8
    version 2 of the License, or (at your option) any later version.
slouken@654
     9
slouken@654
    10
    This library is distributed in the hope that it will be useful,
slouken@654
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@654
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@654
    13
    Library General Public License for more details.
slouken@654
    14
slouken@654
    15
    You should have received a copy of the GNU Library General Public
slouken@654
    16
    License along with this library; if not, write to the Free
slouken@654
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@654
    18
slouken@654
    19
    Sam Lantinga
slouken@654
    20
    slouken@libsdl.org
slouken@654
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@654
    23
slouken@654
    24
/* Tru64 UNIX MME support */
slouken@1342
    25
#include <mme_api.h>
slouken@1342
    26
slouken@1358
    27
#include "SDL_timer.h"
slouken@1342
    28
#include "SDL_audio.h"
slouken@1361
    29
#include "../SDL_audio_c.h"
slouken@654
    30
#include "SDL_mmeaudio.h"
slouken@654
    31
slouken@654
    32
static BOOL inUse[NUM_BUFFERS];
slouken@654
    33
slouken@1895
    34
static void
slouken@1895
    35
SetMMerror(char *function, MMRESULT code)
slouken@654
    36
{
slouken@654
    37
    int len;
slouken@654
    38
    char errbuf[MAXERRORLENGTH];
slouken@654
    39
slouken@1338
    40
    SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
slouken@1336
    41
    len = SDL_strlen(errbuf);
slouken@1895
    42
    waveOutGetErrorText(code, errbuf + len, MAXERRORLENGTH - len);
slouken@1895
    43
    SDL_SetError("%s", errbuf);
slouken@654
    44
}
slouken@654
    45
slouken@1895
    46
static void CALLBACK
icculus@2049
    47
MME_Callback(HWAVEOUT hwo,
slouken@1895
    48
             UINT uMsg, DWORD dwInstance, LPARAM dwParam1, LPARAM dwParam2)
slouken@654
    49
{
slouken@654
    50
    WAVEHDR *wp = (WAVEHDR *) dwParam1;
slouken@654
    51
slouken@1895
    52
    if (uMsg == WOM_DONE)
slouken@1895
    53
        inUse[wp->dwUser] = FALSE;
slouken@654
    54
}
slouken@654
    55
slouken@1895
    56
static int
icculus@2049
    57
MME_OpenDevice(_THIS, const char *devname, int iscapture)
slouken@654
    58
{
icculus@2049
    59
    int valid_format = 0;
slouken@654
    60
    MMRESULT result;
icculus@2049
    61
    Uint8 *mixbuf = NULL;
slouken@654
    62
    int i;
slouken@654
    63
icculus@2049
    64
    /* Initialize all variables that we clean on shutdown */
icculus@2049
    65
    this->hidden = (struct SDL_PrivateAudioData *)
slouken@2060
    66
        SDL_malloc((sizeof *this->hidden));
icculus@2049
    67
    if (this->hidden == NULL) {
icculus@2049
    68
        SDL_OutOfMemory();
icculus@2049
    69
        return 0;
icculus@2049
    70
    }
icculus@2049
    71
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@654
    72
slouken@654
    73
    /* Set basic WAVE format parameters */
icculus@2049
    74
    this->hidden->shm = mmeAllocMem(sizeof(*this->hidden->shm));
icculus@2049
    75
    if (this->hidden->shm == NULL) {
icculus@2049
    76
        MME_CloseDevice(this);
icculus@2049
    77
        SDL_OutOfMemory();
icculus@2049
    78
        return 0;
slouken@654
    79
    }
icculus@2049
    80
slouken@2060
    81
    SDL_memset(this->hidden->shm, '\0', sizeof(*this->hidden->shm));
icculus@2049
    82
    this->hidden->shm->sound = 0;
icculus@2049
    83
    this->hidden->shm->wFmt.wf.wFormatTag = WAVE_FORMAT_PCM;
slouken@654
    84
slouken@654
    85
    /* Determine the audio parameters from the AudioSpec */
icculus@2049
    86
    /* Try for a closest match on audio format */
icculus@2049
    87
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
icculus@2049
    88
         !valid_format && test_format;) {
icculus@2049
    89
        valid_format = 1;
icculus@2049
    90
        switch (test_format) {
slouken@2060
    91
        case AUDIO_U8:
slouken@2060
    92
        case AUDIO_S16:
slouken@2060
    93
        case AUDIO_S32:
slouken@2060
    94
            break;
slouken@2060
    95
        default:
slouken@2060
    96
            valid_format = 0;
slouken@2060
    97
            test_format = SDL_NextAudioFormat();
icculus@2049
    98
        }
icculus@2049
    99
    }
icculus@2049
   100
icculus@2049
   101
    if (!valid_format) {
icculus@2049
   102
        MME_CloseDevice(this);
slouken@1895
   103
        SDL_SetError("Unsupported audio format");
icculus@2049
   104
        return 0;
slouken@654
   105
    }
slouken@654
   106
icculus@2049
   107
    this->spec.format = test_format;
icculus@2049
   108
    this->hidden->shm->wFmt.wBitsPerSample = SDL_AUDIO_BITSIZE(test_format);
icculus@2049
   109
icculus@2006
   110
    /* !!! FIXME: Can this handle more than stereo? */
icculus@2049
   111
    this->hidden->shm->wFmt.wf.nChannels = this->spec.channels;
icculus@2049
   112
    this->hidden->shm->wFmt.wf.nSamplesPerSec = this->spec.freq;
icculus@2049
   113
    this->hidden->shm->wFmt.wf.nBlockAlign =
slouken@2060
   114
        this->hidden->shm->wFmt.wf.nChannels *
slouken@2060
   115
        this->hidden->shm->wFmt.wBitsPerSample / 8;
icculus@2049
   116
    this->hidden->shm->wFmt.wf.nAvgBytesPerSec =
slouken@2060
   117
        this->hidden->shm->wFmt.wf.nSamplesPerSec *
slouken@2060
   118
        this->hidden->shm->wFmt.wf.nBlockAlign;
slouken@654
   119
slouken@654
   120
    /* Check the buffer size -- minimum of 1/4 second (word aligned) */
icculus@2049
   121
    if (this->spec.samples < (this->spec.freq / 4))
icculus@2049
   122
        this->spec.samples = ((this->spec.freq / 4) + 3) & ~3;
slouken@654
   123
slouken@654
   124
    /* Update the fragment size as size in bytes */
icculus@2049
   125
    SDL_CalculateAudioSpec(&this->spec);
slouken@654
   126
slouken@654
   127
    /* Open the audio device */
icculus@2049
   128
    result = waveOutOpen(&(this->hidden->shm->sound),
slouken@1895
   129
                         WAVE_MAPPER,
icculus@2049
   130
                         &(this->hidden->shm->wFmt.wf),
icculus@2049
   131
                         MME_Callback,
slouken@1895
   132
                         NULL, (CALLBACK_FUNCTION | WAVE_OPEN_SHAREABLE));
slouken@1895
   133
    if (result != MMSYSERR_NOERROR) {
icculus@2049
   134
        MME_CloseDevice(this);
slouken@1895
   135
        SetMMerror("waveOutOpen()", result);
icculus@2049
   136
        return 0;
slouken@654
   137
    }
slouken@654
   138
slouken@654
   139
    /* Create the sound buffers */
icculus@2049
   140
    mixbuf = (Uint8 *) mmeAllocBuffer(NUM_BUFFERS * (this->spec.size));
slouken@1895
   141
    if (mixbuf == NULL) {
icculus@2049
   142
        MME_CloseDevice(this);
icculus@2049
   143
        SDL_OutOfMemory();
icculus@2049
   144
        return 0;
slouken@654
   145
    }
icculus@2049
   146
    this->hidden->mixbuf = mixbuf;
slouken@654
   147
slouken@654
   148
    for (i = 0; i < NUM_BUFFERS; i++) {
icculus@2049
   149
        this->hidden->shm->wHdr[i].lpData = &mixbuf[i * (this->spec.size)];
icculus@2049
   150
        this->hidden->shm->wHdr[i].dwBufferLength = this->spec.size;
icculus@2049
   151
        this->hidden->shm->wHdr[i].dwFlags = 0;
icculus@2049
   152
        this->hidden->shm->wHdr[i].dwUser = i;
slouken@2060
   153
        this->hidden->shm->wHdr[i].dwLoops = 0; /* loop control counter */
slouken@2060
   154
        this->hidden->shm->wHdr[i].lpNext = NULL;       /* reserved for driver */
icculus@2049
   155
        this->hidden->shm->wHdr[i].reserved = 0;
slouken@1895
   156
        inUse[i] = FALSE;
slouken@654
   157
    }
icculus@2049
   158
    this->hidden->next_buffer = 0;
icculus@2049
   159
icculus@2049
   160
    return 1;
slouken@654
   161
}
slouken@654
   162
slouken@1895
   163
static void
icculus@2049
   164
MME_WaitDevice(_THIS)
slouken@654
   165
{
icculus@2049
   166
    while (inUse[this->hidden->next_buffer]) {
slouken@1895
   167
        mmeWaitForCallbacks();
slouken@1895
   168
        mmeProcessCallbacks();
slouken@755
   169
    }
slouken@654
   170
}
slouken@654
   171
slouken@1895
   172
static Uint8 *
icculus@2049
   173
MME_GetDeviceBuf(_THIS)
slouken@654
   174
{
icculus@2049
   175
    void *retval = this->hidden->shm->wHdr[this->hidden->next_buffer].lpData;
icculus@2049
   176
    inUse[this->hidden->next_buffer] = TRUE;
icculus@2049
   177
    return (Uint8 *) retval;
slouken@654
   178
}
slouken@654
   179
slouken@1895
   180
static void
icculus@2049
   181
MME_PlayDevice(_THIS)
slouken@654
   182
{
slouken@654
   183
    /* Queue it up */
icculus@2049
   184
    waveOutWrite(this->hidden->shm->sound,
icculus@2049
   185
                 &(this->hidden->shm->wHdr[this->hidden->next_buffer]),
slouken@2060
   186
                 sizeof(WAVEHDR));
icculus@2049
   187
    this->hidden->next_buffer = (this->hidden->next_buffer + 1) % NUM_BUFFERS;
slouken@654
   188
}
slouken@654
   189
slouken@1895
   190
static void
slouken@1895
   191
MME_WaitDone(_THIS)
slouken@654
   192
{
slouken@654
   193
    MMRESULT result;
slouken@654
   194
    int i;
slouken@654
   195
icculus@2049
   196
    if (this->hidden->shm->sound) {
slouken@1895
   197
        for (i = 0; i < NUM_BUFFERS; i++)
slouken@1895
   198
            while (inUse[i]) {
slouken@1895
   199
                mmeWaitForCallbacks();
slouken@1895
   200
                mmeProcessCallbacks();
slouken@1895
   201
            }
icculus@2049
   202
        result = waveOutReset(this->hidden->shm->sound);
slouken@1895
   203
        if (result != MMSYSERR_NOERROR)
slouken@1895
   204
            SetMMerror("waveOutReset()", result);
slouken@1895
   205
        mmeProcessCallbacks();
slouken@654
   206
    }
slouken@654
   207
}
slouken@654
   208
slouken@1895
   209
static void
icculus@2049
   210
MME_CloseDevice(_THIS)
slouken@654
   211
{
icculus@2049
   212
    if (this->hidden != NULL) {
icculus@2049
   213
        MMRESULT result;
slouken@654
   214
icculus@2049
   215
        if (this->hidden->mixbuf) {
icculus@2049
   216
            result = mmeFreeBuffer(this->hidden->mixbuf);
icculus@2049
   217
            if (result != MMSYSERR_NOERROR)
icculus@2049
   218
                SetMMerror("mmeFreeBuffer", result);
icculus@2049
   219
            this->hidden->mixbuf = NULL;
icculus@2049
   220
        }
slouken@654
   221
icculus@2049
   222
        if (this->hidden->shm) {
icculus@2049
   223
            if (this->hidden->shm->sound) {
icculus@2049
   224
                result = waveOutClose(this->hidden->shm->sound);
icculus@2049
   225
                if (result != MMSYSERR_NOERROR)
icculus@2049
   226
                    SetMMerror("waveOutClose()", result);
icculus@2049
   227
                mmeProcessCallbacks();
icculus@2049
   228
            }
icculus@2049
   229
            result = mmeFreeMem(this->hidden->shm);
slouken@1895
   230
            if (result != MMSYSERR_NOERROR)
icculus@2049
   231
                SetMMerror("mmeFreeMem()", result);
icculus@2049
   232
            this->hidden->shm = NULL;
slouken@1895
   233
        }
icculus@2049
   234
icculus@2049
   235
        SDL_free(this->hidden);
icculus@2049
   236
        this->hidden = NULL;
slouken@654
   237
    }
slouken@654
   238
}
slouken@654
   239
icculus@2049
   240
static int
slouken@2060
   241
MME_Init(SDL_AudioDriverImpl * impl)
icculus@2049
   242
{
icculus@2049
   243
    /* Set the function pointers */
icculus@2049
   244
    impl->OpenDevice = MME_OpenDevice;
icculus@2049
   245
    impl->WaitDevice = MME_WaitDevice;
icculus@2049
   246
    impl->WaitDone = MME_WaitDone;
icculus@2049
   247
    impl->PlayDevice = MME_PlayDevice;
icculus@2049
   248
    impl->GetDeviceBuf = MME_GetDeviceBuf;
icculus@2049
   249
    impl->CloseDevice = MME_CloseDevice;
icculus@2049
   250
    impl->OnlyHasDefaultOutputDevice = 1;
icculus@2049
   251
icculus@2049
   252
    return 1;
icculus@2049
   253
}
icculus@2049
   254
icculus@2049
   255
/* !!! FIXME: Windows "windib" driver is called waveout, too */
icculus@2049
   256
AudioBootStrap MMEAUDIO_bootstrap = {
icculus@2049
   257
    "waveout", "Tru64 MME WaveOut", MME_Init, 0
icculus@2049
   258
};
icculus@2049
   259
slouken@1895
   260
/* vi: set ts=4 sw=4 expandtab: */