1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3  * Copyright (c) 1999-2001 Carnegie Mellon University.  All rights
4  * reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * This work was supported in part by funding from the Defense Advanced
19  * Research Projects Agency and the National Science Foundation of the
20  * United States of America, and the CMU Sphinx Speech Consortium.
21  *
22  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
23  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * ====================================================================
35  *
36  */
37 
38 /*
39  * HISTORY
40  *
41  * 17-Apr-98	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
42  * 		Added ad_open_play_sps(), and made ad_open_play() call it.
43  *
44  * 10-Jun-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
45  * 		Added ad_play_t type to all calls.
46  *
47  * 03-Jun-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
48  * 		Created.
49  */
50 
51 
52 #include <windows.h>
53 #include <mmsystem.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 
58 #include "sphinxbase/prim_type.h"
59 #include "sphinxbase/ad.h"
60 
61 
62 #define WO_BUFSIZE	3200    /* Samples/buf */
63 #define N_WO_BUF	2       /* #Playback bufs */
64 
65 /* Silvio Moioli: using OutputDebugStringW instead of OutputDebugString */
66 #ifdef _WIN32_WCE
67 #include "ckd_alloc.h"
68 static void
waveout_error(char * src,int32 ret)69 waveout_error(char *src, int32 ret)
70 {
71     TCHAR errbuf[512];
72     wchar_t* werrbuf;
73     size_t len;
74 
75     waveOutGetErrorText(ret, errbuf, sizeof(errbuf));
76     len = mbstowcs(NULL, errbuf, 0) + 1;
77     werrbuf = ckd_calloc(len, sizeof(*werrbuf));
78     mbstowcs(werrbuf, errbuf, len);
79 
80     OutputDebugStringW(werrbuf);
81  }
82 
83 #else
84 static void
waveout_error(char * src,int32 ret)85 waveout_error(char *src, int32 ret)
86 {
87     char errbuf[1024];
88 
89     waveOutGetErrorText(ret, errbuf, sizeof(errbuf));
90     fprintf(stderr, "%s error %d: %s\n", src, ret, errbuf);
91 }
92 #endif
93 
94 
95 static void
waveout_free_buf(ad_wbuf_t * b)96 waveout_free_buf(ad_wbuf_t * b)
97 {
98     GlobalUnlock(b->h_whdr);
99     GlobalFree(b->h_whdr);
100     GlobalUnlock(b->h_buf);
101     GlobalFree(b->h_buf);
102 }
103 
104 
105 static int32
waveout_alloc_buf(ad_wbuf_t * b,int32 samples_per_buf)106 waveout_alloc_buf(ad_wbuf_t * b, int32 samples_per_buf)
107 {
108     HGLOBAL h_buf;
109     LPSTR p_buf;
110     HGLOBAL h_whdr;
111     LPWAVEHDR p_whdr;
112 
113     /* Allocate data buffer */
114     h_buf =
115         GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
116                     samples_per_buf * sizeof(int16));
117     if (!h_buf) {
118         fprintf(stderr, "GlobalAlloc failed\n");
119         return -1;
120     }
121     if ((p_buf = GlobalLock(h_buf)) == NULL) {
122         GlobalFree(h_buf);
123         fprintf(stderr, "GlobalLock failed\n");
124         return -1;
125     }
126 
127     /* Allocate WAVEHDR structure */
128     h_whdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
129     if (h_whdr == NULL) {
130         GlobalUnlock(h_buf);
131         GlobalFree(h_buf);
132 
133         fprintf(stderr, "GlobalAlloc failed\n");
134         return -1;
135     }
136     if ((p_whdr = GlobalLock(h_whdr)) == NULL) {
137         GlobalUnlock(h_buf);
138         GlobalFree(h_buf);
139         GlobalFree(h_whdr);
140 
141         fprintf(stderr, "GlobalLock failed\n");
142         return -1;
143     }
144 
145     b->h_buf = h_buf;
146     b->p_buf = p_buf;
147     b->h_whdr = h_whdr;
148     b->p_whdr = p_whdr;
149 
150     p_whdr->lpData = p_buf;
151     p_whdr->dwBufferLength = samples_per_buf * sizeof(int16);
152     p_whdr->dwUser = 0L;
153     p_whdr->dwFlags = 0L;
154     p_whdr->dwLoops = 0L;
155 
156     return 0;
157 }
158 
159 
160 static int32
waveout_enqueue_buf(HWAVEOUT h,LPWAVEHDR whdr)161 waveout_enqueue_buf(HWAVEOUT h, LPWAVEHDR whdr)
162 {
163     int32 st;
164 
165     if ((st = waveOutPrepareHeader(h, whdr, sizeof(WAVEHDR))) != 0) {
166         waveout_error("waveOutPrepareHeader", st);
167         return -1;
168     }
169 
170     if ((st = waveOutWrite(h, whdr, sizeof(WAVEHDR))) != 0) {
171         waveout_error("waveOutWrite", st);
172         return -1;
173     }
174 
175     return 0;
176 }
177 
178 
179 static HWAVEOUT
waveout_open(int32 samples_per_sec,int32 bytes_per_sample)180 waveout_open(int32 samples_per_sec, int32 bytes_per_sample)
181 {
182     WAVEFORMATEX wfmt;
183     int32 st;
184     HWAVEOUT h;
185 
186     if (bytes_per_sample != sizeof(int16)) {
187         fprintf(stderr, "bytes/sample != %d\n", sizeof(int16));
188         return NULL;
189     }
190 
191     wfmt.wFormatTag = WAVE_FORMAT_PCM;
192     wfmt.nChannels = 1;
193     wfmt.nSamplesPerSec = samples_per_sec;
194     wfmt.nAvgBytesPerSec = samples_per_sec * bytes_per_sample;
195     wfmt.nBlockAlign = bytes_per_sample;
196     wfmt.wBitsPerSample = 8 * bytes_per_sample;
197     wfmt.cbSize = 0;
198 
199     /* There should be a check here for a device of the desired type; later... */
200 
201     st = waveOutOpen((LPHWAVEOUT) & h, WAVE_MAPPER,
202                      (LPWAVEFORMATEX) & wfmt, (DWORD) 0L, 0L,
203                      (DWORD) CALLBACK_NULL);
204     if (st != 0) {
205         waveout_error("waveOutOpen", st);
206         return NULL;
207     }
208 
209     return h;
210 }
211 
212 
213 static void
waveout_mem_cleanup(ad_play_t * p,int32 n_buf)214 waveout_mem_cleanup(ad_play_t * p, int32 n_buf)
215 {
216     int32 i;
217 
218     for (i = 0; i < n_buf; i++)
219         waveout_free_buf(&(p->wo_buf[i]));
220     if (p->wo_buf)
221         free(p->wo_buf);
222     if (p->busy)
223         free(p->busy);
224 }
225 
226 
227 static int32
waveout_close(ad_play_t * p)228 waveout_close(ad_play_t * p)
229 {
230     int32 st;
231 
232     waveout_mem_cleanup(p, N_WO_BUF);
233 
234     if ((st = waveOutClose(p->h_waveout)) != 0) {
235         waveout_error("waveOutClose", st);
236         return -1;
237     }
238 
239     free(p);
240 
241     return 0;
242 }
243 
244 
245 ad_play_t *
ad_open_play_sps(int32 sps)246 ad_open_play_sps(int32 sps)
247 {
248     ad_play_t *p;
249     int32 i;
250     HWAVEOUT h;
251 
252     if ((h = waveout_open(sps, sizeof(int16))) == NULL)
253         return NULL;
254 
255     if ((p = (ad_play_t *) calloc(1, sizeof(ad_play_t))) == NULL) {
256         fprintf(stderr, "calloc(1,%d) failed\n", sizeof(ad_play_t));
257         waveOutClose(h);
258         return NULL;
259     }
260     if ((p->wo_buf =
261          (ad_wbuf_t *) calloc(N_WO_BUF, sizeof(ad_wbuf_t))) == NULL) {
262         fprintf(stderr, "calloc(%d,%d) failed\n", N_WO_BUF,
263                 sizeof(ad_wbuf_t));
264         free(p);
265         waveOutClose(h);
266 
267         return NULL;
268     }
269     if ((p->busy = (char *) calloc(N_WO_BUF, sizeof(char))) == NULL) {
270         fprintf(stderr, "calloc(%d,%d) failed\n", N_WO_BUF, sizeof(char));
271         waveout_mem_cleanup(p, 0);
272         free(p);
273         waveOutClose(h);
274 
275         return NULL;
276     }
277     for (i = 0; i < N_WO_BUF; i++) {
278         if (waveout_alloc_buf(&(p->wo_buf[i]), WO_BUFSIZE) < 0) {
279             waveout_mem_cleanup(p, i);
280             free(p);
281             waveOutClose(h);
282 
283             return NULL;
284         }
285     }
286 
287     p->h_waveout = h;
288     p->playing = 0;
289     p->opened = 1;
290     p->nxtbuf = 0;
291     p->sps = sps;
292     p->bps = sizeof(int16);     /* HACK!! Hardwired value for bytes/sec */
293 
294     return p;
295 }
296 
297 
298 ad_play_t *
ad_open_play(void)299 ad_open_play(void)
300 {
301     return (ad_open_play_sps(DEFAULT_SAMPLES_PER_SEC));
302 }
303 
304 
305 int32
ad_close_play(ad_play_t * p)306 ad_close_play(ad_play_t * p)
307 {
308     if (!p->opened)
309         return 0;
310 
311     if (p->playing)
312         if (ad_stop_play(p) < 0)
313             return -1;
314 
315     if (waveout_close(p) < 0)
316         return -1;
317 
318     return 0;
319 }
320 
321 
322 int32
ad_start_play(ad_play_t * p)323 ad_start_play(ad_play_t * p)
324 {
325     int32 i;
326 
327     if ((!p->opened) || p->playing)
328         return -1;
329 
330     for (i = 0; i < N_WO_BUF; i++)
331         p->busy[i] = 0;
332     p->nxtbuf = 0;
333     p->playing = 1;
334 
335     return 0;
336 }
337 
338 
339 int32
ad_stop_play(ad_play_t * p)340 ad_stop_play(ad_play_t * p)
341 {
342     int32 i, st;
343     LPWAVEHDR whdr;
344 
345     if ((!p->opened) || (!p->playing))
346         return -1;
347 
348 #if 0
349     whdr->dwUser = (plen <= 0) ? 1 : 0;
350 #endif
351 
352     /* Wait for all buffers to be emptied and unprepare them */
353     for (i = 0; i < N_WO_BUF; i++) {
354         whdr = p->wo_buf[i].p_whdr;
355 
356         while (p->busy[i] && (!(whdr->dwFlags & WHDR_DONE)))
357             Sleep(100);
358 
359         st = waveOutUnprepareHeader(p->h_waveout, whdr, sizeof(WAVEHDR));
360         if (st != 0) {
361             waveout_error("waveOutUnprepareHeader", st);
362             return -1;
363         }
364 
365         p->busy[i] = 0;
366     }
367 
368     return 0;
369 }
370 
371 
372 int32
ad_write(ad_play_t * p,int16 * buf,int32 size)373 ad_write(ad_play_t * p, int16 * buf, int32 size)
374 {
375     int32 i, k, len, st;
376     LPWAVEHDR whdr;
377 
378     if ((!p->opened) || (!p->playing))
379         return -1;
380 
381     len = 0;
382 
383     for (i = 0; (i < N_WO_BUF) && (size > 0); i++) {
384         whdr = p->wo_buf[p->nxtbuf].p_whdr;
385 
386         if (p->busy[p->nxtbuf]) {
387             if (!(whdr->dwFlags & WHDR_DONE))
388                 return len;
389 
390             st = waveOutUnprepareHeader(p->h_waveout, whdr,
391                                         sizeof(WAVEHDR));
392             if (st != 0) {
393                 waveout_error("waveOutUnprepareHeader", st);
394                 return -1;
395             }
396 
397             p->busy[p->nxtbuf] = 0;
398         }
399 
400         k = (size > WO_BUFSIZE) ? WO_BUFSIZE : size;
401 
402         whdr->dwBufferLength = k * sizeof(int16);
403         memcpy(whdr->lpData, (LPSTR) buf, k * sizeof(int16));
404 
405         if (waveout_enqueue_buf(p->h_waveout, whdr) < 0)
406             return -1;
407 
408         buf += k;
409         size -= k;
410         len += k;
411 
412         p->busy[(p->nxtbuf)++] = 1;
413         if (p->nxtbuf >= N_WO_BUF)
414             p->nxtbuf = 0;
415     }
416 
417     return len;
418 }
419