1 /*
2 * madplay - MPEG audio decoder and player
3 * Copyright (C) 2000-2004 Robert Leslie
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Id: audio_win32.c,v 1.57 2004/01/23 09:41:31 rob Exp $
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include "global.h"
27
28 # include <windows.h>
29 # include <mmsystem.h>
30 # include <mad.h>
31
32 # include "gettext.h"
33
34 # include "audio.h"
35
36 static int opened;
37
38 static HWAVEOUT wave_handle;
39 static audio_pcmfunc_t *audio_pcm;
40 static unsigned int samplerate, samplesize;
41
42 # define NBUFFERS 2
43
44 static struct buffer {
45 WAVEHDR wave_header;
46 HANDLE event_handle;
47 int playing;
48 unsigned int pcm_length;
49 unsigned char pcm_data[48000 * 4 * 2];
50 } output[NBUFFERS];
51
52 static int bindex;
53
54 static
error_text(MMRESULT result)55 char const *error_text(MMRESULT result)
56 {
57 static char buffer[MAXERRORLENGTH];
58
59 if (waveOutGetErrorText(result, buffer, sizeof(buffer)) != MMSYSERR_NOERROR)
60 return _("error getting error text");
61
62 return buffer;
63 }
64
65 static
init(struct audio_init * init)66 int init(struct audio_init *init)
67 {
68 int i;
69
70 opened = 0;
71
72 for (i = 0; i < NBUFFERS; ++i) {
73 output[i].event_handle = CreateEvent(0, FALSE /* manual reset */,
74 FALSE /* initial state */, 0);
75 if (output[i].event_handle == NULL) {
76 while (i--)
77 CloseHandle(output[i].event_handle);
78
79 audio_error = _("failed to create synchronization object");
80 return -1;
81 }
82
83 output[i].playing = 0;
84 output[i].pcm_length = 0;
85 }
86
87 bindex = 0;
88
89 /* try to obtain high priority status */
90
91 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
92 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
93
94 return 0;
95 }
96
97 static
callback(HWAVEOUT handle,UINT message,DWORD data,DWORD param1,DWORD param2)98 void CALLBACK callback(HWAVEOUT handle, UINT message, DWORD data,
99 DWORD param1, DWORD param2)
100 {
101 WAVEHDR *header;
102 struct buffer *buffer;
103
104 switch (message) {
105 case WOM_DONE:
106 header = (WAVEHDR *) param1;
107 buffer = (struct buffer *) header->dwUser;
108
109 if (SetEvent(buffer->event_handle) == 0) {
110 /* error */
111 }
112 break;
113
114 case WOM_OPEN:
115 case WOM_CLOSE:
116 break;
117 }
118 }
119
120 static
set_format(WAVEFORMATEX * format,unsigned int channels,unsigned int speed,unsigned int bits)121 void set_format(WAVEFORMATEX *format, unsigned int channels,
122 unsigned int speed, unsigned int bits)
123 {
124 unsigned int block_al;
125 unsigned long bytes_ps;
126
127 block_al = channels * bits / 8;
128 bytes_ps = speed * block_al;
129
130 format->wFormatTag = WAVE_FORMAT_PCM;
131 format->nChannels = channels;
132 format->nSamplesPerSec = speed;
133 format->nAvgBytesPerSec = bytes_ps;
134 format->nBlockAlign = block_al;
135 format->wBitsPerSample = bits;
136 format->cbSize = 0;
137 }
138
139 static
open_dev(HWAVEOUT * handle,unsigned int channels,unsigned int speed,unsigned int depth)140 int open_dev(HWAVEOUT *handle, unsigned int channels, unsigned int speed,
141 unsigned int depth)
142 {
143 WAVEFORMATEX format;
144 MMRESULT error;
145
146 set_format(&format, channels, speed, depth);
147
148 error = waveOutOpen(handle, WAVE_MAPPER, &format,
149 (DWORD) callback, 0, CALLBACK_FUNCTION);
150 if (error != MMSYSERR_NOERROR) {
151 audio_error = error_text(error);
152 return -1;
153 }
154
155 opened = 1;
156
157 return 0;
158 }
159
160 static
close_dev(HWAVEOUT handle)161 int close_dev(HWAVEOUT handle)
162 {
163 MMRESULT error;
164
165 opened = 0;
166
167 error = waveOutClose(handle);
168 if (error != MMSYSERR_NOERROR) {
169 audio_error = error_text(error);
170 return -1;
171 }
172
173 return 0;
174 }
175
176 static
set_pause(int flag)177 int set_pause(int flag)
178 {
179 static int paused;
180 MMRESULT error;
181
182 if (flag && !paused) {
183 error = waveOutPause(wave_handle);
184 if (error != MMSYSERR_NOERROR) {
185 audio_error = error_text(error);
186 return -1;
187 }
188 }
189 else if (!flag && paused) {
190 error = waveOutRestart(wave_handle);
191 if (error != MMSYSERR_NOERROR) {
192 audio_error = error_text(error);
193 return -1;
194 }
195 }
196
197 paused = flag;
198
199 return 0;
200 }
201
202 static
write_dev(HWAVEOUT handle,struct buffer * buffer)203 int write_dev(HWAVEOUT handle, struct buffer *buffer)
204 {
205 MMRESULT error;
206
207 buffer->wave_header.lpData = buffer->pcm_data;
208 buffer->wave_header.dwBufferLength = buffer->pcm_length;
209 buffer->wave_header.dwUser = (DWORD) buffer;
210 buffer->wave_header.dwFlags = 0;
211
212 error = waveOutPrepareHeader(handle, &buffer->wave_header,
213 sizeof(buffer->wave_header));
214 if (error != MMSYSERR_NOERROR) {
215 audio_error = error_text(error);
216 return -1;
217 }
218
219 error = waveOutWrite(handle, &buffer->wave_header,
220 sizeof(buffer->wave_header));
221 if (error != MMSYSERR_NOERROR) {
222 audio_error = error_text(error);
223 return -1;
224 }
225
226 buffer->playing = 1;
227
228 return 0;
229 }
230
231 static
wait(struct buffer * buffer)232 int wait(struct buffer *buffer)
233 {
234 MMRESULT error;
235
236 if (!buffer->playing)
237 return 0;
238
239 switch (WaitForSingleObject(buffer->event_handle, INFINITE)) {
240 case WAIT_OBJECT_0:
241 break;
242
243 case WAIT_ABANDONED:
244 audio_error = _("wait abandoned");
245 return -1;
246
247 case WAIT_TIMEOUT:
248 audio_error = _("wait timeout");
249 return -1;
250
251 case WAIT_FAILED:
252 default:
253 audio_error = _("wait failed");
254 return -1;
255 }
256
257 buffer->playing = 0;
258
259 error = waveOutUnprepareHeader(wave_handle, &buffer->wave_header,
260 sizeof(buffer->wave_header));
261 if (error != MMSYSERR_NOERROR) {
262 audio_error = error_text(error);
263 return -1;
264 }
265
266 return 0;
267 }
268
269 static
drain(void)270 int drain(void)
271 {
272 int i, result = 0;
273
274 if (set_pause(0) == -1)
275 result = -1;
276
277 if (output[bindex].pcm_length &&
278 write_dev(wave_handle, &output[bindex]) == -1)
279 result = -1;
280
281 for (i = 0; i < NBUFFERS; ++i) {
282 if (wait(&output[i]) == -1)
283 result = -1;
284 }
285
286 output[bindex].pcm_length = 0;
287
288 return result;
289 }
290
291 static
config(struct audio_config * config)292 int config(struct audio_config *config)
293 {
294 unsigned int bitdepth;
295
296 bitdepth = config->precision & ~7;
297 if (bitdepth == 0)
298 bitdepth = 16;
299 else if (bitdepth > 32)
300 bitdepth = 32;
301
302 if (opened) {
303 if (drain() == -1)
304 return -1;
305
306 close_dev(wave_handle);
307 }
308
309 if (open_dev(&wave_handle, config->channels, config->speed, bitdepth) == -1)
310 return -1;
311
312 switch (config->precision = bitdepth) {
313 case 8:
314 audio_pcm = audio_pcm_u8;
315 break;
316
317 case 16:
318 audio_pcm = audio_pcm_s16le;
319 break;
320
321 case 24:
322 audio_pcm = audio_pcm_s24le;
323 break;
324
325 case 32:
326 audio_pcm = audio_pcm_s32le;
327 break;
328 }
329
330 samplerate = config->speed;
331 samplesize = bitdepth / 8;
332
333 return 0;
334 }
335
336 static
play(struct audio_play * play)337 int play(struct audio_play *play)
338 {
339 struct buffer *buffer;
340 unsigned int len;
341
342 if (set_pause(0) == -1)
343 return -1;
344
345 buffer = &output[bindex];
346
347 /* wait for block to finish playing */
348
349 if (buffer->pcm_length == 0 && wait(buffer) == -1)
350 return -1;
351
352 /* prepare block */
353
354 len = audio_pcm(&buffer->pcm_data[buffer->pcm_length], play->nsamples,
355 play->samples[0], play->samples[1], play->mode, play->stats);
356
357 buffer->pcm_length += len;
358
359 if (buffer->pcm_length + MAX_NSAMPLES * samplesize * 2 >
360 samplerate * samplesize * 2) {
361 write_dev(wave_handle, buffer);
362
363 bindex = (bindex + 1) % NBUFFERS;
364 output[bindex].pcm_length = 0;
365 }
366
367 return 0;
368 }
369
370 static
stop(struct audio_stop * stop)371 int stop(struct audio_stop *stop)
372 {
373 int result = 0;
374 MMRESULT error;
375
376 if (stop->flush) {
377 error = waveOutReset(wave_handle);
378 if (error != MMSYSERR_NOERROR) {
379 audio_error = error_text(error);
380 result = -1;
381 }
382
383 output[bindex].pcm_length = 0;
384
385 return result;
386 }
387
388 return set_pause(1);
389 }
390
391 static
finish(struct audio_finish * finish)392 int finish(struct audio_finish *finish)
393 {
394 int i, result = 0;
395
396 if (opened) {
397 if (drain() == -1)
398 result = -1;
399 if (close_dev(wave_handle) == -1)
400 result = -1;
401 }
402
403 /* restore priority status */
404
405 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
406 SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
407
408 for (i = 0; i < NBUFFERS; ++i) {
409 if (CloseHandle(output[i].event_handle) == 0 && result == 0) {
410 audio_error = _("failed to close synchronization object");
411 result = -1;
412 }
413 }
414
415 return result;
416 }
417
audio_win32(union audio_control * control)418 int audio_win32(union audio_control *control)
419 {
420 audio_error = 0;
421
422 switch (control->command) {
423 case AUDIO_COMMAND_INIT:
424 return init(&control->init);
425
426 case AUDIO_COMMAND_CONFIG:
427 return config(&control->config);
428
429 case AUDIO_COMMAND_PLAY:
430 return play(&control->play);
431
432 case AUDIO_COMMAND_STOP:
433 return stop(&control->stop);
434
435 case AUDIO_COMMAND_FINISH:
436 return finish(&control->finish);
437 }
438
439 return 0;
440 }
441