e14743d1 |
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 | /* 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 | |