comparison src/audio/mme/SDL_mmeaudio.c @ 2049:5f6550e5184f

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