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