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