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
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"
76static void
77wavein_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
92static void
93wavein_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
103static void
104wavein_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
113static int32
114wavein_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
168static int32
169wavein_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
186static HWAVEIN
187wavein_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
219static int32
220wavein_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
255ad_rec_t *
256ad_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. */
311ad_rec_t *
312ad_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
319ad_rec_t *
320ad_open_sps(int32 sps)
321{
322 return (ad_open_sps_bufsize
323 (sps, WI_BUFSIZE * DEFAULT_N_WI_BUF * 1000 / sps));
324}
325
326
327ad_rec_t *
328ad_open(void)
329{
330 return (ad_open_sps(DEFAULT_SAMPLES_PER_SEC)); /* HACK!! Rename this constant */
331}
332
333
334int32
335ad_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
351int32
352ad_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
374int32
375ad_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
403int32
404ad_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}
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(void)
Open the default audio device.
Definition ad_alsa.c:296
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
Sphinx's memory allocation/deallocation routines.
#define ckd_calloc(n, sz)
Macros to simplify the use of above functions.
Definition ckd_alloc.h:248
Basic type definitions used in Sphinx.
Definition ad.h:255
int32 sps
Samples/sec.
Definition ad.h:256
int32 bps
Bytes/sample.
Definition ad.h:257