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