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