root/rat/trunk/auddev_win32.c @ 3710

Revision 3710, 52.4 KB (checked in by piers, 8 years ago)

Two commented items:
1) as per AG version of RAT the mesg is commented as we ship out debug version and it can generate too many messages in some instances:
debug_msg("Write/Right out of buffers ???\n");
2) Commented out the following assert in w32sdk_audio_read()
assert(whCur->dwFlags & WHDR_INQUEUE)
Which may be not be necessary given the error status is checked beforehand. This assert triggered on an the ee.ucl AG Node which normally runs ok so commenting it seemed a potential solution - it a slightly tentative fix.

  • 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 = 0; /* Set 0 (from 100) as in AG version */
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                                        /* PO-XXX Commenting out as this may not always be a problem
960                                        as mentioned in AG version. It seems to happen alot in normal operation
961                                        debug_msg("Write/Right out of buffers ???\n");*/
962                                        break;
963                                }
964                                this_write = min(buf_bytes - done, (int)blksz);
965                                whCur->dwBufferLength = this_write;
966                memcpy(whCur->lpData, buf + done, 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                        /* PO-XXX: Commenting out as it appears to be too keen a check in some instances
1161                           e.g. Some drivers aren't perfect.
1162                        assert(whCur->dwFlags & WHDR_INQUEUE);*/
1163                        dwBytesUsedAtReadHead = 0;
1164                        added++;
1165                }
1166                assert((int)dwBytesUsedAtReadHead < blksz);
1167        }
1168
1169        assert(done <= buf_bytes);
1170        UNUSED(ad);
1171        return done;
1172}
1173
1174void
1175w32sdk_audio_drain(audio_desc_t ad)
1176{
1177        waveInStop(shWaveIn);
1178        w32sdk_audio_read(ad, NULL, 10000000);
1179        waveInStart(shWaveIn);
1180}
1181
1182static void dumpReadHdrStats()
1183{
1184        WAVEHDR *whp;
1185        int i, done, inqueue, prepared, ready;
1186
1187        done = inqueue = prepared = 0;
1188        for(i = 0; i < nblks; i++) {
1189                if (whReadHdrs[i].dwFlags & WHDR_DONE) {
1190                        done++;
1191                }
1192                if (whReadHdrs[i].dwFlags & WHDR_INQUEUE) {
1193                        inqueue++;
1194                }
1195                if (whReadHdrs[i].dwFlags & WHDR_PREPARED) {
1196                        prepared++;
1197                }
1198        }
1199
1200        ready = 0;
1201        whp = whReadList;
1202        while (whp != NULL) {
1203                ready++;
1204                whp = whp->lpNext;
1205        }
1206        debug_msg("done %d inqueue %d prepared %d ready %d\n",
1207                done, inqueue, prepared, ready);
1208}
1209
1210void
1211w32sdk_audio_wait_for(audio_desc_t ad, int delay_ms)
1212{
1213        if (whReadList == NULL) {
1214                DWORD dwRes;
1215                dwRes = WaitForSingleObject(hAudioReady, delay_ms);
1216                switch(dwRes) {
1217                case WAIT_TIMEOUT:
1218                        debug_msg("No audio (%d ms wait timeout)\n", delay_ms);
1219                        dumpReadHdrStats();
1220                        break;
1221                case WAIT_FAILED:
1222                        debug_msg("Wait failed (error %u)\n", GetLastError());
1223                        break;
1224                }
1225                ResetEvent(hAudioReady);
1226        }
1227        waveInStart(shWaveIn);
1228        UNUSED(ad);
1229}
1230
1231static int audio_dev_open = 0;
1232
1233static int
1234w32sdk_audio_open_mixer_probe(audio_desc_t ad, audio_format *fmt, audio_format *ofmt, int probe)
1235{
1236        static int virgin;
1237        WAVEFORMATEX owfx, wfx;
1238        UINT uWavIn, uWavOut;
1239
1240        if (audio_dev_open) {
1241                debug_msg("Device not closed! Fix immediately");
1242                w32sdk_audio_close(ad);
1243        }
1244
1245        assert(audio_format_match(fmt, ofmt));
1246        if (fmt->encoding != DEV_S16) {
1247                return FALSE; /* Only support L16 for time being */
1248        }
1249
1250        if (mixGetInputInfo(ad, &uWavIn, &dwRecLineID) != TRUE) {
1251                debug_msg("Could not get wave in or mixer destination for mix %u\n", ad);
1252                return FALSE;
1253        }
1254
1255        if (mixGetOutputInfo(ad, &uWavOut, &dwVolLineID) != TRUE) {
1256                debug_msg("Could not get wave out or mixer destination for mix %u\n", ad);
1257                return FALSE;
1258        }
1259
1260        if (mixSetup(ad) == FALSE) {
1261                return FALSE; /* Could not secure mixer */
1262        }
1263
1264        mixSaveControls(ad, &control_list);
1265
1266        wfx.wFormatTag      = WAVE_FORMAT_PCM;
1267        wfx.nChannels       = (WORD)fmt->channels;
1268        wfx.nSamplesPerSec  = fmt->sample_rate;
1269        wfx.wBitsPerSample  = (WORD)fmt->bits_per_sample;
1270        smplsz              = wfx.wBitsPerSample / 8;
1271        wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * smplsz;
1272        wfx.nBlockAlign     = (WORD)(wfx.nChannels * smplsz);
1273        wfx.cbSize          = 0;
1274
1275        memcpy(&owfx, &wfx, sizeof(wfx));
1276
1277        /* Use 1/8 sec device buffer */
1278        blksz  = fmt->bytes_per_block;
1279        nblks  = (wfx.nAvgBytesPerSec / blksz) / 8;
1280
1281        if (w32sdk_audio_open_in_probe(uWavIn, &wfx, probe) == FALSE){
1282                debug_msg("Open input failed\n");
1283                return FALSE;
1284        }
1285
1286        assert(memcmp(&owfx, &wfx, sizeof(WAVEFORMATEX)) == 0);
1287
1288        if (w32sdk_audio_open_out_probe(uWavOut, &wfx, probe) == FALSE) {
1289                debug_msg("Open output failed\n");
1290                w32sdk_audio_close_in();
1291                return FALSE;
1292        }
1293
1294        /* because these get can corrupted... */
1295        assert(memcmp(&owfx, &wfx, sizeof(WAVEFORMATEX)) == 0);
1296
1297        /* Set process priority as high as we can go without special permissions on */
1298        /* on NT.  Although this priority may seem anti-social, it's not that bad   */
1299        /* since we block whilst waiting for audio events.                          */
1300        SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
1301
1302        /* SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);  */
1303
1304        if (!have_probed[ad]) {
1305                have_probed[ad] = w32sdk_probe_formats(ad);
1306        }
1307
1308        audio_dev_open = TRUE;
1309        return TRUE;
1310}
1311static int
1312w32sdk_audio_open_mixer(audio_desc_t ad, audio_format *fmt, audio_format *ofmt)
1313{
1314  return w32sdk_audio_open_mixer_probe(ad, fmt, ofmt, 0);
1315}
1316
1317int
1318w32sdk_audio_open(audio_desc_t ad, audio_format *ifmt, audio_format *ofmt)
1319{
1320        ad = mapAudioDescToMixerID(ad);
1321        return w32sdk_audio_open_mixer(ad, ifmt, ofmt);
1322}
1323
1324static void
1325w32sdk_audio_close_mixer(audio_desc_t ad)
1326{
1327        MMRESULT mmr;
1328
1329        debug_msg("Closing input device.\n");
1330        w32sdk_audio_close_in();
1331
1332        debug_msg("Closing output device.\n");
1333        w32sdk_audio_close_out();
1334
1335        if (input_ports != NULL) {
1336                xfree(input_ports);
1337                input_ports = NULL;
1338        }
1339
1340        if (loop_ports != NULL) {
1341                xfree(loop_ports);
1342                loop_ports = NULL;
1343        }
1344
1345        mixRestoreControls(ad, &control_list);
1346        mmr = mixerClose(hMixer); hMixer = 0;
1347        if (mmr != MMSYSERR_NOERROR) {
1348                debug_msg("mixerClose failed: %s\n", mixGetErrorText(mmr));
1349        }
1350
1351        audio_dev_open = FALSE;
1352        UNUSED(ad);
1353}
1354
1355void
1356w32sdk_audio_close(audio_desc_t ad)
1357{
1358        w32sdk_audio_close_mixer(ad);
1359}
1360
1361int
1362w32sdk_audio_duplex(audio_desc_t ad)
1363{
1364        UNUSED(ad);
1365        return (TRUE);
1366}
1367
1368
1369void
1370w32sdk_audio_non_block(audio_desc_t ad)
1371{
1372        UNUSED(ad);
1373        debug_msg("Windows audio interface is asynchronous!\n");
1374}
1375
1376void
1377w32sdk_audio_block(audio_desc_t ad)
1378{
1379        UNUSED(ad);
1380        debug_msg("Windows audio interface is asynchronous!\n");
1381}
1382
1383void
1384w32sdk_audio_set_ogain(audio_desc_t ad, int level)
1385{
1386        DWORD   vol;
1387
1388        UNUSED(ad);
1389
1390        play_vol = level;
1391
1392        if (shWaveOut == 0)
1393                return;
1394
1395        vol = rat_to_device(level);
1396        error = waveOutSetVolume(shWaveOut, vol);
1397        if (error) {
1398                waveOutGetErrorText(error, errorText, sizeof(errorText));
1399                debug_msg("Win32Audio: waveOutSetVolume: %s\n", errorText);
1400        }
1401}
1402
1403int
1404w32sdk_audio_get_ogain(audio_desc_t ad)
1405{
1406        DWORD   vol;
1407
1408        UNUSED(ad);
1409
1410        if (shWaveOut == 0) {
1411                return play_vol;
1412        }
1413
1414        error = waveOutGetVolume(shWaveOut, &vol);
1415        if (error) {
1416                waveOutGetErrorText(error, errorText, sizeof(errorText));
1417                debug_msg("Win32Audio: waveOutGetVolume Error: %s\n", errorText);
1418                return 0;
1419        } else {
1420                return (device_to_rat(vol));
1421        }
1422}
1423
1424void
1425w32sdk_audio_loopback(audio_desc_t ad, int gain)
1426{
1427        UNUSED(ad);
1428
1429        nLoopGain = gain;
1430}
1431
1432#define WIN32_SPEAKER 0x101010
1433static audio_port_details_t outports[] = {
1434        { WIN32_SPEAKER, AUDIO_PORT_SPEAKER}
1435};
1436#define NUM_OPORTS (sizeof(outports)/sizeof(outports[0]))
1437
1438void
1439w32sdk_audio_oport_set(audio_desc_t ad, audio_port_t port)
1440{
1441        UNUSED(ad);
1442        UNUSED(port);
1443}
1444
1445/* Return selected output port */
1446audio_port_t
1447w32sdk_audio_oport_get(audio_desc_t ad)
1448{
1449        UNUSED(ad);
1450        return (WIN32_SPEAKER);
1451}
1452
1453int
1454w32sdk_audio_oport_count(audio_desc_t ad)
1455{
1456        UNUSED(ad);
1457        return (int)NUM_OPORTS;
1458}
1459
1460const audio_port_details_t*
1461w32sdk_audio_oport_details(audio_desc_t ad, int idx)
1462{
1463        UNUSED(ad);
1464        assert(idx >= 0 && idx < NUM_OPORTS);
1465        return &outports[idx];
1466}
1467
1468void
1469w32sdk_audio_iport_set(audio_desc_t ad, audio_port_t port)
1470{
1471        char portname[MIXER_LONG_NAME_CHARS+1];
1472        int i, j, gain;
1473        UNUSED(ad);
1474
1475        for(i = 0; i < n_input_ports; i++) {
1476                if (input_ports[i].port == port) {
1477                        /* save gain */
1478                        gain = mixerGetLineGain((HMIXEROBJ)hMixer, input_ports[iport].port);
1479                        debug_msg("Gain %d\n", gain);
1480                        if (mixerGetLineName((HMIXEROBJ)hMixer, input_ports[i].port, portname, MIXER_LONG_NAME_CHARS)) {
1481                                mixerEnableInputLine((HMIXEROBJ)hMixer, portname);
1482                        }
1483                        mixerSetLineGain((HMIXEROBJ)hMixer, input_ports[i].port, gain);
1484
1485                        /* Do loopback */
1486                        for(j = 0; j < n_loop_ports && nLoopGain != 0; j++) {
1487                                if (strcmp(loop_ports[j].name, input_ports[i].name) == 0) {
1488                                        mixerEnableOutputLine((HMIXEROBJ)hMixer, loop_ports[j].port, 1);
1489                                        /* mixerSetLineGain((HMIXEROBJ)hMixer, loop_ports[j].port, nLoopGain); */
1490                                }
1491                        }
1492                        iport = i;
1493                        return;
1494                }
1495        }
1496        debug_msg("Port %d not found\n", port);
1497}
1498
1499/* Return selected input port */
1500audio_port_t
1501w32sdk_audio_iport_get(audio_desc_t ad)
1502{
1503        UNUSED(ad);
1504        return input_ports[iport].port;
1505}
1506
1507int
1508w32sdk_audio_iport_count(audio_desc_t ad)
1509{
1510        UNUSED(ad);
1511        return n_input_ports;
1512}
1513
1514const audio_port_details_t*
1515w32sdk_audio_iport_details(audio_desc_t ad, int idx)
1516{
1517        UNUSED(ad);
1518        assert(idx >= 0 && idx < n_input_ports);
1519        return &input_ports[idx];
1520}
1521
1522void
1523w32sdk_audio_set_igain(audio_desc_t ad, int level)
1524{
1525        UNUSED(ad);
1526        assert(iport >= 0 && iport < n_input_ports);
1527        mixerSetLineGain((HMIXEROBJ)hMixer, input_ports[iport].port, level);
1528}
1529
1530int
1531w32sdk_audio_get_igain(audio_desc_t ad)
1532{
1533        UNUSED(ad);
1534        assert(iport >= 0 && iport < n_input_ports);
1535        return mixerGetLineGain((HMIXEROBJ)hMixer, input_ports[iport].port);
1536}
1537
1538/* Probing support */
1539
1540static audio_format af_sup[W32SDK_MAX_DEVICES][10];
1541static int          n_af_sup[W32SDK_MAX_DEVICES];
1542
1543int
1544w32sdk_probe_format(int rate, int channels)
1545{
1546        WAVEFORMATEX wfx;
1547
1548        wfx.cbSize = 0; /* PCM format */
1549        wfx.wFormatTag      = WAVE_FORMAT_PCM;
1550        wfx.wBitsPerSample  = 16; /* 16 bit linear */
1551        wfx.nChannels       = channels;
1552        wfx.nSamplesPerSec  = rate;
1553        wfx.nBlockAlign     = wfx.wBitsPerSample / 8 * wfx.nChannels;
1554        wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
1555
1556        if (waveInOpen(NULL, (UINT)shWaveIn, &wfx, (UINT)NULL, (UINT)NULL, WAVE_FORMAT_QUERY)) {
1557                debug_msg("%d %d supported\n", rate, channels);
1558                return TRUE;
1559        }
1560
1561        debug_msg("%d %d not supported\n", rate, channels);
1562        return FALSE;
1563}
1564
1565int
1566w32sdk_probe_formats(audio_desc_t ad)
1567{
1568        int rate, channels;
1569
1570        for (rate = 8000; rate <= 48000; rate+=8000) {
1571                if (rate == 24000 || rate == 40000) continue;
1572                for(channels = 1; channels <= 2; channels++) {
1573                        if (w32sdk_probe_format(rate, channels)) {
1574                                af_sup[ad][n_af_sup[ad]].sample_rate = rate;
1575                                af_sup[ad][n_af_sup[ad]].channels    = channels;
1576                                n_af_sup[ad]++;
1577                        }
1578                }
1579        }
1580        return (n_af_sup[ad] ? TRUE : FALSE); /* Managed to find at least 1 we support    */
1581                                              /* We have this test since if we cannot get */
1582                                              /* device now (because in use elsewhere)    */
1583                                              /* we will want to test it later            */
1584}
1585
1586int
1587w32sdk_audio_supports(audio_desc_t ad, audio_format *paf)
1588{
1589        int i;
1590        ad = mapAudioDescToMixerID(ad);
1591        for(i = 0; i < n_af_sup[ad]; i++) {
1592                if (af_sup[ad][i].sample_rate  == paf->sample_rate &&
1593                        af_sup[ad][i].channels == paf->channels) {
1594                        return TRUE;
1595                }
1596        }
1597        return FALSE;
1598}
1599
1600static int   nMixersWithFullDuplex = 0;
1601static UINT *mixerIdMap;
1602
1603static UINT
1604mapAudioDescToMixerID(audio_desc_t ad)
1605{
1606        return mixerIdMap[ad];
1607}
1608
1609int
1610w32sdk_audio_init(void)
1611{
1612        audio_format af;
1613        unsigned int i;
1614
1615        mixerIdMap = (UINT*)xmalloc(sizeof(UINT) * mixerGetNumDevs());
1616
1617        af.bits_per_sample = 16;
1618        af.bytes_per_block = 320;
1619        af.channels        = 1;
1620        af.encoding        = DEV_S16;
1621        af.sample_rate     = 8000;
1622
1623        for(i = 0; i < mixerGetNumDevs(); i++) {
1624                if (w32sdk_audio_open_mixer_probe(i, &af, &af, 1)) {
1625                        w32sdk_audio_close_mixer(i);
1626                        mixerIdMap[nMixersWithFullDuplex] = i;
1627                        nMixersWithFullDuplex++;
1628                }
1629        }
1630
1631        return nMixersWithFullDuplex;
1632}
1633
1634int
1635w32sdk_audio_free(void)
1636{
1637        xfree(mixerIdMap);
1638        return TRUE;
1639}
1640
1641int
1642w32sdk_get_device_count(void)
1643{
1644        /* We are only interested in devices with mixers */
1645        return (int)nMixersWithFullDuplex;
1646}
1647
1648static char tmpname[MAXPNAMELEN];
1649
1650char *
1651w32sdk_get_device_name(int idx)
1652{
1653        MIXERCAPS mc;
1654        idx = mapAudioDescToMixerID(idx);
1655        if ((UINT)idx < mixerGetNumDevs()) {
1656                mixerGetDevCaps((UINT)idx, &mc, sizeof(mc));
1657                strncpy(tmpname, mc.szPname, MAXPNAMELEN);
1658                return tmpname;
1659        }
1660        return NULL;
1661}
1662#endif /* WIN32 */
Note: See TracBrowser for help on using the browser.