1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 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 (at) 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 /* Audio driver functions */ 35 static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec); 36 static void MME_WaitAudio(_THIS); 37 static Uint8 *MME_GetAudioBuf(_THIS); 38 static void MME_PlayAudio(_THIS); 39 static void MME_WaitDone(_THIS); 40 static void MME_CloseAudio(_THIS); 41 42 /* Audio driver bootstrap functions */ 43 static int Audio_Available(void) 44 { 45 return(1); 46 } 47 48 static void Audio_DeleteDevice(SDL_AudioDevice *device) 49 { 50 if ( device ) { 51 if ( device->hidden ) { 52 SDL_free(device->hidden); 53 device->hidden = NULL; 54 } 55 SDL_free(device); 56 device = NULL; 57 } 58 } 59 60 static SDL_AudioDevice *Audio_CreateDevice(int devindex) 61 { 62 SDL_AudioDevice *this; 63 64 /* Initialize all variables that we clean on shutdown */ 65 this = SDL_malloc(sizeof(SDL_AudioDevice)); 66 if ( this ) { 67 SDL_memset(this, 0, (sizeof *this)); 68 this->hidden = SDL_malloc((sizeof *this->hidden)); 69 } 70 if ( (this == NULL) || (this->hidden == NULL) ) { 71 SDL_OutOfMemory(); 72 if ( this ) { 73 SDL_free(this); 74 } 75 return(0); 76 } 77 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 78 /* Set the function pointers */ 79 this->OpenAudio = MME_OpenAudio; 80 this->WaitAudio = MME_WaitAudio; 81 this->PlayAudio = MME_PlayAudio; 82 this->GetAudioBuf = MME_GetAudioBuf; 83 this->WaitDone = MME_WaitDone; 84 this->CloseAudio = MME_CloseAudio; 85 this->free = Audio_DeleteDevice; 86 87 return this; 88 } 89 90 AudioBootStrap MMEAUDIO_bootstrap = { 91 "waveout", "Tru64 MME WaveOut", 92 Audio_Available, Audio_CreateDevice 93 }; 94 95 static void SetMMerror(char *function, MMRESULT code) 96 { 97 int len; 98 char errbuf[MAXERRORLENGTH]; 99 100 SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function); 101 len = SDL_strlen(errbuf); 102 waveOutGetErrorText(code, errbuf+len, MAXERRORLENGTH-len); 103 SDL_SetError("%s",errbuf); 104 } 105 106 static void CALLBACK MME_CALLBACK(HWAVEOUT hwo, 107 UINT uMsg, 108 DWORD dwInstance, 109 LPARAM dwParam1, 110 LPARAM dwParam2) 111 { 112 WAVEHDR *wp = (WAVEHDR *) dwParam1; 113 114 if ( uMsg == WOM_DONE ) 115 inUse[wp->dwUser] = FALSE; 116 } 117 118 static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec) 119 { 120 MMRESULT result; 121 int i; 122 123 mixbuf = NULL; 124 125 /* Set basic WAVE format parameters */ 126 shm = mmeAllocMem(sizeof(*shm)); 127 if ( shm == NULL ) { 128 SDL_SetError("Out of memory: shm"); 129 return(-1); 130 } 131 shm->sound = 0; 132 shm->wFmt.wf.wFormatTag = WAVE_FORMAT_PCM; 133 134 /* Determine the audio parameters from the AudioSpec */ 135 switch ( spec->format & 0xFF ) { 136 case 8: 137 /* Unsigned 8 bit audio data */ 138 spec->format = AUDIO_U8; 139 shm->wFmt.wBitsPerSample = 8; 140 break; 141 case 16: 142 /* Signed 16 bit audio data */ 143 spec->format = AUDIO_S16; 144 shm->wFmt.wBitsPerSample = 16; 145 break; 146 default: 147 SDL_SetError("Unsupported audio format"); 148 return(-1); 149 } 150 151 shm->wFmt.wf.nChannels = spec->channels; 152 shm->wFmt.wf.nSamplesPerSec = spec->freq; 153 shm->wFmt.wf.nBlockAlign = 154 shm->wFmt.wf.nChannels * shm->wFmt.wBitsPerSample / 8; 155 shm->wFmt.wf.nAvgBytesPerSec = 156 shm->wFmt.wf.nSamplesPerSec * shm->wFmt.wf.nBlockAlign; 157 158 /* Check the buffer size -- minimum of 1/4 second (word aligned) */ 159 if ( spec->samples < (spec->freq/4) ) 160 spec->samples = ((spec->freq/4)+3)&~3; 161 162 /* Update the fragment size as size in bytes */ 163 SDL_CalculateAudioSpec(spec); 164 165 /* Open the audio device */ 166 result = waveOutOpen(&(shm->sound), 167 WAVE_MAPPER, 168 &(shm->wFmt.wf), 169 MME_CALLBACK, 170 NULL, 171 (CALLBACK_FUNCTION|WAVE_OPEN_SHAREABLE)); 172 if ( result != MMSYSERR_NOERROR ) { 173 SetMMerror("waveOutOpen()", result); 174 return(-1); 175 } 176 177 /* Create the sound buffers */ 178 mixbuf = (Uint8 *)mmeAllocBuffer(NUM_BUFFERS * (spec->size)); 179 if ( mixbuf == NULL ) { 180 SDL_SetError("Out of memory: mixbuf"); 181 return(-1); 182 } 183 184 for (i = 0; i < NUM_BUFFERS; i++) { 185 shm->wHdr[i].lpData = &mixbuf[i * (spec->size)]; 186 shm->wHdr[i].dwBufferLength = spec->size; 187 shm->wHdr[i].dwFlags = 0; 188 shm->wHdr[i].dwUser = i; 189 shm->wHdr[i].dwLoops = 0; /* loop control counter */ 190 shm->wHdr[i].lpNext = NULL; /* reserved for driver */ 191 shm->wHdr[i].reserved = 0; 192 inUse[i] = FALSE; 193 } 194 next_buffer = 0; 195 return 0; 196 } 197 198 static void MME_WaitAudio(_THIS) 199 { 200 while ( inUse[next_buffer] ) { 201 mmeWaitForCallbacks(); 202 mmeProcessCallbacks(); 203 } 204 } 205 206 static Uint8 *MME_GetAudioBuf(_THIS) 207 { 208 Uint8 *retval; 209 210 inUse[next_buffer] = TRUE; 211 retval = (Uint8 *)(shm->wHdr[next_buffer].lpData); 212 return retval; 213 } 214 215 static void MME_PlayAudio(_THIS) 216 { 217 /* Queue it up */ 218 waveOutWrite(shm->sound, &(shm->wHdr[next_buffer]), sizeof(WAVEHDR)); 219 next_buffer = (next_buffer+1)%NUM_BUFFERS; 220 } 221 222 static void MME_WaitDone(_THIS) 223 { 224 MMRESULT result; 225 int i; 226 227 if ( shm->sound ) { 228 for (i = 0; i < NUM_BUFFERS; i++) 229 while ( inUse[i] ) { 230 mmeWaitForCallbacks(); 231 mmeProcessCallbacks(); 232 } 233 result = waveOutReset(shm->sound); 234 if ( result != MMSYSERR_NOERROR ) 235 SetMMerror("waveOutReset()", result); 236 mmeProcessCallbacks(); 237 } 238 } 239 240 static void MME_CloseAudio(_THIS) 241 { 242 MMRESULT result; 243 244 if ( mixbuf ) { 245 result = mmeFreeBuffer(mixbuf); 246 if (result != MMSYSERR_NOERROR ) 247 SetMMerror("mmeFreeBuffer", result); 248 mixbuf = NULL; 249 } 250 251 if ( shm ) { 252 if ( shm->sound ) { 253 result = waveOutClose(shm->sound); 254 if (result != MMSYSERR_NOERROR ) 255 SetMMerror("waveOutClose()", result); 256 mmeProcessCallbacks(); 257 } 258 result = mmeFreeMem(shm); 259 if (result != MMSYSERR_NOERROR ) 260 SetMMerror("mmeFreeMem()", result); 261 shm = NULL; 262 } 263 } 264 265