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