1 /*************************************************************************/
2 /* */
3 /* Cepstral, LLC */
4 /* Copyright (c) 2001 */
5 /* All Rights Reserved. */
6 /* */
7 /* Permission is hereby granted, free of charge, to use and distribute */
8 /* this software and its documentation without restriction, including */
9 /* without limitation the rights to use, copy, modify, merge, publish, */
10 /* distribute, sublicense, and/or sell copies of this work, and to */
11 /* permit persons to whom this work is furnished to do so, subject to */
12 /* the following conditions: */
13 /* 1. The code must retain the above copyright notice, this list of */
14 /* conditions and the following disclaimer. */
15 /* 2. Any modifications must be clearly marked as such. */
16 /* 3. Original authors' names are not deleted. */
17 /* 4. The authors' names are not used to endorse or promote products */
18 /* derived from this software without specific prior written */
19 /* permission. */
20 /* */
21 /* CEPSTRAL, LLC AND THE CONTRIBUTORS TO THIS WORK DISCLAIM ALL */
22 /* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED */
23 /* WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL */
24 /* CEPSTRAL, LLC NOR THE CONTRIBUTORS BE LIABLE FOR ANY SPECIAL, */
25 /* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER */
26 /* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION */
27 /* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR */
28 /* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
29 /* */
30 /*************************************************************************/
31 /* Author: David Huggins-Daines (dhd@cepstral.com) */
32 /* Date: October 2001 */
33 /*************************************************************************/
34 /* */
35 /* au_wince.c: audio support for Windows CE and Win32 */
36 /* (IHNJ, IJLS "wince") */
37 /* */
38 /*************************************************************************/
39
40 #include <windows.h>
41 #include <mmsystem.h>
42
43 #include "cst_audio.h"
44 #include "cst_alloc.h"
45
46 typedef struct au_wince_pdata_struct {
47 HWAVEOUT wo;
48 HANDLE bevt;
49 HANDLE wevt;
50 LONG bcnt;
51 int in_reset;
52 void **fq;
53 int fqlen;
54 int fqmaxlen;
55 } au_wince_pdata;
56
add_to_free_queue(cst_audiodev * ad,void * datum)57 void add_to_free_queue(cst_audiodev *ad, void *datum)
58 {
59 au_wince_pdata *pd = ad->platform_data;
60
61 if (pd->fqlen == pd->fqmaxlen && !(pd->fqmaxlen % 32))
62 {
63 pd->fqmaxlen += 32;
64 pd->fq = (void **)realloc(pd->fq, pd->fqmaxlen * sizeof(void *));
65 if (!pd->fq)
66 {
67 cst_errmsg("Out of memory\n");
68 cst_error();
69 }
70 }
71 pd->fq[pd->fqlen++] = datum;
72 }
73
finish_header(HWAVEOUT drvr,WAVEHDR * hdr)74 static void finish_header(HWAVEOUT drvr, WAVEHDR *hdr)
75 {
76 if (waveOutUnprepareHeader(drvr,hdr,sizeof(*hdr))
77 != MMSYSERR_NOERROR)
78 {
79 cst_errmsg("Failed to unprepare header %p\n", hdr);
80 cst_error();
81 }
82 cst_free(hdr->lpData);
83 cst_free(hdr);
84 }
85
sndbuf_done(HWAVEOUT drvr,UINT msg,DWORD udata,DWORD param1,DWORD param2)86 void CALLBACK sndbuf_done(HWAVEOUT drvr, UINT msg,
87 DWORD udata, DWORD param1, DWORD param2)
88 {
89 WAVEHDR *hdr = (WAVEHDR *)param1;
90 cst_audiodev *ad = (cst_audiodev *)udata;
91 au_wince_pdata *pd = ad->platform_data;
92
93 if (msg == MM_WOM_DONE && hdr && (hdr->dwFlags & WHDR_DONE)) {
94 LONG c;
95
96 c = InterlockedDecrement(&pd->bcnt);
97 if (c == 0)
98 SetEvent(pd->bevt);
99 if (c == 7)
100 SetEvent(pd->wevt);
101 if (pd->in_reset) add_to_free_queue(ad, hdr);
102 }
103 }
104
audio_open_wince(int sps,int channels,int fmt)105 cst_audiodev *audio_open_wince(int sps, int channels, int fmt)
106 {
107 cst_audiodev *ad;
108 au_wince_pdata *pd;
109 HWAVEOUT wo;
110 WAVEFORMATEX wfx;
111 MMRESULT err;
112
113 ad = cst_alloc(cst_audiodev,1);
114 ad->sps = ad->real_sps = sps;
115 ad->channels = ad->real_channels = channels;
116 ad->fmt = ad->real_fmt = fmt;
117
118 memset(&wfx,0,sizeof(wfx));
119 wfx.nChannels = channels;
120 wfx.nSamplesPerSec = sps;
121
122 switch (fmt)
123 {
124 case CST_AUDIO_LINEAR16:
125 wfx.wFormatTag = WAVE_FORMAT_PCM;
126 wfx.wBitsPerSample = 16;
127 break;
128 case CST_AUDIO_LINEAR8:
129 wfx.wFormatTag = WAVE_FORMAT_PCM;
130 wfx.wBitsPerSample = 8;
131 break;
132 default:
133 cst_errmsg("audio_open_wince: unsupported format %d\n", fmt);
134 cst_free(ad);
135 cst_error();
136 }
137 wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8;
138 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec*wfx.nBlockAlign;
139 err = waveOutOpen(&wo,WAVE_MAPPER,&wfx,
140 (DWORD)sndbuf_done,(DWORD)ad,
141 CALLBACK_FUNCTION);
142 if (err != MMSYSERR_NOERROR)
143 {
144 cst_errmsg("Failed to open output device: %x\n", err);
145 cst_free(ad);
146 cst_error();
147 }
148
149 pd = cst_alloc(au_wince_pdata,1);
150 pd->wo = wo;
151 pd->bevt = CreateEvent(NULL,FALSE,FALSE,NULL);
152 pd->wevt = CreateEvent(NULL,FALSE,FALSE,NULL);
153 pd->bcnt = 0;
154 ad->platform_data = pd;
155 return ad;
156 }
157
free_queue_empty(cst_audiodev * ad)158 static void free_queue_empty(cst_audiodev *ad)
159 {
160 au_wince_pdata *pd = ad->platform_data;
161
162 while (pd->fqlen)
163 {
164 finish_header(pd->wo, pd->fq[--pd->fqlen]);
165 }
166 }
167
audio_close_wince(cst_audiodev * ad)168 int audio_close_wince(cst_audiodev *ad)
169 {
170 au_wince_pdata *pd = ad->platform_data;
171 MMRESULT err;
172
173 if (ad)
174 {
175 /* Okay, I actually think this isn't a race, because
176 bcnt is only ever decremented asynchronously, and
177 the asynchronous callback can't be interrupted. So
178 the only issue is whether it hits zero between the
179 time we test it and the time we start waiting, and
180 in this case, the event will get set anyway. */
181 if (pd->bcnt > 0)
182 WaitForSingleObject(pd->bevt, INFINITE);
183 pd->in_reset = 1;
184 err = waveOutReset(pd->wo);
185 if (err != MMSYSERR_NOERROR)
186 {
187 cst_errmsg("Failed to reset output device: %x\n", err);
188 cst_error();
189 }
190 pd->in_reset = 0;
191 free_queue_empty(ad);
192 err = waveOutClose(pd->wo);
193 if (err != MMSYSERR_NOERROR)
194 {
195 cst_errmsg("Failed to close output device: %x\n", err);
196 cst_error();
197 }
198 cst_free(pd);
199 cst_free(ad);
200 }
201 return 0;
202 }
203
audio_write_wince(cst_audiodev * ad,void * samples,int num_bytes)204 int audio_write_wince(cst_audiodev *ad, void *samples, int num_bytes)
205 {
206 au_wince_pdata *pd = ad->platform_data;
207 WAVEHDR *hdr;
208 MMRESULT err;
209
210 if (num_bytes == 0)
211 return 0;
212
213 hdr = cst_alloc(WAVEHDR,1);
214 hdr->lpData = cst_alloc(char,num_bytes);
215 memcpy(hdr->lpData,samples,num_bytes);
216 hdr->dwBufferLength = num_bytes;
217
218 err = waveOutPrepareHeader(pd->wo, hdr, sizeof(*hdr));
219 if (err != MMSYSERR_NOERROR)
220 {
221 cst_errmsg("Failed to prepare header %p: %x\n", hdr, err);
222 cst_error();
223 }
224
225 if (InterlockedIncrement(&pd->bcnt) == 8)
226 WaitForSingleObject(pd->wevt, INFINITE);
227 err = waveOutWrite(pd->wo, hdr, sizeof(*hdr));
228 if (err != MMSYSERR_NOERROR)
229 {
230 cst_errmsg("Failed to write header %p: %x\n", hdr, err);
231 cst_error();
232 }
233 return num_bytes;
234 }
235
audio_flush_wince(cst_audiodev * ad)236 int audio_flush_wince(cst_audiodev *ad)
237 {
238 au_wince_pdata *pd = ad->platform_data;
239
240 if (pd->bcnt > 0)
241 WaitForSingleObject(pd->bevt, INFINITE);
242 return 0;
243 }
244
audio_drain_wince(cst_audiodev * ad)245 int audio_drain_wince(cst_audiodev *ad)
246 {
247 au_wince_pdata *pd = ad->platform_data;
248
249 pd->in_reset = 1;
250 waveOutReset(pd->wo);
251 pd->in_reset = 0;
252 free_queue_empty(ad);
253 if (pd->bcnt > 0)
254 WaitForSingleObject(pd->bevt, INFINITE);
255 return 0;
256 }
257