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