root/rat/trunk/auddev_win32.c @ 3642

Revision 3642, 52.1 KB (checked in by ucacoxh, 11 years ago)

s/[ \t]+$//

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * FILE:        auddev_win32.c
3 *
4 * Reworked by Orion Hodson from RAT 3.0 code.
5 *
6 * Assorted fixes and multilingual comments from Michael Wallbaum
7 * <wallbaum@informatik.rwth-aachen.de>
8 *
9 * Copyright (c) 1995-2001 University College London
10 * All rights reserved.
11 */
12
13#ifndef HIDE_SOURCE_STRINGS
14static const char cvsid[] =
15        "$Id$";
16#endif /* HIDE_SOURCE_STRINGS */
17
18#ifdef WIN32
19
20#include "config_win32.h"
21#include "audio.h"
22#include "debug.h"
23#include "memory.h"
24#include "auddev_win32.h"
25#include "audio_types.h"
26#include "audio_fmt.h"
27#include "util.h"
28#include "mmsystem.h"
29
30#define MAX_DEVICE_GAIN 0xffff
31#define rat_to_device(x)        (((x) * MAX_DEVICE_GAIN / MAX_AMP) << 16 | ((x) * MAX_DEVICE_GAIN / MAX_AMP))
32#define device_to_rat(x)        ((x & 0xffff) * MAX_AMP / MAX_DEVICE_GAIN)
33
34#define W32SDK_MAX_DEVICES 20
35static  int have_probed[W32SDK_MAX_DEVICES];
36static  int w32sdk_probe_formats(audio_desc_t ad);
37
38static int  error = 0;
39static char errorText[MAXERRORLENGTH];
40static int  nLoopGain = 100;
41#define     MAX_DEV_NAME 64
42
43static UINT mapAudioDescToMixerID(audio_desc_t ad);
44
45/* mcd_elem_t is a node used to store control state so
46 * we can restore mixer controls when device closes.
47 */
48
49typedef struct s_mcd_elem {
50        MIXERCONTROLDETAILS *pmcd;
51        struct s_mcd_elem   *next;
52} mcd_elem_t;
53
54static mcd_elem_t *control_list;
55
56#define MIX_ERR_LEN 32
57#define MIX_MAX_CTLS 8
58#define MIX_MAX_GAIN 100
59
60static int32_t  play_vol, rec_vol;
61static HMIXER   hMixer;
62
63static DWORD    dwRecLineID, dwVolLineID;
64
65static audio_port_details_t *input_ports, *loop_ports;
66static int                   n_input_ports, n_loop_ports;
67static int iport; /* Current input port */
68
69/* Macro to convert macro name to string so we diagnose controls and error  */
70/* codes.                                                                   */
71#define CASE_STRING(x) case x: return #x
72
73/* DEBUGGING FUNCTIONS ******************************************************/
74
75static const char *
76mixGetErrorText(MMRESULT mmr)
77{
78#ifndef NDEBUG
79        switch (mmr) {
80        CASE_STRING(MMSYSERR_NOERROR);
81        CASE_STRING(MIXERR_INVALLINE);
82        CASE_STRING(MIXERR_INVALCONTROL);
83        CASE_STRING(MIXERR_INVALVALUE);
84        CASE_STRING(WAVERR_BADFORMAT);
85        CASE_STRING(MMSYSERR_BADDEVICEID);
86        CASE_STRING(MMSYSERR_INVALFLAG);
87        CASE_STRING(MMSYSERR_INVALHANDLE);
88        CASE_STRING(MMSYSERR_INVALPARAM);
89        CASE_STRING(MMSYSERR_NODRIVER);
90        default:
91                return "Undefined Error";
92        }
93#endif /* NDEBUG */
94        return "Mixer Error.";
95}
96
97static const char *
98mixGetControlType(DWORD dwCtlType)
99{
100#ifndef NDEBUG
101        switch(dwCtlType) {
102        CASE_STRING(MIXERCONTROL_CONTROLTYPE_CUSTOM);
103        CASE_STRING(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
104        CASE_STRING(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
105        CASE_STRING(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
106        CASE_STRING(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
107        CASE_STRING(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
108        CASE_STRING(MIXERCONTROL_CONTROLTYPE_ONOFF);
109        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MUTE);
110        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MONO);
111        CASE_STRING(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
112        CASE_STRING(MIXERCONTROL_CONTROLTYPE_STEREOENH);
113        CASE_STRING(MIXERCONTROL_CONTROLTYPE_BUTTON);
114        CASE_STRING(MIXERCONTROL_CONTROLTYPE_DECIBELS);
115        CASE_STRING(MIXERCONTROL_CONTROLTYPE_SIGNED);
116        CASE_STRING(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
117        CASE_STRING(MIXERCONTROL_CONTROLTYPE_PERCENT);
118        CASE_STRING(MIXERCONTROL_CONTROLTYPE_SLIDER);
119        CASE_STRING(MIXERCONTROL_CONTROLTYPE_PAN);
120        CASE_STRING(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
121        CASE_STRING(MIXERCONTROL_CONTROLTYPE_FADER);
122        CASE_STRING(MIXERCONTROL_CONTROLTYPE_VOLUME);
123        CASE_STRING(MIXERCONTROL_CONTROLTYPE_BASS);
124        CASE_STRING(MIXERCONTROL_CONTROLTYPE_TREBLE);
125        CASE_STRING(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
126        CASE_STRING(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
127        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MUX);
128        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
129        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MIXER);
130        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MICROTIME);
131        CASE_STRING(MIXERCONTROL_CONTROLTYPE_MILLITIME);
132        }
133#endif /* NDEBUG */
134        return "Unknown";
135}
136
137static void
138mixerDumpLineInfo(HMIXEROBJ hMix, DWORD dwLineID)
139{
140        MIXERLINECONTROLS mlc;
141        LPMIXERCONTROL    pmc;
142        MIXERLINE ml;
143        MMRESULT  mmr;
144        UINT      i;
145
146        /* Determine number of controls */
147        ml.cbStruct = sizeof(ml);
148        ml.dwLineID = dwLineID;
149
150        mmr = mixerGetLineInfo((HMIXEROBJ)hMix, &ml, MIXER_GETLINEINFOF_LINEID | MIXER_OBJECTF_HMIXER);
151        if (mmr != MMSYSERR_NOERROR) {
152                debug_msg(mixGetErrorText(mmr));
153                return;
154        }
155
156        pmc = (LPMIXERCONTROL)xmalloc(sizeof(MIXERCONTROL)*ml.cControls);
157        mlc.cbStruct  = sizeof(MIXERLINECONTROLS);
158        mlc.cbmxctrl  = sizeof(MIXERCONTROL);
159        mlc.pamxctrl  = pmc;
160        mlc.cControls = ml.cControls;
161        mlc.dwLineID  = dwLineID;
162
163        mmr = mixerGetLineControls((HMIXEROBJ)hMix, &mlc, MIXER_GETLINECONTROLSF_ALL | MIXER_OBJECTF_HMIXER);
164        if (mmr != MMSYSERR_NOERROR) {
165                debug_msg(mixGetErrorText(mmr));
166                xfree(pmc);
167                return;
168        }
169
170        for(i = 0; i < ml.cControls; i++) {
171                debug_msg("- %u %s\t\t %s\n", i, pmc[i].szName, mixGetControlType(pmc[i].dwControlType));
172        }
173        xfree(pmc);
174}
175
176/* Code for saving control states when claiming device, so we can restore the
177 * config when we release the device.  Lots of request for this
178 */
179
180int
181mcd_elem_add_control(mcd_elem_t **pplist, MIXERCONTROLDETAILS *pmcd)
182{
183        mcd_elem_t *elem;
184
185        elem = (mcd_elem_t*)xmalloc(sizeof(mcd_elem_t));
186        if (elem) {
187                elem->pmcd = pmcd;
188                elem->next = *pplist;
189                *pplist    = elem;
190                return TRUE;
191        }
192        return FALSE;
193}
194
195MIXERCONTROLDETAILS*
196mcd_elem_get_control(mcd_elem_t **pplist)
197{
198        MIXERCONTROLDETAILS *pmcd;
199        mcd_elem_t *elem;
200
201        elem = *pplist;
202        if (elem) {
203                pmcd    = elem->pmcd;
204                *pplist = elem->next;
205                xfree(elem);
206                return pmcd;
207        }
208        return NULL;
209}
210
211void
212mixRestoreControls(UINT uMix, mcd_elem_t **pplist)
213{
214        MIXERCONTROLDETAILS *pmcd;
215        MMRESULT mmr;
216
217        return;
218
219        while((pmcd = mcd_elem_get_control(pplist)) != NULL) {
220                mmr = mixerSetControlDetails((HMIXEROBJ)uMix, pmcd, MIXER_OBJECTF_MIXER);
221                xfree(pmcd->paDetails);
222                xfree(pmcd);
223                if (mmr != MMSYSERR_NOERROR) {
224                        debug_msg("mixerSetControlDetails: %s\n", mixGetErrorText(mmr));
225                        continue;
226                }
227        }
228        assert(*pplist == NULL);
229}
230
231void
232mixSaveLine(UINT uMix, MIXERLINE *pml, mcd_elem_t **pplist)
233{
234        MIXERCONTROLDETAILS *pmcd;
235        MIXERLINECONTROLS mlc;
236        MIXERCONTROL     *pmc;
237        MMRESULT          mmr;
238        UINT              i;
239
240        /* Retrieve control types */
241        pmc = (MIXERCONTROL*)xmalloc(sizeof(MIXERCONTROL)*pml->cControls);
242
243        mlc.cbStruct  = sizeof(mlc);
244        mlc.dwLineID  = pml->dwLineID;
245        mlc.cControls = pml->cControls;
246        mlc.pamxctrl  = pmc;
247        mlc.cbmxctrl  = sizeof(MIXERCONTROL);
248
249        debug_msg("Saving %s\n", pml->szName);
250
251        mmr = mixerGetLineControls((HMIXEROBJ)uMix, &mlc, MIXER_GETLINECONTROLSF_ALL | MIXER_OBJECTF_MIXER);
252        if (mmr != MMSYSERR_NOERROR) {
253                debug_msg("mixerGetLineControls: %s\n", mixGetErrorText(mmr));
254                xfree(pmc);
255                return;
256        }
257
258        for(i = 0; i < pml->cControls; i++) {
259                DWORD itemCnt, itemSz;
260                if (pmc[i].cMultipleItems == 0) {
261                        itemCnt = 1;
262                } else {
263                        itemCnt = pmc[i].cMultipleItems;
264                }
265
266                switch(pmc[i].dwControlType & MIXERCONTROL_CT_UNITS_MASK) {
267                        /* Our application on affects boolean types (mute, on/off) and unsigned (vol) */
268                case MIXERCONTROL_CT_UNITS_BOOLEAN:
269                        itemSz = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
270                        break;
271                case MIXERCONTROL_CT_UNITS_UNSIGNED:
272                        itemSz = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
273                        break;
274                default:
275                        debug_msg("not done %s\n", pmc[i].szName);
276                        continue;
277                }
278                pmcd = (MIXERCONTROLDETAILS*)xmalloc(sizeof(MIXERCONTROLDETAILS));
279                pmcd->cbStruct       = sizeof(MIXERCONTROLDETAILS);
280                pmcd->cMultipleItems = pmc[i].cMultipleItems;
281                pmcd->dwControlID    = pmc[i].dwControlID;
282                pmcd->cChannels      = 1;
283                pmcd->paDetails      = (void*)xmalloc(itemSz * itemCnt);
284                pmcd->cbDetails      = itemSz;
285
286                mmr = mixerGetControlDetails((HMIXEROBJ)uMix, pmcd, MIXER_GETCONTROLDETAILSF_VALUE | MIXER_OBJECTF_MIXER);
287                if (mmr != MMSYSERR_NOERROR) {
288                        debug_msg("mixerGetControlDetails: %s\n", mixGetErrorText(mmr));
289                        continue;
290                }
291                mcd_elem_add_control(pplist, pmcd);
292        }
293        xfree(pmc);
294}
295
296
297void
298mixSaveControls(UINT uMix, mcd_elem_t **pplist)
299{
300        MIXERLINE ml, sml;
301        MIXERCAPS mc;
302        MMRESULT  mmr;
303        UINT i,j;
304
305        mmr = mixerGetDevCaps(uMix, &mc, sizeof(mc));
306        if (mmr != MMSYSERR_NOERROR) {
307                debug_msg("mixerGetDevCaps: %s\n", mixGetErrorText(mmr));
308                return;
309        }
310
311        for(i = 0; i < mc.cDestinations; i++) {
312                memset(&ml, 0, sizeof(ml));
313                ml.cbStruct      = sizeof(ml);
314                ml.dwDestination = i;
315                mmr = mixerGetLineInfo((HMIXEROBJ)uMix, &ml, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_DESTINATION);
316                if (mmr != MMSYSERR_NOERROR) {
317                        debug_msg("mixerGetLineInfo: %s\n", mixGetErrorText(mmr));
318                        continue;
319                }
320                mixSaveLine(uMix, &ml, pplist);
321                for (j = 0; j < ml.cConnections; j++) {
322                        memset(&sml, 0, sizeof(sml));
323                        sml.cbStruct = sizeof(sml);
324                        sml.dwSource = j;
325                        mmr = mixerGetLineInfo((HMIXEROBJ)uMix, &sml, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_SOURCE);
326                        if (mmr != MMSYSERR_NOERROR) {
327                                debug_msg("mixerGetLineInfo: %s\n", mixGetErrorText(mmr));
328                                continue;
329                        }
330                        mixSaveLine(uMix, &sml, pplist);
331                }
332        }
333}
334
335/* CODE FOR CONTROLLING INPUT AND OUTPUT (LOOPBACK) LINES *******************
336 * NOTE: the control of input lines and output lines is slightly different
337 * because most card manufacturers put the volume and mute controls for output
338 * as controls on the same output line.  The selection of the input lines is
339 * controlled on the MUX control actually on the recording source, and the
340 * volume control is on a line for the the input port.  To match the input
341 * select and the volume control we use the name of the line the volume
342 * control is assigned to, and this ties in with the names on the MUX.  This
343 * seems to be the only sensible way to correlate the two and it isn't in
344 * the msdn library documentation.  I wasted a fair amount of time, trying
345 * to match the name of the volume control and names in the MUX list, and
346 * got this to work for all but one card.
347 */
348
349/* mixGetInputInfo - attempt to find corresponding wavein index
350* for mixer uMix and corresponding destination line of mixer.
351* Returns TRUE if successful.
352*/
353
354int mixGetInputInfo(UINT uMix, UINT *puWavIn, DWORD *pdwLineID)
355{
356        UINT i, nWavIn;
357        MIXERLINE  ml;
358        MMRESULT   mmr;
359        WAVEINCAPS wic;
360        MIXERCAPS  mc;
361
362        mmr = mixerGetDevCaps(uMix, &mc, sizeof(mc));
363        if (mmr != MMSYSERR_NOERROR) {
364                debug_msg("mixerGetDevCaps: %s\n", mixGetErrorText(mmr));
365                return FALSE;
366        }
367
368        nWavIn = waveInGetNumDevs();
369        for(i = 0; i < nWavIn; i++) {
370                mmr = waveInGetDevCaps(i, &wic, sizeof(wic));
371                if (mmr != MMSYSERR_NOERROR) {
372                        debug_msg("waveInGetDevCaps: %s\n", mixGetErrorText(mmr));
373                        continue;
374                }
375
376                ml.cbStruct       = sizeof(ml);
377                ml.Target.dwType  = MIXERLINE_TARGETTYPE_WAVEIN;
378                strncpy(ml.Target.szPname, wic.szPname, MAXPNAMELEN);
379                ml.Target.vDriverVersion = wic.vDriverVersion;
380                ml.Target.wMid    = wic.wMid;
381                ml.Target.wPid    = wic.wPid;
382
383                mmr = mixerGetLineInfo((HMIXEROBJ)uMix, &ml, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_TARGETTYPE);
384                if (mmr == MMSYSERR_NOERROR) {
385                        *puWavIn          = i;
386                        *pdwLineID = ml.dwLineID;
387                        debug_msg("Input: %s(%d - %d)\n", ml.szName, ml.dwDestination, ml.dwLineID);
388                        return TRUE;
389                } else {
390                        debug_msg("mixerGetLineInfo (ignore this error): %s\n", mixGetErrorText(mmr));
391                }
392        }
393        return FALSE;
394}
395
396/* mixGetOutputInfo - attempt to find corresponding waveout index
397 * and corresponding destination line of mixer.  Returns TRUE if
398 * successful.
399 */
400int
401mixGetOutputInfo(UINT uMix, UINT *puWavOut, DWORD *pdwLineID)
402{
403        UINT i, nWavOut;
404        MIXERLINE  ml;
405        MMRESULT   mmr;
406        WAVEOUTCAPS woc;
407        MIXERCAPS  mc;
408
409        mmr = mixerGetDevCaps(uMix, &mc, sizeof(mc));
410        if (mmr != MMSYSERR_NOERROR) {
411                debug_msg("mixerGetDevCaps: %s\n", mixGetErrorText(mmr));
412                return FALSE;
413        }
414
415        nWavOut = waveOutGetNumDevs();
416        for(i = 0; i < nWavOut; i++) {
417                mmr = waveOutGetDevCaps(i, &woc, sizeof(woc));
418                if (mmr != MMSYSERR_NOERROR) {
419                        debug_msg("waveOutGetDevCaps: %s\n", mixGetErrorText(mmr));
420                        continue;
421                }
422                ml.cbStruct       = sizeof(ml);
423                ml.Target.dwType  = MIXERLINE_TARGETTYPE_WAVEOUT;
424                strncpy(ml.Target.szPname, woc.szPname, MAXPNAMELEN);
425                ml.Target.vDriverVersion = woc.vDriverVersion;
426                ml.Target.wMid    = woc.wMid;
427                ml.Target.wPid    = woc.wPid;
428
429                mmr = mixerGetLineInfo((HMIXEROBJ)uMix, &ml, MIXER_OBJECTF_MIXER | MIXER_GETLINEINFOF_TARGETTYPE);
430                if (mmr == MMSYSERR_NOERROR) {
431                        *puWavOut  = i;
432                        *pdwLineID = ml.dwLineID;
433                        debug_msg("Output: %s(%d - %d)\n", ml.szName, ml.dwDestination, ml.dwLineID);
434                        return TRUE;
435                }
436        }
437        return FALSE;
438}
439
440/* mixerEnableInputLine - enables the input line whose name starts with beginning of portname.
441 * We cannot just use the port index like we do for volume because the mute controls are
442 * not necessarily in the same order as the volume controls (grrr!).  The only card
443 * that we have seen where this is necessary is the Winnov Videum AV, but there are
444 * bound to be others.
445 * Muting for input lines on the toplevel control (Rec, or whatever driver happens to call it).
446 * It usually has a single control a MUX/Mixer that has "multiple items", one mute for
447 * each input line.  Depending on the control type it may be legal to have multiple input
448 * lines enabled, or just one.  So mixerEnableInputLine disables all lines other than
449 * one selected.
450 */
451
452static int
453mixerEnableInputLine(HMIXEROBJ hMix, char *portname)
454{
455        MIXERCONTROLDETAILS_BOOLEAN *mcdbState;
456        MIXERCONTROLDETAILS_LISTTEXT *mcdlText;
457        MIXERCONTROLDETAILS mcd;
458        MIXERLINECONTROLS mlc;
459        MIXERCONTROL mc;
460        MIXERLINE ml;
461        MMRESULT  mmr;
462        UINT      i, matchLine;
463
464        ml.cbStruct = sizeof(ml);
465        ml.dwLineID = dwRecLineID;
466
467        mmr = mixerGetLineInfo(hMix, &ml, MIXER_GETLINEINFOF_LINEID|MIXER_OBJECTF_HMIXER);
468        if (mmr != MMSYSERR_NOERROR) {
469                debug_msg("mixerGetLineInfo: %s\n", mixGetErrorText(mmr));
470        }
471
472        /* Get Mixer/MUX control information (need control id to set and get control details) */
473        mlc.cbStruct      = sizeof(mlc);
474        mlc.dwLineID      = ml.dwLineID;
475        mlc.pamxctrl      = &mc;
476        mlc.cbmxctrl      = sizeof(mc);
477
478        mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; /* Single Select */
479        mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_HMIXER);
480        if (mmr != MMSYSERR_NOERROR) {
481                mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; /* Multiple Select */
482                mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_HMIXER);
483                if (mmr != MMSYSERR_NOERROR) {
484                        debug_msg("mixerGetLineControls: %s\n", mixGetErrorText(mmr));
485                        return FALSE;
486                }
487        }
488
489        mcd.cbStruct    = sizeof(mcd);
490        mcd.dwControlID = mc.dwControlID;
491        mcd.cChannels   = 1;
492        mcd.cMultipleItems = mc.cMultipleItems;
493        mcdlText = (MIXERCONTROLDETAILS_LISTTEXT*)xmalloc(sizeof(MIXERCONTROLDETAILS_LISTTEXT)*mc.cMultipleItems);
494        mcd.paDetails = mcdlText;
495        mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT);
496        mmr = mixerGetControlDetails(hMix, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT | MIXER_OBJECTF_MIXER);
497
498        matchLine = 0;
499        for(i = 0; i < mcd.cMultipleItems; i++) {
500                if (!strcmp(mcdlText[i].szName, portname)) {
501                        matchLine = i;
502                        break;
503                }
504        }
505        xfree(mcdlText);
506
507        /* Now get control itself */
508        mcd.cbStruct    = sizeof(mcd);
509        mcd.dwControlID = mc.dwControlID;
510        mcd.cChannels   = 1;
511        mcd.cMultipleItems = mc.cMultipleItems;
512        mcdbState = (MIXERCONTROLDETAILS_BOOLEAN*)xmalloc(sizeof(MIXERCONTROLDETAILS_BOOLEAN)*mc.cMultipleItems);
513        mcd.paDetails = mcdbState;
514        mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
515
516        mmr = mixerGetControlDetails(hMix, &mcd, MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_MIXER);
517        if (mmr != MMSYSERR_NOERROR) {
518                debug_msg("mixerGetControlDetails: %s\n", mixGetErrorText(mmr));
519                xfree(mcdbState);
520                return FALSE;
521        }
522
523        for(i = 0; i < mcd.cMultipleItems; i++) {
524                if (i == matchLine) {
525                        mcdbState[i].fValue = TRUE;
526                } else {
527                        mcdbState[i].fValue = FALSE;
528                }
529        }
530
531        mmr = mixerSetControlDetails(hMix, &mcd, MIXER_OBJECTF_MIXER);
532        if (mmr != MMSYSERR_NOERROR) {
533                debug_msg("mixerSetControlDetails: %s\n", mixGetErrorText(mmr));
534                xfree(mcdbState);
535                return FALSE;
536        }
537
538        xfree(mcdbState);
539        return TRUE;
540}
541
542static int
543mixerEnableOutputLine(HMIXEROBJ hMix, DWORD dwLineID, int state)
544{
545        MIXERCONTROLDETAILS_BOOLEAN mcdbState;
546        MIXERCONTROLDETAILS mcd;
547        MIXERLINECONTROLS mlc;
548        MIXERCONTROL      mc;
549        MMRESULT          mmr;
550
551        mlc.cbStruct      = sizeof(mlc);
552        mlc.pamxctrl      = &mc;
553        mlc.cbmxctrl      = sizeof(MIXERCONTROL);
554        mlc.dwLineID      = dwLineID;
555        mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
556
557        mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
558        if (mmr != MMSYSERR_NOERROR) {
559                mlc.cbStruct      = sizeof(mlc);
560                mlc.pamxctrl      = &mc;
561                mlc.cbmxctrl      = sizeof(MIXERCONTROL);
562                mlc.dwLineID      = dwLineID;
563                mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_ONOFF;
564                mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
565                if (mmr != MMSYSERR_NOERROR) {
566                        debug_msg("Could not get mute control for line 0x%08x: %s\n",
567                                dwLineID,
568                                mixGetErrorText(mmr));
569                        mixerDumpLineInfo(hMix, dwLineID);
570                        return FALSE;
571                }
572        }
573
574        mcd.cbStruct       = sizeof(mcd);
575        mcd.dwControlID    = mc.dwControlID;
576        mcd.cChannels      = 1;
577        mcd.cMultipleItems = mc.cMultipleItems;
578        mcd.cbDetails      = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
579        mcd.paDetails      = &mcdbState;
580        mcdbState.fValue   = !((UINT)state);
581
582        mmr = mixerSetControlDetails((HMIXEROBJ)hMix, &mcd, MIXER_OBJECTF_HMIXER);
583        if (mmr != MMSYSERR_NOERROR) {
584                debug_msg("Could not set mute state for line 0x%08x\n", dwLineID);
585                return FALSE;
586        }
587        return TRUE;
588}
589
590/* MixerSetLineGain - sets gain of line (range 0-MIX_MAX_GAIN) */
591static int
592mixerSetLineGain(HMIXEROBJ hMix, DWORD dwLineID, int gain)
593{
594        MIXERCONTROLDETAILS_UNSIGNED mcduGain;
595        MIXERCONTROLDETAILS mcd;
596        MIXERLINECONTROLS mlc;
597        MIXERCONTROL      mc;
598        MMRESULT          mmr;
599
600        mlc.cbStruct      = sizeof(mlc);
601        mlc.pamxctrl      = &mc;
602        mlc.cbmxctrl      = sizeof(MIXERCONTROL);
603        mlc.dwLineID      = dwLineID;
604        mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
605
606        mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
607        if (mmr != MMSYSERR_NOERROR) {
608                debug_msg("Could not volume control for line 0x%08x: %s\n",
609                        dwLineID,
610                        mixGetErrorText(mmr));
611                return FALSE;
612        }
613
614        mcd.cbStruct       = sizeof(mcd);
615        mcd.dwControlID    = mc.dwControlID;
616        mcd.cChannels      = 1;
617        mcd.cMultipleItems = mc.cMultipleItems;
618        mcd.cbDetails      = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
619        mcd.paDetails      = &mcduGain;
620        mcduGain.dwValue   = ((mc.Bounds.dwMaximum - mc.Bounds.dwMinimum) * gain)/MIX_MAX_GAIN;
621
622        mmr = mixerSetControlDetails((HMIXEROBJ)hMix, &mcd, MIXER_OBJECTF_HMIXER);
623        if (mmr != MMSYSERR_NOERROR) {
624                debug_msg("Could not set gain for line 0x%08x: %s\n", dwLineID, mixGetErrorText(mmr));
625                return FALSE;
626        }
627        return TRUE;
628}
629
630/* MixerGetLineGain - returns gain of line (range 0-MIX_MAX_GAIN) */
631static int
632mixerGetLineGain(HMIXEROBJ hMix, DWORD dwLineID)
633{
634        MIXERCONTROLDETAILS_UNSIGNED mcduGain;
635        MIXERCONTROLDETAILS mcd;
636        MIXERLINECONTROLS mlc;
637        MIXERCONTROL      mc;
638        MMRESULT          mmr;
639
640        mlc.cbStruct      = sizeof(mlc);
641        mlc.pamxctrl      = &mc;
642        mlc.cbmxctrl      = sizeof(MIXERCONTROL);
643        mlc.dwLineID      = dwLineID;
644        mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
645
646        mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
647        if (mmr != MMSYSERR_NOERROR) {
648                debug_msg("Could not find volume control for line 0x%08x\n", dwLineID);
649                return 0;
650        }
651
652        mcd.cbStruct       = sizeof(mcd);
653        mcd.dwControlID    = mc.dwControlID;
654        mcd.cChannels      = 1;
655        mcd.cMultipleItems = mc.cMultipleItems;
656        mcd.cbDetails      = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
657        mcd.paDetails      = &mcduGain;
658
659        mmr = mixerGetControlDetails((HMIXEROBJ)hMix, &mcd, MIXER_OBJECTF_HMIXER);
660        if (mmr != MMSYSERR_NOERROR) {
661                debug_msg("Could not get gain for line 0x%08x\n", dwLineID);
662                return 0;
663        }
664        return (int)(mcduGain.dwValue * MIX_MAX_GAIN / (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum));
665}
666
667static int
668mixerGetLineName(HMIXEROBJ hMix, DWORD dwLineID, char *szName, UINT uLen)
669{
670        MIXERLINE           ml;
671        MIXERLINECONTROLS   mlc;
672        MIXERCONTROL        mc;
673        MMRESULT            mmr;
674
675        mlc.cbStruct      = sizeof(mlc);
676        mlc.pamxctrl      = &mc;
677        mlc.cbmxctrl      = sizeof(MIXERCONTROL);
678        mlc.dwLineID      = dwLineID;
679        mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
680/*
681        mmr = mixerGetLineControls(hMix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE | MIXER_OBJECTF_HMIXER);
682        if (mmr != MMSYSERR_NOERROR) {
683                debug_msg("Could not find volume control for line 0x%08x: %s\n", dwLineID, mixGetErrorText(mmr));
684                return FALSE;
685        }
686*/
687        memset(&ml,0, sizeof(MIXERLINE));
688        ml.cbStruct = sizeof(MIXERLINE);
689        ml.dwLineID = dwLineID;
690        mmr = mixerGetLineInfo(hMix, &ml, MIXER_GETLINEINFOF_LINEID);
691        if (mmr != MMSYSERR_NOERROR) {
692                debug_msg("poo");
693        }
694        debug_msg("line name %s\n", ml.szName);
695        strncpy(szName, ml.szName, uLen);
696        return TRUE;
697}
698
699/* MixQueryControls: Get all line names and id's, fill into ppapd, and return number of lines */
700static int
701mixQueryControls(HMIXEROBJ hMix, DWORD dwLineID, audio_port_details_t** ppapd)
702{
703        MIXERCAPS mc;
704        MIXERLINE mlt, mlc;
705        audio_port_details_t *papd;
706        MMRESULT mmr;
707        UINT     i;
708
709        /* Videum bug work around - Videum does not fill in number of connections if
710         * called using MIXER_GETLINEINFOF_LINEID.  This is the only driver with this problem,
711         * but this seems to work in all cases.
712         */
713        mixerGetDevCaps((UINT)hMix, &mc, sizeof(mc));
714        for(i = 0; i < mc.cDestinations; i++) {
715                memset(&mlt, 0, sizeof(mlt));
716                mlt.cbStruct = sizeof(mlt);
717                mlt.dwDestination = i;
718                mmr = mixerGetLineInfo(hMix, &mlt, MIXER_GETLINEINFOF_DESTINATION);
719                if (mmr != MMSYSERR_NOERROR) {
720                        debug_msg("mixerGetLineInfo: %s\n", mixGetErrorText(mmr));
721                        continue;
722                }
723                if (mlt.dwLineID == dwLineID) {
724                        break;
725                }
726        }
727
728        papd = (audio_port_details_t*)xmalloc(sizeof(audio_port_details_t)*mlt.cConnections);
729        if (papd == NULL) {
730                return 0;
731        }
732
733        mixerDumpLineInfo((HMIXEROBJ)hMix, mlt.dwLineID);
734
735        for(i = 0; i < mlt.cConnections; i++) {
736                memcpy(&mlc, &mlt, sizeof(mlc));
737                mlc.dwSource = i;
738                mmr = mixerGetLineInfo((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINEINFOF_SOURCE|MIXER_OBJECTF_HMIXER);
739                if (mmr != MMSYSERR_NOERROR) {
740                        xfree(papd);
741                        return 0;
742                }
743                strncpy(papd[i].name, mlc.szName, AUDIO_PORT_NAME_LENGTH);
744                papd[i].port = mlc.dwLineID;
745        }
746
747        *ppapd = papd;
748        return (int)mlt.cConnections;
749}
750
751/* XXX make_microphone_first_port is a hack to make microphone
752 * the first (default) port.  Of course this only works for
753 * english language drivers...
754 */
755
756static int
757make_microphone_first_port(audio_port_details_t *ports, int n_ports)
758{
759        audio_port_details_t tmp;
760        int i;
761
762        for(i = 1; i < n_ports; i++) {
763                if (!strncasecmp("mic", ports[i].name, 3) ||
764                    !strncasecmp("mik", ports[i].name, 3)) {
765                        memcpy(&tmp, ports + i, sizeof(tmp));
766                        memcpy(ports + i , ports, sizeof(ports[0]));
767                        memcpy(ports, &tmp, sizeof(ports[0]));
768                        return TRUE;
769                }
770        }
771
772        return FALSE;
773}
774
775static int
776mixSetup(UINT uMixer)
777{
778        MIXERCAPS mc;
779        MMRESULT  res;
780
781        if (hMixer)  {mixerClose(hMixer);  hMixer  = 0;}
782
783        res = mixerOpen(&hMixer, uMixer, (unsigned long)NULL, (unsigned long)NULL, MIXER_OBJECTF_MIXER);
784        if (res != MMSYSERR_NOERROR) {
785                debug_msg("mixerOpen failed: %s\n", mixGetErrorText(res));
786                return FALSE;
787        }
788
789        res = mixerGetDevCaps((UINT)hMixer, &mc, sizeof(mc));
790        if (res != MMSYSERR_NOERROR) {
791                debug_msg("mixerGetDevCaps failed: %s\n", mixGetErrorText(res));
792                return FALSE;
793        }
794
795        if (mc.cDestinations < 2) {
796                debug_msg("mixer does not have 2 destinations?\n");
797                return FALSE;
798        }
799
800        if (input_ports != NULL) {
801                xfree(input_ports);
802                input_ports   = NULL;
803                n_input_ports = 0;
804        }
805
806        n_input_ports = mixQueryControls((HMIXEROBJ)hMixer, dwRecLineID, &input_ports);
807        debug_msg("Input ports %d\n", n_input_ports);
808        if (n_input_ports == 0) {
809                return FALSE;
810        }
811
812        make_microphone_first_port(input_ports, n_input_ports);
813
814        if (loop_ports != NULL) {
815                xfree(loop_ports);
816                loop_ports   = NULL;
817                n_loop_ports = 0;
818        }
819
820        n_loop_ports = mixQueryControls((HMIXEROBJ)hMixer, dwVolLineID, &loop_ports);
821        debug_msg("Loop ports %d\n", n_loop_ports);
822        if (n_loop_ports == 0) {
823                return 0;
824        }
825        return TRUE;
826}
827
828/* Global variables used by read and write processing                                    */
829static int blksz;
830static int nblks;
831static int smplsz;
832
833/* AUDIO OUTPUT RELATED FN's                                                             */
834static HWAVEOUT shWaveOut;         /* Handle for wave output                             */
835static WAVEHDR *whWriteHdrs;       /* Pointer to blovk of wavehdr's alloced for writing  */
836static u_char  *lpWriteData;       /* Pointer to raw audio data buffer                   */
837
838static int
839w32sdk_audio_open_out_probe(UINT uId, WAVEFORMATEX *pwfx, int probe)
840{
841        MMRESULT        mmr;
842        int             i;
843
844        if (shWaveOut) {
845                return (TRUE);
846        }
847
848        mmr = waveOutOpen(&shWaveOut, uId, pwfx, 0, 0, CALLBACK_NULL);
849        if (mmr != MMSYSERR_NOERROR) {
850                waveOutGetErrorText(mmr, errorText, sizeof(errorText));
851                debug_msg("waveOutOpen: (%d) %s\n", mmr, errorText);
852                return (FALSE);
853        }
854
855        if (lpWriteData != NULL) {
856            xfree(lpWriteData);
857        }
858        lpWriteData = (u_char*)xmalloc(nblks * blksz);
859        memset(lpWriteData, 0, nblks * blksz);
860
861        if (whWriteHdrs != NULL) {
862            xfree(whWriteHdrs);
863        }
864        whWriteHdrs = (WAVEHDR*)xmalloc(sizeof(WAVEHDR)*nblks);
865        memset(whWriteHdrs, 0, sizeof(WAVEHDR)*nblks);
866
867        if (!probe) {
868          for (i = 0; i < nblks; i++) {
869                whWriteHdrs[i].dwFlags        = 0;
870                whWriteHdrs[i].dwBufferLength = blksz;
871                whWriteHdrs[i].lpData         = lpWriteData + i * blksz;
872                whWriteHdrs[i].dwUser         = i; /* For debugging purposes */
873                mmr = waveOutPrepareHeader(shWaveOut, &whWriteHdrs[i], sizeof(WAVEHDR));
874                whWriteHdrs[i].dwFlags |= WHDR_DONE; /* Mark buffer as done - used to find free buffers */
875                assert(mmr == MMSYSERR_NOERROR);
876          }
877        }
878
879        return (TRUE);
880}
881static int
882w32sdk_audio_open_out(UINT uId, WAVEFORMATEX *pwfx)
883{
884  return w32sdk_audio_open_out_probe(uId, pwfx, 0);
885}
886static void
887w32sdk_audio_close_out()
888{
889        int i;
890
891        if (shWaveOut == 0) {
892                return;
893        }
894
895        waveOutReset(shWaveOut);
896
897        for (i = 0; i < nblks; i++) {
898                if (whWriteHdrs[i].dwFlags & WHDR_PREPARED) {
899                        waveOutUnprepareHeader(shWaveOut, &whWriteHdrs[i], sizeof(WAVEHDR));
900                }
901        }
902
903        waveOutClose(shWaveOut);
904
905        xfree(whWriteHdrs); whWriteHdrs = NULL;
906        xfree(lpWriteData); lpWriteData  = NULL;
907
908        xmemchk();
909        shWaveOut = 0;
910}
911
912
913#define WRITE_ERROR_STILL_PLAYING 33
914
915const char *waveOutError(MMRESULT mmr)
916{
917        switch (mmr){
918                CASE_STRING(MMSYSERR_NOERROR);
919                CASE_STRING(MMSYSERR_INVALHANDLE);
920                CASE_STRING(MMSYSERR_NODRIVER);
921                CASE_STRING(MMSYSERR_NOMEM);
922                CASE_STRING(WAVERR_UNPREPARED);
923                CASE_STRING(WRITE_ERROR_STILL_PLAYING);
924        default:
925                return "Unknown";
926        }
927}
928
929WAVEHDR *
930w32sdk_audio_write_get_buffer()
931{
932        int      i;
933
934        for (i = 0; i < nblks; i++) {
935                assert(whWriteHdrs[i].dwFlags & WHDR_PREPARED);
936                if (whWriteHdrs[i].dwFlags & WHDR_DONE) {
937                        whWriteHdrs[i].dwFlags &= WHDR_PREPARED;
938                        return &whWriteHdrs[i];
939                }
940        }
941        return NULL;
942}
943
944int
945w32sdk_audio_write(audio_desc_t ad, u_char *buf , int buf_bytes)
946{
947        WAVEHDR   *whCur;
948        MMRESULT   mmr;
949        int        done, this_write;
950
951        /* THis is slightly ugly because we handle writes of any size, not just */
952        /* multiples of blksz. RAT likes to write multiples of the cushion step */
953        /* size which is usually 1/2 device blksz.                              */
954
955        done = 0;
956        while(done < buf_bytes) {
957                whCur = w32sdk_audio_write_get_buffer();
958                if (whCur == NULL) {
959                        debug_msg("Write/Right out of buffers ???\n");
960                        break;
961                }
962                this_write = min(buf_bytes - done, (int)blksz);
963                whCur->dwBufferLength = this_write;
964                memcpy(whCur->lpData,
965                        buf + done,
966                        this_write);
967                done  += this_write;
968                mmr    = waveOutWrite(shWaveOut, whCur, sizeof(WAVEHDR));
969                if (mmr == WRITE_ERROR_STILL_PLAYING) {
970                        debug_msg("Device filled\n");
971                        break;
972                }
973                assert(mmr == MMSYSERR_NOERROR);
974        }
975
976        assert(done <= buf_bytes);
977
978        return done;
979}
980
981/* AUDIO INPUT RELATED FN's *********************************/
982
983static HWAVEIN  shWaveIn;               /* Handle for wave input                                */
984static WAVEHDR  *whReadHdrs;            /* Pointer to block of wavehdr's allocated for reading  */
985static u_char   *lpReadData;            /* Pointer to raw audio data buffer                     */
986static WAVEHDR  *whReadList;            /* List of wave headers that have been read but not     */
987                                        /* given to the application.                            */
988static DWORD     dwBytesUsedAtReadHead; /* Number of bytes that have already been read at head  */
989static HANDLE    hAudioReady;           /* Audio Ready Event */
990
991int
992w32sdk_audio_is_ready(audio_desc_t ad)
993{
994        UNUSED(ad);
995        return (whReadList != NULL);
996}
997
998static void CALLBACK
999waveInProc(HWAVEIN hwi,
1000           UINT    uMsg,
1001           DWORD   dwInstance,
1002           DWORD   dwParam1,
1003           DWORD   dwParam2)
1004{
1005        WAVEHDR *whRead, **whInsert;
1006
1007        switch(uMsg) {
1008        case WIM_DATA:
1009                whRead = (WAVEHDR*)dwParam1;
1010                /* Insert block at the available list */
1011                whRead->lpNext   = NULL;
1012                whInsert = &whReadList;
1013                while(*whInsert != NULL) {
1014                        whInsert = &((*whInsert)->lpNext);
1015                }
1016                *whInsert = whRead;
1017                SetEvent(hAudioReady);
1018                break;
1019        default:
1020                ;  /* nothing to do currently */
1021        }
1022        UNUSED(dwInstance);
1023        UNUSED(dwParam2);
1024        UNUSED(hwi);
1025
1026        return;
1027}
1028
1029static int
1030w32sdk_audio_open_in_probe(UINT uId, WAVEFORMATEX *pwfx, int probe)
1031{
1032        MMRESULT mmr;
1033        int      i;
1034
1035        if (shWaveIn) {
1036                return (TRUE);
1037        }
1038
1039        if (lpReadData != NULL) {
1040                xfree(lpReadData);
1041        }
1042        lpReadData = (u_char*)xmalloc(nblks * blksz);
1043        memset(lpReadData, 0, nblks * blksz);
1044
1045        if (whReadHdrs != NULL) {
1046                xfree(whReadHdrs);
1047        }
1048        whReadHdrs = (WAVEHDR*)xmalloc(sizeof(WAVEHDR)*nblks);
1049        memset(whReadHdrs, 0, sizeof(WAVEHDR)*nblks);
1050
1051        mmr = waveInOpen(&shWaveIn,
1052                         uId,
1053                         pwfx,
1054                         (DWORD)waveInProc,
1055                         0,
1056                         (probe ? CALLBACK_NULL : CALLBACK_FUNCTION));
1057
1058        if (mmr != MMSYSERR_NOERROR) {
1059                waveInGetErrorText(mmr, errorText, sizeof(errorText));
1060                debug_msg("waveInOpen: (%d) %s\n", mmr, errorText);
1061                return (FALSE);
1062        }
1063
1064        if (!probe) {
1065          /* Initialize wave headers */
1066          for (i = 0; i < nblks; i++) {
1067                whReadHdrs[i].lpData         = lpReadData + i * blksz;
1068                whReadHdrs[i].dwBufferLength = blksz;
1069                whReadHdrs[i].dwFlags        = 0;
1070                mmr = waveInPrepareHeader(shWaveIn, &whReadHdrs[i], sizeof(WAVEHDR));
1071                assert(mmr == MMSYSERR_NOERROR);
1072                mmr = waveInAddBuffer(shWaveIn, &whReadHdrs[i], sizeof(WAVEHDR));
1073                assert(mmr == MMSYSERR_NOERROR);
1074          }
1075
1076          whReadList           = NULL;
1077          dwBytesUsedAtReadHead = 0;
1078
1079          error = waveInStart(shWaveIn);
1080          if (error) {
1081                waveInGetErrorText(error, errorText, sizeof(errorText));
1082                debug_msg("Win32Audio: waveInStart: (%d) %s\n", error, errorText);
1083                exit(1);
1084          }
1085          hAudioReady = CreateEvent(NULL, TRUE, FALSE, "RAT Audio Ready");
1086        }
1087
1088        return (TRUE);
1089}
1090static int
1091w32sdk_audio_open_in(UINT uId, WAVEFORMATEX *pwfx)
1092{
1093  return w32sdk_audio_open_in_probe(uId, pwfx, 0);
1094}
1095
1096static void
1097w32sdk_audio_close_in()
1098{
1099        int             i;
1100
1101        if (shWaveIn == 0)
1102                return;
1103
1104        waveInStop(shWaveIn);
1105        waveInReset(shWaveIn);
1106
1107        for (i = 0; i < nblks; i++) {
1108                if (whReadHdrs[i].dwFlags & WHDR_PREPARED) {
1109                        waveInUnprepareHeader(shWaveIn, &whReadHdrs[i], sizeof(WAVEHDR));
1110                }
1111        }
1112        whReadList = NULL;
1113
1114        waveInClose(shWaveIn);
1115        shWaveIn = 0;
1116
1117        xfree(whReadHdrs);
1118        whReadHdrs = NULL;
1119
1120        xfree(lpReadData);
1121        lpReadData  = NULL;
1122
1123        xmemchk();
1124}
1125
1126int
1127w32sdk_audio_read(audio_desc_t ad, u_char *buf, int buf_bytes)
1128{
1129        WAVEHDR *whCur;
1130        MMRESULT mmr;
1131        int done = 0, this_read;
1132        static int added;
1133
1134        /* This is slightly ugle because we want to be able to operate when     */
1135        /* buf_bytes has any value, not just a multiple of blksz.  In principle */
1136        /* we do this so the device blksz does not have to match application    */
1137        /* blksz.  I.e. can reduce process usage by using larger blocks at      */
1138        /* device whilst the app operates on smaller blocks.                    */
1139
1140        while(whReadList != NULL && done < buf_bytes) {
1141                whCur = whReadList;
1142                this_read = min((int)(whCur->dwBytesRecorded - dwBytesUsedAtReadHead), buf_bytes - done);
1143                if (buf) {
1144                        memcpy(buf + done,
1145                               whCur->lpData + dwBytesUsedAtReadHead,
1146                               this_read);
1147                }
1148                done                  += this_read;
1149                dwBytesUsedAtReadHead += this_read;
1150                if (dwBytesUsedAtReadHead == whCur->dwBytesRecorded) {
1151                        whReadList = whReadList->lpNext;
1152                        /* Finished with the block give it device */
1153                        assert(whCur->dwFlags & WHDR_DONE);
1154                        assert(whCur->dwFlags & ~WHDR_INQUEUE);
1155                        whCur->lpNext          = NULL;
1156                        whCur->dwBytesRecorded = 0;
1157                        whCur->dwFlags        &= ~WHDR_DONE;
1158                        mmr = waveInAddBuffer(shWaveIn, whCur, sizeof(WAVEHDR));
1159                        assert(mmr == MMSYSERR_NOERROR);
1160                        assert(whCur->dwFlags & WHDR_INQUEUE);
1161                        dwBytesUsedAtReadHead = 0;
1162                        added++;
1163                }
1164                assert((int)dwBytesUsedAtReadHead < blksz);
1165        }
1166
1167        assert(done <= buf_bytes);
1168        UNUSED(ad);
1169        return done;
1170}
1171
1172void
1173w32sdk_audio_drain(audio_desc_t ad)
1174{
1175        waveInStop(shWaveIn);
1176        w32sdk_audio_read(ad, NULL, 10000000);
1177        waveInStart(shWaveIn);
1178}
1179
1180static void dumpReadHdrStats()
1181{
1182        WAVEHDR *whp;
1183        int i, done, inqueue, prepared, ready;
1184
1185        done = inqueue = prepared = 0;
1186        for(i = 0; i < nblks; i++) {
1187                if (whReadHdrs[i].dwFlags & WHDR_DONE) {
1188                        done++;
1189                }
1190                if (whReadHdrs[i].dwFlags & WHDR_INQUEUE) {
1191                        inqueue++;
1192                }
1193                if (whReadHdrs[i].dwFlags & WHDR_PREPARED) {
1194                        prepared++;
1195                }
1196        }
1197
1198        ready = 0;
1199        whp = whReadList;
1200        while (whp != NULL) {
1201                ready++;
1202                whp = whp->lpNext;
1203        }
1204        debug_msg("done %d inqueue %d prepared %d ready %d\n",
1205                done, inqueue, prepared, ready);
1206}
1207
1208void
1209w32sdk_audio_wait_for(audio_desc_t ad, int delay_ms)
1210{
1211        if (whReadList == NULL) {
1212                DWORD dwRes;
1213                dwRes = WaitForSingleObject(hAudioReady, delay_ms);
1214                switch(dwRes) {
1215                case WAIT_TIMEOUT:
1216                        debug_msg("No audio (%d ms wait timeout)\n", delay_ms);
1217                        dumpReadHdrStats();
1218                        break;
1219                case WAIT_FAILED:
1220                        debug_msg("Wait failed (error %u)\n", GetLastError());
1221                        break;
1222                }
1223                ResetEvent(hAudioReady);
1224        }
1225        waveInStart(shWaveIn);
1226        UNUSED(ad);
1227}
1228
1229static int audio_dev_open = 0;
1230
1231static int
1232w32sdk_audio_open_mixer_probe(audio_desc_t ad, audio_format *fmt, audio_format *ofmt, int probe)
1233{
1234        static int virgin;
1235        WAVEFORMATEX owfx, wfx;
1236        UINT uWavIn, uWavOut;
1237
1238        if (audio_dev_open) {
1239                debug_msg("Device not closed! Fix immediately");
1240                w32sdk_audio_close(ad);
1241        }
1242
1243        assert(audio_format_match(fmt, ofmt));
1244        if (fmt->encoding != DEV_S16) {
1245                return FALSE; /* Only support L16 for time being */
1246        }
1247
1248        if (mixGetInputInfo(ad, &uWavIn, &dwRecLineID) != TRUE) {
1249                debug_msg("Could not get wave in or mixer destination for mix %u\n", ad);
1250                return FALSE;
1251        }
1252
1253        if (mixGetOutputInfo(ad, &uWavOut, &dwVolLineID) != TRUE) {
1254                debug_msg("Could not get wave out or mixer destination for mix %u\n", ad);
1255                return FALSE;
1256        }
1257
1258        if (mixSetup(ad) == FALSE) {
1259                return FALSE; /* Could not secure mixer */
1260        }
1261
1262        mixSaveControls(ad, &control_list);
1263
1264        wfx.wFormatTag      = WAVE_FORMAT_PCM;
1265        wfx.nChannels       = (WORD)fmt->channels;
1266        wfx.nSamplesPerSec  = fmt->sample_rate;
1267        wfx.wBitsPerSample  = (WORD)fmt->bits_per_sample;
1268        smplsz              = wfx.wBitsPerSample / 8;
1269        wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * smplsz;
1270        wfx.nBlockAlign     = (WORD)(wfx.nChannels * smplsz);
1271        wfx.cbSize          = 0;
1272
1273        memcpy(&owfx, &wfx, sizeof(wfx));
1274
1275        /* Use 1/8 sec device buffer */
1276        blksz  = fmt->bytes_per_block;
1277        nblks  = (wfx.nAvgBytesPerSec / blksz) / 8;
1278
1279        if (w32sdk_audio_open_in_probe(uWavIn, &wfx, probe) == FALSE){
1280                debug_msg("Open input failed\n");
1281                return FALSE;
1282        }
1283
1284        assert(memcmp(&owfx, &wfx, sizeof(WAVEFORMATEX)) == 0);
1285
1286        if (w32sdk_audio_open_out_probe(uWavOut, &wfx, probe) == FALSE) {
1287                debug_msg("Open output failed\n");
1288                w32sdk_audio_close_in();
1289                return FALSE;
1290        }
1291
1292        /* because these get can corrupted... */
1293        assert(memcmp(&owfx, &wfx, sizeof(WAVEFORMATEX)) == 0);
1294
1295        /* Set process priority as high as we can go without special permissions on */
1296        /* on NT.  Although this priority may seem anti-social, it's not that bad   */
1297        /* since we block whilst waiting for audio events.                          */
1298        SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
1299
1300        /* SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);  */
1301
1302        if (!have_probed[ad]) {
1303                have_probed[ad] = w32sdk_probe_formats(ad);
1304        }
1305
1306        audio_dev_open = TRUE;
1307        return TRUE;
1308}
1309static int
1310w32sdk_audio_open_mixer(audio_desc_t ad, audio_format *fmt, audio_format *ofmt)
1311{
1312  return w32sdk_audio_open_mixer_probe(ad, fmt, ofmt, 0);
1313}
1314
1315int
1316w32sdk_audio_open(audio_desc_t ad, audio_format *ifmt, audio_format *ofmt)
1317{
1318        ad = mapAudioDescToMixerID(ad);
1319        return w32sdk_audio_open_mixer(ad, ifmt, ofmt);
1320}
1321
1322static void
1323w32sdk_audio_close_mixer(audio_desc_t ad)
1324{
1325        MMRESULT mmr;
1326
1327        debug_msg("Closing input device.\n");
1328        w32sdk_audio_close_in();
1329
1330        debug_msg("Closing output device.\n");
1331        w32sdk_audio_close_out();
1332
1333        if (input_ports != NULL) {
1334                xfree(input_ports);
1335                input_ports = NULL;
1336        }
1337
1338        if (loop_ports != NULL) {
1339                xfree(loop_ports);
1340                loop_ports = NULL;
1341        }
1342
1343        mixRestoreControls(ad, &control_list);
1344        mmr = mixerClose(hMixer); hMixer = 0;
1345        if (mmr != MMSYSERR_NOERROR) {
1346                debug_msg("mixerClose failed: %s\n", mixGetErrorText(mmr));
1347        }
1348
1349        audio_dev_open = FALSE;
1350        UNUSED(ad);
1351}
1352
1353void
1354w32sdk_audio_close(audio_desc_t ad)
1355{
1356        w32sdk_audio_close_mixer(ad);
1357}
1358
1359int
1360w32sdk_audio_duplex(audio_desc_t ad)
1361{
1362        UNUSED(ad);
1363        return (TRUE);
1364}
1365
1366
1367void
1368w32sdk_audio_non_block(audio_desc_t ad)
1369{
1370        UNUSED(ad);
1371        debug_msg("Windows audio interface is asynchronous!\n");
1372}
1373
1374void
1375w32sdk_audio_block(audio_desc_t ad)
1376{
1377        UNUSED(ad);
1378        debug_msg("Windows audio interface is asynchronous!\n");
1379}
1380
1381void
1382w32sdk_audio_set_ogain(audio_desc_t ad, int level)
1383{
1384        DWORD   vol;
1385
1386        UNUSED(ad);
1387
1388        play_vol = level;
1389
1390        if (shWaveOut == 0)
1391                return;
1392
1393        vol = rat_to_device(level);
1394        error = waveOutSetVolume(shWaveOut, vol);
1395        if (error) {
1396                waveOutGetErrorText(error, errorText, sizeof(errorText));
1397                debug_msg("Win32Audio: waveOutSetVolume: %s\n", errorText);
1398        }
1399}
1400
1401int
1402w32sdk_audio_get_ogain(audio_desc_t ad)
1403{
1404        DWORD   vol;
1405
1406        UNUSED(ad);
1407
1408        if (shWaveOut == 0) {
1409                return play_vol;
1410        }
1411
1412        error = waveOutGetVolume(shWaveOut, &vol);
1413        if (error) {
1414                waveOutGetErrorText(error, errorText, sizeof(errorText));
1415                debug_msg("Win32Audio: waveOutGetVolume Error: %s\n", errorText);
1416                return 0;
1417        } else {
1418                return (device_to_rat(vol));
1419        }
1420}
1421
1422void
1423w32sdk_audio_loopback(audio_desc_t ad, int gain)
1424{
1425        UNUSED(ad);
1426
1427        nLoopGain = gain;
1428}
1429
1430#define WIN32_SPEAKER 0x101010
1431static audio_port_details_t outports[] = {
1432        { WIN32_SPEAKER, AUDIO_PORT_SPEAKER}
1433};
1434#define NUM_OPORTS (sizeof(outports)/sizeof(outports[0]))
1435
1436void
1437w32sdk_audio_oport_set(audio_desc_t ad, audio_port_t port)
1438{
1439        UNUSED(ad);
1440        UNUSED(port);
1441}
1442
1443/* Return selected output port */
1444audio_port_t
1445w32sdk_audio_oport_get(audio_desc_t ad)
1446{
1447        UNUSED(ad);
1448        return (WIN32_SPEAKER);
1449}
1450
1451int
1452w32sdk_audio_oport_count(audio_desc_t ad)
1453{
1454        UNUSED(ad);
1455        return (int)NUM_OPORTS;
1456}
1457
1458const audio_port_details_t*
1459w32sdk_audio_oport_details(audio_desc_t ad, int idx)
1460{
1461        UNUSED(ad);
1462        assert(idx >= 0 && idx < NUM_OPORTS);
1463        return &outports[idx];
1464}
1465
1466void
1467w32sdk_audio_iport_set(audio_desc_t ad, audio_port_t port)
1468{
1469        char portname[MIXER_LONG_NAME_CHARS+1];
1470        int i, j, gain;
1471        UNUSED(ad);
1472
1473        for(i = 0; i < n_input_ports; i++) {
1474                if (input_ports[i].port == port) {
1475                        /* save gain */
1476                        gain = mixerGetLineGain((HMIXEROBJ)hMixer, input_ports[iport].port);
1477                        debug_msg("Gain %d\n", gain);
1478                        if (mixerGetLineName((HMIXEROBJ)hMixer, input_ports[i].port, portname, MIXER_LONG_NAME_CHARS)) {
1479                                mixerEnableInputLine((HMIXEROBJ)hMixer, portname);
1480                        }
1481                        mixerSetLineGain((HMIXEROBJ)hMixer, input_ports[i].port, gain);
1482
1483                        /* Do loopback */
1484                        for(j = 0; j < n_loop_ports && nLoopGain != 0; j++) {
1485                                if (strcmp(loop_ports[j].name, input_ports[i].name) == 0) {
1486                                        mixerEnableOutputLine((HMIXEROBJ)hMixer, loop_ports[j].port, 1);
1487                                        /* mixerSetLineGain((HMIXEROBJ)hMixer, loop_ports[j].port, nLoopGain); */
1488                                }
1489                        }
1490                        iport = i;
1491                        return;
1492                }
1493        }
1494        debug_msg("Port %d not found\n", port);
1495}
1496
1497/* Return selected input port */
1498audio_port_t
1499w32sdk_audio_iport_get(audio_desc_t ad)
1500{
1501        UNUSED(ad);
1502        return input_ports[iport].port;
1503}
1504
1505int
1506w32sdk_audio_iport_count(audio_desc_t ad)
1507{
1508        UNUSED(ad);
1509        return n_input_ports;
1510}
1511
1512const audio_port_details_t*
1513w32sdk_audio_iport_details(audio_desc_t ad, int idx)
1514{
1515        UNUSED(ad);
1516        assert(idx >= 0 && idx < n_input_ports);
1517        return &input_ports[idx];
1518}
1519
1520void
1521w32sdk_audio_set_igain(audio_desc_t ad, int level)
1522{
1523        UNUSED(ad);
1524        assert(iport >= 0 && iport < n_input_ports);
1525        mixerSetLineGain((HMIXEROBJ)hMixer, input_ports[iport].port, level);
1526}
1527
1528int
1529w32sdk_audio_get_igain(audio_desc_t ad)
1530{
1531        UNUSED(ad);
1532        assert(iport >= 0 && iport < n_input_ports);
1533        return mixerGetLineGain((HMIXEROBJ)hMixer, input_ports[iport].port);
1534}
1535
1536/* Probing support */
1537
1538static audio_format af_sup[W32SDK_MAX_DEVICES][10];
1539static int          n_af_sup[W32SDK_MAX_DEVICES];
1540
1541int
1542w32sdk_probe_format(int rate, int channels)
1543{
1544        WAVEFORMATEX wfx;
1545
1546        wfx.cbSize = 0; /* PCM format */
1547        wfx.wFormatTag      = WAVE_FORMAT_PCM;
1548        wfx.wBitsPerSample  = 16; /* 16 bit linear */
1549        wfx.nChannels       = channels;
1550        wfx.nSamplesPerSec  = rate;
1551        wfx.nBlockAlign     = wfx.wBitsPerSample / 8 * wfx.nChannels;
1552        wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
1553
1554        if (waveInOpen(NULL, (UINT)shWaveIn, &wfx, (UINT)NULL, (UINT)NULL, WAVE_FORMAT_QUERY)) {
1555                debug_msg("%d %d supported\n", rate, channels);
1556                return TRUE;
1557        }
1558
1559        debug_msg("%d %d not supported\n", rate, channels);
1560        return FALSE;
1561}
1562
1563int
1564w32sdk_probe_formats(audio_desc_t ad)
1565{
1566        int rate, channels;
1567
1568        for (rate = 8000; rate <= 48000; rate+=8000) {
1569                if (rate == 24000 || rate == 40000) continue;
1570                for(channels = 1; channels <= 2; channels++) {
1571                        if (w32sdk_probe_format(rate, channels)) {
1572                                af_sup[ad][n_af_sup[ad]].sample_rate = rate;
1573                                af_sup[ad][n_af_sup[ad]].channels    = channels;
1574                                n_af_sup[ad]++;
1575                        }
1576                }
1577        }
1578        return (n_af_sup[ad] ? TRUE : FALSE); /* Managed to find at least 1 we support    */
1579                                              /* We have this test since if we cannot get */
1580                                              /* device now (because in use elsewhere)    */
1581                                              /* we will want to test it later            */
1582}
1583
1584int
1585w32sdk_audio_supports(audio_desc_t ad, audio_format *paf)
1586{
1587        int i;
1588        ad = mapAudioDescToMixerID(ad);
1589        for(i = 0; i < n_af_sup[ad]; i++) {
1590                if (af_sup[ad][i].sample_rate  == paf->sample_rate &&
1591                        af_sup[ad][i].channels == paf->channels) {
1592                        return TRUE;
1593                }
1594        }
1595        return FALSE;
1596}
1597
1598static int   nMixersWithFullDuplex = 0;
1599static UINT *mixerIdMap;
1600
1601static UINT
1602mapAudioDescToMixerID(audio_desc_t ad)
1603{
1604        return mixerIdMap[ad];
1605}
1606
1607int
1608w32sdk_audio_init(void)
1609{
1610        audio_format af;
1611        unsigned int i;
1612
1613        mixerIdMap = (UINT*)xmalloc(sizeof(UINT) * mixerGetNumDevs());
1614
1615        af.bits_per_sample = 16;
1616        af.bytes_per_block = 320;
1617        af.channels        = 1;
1618        af.encoding        = DEV_S16;
1619        af.sample_rate     = 8000;
1620
1621        for(i = 0; i < mixerGetNumDevs(); i++) {
1622                if (w32sdk_audio_open_mixer_probe(i, &af, &af, 1)) {
1623                        w32sdk_audio_close_mixer(i);
1624                        mixerIdMap[nMixersWithFullDuplex] = i;
1625                        nMixersWithFullDuplex++;
1626                }
1627        }
1628
1629        return nMixersWithFullDuplex;
1630}
1631
1632int
1633w32sdk_audio_free(void)
1634{
1635        xfree(mixerIdMap);
1636        return TRUE;
1637}
1638
1639int
1640w32sdk_get_device_count(void)
1641{
1642        /* We are only interested in devices with mixers */
1643        return (int)nMixersWithFullDuplex;
1644}
1645
1646static char tmpname[MAXPNAMELEN];
1647
1648char *
1649w32sdk_get_device_name(int idx)
1650{
1651        MIXERCAPS mc;
1652        idx = mapAudioDescToMixerID(idx);
1653        if ((UINT)idx < mixerGetNumDevs()) {
1654                mixerGetDevCaps((UINT)idx, &mc, sizeof(mc));
1655                strncpy(tmpname, mc.szPname, MAXPNAMELEN);
1656                return tmpname;
1657        }
1658        return NULL;
1659}
1660#endif /* WIN32 */
Note: See TracBrowser for help on using the browser.