SphinxBase  0.6
play_win32.c
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
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
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
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
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
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
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
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
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 *
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 *
299 ad_open_play(void)
300 {
301  return (ad_open_play_sps(DEFAULT_SAMPLES_PER_SEC));
302 }
303 
304 
305 int32
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
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
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
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 }
#define ckd_calloc(n, sz)
Macros to simplify the use of above functions.
Definition: ckd_alloc.h:248
Sphinx's memory allocation/deallocation routines.
Basic type definitions used in Sphinx.
generic live audio interface for recording and playback
Definition: ad.h:346
SPHINXBASE_EXPORT int32 ad_write(ad_play_t *, int16 *buf, int32 len)
Queue a block of audio samples for playback.
Definition: play_win32.c:373