SphinxBase  0.6
src/libsphinxad/play_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  * HISTORY
00040  * 
00041  * 17-Apr-98    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00042  *              Added ad_open_play_sps(), and made ad_open_play() call it.
00043  * 
00044  * 10-Jun-96    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00045  *              Added ad_play_t type to all calls.
00046  * 
00047  * 03-Jun-96    M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University
00048  *              Created.
00049  */
00050 
00051 
00052 #include <windows.h>
00053 #include <mmsystem.h>
00054 #include <stdio.h>
00055 #include <stdlib.h>
00056 #include <string.h>
00057 
00058 #include "sphinxbase/prim_type.h"
00059 #include "sphinxbase/ad.h"
00060 
00061 
00062 #define WO_BUFSIZE      3200    /* Samples/buf */
00063 #define N_WO_BUF        2       /* #Playback bufs */
00064 
00065 /* Silvio Moioli: using OutputDebugStringW instead of OutputDebugString */
00066 #ifdef _WIN32_WCE
00067 #include "ckd_alloc.h"
00068 static void
00069 waveout_error(char *src, int32 ret)
00070 {
00071     TCHAR errbuf[512];
00072     wchar_t* werrbuf;
00073     size_t len;
00074 
00075     waveOutGetErrorText(ret, errbuf, sizeof(errbuf));
00076     len = mbstowcs(NULL, errbuf, 0) + 1;
00077     werrbuf = ckd_calloc(len, sizeof(*werrbuf));
00078     mbstowcs(werrbuf, errbuf, len);
00079 
00080     OutputDebugStringW(werrbuf);
00081  }
00082 
00083 #else
00084 static void
00085 waveout_error(char *src, int32 ret)
00086 {
00087     char errbuf[1024];
00088 
00089     waveOutGetErrorText(ret, errbuf, sizeof(errbuf));
00090     fprintf(stderr, "%s error %d: %s\n", src, ret, errbuf);
00091 }
00092 #endif
00093 
00094 
00095 static void
00096 waveout_free_buf(ad_wbuf_t * b)
00097 {
00098     GlobalUnlock(b->h_whdr);
00099     GlobalFree(b->h_whdr);
00100     GlobalUnlock(b->h_buf);
00101     GlobalFree(b->h_buf);
00102 }
00103 
00104 
00105 static int32
00106 waveout_alloc_buf(ad_wbuf_t * b, int32 samples_per_buf)
00107 {
00108     HGLOBAL h_buf;
00109     LPSTR p_buf;
00110     HGLOBAL h_whdr;
00111     LPWAVEHDR p_whdr;
00112 
00113     /* Allocate data buffer */
00114     h_buf =
00115         GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
00116                     samples_per_buf * sizeof(int16));
00117     if (!h_buf) {
00118         fprintf(stderr, "GlobalAlloc failed\n");
00119         return -1;
00120     }
00121     if ((p_buf = GlobalLock(h_buf)) == NULL) {
00122         GlobalFree(h_buf);
00123         fprintf(stderr, "GlobalLock failed\n");
00124         return -1;
00125     }
00126 
00127     /* Allocate WAVEHDR structure */
00128     h_whdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
00129     if (h_whdr == NULL) {
00130         GlobalUnlock(h_buf);
00131         GlobalFree(h_buf);
00132 
00133         fprintf(stderr, "GlobalAlloc failed\n");
00134         return -1;
00135     }
00136     if ((p_whdr = GlobalLock(h_whdr)) == NULL) {
00137         GlobalUnlock(h_buf);
00138         GlobalFree(h_buf);
00139         GlobalFree(h_whdr);
00140 
00141         fprintf(stderr, "GlobalLock failed\n");
00142         return -1;
00143     }
00144 
00145     b->h_buf = h_buf;
00146     b->p_buf = p_buf;
00147     b->h_whdr = h_whdr;
00148     b->p_whdr = p_whdr;
00149 
00150     p_whdr->lpData = p_buf;
00151     p_whdr->dwBufferLength = samples_per_buf * sizeof(int16);
00152     p_whdr->dwUser = 0L;
00153     p_whdr->dwFlags = 0L;
00154     p_whdr->dwLoops = 0L;
00155 
00156     return 0;
00157 }
00158 
00159 
00160 static int32
00161 waveout_enqueue_buf(HWAVEOUT h, LPWAVEHDR whdr)
00162 {
00163     int32 st;
00164 
00165     if ((st = waveOutPrepareHeader(h, whdr, sizeof(WAVEHDR))) != 0) {
00166         waveout_error("waveOutPrepareHeader", st);
00167         return -1;
00168     }
00169 
00170     if ((st = waveOutWrite(h, whdr, sizeof(WAVEHDR))) != 0) {
00171         waveout_error("waveOutWrite", st);
00172         return -1;
00173     }
00174 
00175     return 0;
00176 }
00177 
00178 
00179 static HWAVEOUT
00180 waveout_open(int32 samples_per_sec, int32 bytes_per_sample)
00181 {
00182     WAVEFORMATEX wfmt;
00183     int32 st;
00184     HWAVEOUT h;
00185 
00186     if (bytes_per_sample != sizeof(int16)) {
00187         fprintf(stderr, "bytes/sample != %d\n", sizeof(int16));
00188         return NULL;
00189     }
00190 
00191     wfmt.wFormatTag = WAVE_FORMAT_PCM;
00192     wfmt.nChannels = 1;
00193     wfmt.nSamplesPerSec = samples_per_sec;
00194     wfmt.nAvgBytesPerSec = samples_per_sec * bytes_per_sample;
00195     wfmt.nBlockAlign = bytes_per_sample;
00196     wfmt.wBitsPerSample = 8 * bytes_per_sample;
00197     wfmt.cbSize = 0;
00198 
00199     /* There should be a check here for a device of the desired type; later... */
00200 
00201     st = waveOutOpen((LPHWAVEOUT) & h, WAVE_MAPPER,
00202                      (LPWAVEFORMATEX) & wfmt, (DWORD) 0L, 0L,
00203                      (DWORD) CALLBACK_NULL);
00204     if (st != 0) {
00205         waveout_error("waveOutOpen", st);
00206         return NULL;
00207     }
00208 
00209     return h;
00210 }
00211 
00212 
00213 static void
00214 waveout_mem_cleanup(ad_play_t * p, int32 n_buf)
00215 {
00216     int32 i;
00217 
00218     for (i = 0; i < n_buf; i++)
00219         waveout_free_buf(&(p->wo_buf[i]));
00220     if (p->wo_buf)
00221         free(p->wo_buf);
00222     if (p->busy)
00223         free(p->busy);
00224 }
00225 
00226 
00227 static int32
00228 waveout_close(ad_play_t * p)
00229 {
00230     int32 st;
00231 
00232     waveout_mem_cleanup(p, N_WO_BUF);
00233 
00234     if ((st = waveOutClose(p->h_waveout)) != 0) {
00235         waveout_error("waveOutClose", st);
00236         return -1;
00237     }
00238 
00239     free(p);
00240 
00241     return 0;
00242 }
00243 
00244 
00245 ad_play_t *
00246 ad_open_play_sps(int32 sps)
00247 {
00248     ad_play_t *p;
00249     int32 i;
00250     HWAVEOUT h;
00251 
00252     if ((h = waveout_open(sps, sizeof(int16))) == NULL)
00253         return NULL;
00254 
00255     if ((p = (ad_play_t *) calloc(1, sizeof(ad_play_t))) == NULL) {
00256         fprintf(stderr, "calloc(1,%d) failed\n", sizeof(ad_play_t));
00257         waveOutClose(h);
00258         return NULL;
00259     }
00260     if ((p->wo_buf =
00261          (ad_wbuf_t *) calloc(N_WO_BUF, sizeof(ad_wbuf_t))) == NULL) {
00262         fprintf(stderr, "calloc(%d,%d) failed\n", N_WO_BUF,
00263                 sizeof(ad_wbuf_t));
00264         free(p);
00265         waveOutClose(h);
00266 
00267         return NULL;
00268     }
00269     if ((p->busy = (char *) calloc(N_WO_BUF, sizeof(char))) == NULL) {
00270         fprintf(stderr, "calloc(%d,%d) failed\n", N_WO_BUF, sizeof(char));
00271         waveout_mem_cleanup(p, 0);
00272         free(p);
00273         waveOutClose(h);
00274 
00275         return NULL;
00276     }
00277     for (i = 0; i < N_WO_BUF; i++) {
00278         if (waveout_alloc_buf(&(p->wo_buf[i]), WO_BUFSIZE) < 0) {
00279             waveout_mem_cleanup(p, i);
00280             free(p);
00281             waveOutClose(h);
00282 
00283             return NULL;
00284         }
00285     }
00286 
00287     p->h_waveout = h;
00288     p->playing = 0;
00289     p->opened = 1;
00290     p->nxtbuf = 0;
00291     p->sps = sps;
00292     p->bps = sizeof(int16);     /* HACK!! Hardwired value for bytes/sec */
00293 
00294     return p;
00295 }
00296 
00297 
00298 ad_play_t *
00299 ad_open_play(void)
00300 {
00301     return (ad_open_play_sps(DEFAULT_SAMPLES_PER_SEC));
00302 }
00303 
00304 
00305 int32
00306 ad_close_play(ad_play_t * p)
00307 {
00308     if (!p->opened)
00309         return 0;
00310 
00311     if (p->playing)
00312         if (ad_stop_play(p) < 0)
00313             return -1;
00314 
00315     if (waveout_close(p) < 0)
00316         return -1;
00317 
00318     return 0;
00319 }
00320 
00321 
00322 int32
00323 ad_start_play(ad_play_t * p)
00324 {
00325     int32 i;
00326 
00327     if ((!p->opened) || p->playing)
00328         return -1;
00329 
00330     for (i = 0; i < N_WO_BUF; i++)
00331         p->busy[i] = 0;
00332     p->nxtbuf = 0;
00333     p->playing = 1;
00334 
00335     return 0;
00336 }
00337 
00338 
00339 int32
00340 ad_stop_play(ad_play_t * p)
00341 {
00342     int32 i, st;
00343     LPWAVEHDR whdr;
00344 
00345     if ((!p->opened) || (!p->playing))
00346         return -1;
00347 
00348 #if 0
00349     whdr->dwUser = (plen <= 0) ? 1 : 0;
00350 #endif
00351 
00352     /* Wait for all buffers to be emptied and unprepare them */
00353     for (i = 0; i < N_WO_BUF; i++) {
00354         whdr = p->wo_buf[i].p_whdr;
00355 
00356         while (p->busy[i] && (!(whdr->dwFlags & WHDR_DONE)))
00357             Sleep(100);
00358 
00359         st = waveOutUnprepareHeader(p->h_waveout, whdr, sizeof(WAVEHDR));
00360         if (st != 0) {
00361             waveout_error("waveOutUnprepareHeader", st);
00362             return -1;
00363         }
00364 
00365         p->busy[i] = 0;
00366     }
00367 
00368     return 0;
00369 }
00370 
00371 
00372 int32
00373 ad_write(ad_play_t * p, int16 * buf, int32 size)
00374 {
00375     int32 i, k, len, st;
00376     LPWAVEHDR whdr;
00377 
00378     if ((!p->opened) || (!p->playing))
00379         return -1;
00380 
00381     len = 0;
00382 
00383     for (i = 0; (i < N_WO_BUF) && (size > 0); i++) {
00384         whdr = p->wo_buf[p->nxtbuf].p_whdr;
00385 
00386         if (p->busy[p->nxtbuf]) {
00387             if (!(whdr->dwFlags & WHDR_DONE))
00388                 return len;
00389 
00390             st = waveOutUnprepareHeader(p->h_waveout, whdr,
00391                                         sizeof(WAVEHDR));
00392             if (st != 0) {
00393                 waveout_error("waveOutUnprepareHeader", st);
00394                 return -1;
00395             }
00396 
00397             p->busy[p->nxtbuf] = 0;
00398         }
00399 
00400         k = (size > WO_BUFSIZE) ? WO_BUFSIZE : size;
00401 
00402         whdr->dwBufferLength = k * sizeof(int16);
00403         memcpy(whdr->lpData, (LPSTR) buf, k * sizeof(int16));
00404 
00405         if (waveout_enqueue_buf(p->h_waveout, whdr) < 0)
00406             return -1;
00407 
00408         buf += k;
00409         size -= k;
00410         len += k;
00411 
00412         p->busy[(p->nxtbuf)++] = 1;
00413         if (p->nxtbuf >= N_WO_BUF)
00414             p->nxtbuf = 0;
00415     }
00416 
00417     return len;
00418 }