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