2 * Copyright 2007 dnk <dnk@bjum.net>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
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
27 #define WIN32_LEAN_AND_MEAN
31 static HWAVEOUT wave_out
;
32 static sample_format_t waveout_sf
;
33 static int buffer_size
= 4096;
34 static int buffer_count
= 12;
35 static WAVEHDR
*buffers
;
36 static int buffer_idx
;
37 static int buffers_free
;
39 #define FRAME_SIZE_ALIGN(x) \
40 (((x) / sf_get_frame_size(waveout_sf)) * sf_get_frame_size(waveout_sf))
42 static void waveout_error(const char *name
, int rc
)
44 const char *errstr
= "UNKNOWN";
47 case MMSYSERR_ALLOCATED
: errstr
= "MMSYSERR_ALLOCATED"; break;
48 case MMSYSERR_INVALHANDLE
: errstr
= "MMSYSERR_INVALHANDLE"; break;
49 case MMSYSERR_NODRIVER
: errstr
= "MMSYSERR_NODRIVER"; break;
50 case MMSYSERR_BADDEVICEID
: errstr
= "MMSYSERR_BADDEVICEID"; break;
51 case MMSYSERR_NOMEM
: errstr
= "MMSYSERR_NOMEM"; break;
52 case MMSYSERR_NOTSUPPORTED
: errstr
= "MMSYSERR_NOTSUPPORTED"; break;
53 case WAVERR_STILLPLAYING
: errstr
= "WAVERR_STILLPLAYING"; break;
54 case WAVERR_UNPREPARED
: errstr
= "WAVERR_UNPREPARED"; break;
55 case WAVERR_BADFORMAT
: errstr
= "WAVERR_BADFORMAT"; break;
56 case WAVERR_SYNC
: errstr
= "WAVERR_SYNC"; break;
59 d_print("%s returned error %s (%d)\n", name
, errstr
, rc
);
62 static void clean_buffers(void)
66 /* mark used buffers clean */
67 for (i
= 0; i
< buffer_count
; i
++) {
68 WAVEHDR
*hdr
= &buffers
[(buffer_idx
+ i
) % buffer_count
];
70 if (!(hdr
->dwFlags
& WHDR_DONE
))
73 waveOutUnprepareHeader(wave_out
, hdr
, sizeof(WAVEHDR
));
78 static int waveout_open(sample_format_t sf
)
83 /* WAVEFORMATEX does not support channels > 2, waveOutWrite() wants little endian signed PCM */
84 if (sf_get_bigendian(sf
) || !sf_get_signed(sf
) || sf_get_channels(sf
) > 2) {
85 return -OP_ERROR_SAMPLE_FORMAT
;
88 memset(&format
, 0, sizeof(format
));
89 format
.cbSize
= sizeof(format
);
90 format
.wFormatTag
= WAVE_FORMAT_PCM
;
91 format
.nChannels
= sf_get_channels(sf
);
92 format
.nSamplesPerSec
= sf_get_rate(sf
);
93 format
.wBitsPerSample
= sf_get_bits(sf
);
94 format
.nAvgBytesPerSec
= sf_get_second_size(sf
);
95 format
.nBlockAlign
= sf_get_frame_size(sf
);
97 if ((rc
= waveOutOpen(&wave_out
, WAVE_MAPPER
, &format
, 0, 0, CALLBACK_NULL
)) != MMSYSERR_NOERROR
) {
99 case MMSYSERR_ALLOCATED
:
101 return -OP_ERROR_ERRNO
;
102 case MMSYSERR_BADDEVICEID
:
103 case MMSYSERR_NODRIVER
:
105 return -OP_ERROR_ERRNO
;
108 return -OP_ERROR_ERRNO
;
109 case WAVERR_BADFORMAT
:
110 return -OP_ERROR_SAMPLE_FORMAT
;
112 return -OP_ERROR_INTERNAL
;
116 for (i
= 0; i
< buffer_count
; i
++) {
117 buffers
[i
].dwFlags
= 0;
120 buffers_free
= buffer_count
;
127 static int waveout_close(void)
131 waveOutReset(wave_out
);
135 if ((rc
= waveOutClose(wave_out
)) != MMSYSERR_NOERROR
) {
136 waveout_error("waveOutClose", rc
);
144 static int waveout_init(void)
150 buffers
= xnew(WAVEHDR
, buffer_count
);
151 for (i
= 0; i
< buffer_count
; i
++) {
154 memset(hdr
, 0, sizeof(WAVEHDR
));
155 hdr
->lpData
= xmalloc(buffer_size
);
160 static int waveout_exit(void)
164 for (i
= 0; i
< buffer_count
; i
++) {
165 free(buffers
[i
].lpData
);
173 static int waveout_write(const char *buffer
, int count
)
178 count
= FRAME_SIZE_ALIGN(count
);
183 WAVEHDR
*hdr
= &buffers
[buffer_idx
];
185 if (hdr
->dwFlags
!= 0) {
186 /* no free buffers */
190 len
= FRAME_SIZE_ALIGN( min(count
, buffer_size
) );
191 hdr
->dwBufferLength
= len
;
192 memcpy(hdr
->lpData
, buffer
+ written
, len
);
194 if ((rc
= waveOutPrepareHeader(wave_out
, hdr
, sizeof(WAVEHDR
))) != MMSYSERR_NOERROR
) {
195 waveout_error("waveOutPrepareHeader", rc
);
199 if ((rc
= waveOutWrite(wave_out
, hdr
, sizeof(WAVEHDR
))) != MMSYSERR_NOERROR
) {
200 waveOutUnprepareHeader(wave_out
, hdr
, sizeof(WAVEHDR
));
202 waveout_error("waveOutWrite", rc
);
208 buffer_idx
= (buffer_idx
+ 1) % buffer_count
;
215 static int waveout_pause(void)
217 if (waveOutPause(wave_out
) != MMSYSERR_NOERROR
)
222 static int waveout_unpause(void)
224 if (waveOutRestart(wave_out
) != MMSYSERR_NOERROR
)
229 static int waveout_buffer_space(void)
232 if (buffers_free
== 0)
234 return buffers_free
* FRAME_SIZE_ALIGN(buffer_size
);
237 static int waveout_set_option(int key
, const char *val
)
244 if (str_to_int(val
, &ival
) || ival
< 4096 || ival
> 65536) {
246 return -OP_ERROR_ERRNO
;
255 if (str_to_int(val
, &ival
) || ival
< 2 || ival
> 64) {
257 return -OP_ERROR_ERRNO
;
266 return -OP_ERROR_NOT_OPTION
;
276 static int waveout_get_option(int key
, char **val
)
280 *val
= xnew(char, 22);
281 snprintf(*val
, 22, "%d", buffer_size
);
284 *val
= xnew(char, 22);
285 snprintf(*val
, 22, "%d", buffer_count
);
288 return -OP_ERROR_NOT_OPTION
;
293 const struct output_plugin_ops op_pcm_ops
= {
294 .init
= waveout_init
,
295 .exit
= waveout_exit
,
296 .open
= waveout_open
,
297 .close
= waveout_close
,
298 .write
= waveout_write
,
299 .pause
= waveout_pause
,
300 .unpause
= waveout_unpause
,
301 .buffer_space
= waveout_buffer_space
,
302 .set_option
= waveout_set_option
,
303 .get_option
= waveout_get_option
306 const char * const op_pcm_options
[] = {
312 const int op_priority
= 0;