SphinxBase  0.6
rec_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  * rec.c -- low level audio recording for Windows NT/95.
40  *
41  * HISTORY
42  *
43  * 19-Jan-1999 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
44  * Added AD_ return codes. Added ad_open_sps_bufsize(), and
45  * ad_rec_t.n_buf.
46  *
47  * 07-Mar-98 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
48  * Added ad_open_sps(), and made ad_open() call it.
49  *
50  * 10-Jun-96 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
51  * Added ad_rec_t type to all calls.
52  *
53  * 03-Jun-96 M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
54  * Created.
55  */
56 
57 
58 #include <windows.h>
59 #include <mmsystem.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 
64 #include "sphinxbase/prim_type.h"
65 #include "sphinxbase/ad.h"
66 
67 
68 #define DEFAULT_N_WI_BUF 32 /* #Recording bufs */
69 #define WI_BUFSIZE 2500 /* Samples/buf (Why this specific value??
70  So that at reasonable sampling rates
71  data is returned frequently enough.) */
72 
73 /* Silvio Moioli: using OutputDebugStringW instead of OutputDebugString */
74 #ifdef _WIN32_WCE
75 #include "ckd_alloc.h"
76 static void
77 wavein_error(char *src, int32 ret)
78 {
79  TCHAR errbuf[512];
80  wchar_t* werrbuf;
81  size_t len;
82 
83  waveOutGetErrorText(ret, errbuf, sizeof(errbuf));
84  len = mbstowcs(NULL, errbuf, 0) + 1;
85  werrbuf = ckd_calloc(len, sizeof(*werrbuf));
86  mbstowcs(werrbuf, errbuf, len);
87 
88  OutputDebugStringW(werrbuf);
89 }
90 
91 #else
92 static void
93 wavein_error(char *src, int32 ret)
94 {
95  char errbuf[1024];
96 
97  waveInGetErrorText(ret, errbuf, sizeof(errbuf));
98  fprintf(stderr, "%s error %d: %s\n", src, ret, errbuf);
99 }
100 #endif
101 
102 
103 static void
104 wavein_free_buf(ad_wbuf_t * b)
105 {
106  GlobalUnlock(b->h_whdr);
107  GlobalFree(b->h_whdr);
108  GlobalUnlock(b->h_buf);
109  GlobalFree(b->h_buf);
110 }
111 
112 
113 static int32
114 wavein_alloc_buf(ad_wbuf_t * b, int32 samples_per_buf)
115 {
116  HGLOBAL h_buf; /* handle to data buffer */
117  LPSTR p_buf; /* pointer to data buffer */
118  HGLOBAL h_whdr; /* handle to header */
119  LPWAVEHDR p_whdr; /* pointer to header */
120 
121  /* Allocate data buffer */
122  h_buf =
123  GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
124  samples_per_buf * sizeof(int16));
125  if (!h_buf) {
126  fprintf(stderr, "GlobalAlloc failed\n");
127  return -1;
128  }
129  if ((p_buf = GlobalLock(h_buf)) == NULL) {
130  GlobalFree(h_buf);
131  fprintf(stderr, "GlobalLock failed\n");
132  return -1;
133  }
134 
135  /* Allocate WAVEHDR structure */
136  h_whdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
137  if (h_whdr == NULL) {
138  GlobalUnlock(h_buf);
139  GlobalFree(h_buf);
140 
141  fprintf(stderr, "GlobalAlloc failed\n");
142  return -1;
143  }
144  if ((p_whdr = GlobalLock(h_whdr)) == NULL) {
145  GlobalUnlock(h_buf);
146  GlobalFree(h_buf);
147  GlobalFree(h_whdr);
148 
149  fprintf(stderr, "GlobalLock failed\n");
150  return -1;
151  }
152 
153  b->h_buf = h_buf;
154  b->p_buf = p_buf;
155  b->h_whdr = h_whdr;
156  b->p_whdr = p_whdr;
157 
158  p_whdr->lpData = p_buf;
159  p_whdr->dwBufferLength = samples_per_buf * sizeof(int16);
160  p_whdr->dwUser = 0L;
161  p_whdr->dwFlags = 0L;
162  p_whdr->dwLoops = 0L;
163 
164  return 0;
165 }
166 
167 
168 static int32
169 wavein_enqueue_buf(HWAVEIN h, LPWAVEHDR whdr)
170 {
171  int32 st;
172 
173  if ((st = waveInPrepareHeader(h, whdr, sizeof(WAVEHDR))) != 0) {
174  wavein_error("waveInPrepareHeader", st);
175  return -1;
176  }
177  if ((st = waveInAddBuffer(h, whdr, sizeof(WAVEHDR))) != 0) {
178  wavein_error("waveInAddBuffer", st);
179  return -1;
180  }
181 
182  return 0;
183 }
184 
185 
186 static HWAVEIN
187 wavein_open(int32 samples_per_sec, int32 bytes_per_sample)
188 {
189  WAVEFORMATEX wfmt;
190  int32 st;
191  HWAVEIN h;
192 
193  if (bytes_per_sample != sizeof(int16)) {
194  fprintf(stderr, "bytes/sample != %d\n", sizeof(int16));
195  return NULL;
196  }
197 
198  wfmt.wFormatTag = WAVE_FORMAT_PCM;
199  wfmt.nChannels = 1;
200  wfmt.nSamplesPerSec = samples_per_sec;
201  wfmt.nAvgBytesPerSec = samples_per_sec * bytes_per_sample;
202  wfmt.nBlockAlign = bytes_per_sample;
203  wfmt.wBitsPerSample = 8 * bytes_per_sample;
204 
205  /* There should be a check here for a device of the desired type; later... */
206 
207  st = waveInOpen((LPHWAVEIN) & h, WAVE_MAPPER,
208  (LPWAVEFORMATEX) & wfmt, (DWORD) 0L, 0L,
209  (DWORD) CALLBACK_NULL);
210  if (st != 0) {
211  wavein_error("waveInOpen", st);
212  return NULL;
213  }
214 
215  return h;
216 }
217 
218 
219 static int32
220 wavein_close(ad_rec_t * r)
221 {
222  int32 i, st;
223 
224  /* Unprepare all buffers; multiple unprepares of the same buffer are benign */
225  for (i = 0; i < r->n_buf; i++) {
226  /* Unpreparing an unprepared buffer, on the other hand, fails
227  on Win98/WinME, though this is not documented - dhuggins@cs,
228  2004-07-14 */
229  if (!(r->wi_buf[i].p_whdr->dwFlags & WHDR_PREPARED))
230  continue;
231  st = waveInUnprepareHeader(r->h_wavein,
232  r->wi_buf[i].p_whdr, sizeof(WAVEHDR));
233  if (st != 0) {
234  wavein_error("waveInUnprepareHeader", st);
235  return -1;
236  }
237  }
238 
239  /* Free buffers */
240  for (i = 0; i < r->n_buf; i++)
241  wavein_free_buf(&(r->wi_buf[i]));
242  free(r->wi_buf);
243 
244  if ((st = waveInClose(r->h_wavein)) != 0) {
245  wavein_error("waveInClose", st);
246  return -1;
247  }
248 
249  free(r);
250 
251  return 0;
252 }
253 
254 
255 ad_rec_t *
256 ad_open_sps_bufsize(int32 sps, int32 bufsize_msec)
257 {
258  ad_rec_t *r;
259  int32 i, j;
260  HWAVEIN h;
261 
262  if ((h = wavein_open(sps, sizeof(int16))) == NULL)
263  return NULL;
264 
265  if ((r = (ad_rec_t *) malloc(sizeof(ad_rec_t))) == NULL) {
266  fprintf(stderr, "malloc(%d) failed\n", sizeof(ad_rec_t));
267  waveInClose(h);
268  return NULL;
269  }
270 
271  r->n_buf = ((sps * bufsize_msec) / 1000) / WI_BUFSIZE;
272  if (r->n_buf < DEFAULT_N_WI_BUF)
273  r->n_buf = DEFAULT_N_WI_BUF;
274  printf("Allocating %d buffers of %d samples each\n", r->n_buf,
275  WI_BUFSIZE);
276 
277  if ((r->wi_buf =
278  (ad_wbuf_t *) calloc(r->n_buf, sizeof(ad_wbuf_t))) == NULL) {
279  fprintf(stderr, "calloc(%d,%d) failed\n", r->n_buf,
280  sizeof(ad_wbuf_t));
281  free(r);
282  waveInClose(h);
283 
284  return NULL;
285  }
286  for (i = 0; i < r->n_buf; i++) {
287  if (wavein_alloc_buf(&(r->wi_buf[i]), WI_BUFSIZE) < 0) {
288  for (j = 0; j < i; j++)
289  wavein_free_buf(&(r->wi_buf[j]));
290  free(r->wi_buf);
291  free(r);
292  waveInClose(h);
293 
294  return NULL;
295  }
296  }
297 
298  r->h_wavein = h;
299  r->opened = 1;
300  r->recording = 0;
301  r->curbuf = r->n_buf - 1; /* current buffer with data for application */
302  r->curlen = 0; /* #samples in curbuf remaining to be consumed */
303  r->lastbuf = r->curbuf;
304  r->sps = sps;
305  r->bps = sizeof(int16); /* HACK!! Hardwired value for bytes/sec */
306 
307  return r;
308 }
309 
310 /* FIXME: Dummy function, doesn't actually use dev. */
311 ad_rec_t *
312 ad_open_dev(const char *dev, int32 sps)
313 {
314  return (ad_open_sps_bufsize
315  (sps, WI_BUFSIZE * DEFAULT_N_WI_BUF * 1000 / sps));
316 }
317 
318 
319 ad_rec_t *
320 ad_open_sps(int32 sps)
321 {
322  return (ad_open_sps_bufsize
323  (sps, WI_BUFSIZE * DEFAULT_N_WI_BUF * 1000 / sps));
324 }
325 
326 
327 ad_rec_t *
328 ad_open(void)
329 {
330  return (ad_open_sps(DEFAULT_SAMPLES_PER_SEC)); /* HACK!! Rename this constant */
331 }
332 
333 
334 int32
335 ad_close(ad_rec_t * r)
336 {
337  if (!r->opened)
338  return AD_ERR_NOT_OPEN;
339 
340  if (r->recording)
341  if (ad_stop_rec(r) < 0)
342  return AD_ERR_WAVE;
343 
344  if (wavein_close(r) < 0)
345  return AD_ERR_WAVE;
346 
347  return 0;
348 }
349 
350 
351 int32
352 ad_start_rec(ad_rec_t * r)
353 {
354  int32 i;
355 
356  if ((!r->opened) || r->recording)
357  return -1;
358 
359  for (i = 0; i < r->n_buf; i++)
360  if (wavein_enqueue_buf(r->h_wavein, r->wi_buf[i].p_whdr) < 0)
361  return AD_ERR_WAVE;
362  r->curbuf = r->n_buf - 1; /* current buffer with data for application */
363  r->curlen = 0; /* #samples in curbuf remaining to be consumed */
364 
365  if (waveInStart(r->h_wavein) != 0)
366  return AD_ERR_WAVE;
367 
368  r->recording = 1;
369 
370  return 0;
371 }
372 
373 
374 int32
375 ad_stop_rec(ad_rec_t * r)
376 {
377  int32 i, st;
378 
379  if ((!r->opened) || (!r->recording))
380  return -1;
381 
382  if (waveInStop(r->h_wavein) != 0)
383  return AD_ERR_WAVE;
384 
385  if ((st = waveInReset(r->h_wavein)) != 0) {
386  wavein_error("waveInReset", st);
387  return AD_ERR_WAVE;
388  }
389 
390  /* Wait until all buffers marked done */
391  for (i = 0; i < r->n_buf; i++)
392  while (!(r->wi_buf[i].p_whdr->dwFlags & WHDR_DONE));
393 
394  if ((r->lastbuf = r->curbuf - 1) < 0)
395  r->lastbuf = r->n_buf - 1;
396 
397  r->recording = 0;
398 
399  return 0;
400 }
401 
402 
403 int32
404 ad_read(ad_rec_t * r, int16 * buf, int32 max)
405 {
406  int32 t, st, len;
407  LPWAVEHDR whdr;
408  int16 *sysbufp;
409 
410  if (!r->opened)
411  return AD_ERR_NOT_OPEN;
412 
413  /* Check if all recorded data exhausted */
414  if ((!r->recording) && (r->curbuf == r->lastbuf)
415  && (r->curlen == 0))
416  return AD_EOF;
417 
418  len = 0;
419  while (max > 0) {
420  /* Look for next buffer with recording data */
421  if (r->curlen == 0) {
422  /* No current buffer with data; get next buffer in sequence if available */
423  t = r->curbuf + 1;
424  if (t >= r->n_buf)
425  t = 0;
426 
427  if (!(r->wi_buf[t].p_whdr->dwFlags & WHDR_DONE))
428  return len;
429 
430  r->curbuf = t;
431  r->curlen = r->wi_buf[t].p_whdr->dwBytesRecorded >> 1;
432  r->curoff = 0;
433  }
434 
435  /* Copy data from curbuf to buf */
436  whdr = r->wi_buf[r->curbuf].p_whdr;
437  t = (max < r->curlen) ? max : r->curlen; /* #Samples to copy */
438 
439  if (t > 0) {
440  sysbufp = (int16 *) (whdr->lpData);
441  memcpy(buf, sysbufp + r->curoff, t * sizeof(int16));
442 
443  buf += t;
444  max -= t;
445  r->curoff += t;
446  r->curlen -= t;
447  len += t;
448  }
449 
450  /* If curbuf empty recycle it to system if still recording */
451  if (r->curlen == 0) {
452  if (r->recording) {
453  /* Return empty buffer to system */
454  st = waveInUnprepareHeader(r->h_wavein,
455  whdr, sizeof(WAVEHDR));
456  if (st != 0) {
457  wavein_error("waveInUnprepareHeader", st);
458  return AD_ERR_WAVE;
459  }
460 
461  if (wavein_enqueue_buf(r->h_wavein, whdr) < 0)
462  return AD_ERR_WAVE;
463 
464  }
465  else if (r->curbuf == r->lastbuf) {
466  return len;
467  }
468  }
469  }
470 
471  return len;
472 }
#define ckd_calloc(n, sz)
Macros to simplify the use of above functions.
Definition: ckd_alloc.h:248
Sphinx's memory allocation/deallocation routines.
Definition: ad.h:255
int32 sps
Samples/sec.
Definition: ad.h:256
Basic type definitions used in Sphinx.
int32 bps
Bytes/sample.
Definition: ad.h:257
SPHINXBASE_EXPORT ad_rec_t * ad_open(void)
Open the default audio device.
Definition: ad_alsa.c:296
generic live audio interface for recording and playback
SPHINXBASE_EXPORT ad_rec_t * ad_open_dev(const char *dev, int32 samples_per_sec)
Open a specific audio device for recording.
Definition: ad_alsa.c:252
SPHINXBASE_EXPORT ad_rec_t * ad_open_sps(int32 samples_per_sec)
Open the default audio device with a given sampling rate.
Definition: ad_alsa.c:290