SphinxBase 0.6

src/libsphinxad/rec_win32.c

00001 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
00002 /* ====================================================================
00003  * Copyright (c) 1999-2001 Carnegie Mellon University.  All rights
00004  * reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer. 
00012  *
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in
00015  *    the documentation and/or other materials provided with the
00016  *    distribution.
00017  *
00018  * This work was supported in part by funding from the Defense Advanced 
00019  * Research Projects Agency and the National Science Foundation of the 
00020  * United States of America, and the CMU Sphinx Speech Consortium.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 
00023  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
00024  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00025  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
00026  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00027  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
00028  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
00029  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
00030  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
00031  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
00032  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * ====================================================================
00035  *
00036  */
00037 
00038 /*
00039  * rec.c -- low level audio recording for Windows NT/95.
00040  *
00041  * HISTORY
00042  * 
00043  * 19-Jan-1999  M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00044  *              Added AD_ return codes.  Added ad_open_sps_bufsize(), and
00045  *              ad_rec_t.n_buf.
00046  * 
00047  * 07-Mar-98    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00048  *              Added ad_open_sps(), and made ad_open() call it.
00049  * 
00050  * 10-Jun-96    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00051  *              Added ad_rec_t type to all calls.
00052  * 
00053  * 03-Jun-96    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00054  *              Created.
00055  */
00056 
00057 
00058 #include <windows.h>
00059 #include <mmsystem.h>
00060 #include <stdio.h>
00061 #include <stdlib.h>
00062 #include <string.h>
00063 
00064 #include "sphinxbase/prim_type.h"
00065 #include "sphinxbase/ad.h"
00066 
00067 
00068 #define DEFAULT_N_WI_BUF        32      /* #Recording bufs */
00069 #define WI_BUFSIZE              2500    /* Samples/buf (Why this specific value??
00070                                            So that at reasonable sampling rates
00071                                            data is returned frequently enough.) */
00072 
00073 /* Silvio Moioli: using OutputDebugStringW instead of OutputDebugString */
00074 #ifdef _WIN32_WCE
00075 #include "ckd_alloc.h"
00076 static void
00077 wavein_error(char *src, int32 ret)
00078 {
00079     TCHAR errbuf[512];
00080     wchar_t* werrbuf;
00081     size_t len;
00082 
00083     waveOutGetErrorText(ret, errbuf, sizeof(errbuf));
00084     len = mbstowcs(NULL, errbuf, 0) + 1;
00085     werrbuf = ckd_calloc(len, sizeof(*werrbuf));
00086     mbstowcs(werrbuf, errbuf, len);
00087 
00088     OutputDebugStringW(werrbuf);
00089 }
00090 
00091 #else
00092 static void
00093 wavein_error(char *src, int32 ret)
00094 {
00095     char errbuf[1024];
00096 
00097     waveInGetErrorText(ret, errbuf, sizeof(errbuf));
00098     fprintf(stderr, "%s error %d: %s\n", src, ret, errbuf);
00099 }
00100 #endif
00101 
00102 
00103 static void
00104 wavein_free_buf(ad_wbuf_t * b)
00105 {
00106     GlobalUnlock(b->h_whdr);
00107     GlobalFree(b->h_whdr);
00108     GlobalUnlock(b->h_buf);
00109     GlobalFree(b->h_buf);
00110 }
00111 
00112 
00113 static int32
00114 wavein_alloc_buf(ad_wbuf_t * b, int32 samples_per_buf)
00115 {
00116     HGLOBAL h_buf;              /* handle to data buffer */
00117     LPSTR p_buf;                /* pointer to data buffer */
00118     HGLOBAL h_whdr;             /* handle to header */
00119     LPWAVEHDR p_whdr;           /* pointer to header */
00120 
00121     /* Allocate data buffer */
00122     h_buf =
00123         GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
00124                     samples_per_buf * sizeof(int16));
00125     if (!h_buf) {
00126         fprintf(stderr, "GlobalAlloc failed\n");
00127         return -1;
00128     }
00129     if ((p_buf = GlobalLock(h_buf)) == NULL) {
00130         GlobalFree(h_buf);
00131         fprintf(stderr, "GlobalLock failed\n");
00132         return -1;
00133     }
00134 
00135     /* Allocate WAVEHDR structure */
00136     h_whdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
00137     if (h_whdr == NULL) {
00138         GlobalUnlock(h_buf);
00139         GlobalFree(h_buf);
00140 
00141         fprintf(stderr, "GlobalAlloc failed\n");
00142         return -1;
00143     }
00144     if ((p_whdr = GlobalLock(h_whdr)) == NULL) {
00145         GlobalUnlock(h_buf);
00146         GlobalFree(h_buf);
00147         GlobalFree(h_whdr);
00148 
00149         fprintf(stderr, "GlobalLock failed\n");
00150         return -1;
00151     }
00152 
00153     b->h_buf = h_buf;
00154     b->p_buf = p_buf;
00155     b->h_whdr = h_whdr;
00156     b->p_whdr = p_whdr;
00157 
00158     p_whdr->lpData = p_buf;
00159     p_whdr->dwBufferLength = samples_per_buf * sizeof(int16);
00160     p_whdr->dwUser = 0L;
00161     p_whdr->dwFlags = 0L;
00162     p_whdr->dwLoops = 0L;
00163 
00164     return 0;
00165 }
00166 
00167 
00168 static int32
00169 wavein_enqueue_buf(HWAVEIN h, LPWAVEHDR whdr)
00170 {
00171     int32 st;
00172 
00173     if ((st = waveInPrepareHeader(h, whdr, sizeof(WAVEHDR))) != 0) {
00174         wavein_error("waveInPrepareHeader", st);
00175         return -1;
00176     }
00177     if ((st = waveInAddBuffer(h, whdr, sizeof(WAVEHDR))) != 0) {
00178         wavein_error("waveInAddBuffer", st);
00179         return -1;
00180     }
00181 
00182     return 0;
00183 }
00184 
00185 
00186 static HWAVEIN
00187 wavein_open(int32 samples_per_sec, int32 bytes_per_sample)
00188 {
00189     WAVEFORMATEX wfmt;
00190     int32 st;
00191     HWAVEIN h;
00192 
00193     if (bytes_per_sample != sizeof(int16)) {
00194         fprintf(stderr, "bytes/sample != %d\n", sizeof(int16));
00195         return NULL;
00196     }
00197 
00198     wfmt.wFormatTag = WAVE_FORMAT_PCM;
00199     wfmt.nChannels = 1;
00200     wfmt.nSamplesPerSec = samples_per_sec;
00201     wfmt.nAvgBytesPerSec = samples_per_sec * bytes_per_sample;
00202     wfmt.nBlockAlign = bytes_per_sample;
00203     wfmt.wBitsPerSample = 8 * bytes_per_sample;
00204 
00205     /* There should be a check here for a device of the desired type; later... */
00206 
00207     st = waveInOpen((LPHWAVEIN) & h, WAVE_MAPPER,
00208                     (LPWAVEFORMATEX) & wfmt, (DWORD) 0L, 0L,
00209                     (DWORD) CALLBACK_NULL);
00210     if (st != 0) {
00211         wavein_error("waveInOpen", st);
00212         return NULL;
00213     }
00214 
00215     return h;
00216 }
00217 
00218 
00219 static int32
00220 wavein_close(ad_rec_t * r)
00221 {
00222     int32 i, st;
00223 
00224     /* Unprepare all buffers; multiple unprepares of the same buffer are benign */
00225     for (i = 0; i < r->n_buf; i++) {
00226         /* Unpreparing an unprepared buffer, on the other hand, fails
00227            on Win98/WinME, though this is not documented - dhuggins@cs,
00228            2004-07-14 */
00229         if (!(r->wi_buf[i].p_whdr->dwFlags & WHDR_PREPARED))
00230             continue;
00231         st = waveInUnprepareHeader(r->h_wavein,
00232                                    r->wi_buf[i].p_whdr, sizeof(WAVEHDR));
00233         if (st != 0) {
00234             wavein_error("waveInUnprepareHeader", st);
00235             return -1;
00236         }
00237     }
00238 
00239     /* Free buffers */
00240     for (i = 0; i < r->n_buf; i++)
00241         wavein_free_buf(&(r->wi_buf[i]));
00242     free(r->wi_buf);
00243 
00244     if ((st = waveInClose(r->h_wavein)) != 0) {
00245         wavein_error("waveInClose", st);
00246         return -1;
00247     }
00248 
00249     free(r);
00250 
00251     return 0;
00252 }
00253 
00254 
00255 ad_rec_t *
00256 ad_open_sps_bufsize(int32 sps, int32 bufsize_msec)
00257 {
00258     ad_rec_t *r;
00259     int32 i, j;
00260     HWAVEIN h;
00261 
00262     if ((h = wavein_open(sps, sizeof(int16))) == NULL)
00263         return NULL;
00264 
00265     if ((r = (ad_rec_t *) malloc(sizeof(ad_rec_t))) == NULL) {
00266         fprintf(stderr, "malloc(%d) failed\n", sizeof(ad_rec_t));
00267         waveInClose(h);
00268         return NULL;
00269     }
00270 
00271     r->n_buf = ((sps * bufsize_msec) / 1000) / WI_BUFSIZE;
00272     if (r->n_buf < DEFAULT_N_WI_BUF)
00273         r->n_buf = DEFAULT_N_WI_BUF;
00274     printf("Allocating %d buffers of %d samples each\n", r->n_buf,
00275            WI_BUFSIZE);
00276 
00277     if ((r->wi_buf =
00278          (ad_wbuf_t *) calloc(r->n_buf, sizeof(ad_wbuf_t))) == NULL) {
00279         fprintf(stderr, "calloc(%d,%d) failed\n", r->n_buf,
00280                 sizeof(ad_wbuf_t));
00281         free(r);
00282         waveInClose(h);
00283 
00284         return NULL;
00285     }
00286     for (i = 0; i < r->n_buf; i++) {
00287         if (wavein_alloc_buf(&(r->wi_buf[i]), WI_BUFSIZE) < 0) {
00288             for (j = 0; j < i; j++)
00289                 wavein_free_buf(&(r->wi_buf[j]));
00290             free(r->wi_buf);
00291             free(r);
00292             waveInClose(h);
00293 
00294             return NULL;
00295         }
00296     }
00297 
00298     r->h_wavein = h;
00299     r->opened = 1;
00300     r->recording = 0;
00301     r->curbuf = r->n_buf - 1;   /* current buffer with data for application */
00302     r->curlen = 0;              /* #samples in curbuf remaining to be consumed */
00303     r->lastbuf = r->curbuf;
00304     r->sps = sps;
00305     r->bps = sizeof(int16);     /* HACK!! Hardwired value for bytes/sec */
00306 
00307     return r;
00308 }
00309 
00310 /* FIXME: Dummy function, doesn't actually use dev. */
00311 ad_rec_t *
00312 ad_open_dev(const char *dev, int32 sps)
00313 {
00314     return (ad_open_sps_bufsize
00315             (sps, WI_BUFSIZE * DEFAULT_N_WI_BUF * 1000 / sps));
00316 }
00317 
00318 
00319 ad_rec_t *
00320 ad_open_sps(int32 sps)
00321 {
00322     return (ad_open_sps_bufsize
00323             (sps, WI_BUFSIZE * DEFAULT_N_WI_BUF * 1000 / sps));
00324 }
00325 
00326 
00327 ad_rec_t *
00328 ad_open(void)
00329 {
00330     return (ad_open_sps(DEFAULT_SAMPLES_PER_SEC));      /* HACK!! Rename this constant */
00331 }
00332 
00333 
00334 int32
00335 ad_close(ad_rec_t * r)
00336 {
00337     if (!r->opened)
00338         return AD_ERR_NOT_OPEN;
00339 
00340     if (r->recording)
00341         if (ad_stop_rec(r) < 0)
00342             return AD_ERR_WAVE;
00343 
00344     if (wavein_close(r) < 0)
00345         return AD_ERR_WAVE;
00346 
00347     return 0;
00348 }
00349 
00350 
00351 int32
00352 ad_start_rec(ad_rec_t * r)
00353 {
00354     int32 i;
00355 
00356     if ((!r->opened) || r->recording)
00357         return -1;
00358 
00359     for (i = 0; i < r->n_buf; i++)
00360         if (wavein_enqueue_buf(r->h_wavein, r->wi_buf[i].p_whdr) < 0)
00361             return AD_ERR_WAVE;
00362     r->curbuf = r->n_buf - 1;   /* current buffer with data for application */
00363     r->curlen = 0;              /* #samples in curbuf remaining to be consumed */
00364 
00365     if (waveInStart(r->h_wavein) != 0)
00366         return AD_ERR_WAVE;
00367 
00368     r->recording = 1;
00369 
00370     return 0;
00371 }
00372 
00373 
00374 int32
00375 ad_stop_rec(ad_rec_t * r)
00376 {
00377     int32 i, st;
00378 
00379     if ((!r->opened) || (!r->recording))
00380         return -1;
00381 
00382     if (waveInStop(r->h_wavein) != 0)
00383         return AD_ERR_WAVE;
00384 
00385     if ((st = waveInReset(r->h_wavein)) != 0) {
00386         wavein_error("waveInReset", st);
00387         return AD_ERR_WAVE;
00388     }
00389 
00390     /* Wait until all buffers marked done */
00391     for (i = 0; i < r->n_buf; i++)
00392         while (!(r->wi_buf[i].p_whdr->dwFlags & WHDR_DONE));
00393 
00394     if ((r->lastbuf = r->curbuf - 1) < 0)
00395         r->lastbuf = r->n_buf - 1;
00396 
00397     r->recording = 0;
00398 
00399     return 0;
00400 }
00401 
00402 
00403 int32
00404 ad_read(ad_rec_t * r, int16 * buf, int32 max)
00405 {
00406     int32 t, st, len;
00407     LPWAVEHDR whdr;
00408     int16 *sysbufp;
00409 
00410     if (!r->opened)
00411         return AD_ERR_NOT_OPEN;
00412 
00413     /* Check if all recorded data exhausted */
00414     if ((!r->recording) && (r->curbuf == r->lastbuf)
00415         && (r->curlen == 0))
00416         return AD_EOF;
00417 
00418     len = 0;
00419     while (max > 0) {
00420         /* Look for next buffer with recording data */
00421         if (r->curlen == 0) {
00422             /* No current buffer with data; get next buffer in sequence if available */
00423             t = r->curbuf + 1;
00424             if (t >= r->n_buf)
00425                 t = 0;
00426 
00427             if (!(r->wi_buf[t].p_whdr->dwFlags & WHDR_DONE))
00428                 return len;
00429 
00430             r->curbuf = t;
00431             r->curlen = r->wi_buf[t].p_whdr->dwBytesRecorded >> 1;
00432             r->curoff = 0;
00433         }
00434 
00435         /* Copy data from curbuf to buf */
00436         whdr = r->wi_buf[r->curbuf].p_whdr;
00437         t = (max < r->curlen) ? max : r->curlen;        /* #Samples to copy */
00438 
00439         if (t > 0) {
00440             sysbufp = (int16 *) (whdr->lpData);
00441             memcpy(buf, sysbufp + r->curoff, t * sizeof(int16));
00442 
00443             buf += t;
00444             max -= t;
00445             r->curoff += t;
00446             r->curlen -= t;
00447             len += t;
00448         }
00449 
00450         /* If curbuf empty recycle it to system if still recording */
00451         if (r->curlen == 0) {
00452             if (r->recording) {
00453                 /* Return empty buffer to system */
00454                 st = waveInUnprepareHeader(r->h_wavein,
00455                                            whdr, sizeof(WAVEHDR));
00456                 if (st != 0) {
00457                     wavein_error("waveInUnprepareHeader", st);
00458                     return AD_ERR_WAVE;
00459                 }
00460 
00461                 if (wavein_enqueue_buf(r->h_wavein, whdr) < 0)
00462                     return AD_ERR_WAVE;
00463 
00464             }
00465             else if (r->curbuf == r->lastbuf) {
00466                 return len;
00467             }
00468         }
00469     }
00470 
00471     return len;
00472 }