root/rat/trunk/auddev_win32.c @ 1864

Revision 1864, 30.9 KB (checked in by ucacoxh, 16 years ago)

- Corrected types of mixer fn's. Used to be ok for VC 5.0, but generated errors under

VC 6.0 (types probably used to be the same, but now different).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:        audwin32.c
3 *
4 * Win32 audio interface for RAT.
5 * Portions based on the VAT Win95 port by John Brezak.
6 * Modifications by Isidor Kouvelas <I.Kouvelas@cs.ucl.ac.uk>
7 * and Orion Hodson <O.Hodson@cs.ucl.ac.uk>.
8 *
9 * $Id$
10 *
11 * Copyright (c) 1995,1996 University College London
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, is permitted, for non-commercial use only, provided
16 * that the following conditions are met:
17 * 1. Redistributions of source code must retain the above copyright
18 *    notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *      This product includes software developed by the Computer Science
25 *      Department at University College London
26 * 4. Neither the name of the University nor of the Department may be used
27 *    to endorse or promote products derived from this software without
28 *    specific prior written permission.
29 * Use of this software for commercial purposes is explicitly forbidden
30 * unless prior written permission is obtained from the authors.
31 *
32 *
33 * Copyright (c) 1991-1993 Regents of the University of California.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *      This product includes software developed by the Computer Systems
47 *      Engineering Group at Lawrence Berkeley Laboratory.
48 * 4. Neither the name of the University nor of the Laboratory may be used
49 *    to endorse or promote products derived from this software without
50 *    specific prior written permission.
51 *
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 */
66
67#ifdef WIN32
68
69#include "config.h"
70#include <mmsystem.h>
71#include <mmreg.h>
72#include <stdlib.h>
73#include <string.h>
74#include <errno.h>
75#include "assert.h"
76
77#include "rat_types.h"
78#include "audio.h"
79#include "util.h"
80
81#define rat_to_device(x)        ((x) * 255 / MAX_AMP)
82#define device_to_rat(x)        ((x) * MAX_AMP / 255)
83
84static int              error = 0;
85static char             errorText[MAXERRORLENGTH];
86extern int thread_pri;
87static WAVEFORMATEX     format;
88static int              duplex;
89static char szDevOut[255], szDevIn[255];
90
91/* Mixer Code (C) 1998 Orion Hodson.
92 *
93 * no thanks to the person who wrote the microsoft documentation
94 * (circular and information starved) for the mixer, or the folks
95 * who conceived the api in the first place.
96 *
97 */
98
99#define IsMixSrc(x) ((x)>=MIXERLINE_COMPONENTTYPE_SRC_FIRST && \
100                     (x)<=MIXERLINE_COMPONENTTYPE_SRC_LAST)
101
102#define IsMixDst(x) ((x)>=MIXERLINE_COMPONENTTYPE_DST_FIRST && \
103                     (x)<=MIXERLINE_COMPONENTTYPE_DST_LAST)
104
105#define MAX_MIX_CTLS 8
106
107typedef struct {
108        char      szName[20];
109        DWORD     dwCtlID[MAX_MIX_CTLS];
110        DWORD     dwCtlType[MAX_MIX_CTLS];
111        DWORD     dwCtl[MAX_MIX_CTLS];
112        DWORD     dwMultipleItems[MAX_MIX_CTLS];
113        DWORD     dwLowerBound[MAX_MIX_CTLS];
114        DWORD     dwUpperBound[MAX_MIX_CTLS];
115        int       nCtls;
116} MixCtls;
117
118typedef struct {
119        char szName[12];
120        int  nID;
121} MapEntry;
122
123static MixCtls  mcMixIn[MAX_MIX_CTLS], mcMixOut[MAX_MIX_CTLS];
124static u_int32  nMixIn, nMixOut, nMixInID, curMixIn, curMixOut;
125static int32    play_vol, rec_vol;
126
127static HMIXER hMixIn, hMixOut;
128static UINT   uWavIn, uWavOut;
129
130static MapEntry meInputs[] = {
131        {"mic",  AUDIO_MICROPHONE},
132        {"line", AUDIO_LINE_IN},
133        {"cd",   AUDIO_CD},
134        {"analog cd", AUDIO_CD},
135        {"",0}
136};
137
138static const char *
139audioIDToName(int id, MapEntry *meMap)
140{
141        int i = 0;
142        while(meMap[i].szName) {
143                if (meMap[i].nID == id) return meMap[i].szName;
144                i++;
145        }
146        return (const char *)NULL;
147}
148
149static int 
150nameToAudioID(char *name, MapEntry *meMap)
151{
152        int i = 0;
153        while(meMap[i].szName[0]) {
154                if (strncasecmp(name, meMap[i].szName,3) == 0) return meMap[i].nID;
155                i++;
156        }
157        return -1;
158}
159
160static MixCtls *
161nameToMixCtls(char *szName, MixCtls *mcMix, int nMix)
162{
163        int i;
164        for(i = 0;i < nMix; i++) {
165                if (strncasecmp(szName, mcMix[i].szName,3) == 0) return (mcMix+i);
166        }
167        return (MixCtls*)NULL;
168}
169
170
171static MixCtls *
172audioIDToMixCtls(int id, MapEntry *meMap, MixCtls *mcMix, int nMix)
173{
174        int i,j;
175        const char *szName;
176       
177        if ((const char*)NULL == (szName = audioIDToName(id, meMap))) return (MixCtls*)NULL;
178        for(j = 0; j < nMix; j++) {
179                i = 0;
180                while(meMap[i].szName[0] != 0) {
181                        if (meMap[i].nID == id &&
182                                strncasecmp(meMap[i].szName,mcMix[j].szName, strlen(meMap[i].szName))==0)
183                                return (mcMix + j);
184                        i++;
185                }
186        }
187        return (MixCtls *)NULL;
188}
189
190static int
191mixSetIPort(MixCtls *mcMix)
192{
193        MIXERCONTROLDETAILS_LISTTEXT *ltInputs;
194        MIXERCONTROLDETAILS_BOOLEAN  *bInputs;
195        MIXERCONTROLDETAILS mcd;
196        int i,j,r;
197       
198        assert(mcMix);
199       
200        curMixIn = (mcMix - mcMixIn);
201
202        /* now make sure line is selected */
203        if (strncasecmp(mcMixIn->szName, "rec", 3) != 0) return 0;
204        for(i = 0; i < mcMixIn->nCtls; i++) {
205                switch(mcMixIn->dwCtlType[i]) {
206                case MIXERCONTROL_CONTROLTYPE_MUX:   /* a list with single select   */
207                case MIXERCONTROL_CONTROLTYPE_MIXER: /* a list with multiple select */
208                        mcd.cbStruct       = sizeof(MIXERCONTROLDETAILS);
209                        mcd.dwControlID    = mcMixIn->dwCtlID[i];
210                        mcd.cChannels      = 1;
211                        mcd.cMultipleItems = mcMixIn->dwMultipleItems[i];
212                        mcd.cbDetails      = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
213                        ltInputs           = (MIXERCONTROLDETAILS_LISTTEXT*)xmalloc(mcMixIn->dwMultipleItems[i] * sizeof(MIXERCONTROLDETAILS_LISTTEXT));
214                        mcd.paDetails      = (LPVOID)ltInputs;
215                        r = mixerGetControlDetails((HMIXEROBJ)hMixIn, &mcd,MIXER_GETCONTROLDETAILSF_LISTTEXT);
216                        if (r != MMSYSERR_NOERROR) goto done_multi1;
217                        bInputs = (MIXERCONTROLDETAILS_BOOLEAN*)xmalloc(mcMixIn->dwMultipleItems[i] * sizeof(MIXERCONTROLDETAILS_BOOLEAN));
218                        mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
219                        mcd.paDetails = (LPVOID)bInputs;
220                        mcd.dwControlID    = mcMixIn->dwCtlID[i];
221                        mcd.cChannels      = 1;
222                        mcd.cMultipleItems = mcMixIn->dwMultipleItems[i];
223                        /* now ltInputs contains names of inputs,
224                         * bInputs contains selection status.
225                         */ 
226                        for (j = 0; j < (signed)mcMixIn->dwMultipleItems[i]; j++) {
227                                if (strncasecmp(ltInputs[j].szName, mcMix->szName, strlen(mcMix->szName)) == 0) {
228                                        bInputs[j].fValue = 1;
229                                } else {
230                                        /* force single select even when multiple available */
231                                        bInputs[j].fValue = 0;
232                                }
233                        }
234                        r = mixerSetControlDetails((HMIXEROBJ)hMixIn, &mcd,MIXER_SETCONTROLDETAILSF_VALUE);
235                        xfree(bInputs);
236done_multi1:            xfree(ltInputs);
237                        return 0;           
238                        break;
239                }
240        }
241
242        return 0;
243}
244
245static void
246mixGetControls(HMIXER hMix, char *szDstName, int nDst, MixCtls *mcMix, int *nMix)
247{
248        MIXERLINE         ml;
249        MIXERCONTROL     *mc;
250        MIXERLINECONTROLS mlc;
251        MMRESULT res;
252
253        int i, src, ctl, offset;
254       
255        offset = 0;
256        for(i = 0; i < nDst; i++) {
257                ml.dwDestination = i;
258                ml.cbStruct = sizeof(MIXERLINE);
259                res = mixerGetLineInfo((HMIXEROBJ)hMix, &ml, MIXER_GETLINEINFOF_DESTINATION);
260                if (res != MMSYSERR_NOERROR || strncmp(ml.szShortName, szDstName, strlen(szDstName)) != 0) continue;
261               
262                if (ml.cControls) {
263                        /* Get controls of mixer itself - for input this is
264                         * usually multiplexer + master vol, and for output
265                         * this is usually master mute and vol.
266                         */
267                        offset = 1;
268                        strncpy(mcMix[0].szName, ml.szShortName,20);
269                        mc = (MIXERCONTROL*)xmalloc(sizeof(MIXERCONTROL) * ml.cControls);
270                        mlc.cbStruct   = sizeof(MIXERLINECONTROLS);
271                        mlc.dwLineID   = ml.dwLineID;
272                        mlc.cControls  = ml.cControls;
273                        mlc.cbmxctrl   = sizeof(MIXERCONTROL);
274                        mlc.pamxctrl   = mc;
275                        res = mixerGetLineControls((HMIXEROBJ)hMix, &mlc, MIXER_GETLINECONTROLSF_ALL);
276                        if (res == MMSYSERR_NOERROR) {
277                                src = 0;
278                                for(ctl = 0; ctl < (signed)ml.cControls && ctl < MAX_MIX_CTLS; ctl++) {
279                                        mcMix[src].dwCtlID[ctl]         = mc[ctl].dwControlID;
280                                        mcMix[src].dwCtlType[ctl]       = mc[ctl].dwControlType;
281                                        mcMix[src].dwCtl[ctl]           = mc[ctl].fdwControl;
282                                        mcMix[src].dwMultipleItems[ctl] = mc[ctl].cMultipleItems;
283                                        mcMix[src].dwLowerBound[ctl]    = mc[ctl].Bounds.dwMinimum;
284                                        mcMix[src].dwUpperBound[ctl]    = mc[ctl].Bounds.dwMaximum;
285                                }
286                                mcMix[src].nCtls = ctl;
287                        }
288                        xfree(mc);
289                }
290                (*nMix) = min(ml.cConnections+offset,MAX_MIX_CTLS);
291               
292                for (src = (*nMix)-1; src>=0 ; src--) {
293                        ml.dwSource  = src - offset;
294                        res = mixerGetLineInfo((HMIXEROBJ)hMix, &ml, MIXER_GETLINEINFOF_SOURCE);
295                        if (res != MMSYSERR_NOERROR) continue;
296                        strncpy(mcMix[src].szName, ml.szShortName,20);
297                        mc = (MIXERCONTROL*)xmalloc(sizeof(MIXERCONTROL) * ml.cControls);
298                        mlc.cbStruct   = sizeof(MIXERLINECONTROLS);
299                        mlc.dwLineID   = ml.dwLineID;
300                        mlc.cControls  = ml.cControls;
301                        mlc.cbmxctrl   = sizeof(MIXERCONTROL);
302                        mlc.pamxctrl   = mc;
303                        res = mixerGetLineControls((HMIXEROBJ)hMix, &mlc, MIXER_GETLINECONTROLSF_ALL);
304                        if (res != MMSYSERR_NOERROR) continue;
305                        for(ctl = 0; ctl < (signed)ml.cControls && ctl < MAX_MIX_CTLS; ctl++) {
306                                mcMix[src].dwCtlID[ctl]         = mc[ctl].dwControlID;
307                                mcMix[src].dwCtlType[ctl]       = mc[ctl].dwControlType;
308                                mcMix[src].dwCtl[ctl]           = mc[ctl].fdwControl;
309                                mcMix[src].dwMultipleItems[ctl] = mc[ctl].cMultipleItems;
310                                mcMix[src].dwLowerBound[ctl]    = mc[ctl].Bounds.dwMinimum;
311                                mcMix[src].dwUpperBound[ctl]    = mc[ctl].Bounds.dwMaximum;
312                        }
313                        mcMix[src].nCtls = ctl;
314                        xfree(mc);
315                }
316        }
317
318}
319
320static void 
321mixSetup()
322{
323        MIXERCAPS m;
324        MMRESULT  res;
325        HMIXER    hMix;
326       
327        int i,j, nDevs, nDstIn, nDstOut;
328        char mixName[32];
329
330        if (hMixIn)  {mixerClose(hMixIn);  hMixIn  = 0;}
331        if (hMixOut) {mixerClose(hMixOut); hMixOut = 0;}
332
333        nDevs = mixerGetNumDevs();
334        for(i = 0; i < nDevs; i++) {
335                char doClose = TRUE;
336                /* Strictly we don't need to open mixer here */
337                mixerOpen(&hMix, i, (unsigned long)NULL, (unsigned long)NULL, MIXER_OBJECTF_MIXER);
338                res = mixerGetDevCaps(i,  &m, sizeof(m));
339               
340                j = 0;
341                while(j < 32 && (mixName[j] = tolower(m.szPname[j]))) j++;
342               
343                if (res == MMSYSERR_NOERROR){
344                        if ((unsigned)i == uWavIn) {
345                                hMixIn  = hMix;
346                                nDstIn  = m.cDestinations;
347                                doClose = FALSE;
348#ifdef DEBUG_WIN32_AUDIO
349                                fprintf(stderr, "Input mixer %s\n", m.szPname);
350#endif /* DEBUG_WIN32_AUDIO */
351                        }
352                        if ((unsigned)i == uWavOut) {
353                                hMixOut = hMix;
354                                nDstOut = m.cDestinations;
355                                doClose = FALSE;
356#ifdef DEBUG_WIN32_AUDIO
357                                fprintf(stderr, "Output mixer %s\n", m.szPname);
358#endif /* DEBUG_WIN32_AUDIO */
359                        }
360                }
361                if (doClose) mixerClose(hMix);
362        }
363        /* There are fields within MIXERLINE struct that should say
364         * if line is input or output.  Does not work with SB driver
365         * so we give a string to match to "Rec" or "Vol", great :-( */
366
367        mixGetControls(hMixIn, "Rec", nDstIn,  mcMixIn,  &nMixIn);
368        mixSetIPort(nameToMixCtls("mic", mcMixIn, nMixIn));
369}
370
371static int blksz;
372static int nblks;
373static int smplsz;
374/* AUDIO OUTPUT RELATED FN's ********************************/
375
376static WAVEHDR *write_hdrs, *write_curr, *write_tail;
377static u_char  *write_mem;
378static int      write_hdrs_used;
379static HWAVEOUT shWaveOut;
380
381static int
382audio_open_out()
383{
384        int             i;
385        WAVEHDR         *whp;
386        u_char          *bp;
387
388        if (shWaveOut)
389                return (TRUE);
390
391        error = waveOutOpen(&shWaveOut, WAVE_MAPPER, &format, 0, 0, CALLBACK_NULL);
392        if (error) {
393#ifdef DEBUG
394                waveOutGetErrorText(error, errorText, sizeof(errorText));
395                debug_msg("waveOutOpen: (%d) %s\n", error, errorText);
396#endif
397                return (FALSE);
398        }
399
400        if (write_mem != NULL) xfree(write_mem);
401        write_mem = (u_char*)xmalloc(nblks * blksz);
402        if (write_hdrs != NULL) xfree(write_hdrs);
403        write_hdrs = (WAVEHDR*)xmalloc(sizeof(WAVEHDR)*nblks);
404        memset(write_hdrs, 0, sizeof(WAVEHDR)*nblks);
405        for (i = 0, whp = write_hdrs, bp = write_mem; i < nblks; i++, whp++, bp += blksz) {
406                whp->dwFlags        = 0;
407                whp->dwBufferLength = blksz;
408                whp->lpData         = bp;
409                error = waveOutPrepareHeader(shWaveOut, whp, sizeof(WAVEHDR));
410                if (error) {
411                        waveOutGetErrorText(error, errorText, sizeof(errorText));
412                        fprintf(stderr, "Win32Audio: waveOutPrepareHeader: %s\n", errorText);
413                        exit(1);
414                }
415        }
416        write_tail      = write_curr = write_hdrs;
417        write_hdrs_used = 0;
418        return (TRUE);
419}
420
421static void
422audio_close_out()
423{
424        int     i;
425        WAVEHDR         *whp;
426
427        if (shWaveOut == 0)
428                return;
429
430        waveOutReset(shWaveOut);
431
432        for (i = 0, whp = write_hdrs; i < nblks; i++, whp++)
433                if (whp->dwFlags & WHDR_PREPARED)
434                        waveOutUnprepareHeader(shWaveOut, whp, sizeof(WAVEHDR));
435
436        (void) waveOutClose(shWaveOut);
437        xfree(write_hdrs); write_hdrs = NULL;
438        xfree(write_mem);  write_mem  = NULL;
439        xmemchk();
440        shWaveOut = 0;
441}
442
443#define WRITE_ERROR_STILL_PLAYING 33
444
445int
446audio_write(int audio_fd, sample *cp, int remain)
447{
448        int             error, len, ret;
449
450        if (shWaveOut == 0)
451                return (remain);
452
453        ret = remain;
454        if (write_hdrs_used > 4*nblks/5) {
455                char errmsg[80];
456                sprintf(errmsg,
457                                "Running out of write buffers %d left\n",
458                                write_hdrs_used);
459                OutputDebugString(errmsg);
460        }
461
462        for (; remain > 0; remain -= len) {
463                if (write_curr->dwFlags & WHDR_DONE) {
464                        /* Have overdone it! */
465                        char msg[80];
466                        sprintf(msg,
467                                "audio_write, reached end of buffer (%06d bytes remain)\n",
468                                remain);
469                        OutputDebugString(msg);
470                        return (ret - remain);
471                }
472
473                len = remain > blksz/smplsz ? blksz/smplsz : remain;
474
475                memcpy(write_curr->lpData, cp, len * smplsz);
476                cp += len;
477
478                error = waveOutWrite(shWaveOut, write_curr, sizeof(WAVEHDR));
479               
480                if (error == WRITE_ERROR_STILL_PLAYING) { /* We've filled device buffer ? */
481                                char msg[80];
482                                sprintf(msg,
483                                                "Win32Audio - device filled. Discarding %d bytes.\n",
484                                                ret - remain);
485                                OutputDebugString(msg);
486                                        /* we return as if we wrote everything out
487                                         * to give buffer a little breathing room
488                                         */
489
490                                return ret;
491                } else if (error) {
492                        waveOutGetErrorText(error, errorText, sizeof(errorText));
493                        fprintf(stderr,
494                                        "Win32Audio: waveOutWrite (%d): %s\n",
495                                        error,
496                                        errorText);
497                        return (ret - remain);
498                }
499
500                write_curr++;
501                write_hdrs_used++;
502                if (write_curr >= write_hdrs + nblks)
503                        write_curr = write_hdrs;
504        }
505        return (ret);
506}
507
508/* AUDIO INPUT RELATED FN's *********************************/
509
510static unsigned char audio_ready = 0;
511
512unsigned char
513is_audio_ready()
514{
515#ifdef DEBUG
516        if (audio_ready>nblks/5) {
517                printf("Lots of audio available (%d blocks)\n", audio_ready);
518        }
519#endif
520
521        return (audio_ready>0) ? TRUE : FALSE;
522}
523
524static void CALLBACK
525waveInProc(HWAVEIN hwi,
526                   UINT    uMsg,
527                   DWORD   dwInstance,
528                   DWORD   dwParam1,
529                   DWORD   dwParam2)
530{
531        switch(uMsg) {
532        case WIM_DATA:
533                audio_ready++;
534                break;
535        default:
536              ;  /* nothing to do currently */
537        }
538        return;
539}
540
541static WAVEHDR  *read_hdrs, *read_curr;
542static u_char   *read_mem;
543static HWAVEIN  shWaveIn;
544
545static int
546audio_open_in()
547{
548        WAVEHDR *whp;
549        int          l;
550        u_char  *bp;
551
552        if (shWaveIn) return (TRUE);
553
554        if (read_mem != NULL) xfree(read_mem);
555        read_mem = (u_char*)xmalloc(nblks * blksz);
556
557        if (read_hdrs != NULL) xfree(read_hdrs);
558        read_hdrs = (WAVEHDR*)xmalloc(sizeof(WAVEHDR)*nblks);
559
560        error = waveInOpen(&shWaveIn,
561                           WAVE_MAPPER,
562                           &format,
563                           (unsigned long)waveInProc,
564                           0,
565                           CALLBACK_FUNCTION);
566        if (error != MMSYSERR_NOERROR) {
567                waveInGetErrorText(error, errorText, sizeof(errorText));
568                fprintf(stderr, "waveInOpen: (%d) %s\n", error, errorText);
569                return (FALSE);
570        }
571
572        /* Provide buffers for reading */
573        audio_ready = 0;
574        for (l = 0, whp = read_hdrs, bp = read_mem; l < nblks; l++, whp++, bp += blksz) {
575                whp->lpData = bp;
576                whp->dwBufferLength = blksz;
577                whp->dwFlags = 0;
578                error = waveInPrepareHeader(shWaveIn, whp, sizeof(WAVEHDR));
579                if (error) {
580                        waveInGetErrorText(error, errorText, sizeof(errorText));
581                        fprintf(stderr, "waveInPrepareHeader: (%d) %s\n", error, errorText);
582                        exit(1);
583                }
584                error = waveInAddBuffer(shWaveIn, whp, sizeof(WAVEHDR));
585                if (error) {
586                        waveInGetErrorText(error, errorText, sizeof(errorText));
587                        fprintf(stderr, "waveInAddBuffer: (%d) %s\n", error, errorText);
588                        exit(1);
589                }
590        }
591        read_curr = read_hdrs;
592
593        error = waveInStart(shWaveIn);
594        if (error) {
595                waveInGetErrorText(error, errorText, sizeof(errorText));
596                fprintf(stderr, "Win32Audio: waveInStart: (%d) %s\n", error, errorText);
597                exit(1);
598        }
599
600        return (TRUE);
601}
602
603static void
604audio_close_in()
605{
606        int             i;
607        WAVEHDR         *whp;
608
609        if (shWaveIn == 0)
610                return;
611       
612        waveInStop(shWaveIn);
613        waveInReset(shWaveIn);
614
615        for (i = 0, whp = read_hdrs; i < nblks; i++, whp++)
616                if (whp->dwFlags & WHDR_PREPARED)
617                        waveInUnprepareHeader(shWaveIn, whp, sizeof(WAVEHDR));
618
619        waveInClose(shWaveIn);
620        shWaveIn = 0;
621        xfree(read_hdrs); read_hdrs = NULL;
622        xfree(read_mem);  read_mem  = NULL;
623        xmemchk();
624}
625
626int
627audio_read(int audio_fd, sample *buf, int samples)
628{
629        static int virgin = 0;
630        int len = 0;
631
632        if (!virgin) {
633                fprintf(stderr,"ready %d\n", audio_ready);
634                virgin++;
635        }
636
637        if (shWaveIn == 0) {
638        /* officially we dont support half-duplex anymore but just in case */
639                assert(shWaveOut);
640                for (len = 0; write_tail->dwFlags & WHDR_DONE;) {
641                        if (len + write_tail->dwBufferLength / smplsz > (unsigned)samples)
642                                break;
643                        else
644                                len += write_tail->dwBufferLength / smplsz;
645
646                        write_tail->dwFlags &=~ WHDR_DONE;
647                        write_tail++;
648                        write_hdrs_used--;
649                        if (write_tail >= write_hdrs + nblks)
650                                write_tail = write_hdrs;
651                }
652
653                if (write_curr == write_tail && len + blksz/smplsz <= samples)
654                        len += blksz/smplsz;
655
656                return (len);
657        } else if (duplex) {
658                while (write_tail->dwFlags & WHDR_DONE) {
659                        write_tail->dwFlags &= ~WHDR_DONE;
660                        write_tail++;
661                        write_hdrs_used--;
662                        if (write_tail >= write_hdrs + nblks)
663                                write_tail = write_hdrs;
664                }
665        }
666
667        if (audio_ready) {
668                while ((read_curr->dwFlags & WHDR_DONE) && len < samples) {
669                        memcpy(buf, read_curr->lpData, blksz);
670                        buf += blksz/smplsz;
671                        len += blksz/smplsz;
672                        read_curr->dwFlags &= ~WHDR_DONE;
673                        error = waveInAddBuffer(shWaveIn, read_curr, sizeof(WAVEHDR));
674                        if (error) {
675                                waveInGetErrorText(error, errorText, sizeof(errorText));
676                                fprintf(stderr, "waveInAddBuffer: (%d) %s\n", error, errorText);
677                                exit(1);
678                        }
679                        read_curr++;
680                        if (read_curr == read_hdrs + nblks) read_curr = read_hdrs;
681                        if (audio_ready > 0) audio_ready--;
682                }
683#ifdef DEBUG
684                if (audio_ready > 3*nblks/4) {
685                        char msg[255];
686                        int i,used;
687                        for(i=0,used=0;i<nblks;i++)
688                                if (read_hdrs[i].dwFlags & WHDR_DONE) used++;
689                        sprintf(msg, "RB small %d of %d, samples %d len %d, ready %d\n", used, nblks, samples, len, audio_ready);
690                        OutputDebugString(msg);
691                }
692#endif
693        }
694
695        return (len);
696}
697
698/* BEST OF THE REST (SIC) ************************************/
699
700int audio_get_channels()
701{
702        return format.nChannels;
703}
704
705int
706audio_open(audio_format fmt)
707{
708        static int virgin;
709        WAVEFORMATEX tfmt;
710       
711        if (virgin == 0) {
712                HKEY hKey = HKEY_CURRENT_USER;
713                WAVEOUTCAPS woc;
714                WAVEINCAPS  wic;
715                UINT        uDevId,uNumDevs;
716
717                RegGetValue(&hKey,
718                    "Software\\Microsoft\\Multimedia\\Sound Mapper",
719                    "Playback",
720                    szDevOut,
721                    255);
722                RegGetValue(&hKey,
723                    "Software\\Microsoft\\Multimedia\\Sound Mapper",
724                    "Record",
725                    szDevIn,
726                    255);
727               
728                uWavOut  = WAVE_MAPPER;
729                uNumDevs = waveOutGetNumDevs();
730                for (uDevId = 0; uDevId < uNumDevs; uDevId++) {
731                       waveOutGetDevCaps(uDevId, &woc, sizeof(woc));
732                       if (strcmp(woc.szPname, szDevOut) == 0) {
733                               uWavOut = uDevId;
734                               break;
735                       }
736                }
737               
738                uWavIn   = WAVE_MAPPER;
739                uNumDevs = waveInGetNumDevs();
740                for (uDevId = 0; uDevId < uNumDevs; uDevId++) {
741                       waveInGetDevCaps(uDevId, &wic, sizeof(wic));
742                       if (strcmp(wic.szPname, szDevIn) == 0) {
743                               uWavIn = uDevId;
744                               break;
745                       }
746                }
747               
748                if (mixerGetNumDevs()) {
749                        mixSetup();   
750                }
751                virgin = 1;
752        }
753
754        format.wFormatTag      = WAVE_FORMAT_PCM;
755        format.nChannels       = fmt.num_channels;
756        format.nSamplesPerSec  = fmt.sample_rate;
757        format.wBitsPerSample  = fmt.bits_per_sample;
758        smplsz                 = format.wBitsPerSample / 8;
759        format.nAvgBytesPerSec = format.nChannels * format.nSamplesPerSec * smplsz;
760        format.nBlockAlign     = format.nChannels * smplsz;
761        format.cbSize          = 0;
762        memcpy(&tfmt, &format, sizeof(format));
763        /* Use 1 sec device buffer */
764        blksz  = fmt.blocksize * smplsz;
765        nblks  = format.nAvgBytesPerSec / blksz;
766        if (audio_open_in() == FALSE)   return -1;
767        if ((duplex = audio_open_out()) == FALSE) {
768                audio_close_in();
769                return -1;
770        }
771        /* because i've seen these get corrupted... */
772        assert(tfmt.wFormatTag      == format.wFormatTag);
773        assert(tfmt.nChannels       == format.nChannels);
774        assert(tfmt.nSamplesPerSec  == format.nSamplesPerSec);
775        assert(tfmt.wBitsPerSample  == format.wBitsPerSample);
776        assert(tfmt.nAvgBytesPerSec == format.nAvgBytesPerSec);
777        assert(tfmt.nBlockAlign     == format.nBlockAlign);
778       
779        switch(thread_pri) {
780        case 1:
781                SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
782                break;
783        case 2:
784                SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
785                break;
786        case 3:
787                SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
788                break;
789        default:
790                break;
791        }
792
793        return 1;
794}
795
796
797void
798audio_close(int audio_fd)
799{
800#ifdef DEBUG
801        fprintf(stderr, "Closing input device.\n");
802#endif
803        audio_close_in();
804#ifdef DEBUG
805        fprintf(stderr, "Closing output device.\n");
806#endif DEBUG
807        audio_close_out();
808#ifdef DEBUG
809        fprintf(stderr, "Closed output device.\n");
810#endif DEBUG
811}
812
813int
814audio_duplex(int audio_fd)
815{
816        return (duplex);
817}
818
819
820void
821audio_drain(int audio_fd)
822{
823}
824
825void
826audio_non_block(int audio_fd)
827{
828}
829
830void
831audio_set_gain(int audio_fd, int level)
832{
833        int i;
834        MIXERCONTROLDETAILS          mcd;
835        MIXERCONTROLDETAILS_UNSIGNED mcduDevLevel;
836        MMRESULT r;
837        UNUSED(audio_fd);
838       
839        for(i = 0; i < mcMixIn[curMixIn].nCtls; i++) {
840                switch (mcMixIn[curMixIn].dwCtlType[i]) {
841                case MIXERCONTROL_CONTROLTYPE_VOLUME:
842                        mcd.cbStruct       = sizeof(MIXERCONTROLDETAILS);
843                        mcd.dwControlID    = mcMixIn[curMixIn].dwCtlID[i];
844                        mcd.cChannels      = 1;
845                        mcd.cMultipleItems = 0;
846                        mcd.cbDetails      = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
847                        mcd.paDetails      = &mcduDevLevel;
848                        mcduDevLevel.dwValue = ((mcMixIn[curMixIn].dwUpperBound[i] - mcMixIn[curMixIn].dwLowerBound[i])/100) * level + mcMixIn[curMixIn].dwLowerBound[i];
849                        fprintf(stderr, "audio_set_gain rat %d dev %d\n", level, mcduDevLevel.dwValue);
850                        play_vol   = level;
851                        r = mixerSetControlDetails((HMIXEROBJ)hMixIn, &mcd, MIXER_OBJECTF_HMIXER);
852                        switch (r) {
853                                case MMSYSERR_NOERROR:    break;
854                                case MIXERR_INVALLINE:     fprintf(stderr, "invalid line\n"); break;
855                                case MIXERR_INVALCONTROL:  fprintf(stderr, "invalid control\n"); break;
856                                case MIXERR_INVALVALUE:    fprintf(stderr, "invalid value\n"); break;
857                                case MMSYSERR_BADDEVICEID: fprintf(stderr, "bad device id\n");   break;
858                                case MMSYSERR_INVALFLAG:   fprintf(stderr, "invalid flag\n");    break;
859                                case MMSYSERR_INVALHANDLE: fprintf(stderr, "invalid handle\n");  break;
860                                case MMSYSERR_INVALPARAM:  fprintf(stderr, "invalid param\n");   break;
861                                case MMSYSERR_NODRIVER:    fprintf(stderr, "no driver!\n");      break;
862                                default:                   fprintf(stderr, "mixerSetControlDetails ?");
863                        }
864                        break;
865                }
866        }
867}
868
869int
870audio_get_gain(int audio_fd)
871{
872        fprintf(stderr, "audio_get_gain: %d\n", rec_vol);
873        return (rec_vol);
874}
875
876void
877audio_set_volume(int audio_fd, int level)
878{
879        DWORD   vol;
880
881        play_vol = level;
882
883        if (shWaveOut == 0)
884                return;
885
886        level = rat_to_device(level);
887        if (level >= 255)
888                level = (short)-1;
889        else
890                level <<= 8;
891        vol = level | (level << 16);
892
893        error = waveOutSetVolume(shWaveOut, vol);
894        if (error) {
895#ifdef DEBUG
896                waveOutGetErrorText(error, errorText, sizeof(errorText));
897                fprintf(stderr, "Win32Audio: waveOutSetVolume: %s\n", errorText);
898#endif
899        }
900}
901
902int
903audio_get_volume(int audio_fd)
904{
905        DWORD   vol;
906
907        if (shWaveOut == 0)
908                return (play_vol);
909
910        error = waveOutGetVolume(shWaveOut, &vol);
911        if (error) {
912#ifdef DEBUG
913                waveOutGetErrorText(error, errorText, sizeof(errorText));
914                fprintf(stderr, "Win32Audio: waveOutGetVolume Error: %s\n", errorText);
915#endif
916                return (0);
917        } else
918                return (device_to_rat(vol & 0xff));
919}
920
921void
922audio_set_oport(int audio_fd, int port)
923{
924        UNUSED(audio_fd);
925        UNUSED(port);
926}
927
928/* Return selected output port */
929int audio_get_oport(int audio_fd)
930{
931        return (AUDIO_SPEAKER);
932}
933
934/* Select next available output port */
935int
936audio_next_oport(int audio_fd)
937{
938        return (AUDIO_SPEAKER);
939}
940
941void 
942audio_set_iport(int audio_fd, int port)
943{
944        MixCtls *mcMix;
945       
946        UNUSED(audio_fd);
947
948        mcMix = audioIDToMixCtls(port, meInputs, mcMixIn, nMixIn);
949       
950        if (mcMix) mixSetIPort(mcMix);
951}
952
953/* Return selected input port */
954int
955audio_get_iport(int audio_fd)
956{
957        int id = nameToAudioID(mcMixIn[curMixIn].szName, meInputs);
958        return (id);
959}
960
961/* Select next available input port */
962int
963audio_next_iport(int audio_fd)
964{
965        u_int32 trialMixIn;
966        int id = -1;
967
968        trialMixIn = curMixIn;
969        do {
970                trialMixIn = (trialMixIn + 1) % nMixIn;
971                id = nameToAudioID(mcMixIn[trialMixIn].szName, meInputs);
972        } while(id == -1);
973        mixSetIPort(audioIDToMixCtls(id, meInputs, mcMixIn, nMixIn));
974        return (id);
975}
976
977#endif /* WIN32 */
Note: See TracBrowser for help on using the browser.