mixer.c 48.1 KB
Newer Older
1 2
/* -*- tab-width: 8; c-basic-offset: 4 -*- */

Alexandre Julliard's avatar
Alexandre Julliard committed
3 4 5
/*
 * Sample MIXER Wine Driver for Linux
 *
6
 * Copyright 	1997 Marcus Meissner
7
 * 		1999,2001 Eric Pouech
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
22 23
 */

24 25 26 27
/* TODO:
 * + implement notification mechanism when state of mixer's controls
 */

28
#include "config.h"
29
#include "wine/port.h"
30

Alexandre Julliard's avatar
Alexandre Julliard committed
31
#include <stdlib.h>
32
#include <stdarg.h>
33
#include <stdio.h>
34
#include <string.h>
35 36 37
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
38
#include <fcntl.h>
39
#include <errno.h>
40
#include <assert.h>
41 42 43 44
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif

45 46
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
47
#include "windef.h"
48
#include "winbase.h"
49
#include "winnls.h"
50 51
#include "mmddk.h"
#include "oss.h"
52
#include "wine/unicode.h"
53
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
54

55
WINE_DEFAULT_DEBUG_CHANNEL(mixer);
56

57
#ifdef HAVE_OSS
58

59 60
#define MAX_MIXERDRV     (6)

61
#define	WINE_MIXER_MANUF_ID		0xAA
62
#define	WINE_MIXER_PRODUCT_ID		0x55
63 64 65
#define	WINE_MIXER_VERSION		0x0100
#define	WINE_MIXER_NAME			"WINE OSS Mixer"

66 67
#define WINE_CHN_MASK(_x)		(1L << (_x))
#define WINE_CHN_SUPPORTS(_c, _x)	((_c) & WINE_CHN_MASK(_x))
68
/* Bass and Treble are no longer in the mask as Windows does not handle them */
69
#define WINE_MIXER_MASK_SPEAKER		(WINE_CHN_MASK(SOUND_MIXER_SYNTH)  | \
70 71 72
                                         WINE_CHN_MASK(SOUND_MIXER_PCM)    | \
                                         WINE_CHN_MASK(SOUND_MIXER_LINE)   | \
                                         WINE_CHN_MASK(SOUND_MIXER_MIC)    | \
73 74 75 76 77 78
                                         WINE_CHN_MASK(SOUND_MIXER_CD)     )

#define WINE_MIXER_MASK_RECORD		(WINE_CHN_MASK(SOUND_MIXER_SYNTH)  | \
                                         WINE_CHN_MASK(SOUND_MIXER_LINE)   | \
                                         WINE_CHN_MASK(SOUND_MIXER_MIC)    | \
                                         WINE_CHN_MASK(SOUND_MIXER_IMIX)   )
79

80 81
/* FIXME: the two following string arrays should be moved to a resource file in a string table */
/* if it's done, better use a struct to hold labels, name, and muted channel volume cache */
82 83
static const char * const MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
static const char * const MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
84 85 86 87

struct mixerCtrl
{
    DWORD		dwLineID;
88
    MIXERCONTROLW	ctrl;
89 90
};

91
struct mixer
92
{
93 94
    char*		name;
    char*		dev_name;
95 96 97 98 99 100 101 102 103 104 105 106 107 108
    int			volume[SOUND_MIXER_NRDEVICES];
    int			devMask;
    int			stereoMask;
    int			recMask;
    BOOL		singleRecChannel;
    struct mixerCtrl*	ctrl;
    int			numCtrl;
};

#define LINEID_DST	0xFFFF
#define LINEID_SPEAKER	0x0000
#define LINEID_RECORD	0x0001

static int		MIX_NumMixers;
109
static struct mixer	MIX_Mixers[MAX_MIXERDRV];
110

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
/**************************************************************************
 */

static const char * getMessage(UINT uMsg)
{
    static char str[64];
#define MSG_TO_STR(x) case x: return #x;
    switch (uMsg) {
    MSG_TO_STR(DRVM_INIT);
    MSG_TO_STR(DRVM_EXIT);
    MSG_TO_STR(DRVM_ENABLE);
    MSG_TO_STR(DRVM_DISABLE);
    MSG_TO_STR(MXDM_GETDEVCAPS);
    MSG_TO_STR(MXDM_GETLINEINFO);
    MSG_TO_STR(MXDM_GETNUMDEVS);
    MSG_TO_STR(MXDM_OPEN);
    MSG_TO_STR(MXDM_CLOSE);
    MSG_TO_STR(MXDM_GETLINECONTROLS);
    MSG_TO_STR(MXDM_GETCONTROLDETAILS);
    MSG_TO_STR(MXDM_SETCONTROLDETAILS);
    }
#undef MSG_TO_STR
    sprintf(str, "UNKNOWN(%08x)", uMsg);
    return str;
}

static const char * getIoctlCommand(int command)
{
    static char str[64];
#define IOCTL_TO_STR(x) case x: return #x;
    switch (command) {
    IOCTL_TO_STR(SOUND_MIXER_VOLUME);
    IOCTL_TO_STR(SOUND_MIXER_BASS);
    IOCTL_TO_STR(SOUND_MIXER_TREBLE);
    IOCTL_TO_STR(SOUND_MIXER_SYNTH);
    IOCTL_TO_STR(SOUND_MIXER_PCM);
    IOCTL_TO_STR(SOUND_MIXER_SPEAKER);
    IOCTL_TO_STR(SOUND_MIXER_LINE);
    IOCTL_TO_STR(SOUND_MIXER_MIC);
    IOCTL_TO_STR(SOUND_MIXER_CD);
    IOCTL_TO_STR(SOUND_MIXER_IMIX);
    IOCTL_TO_STR(SOUND_MIXER_ALTPCM);
    IOCTL_TO_STR(SOUND_MIXER_RECLEV);
    IOCTL_TO_STR(SOUND_MIXER_IGAIN);
    IOCTL_TO_STR(SOUND_MIXER_OGAIN);
    IOCTL_TO_STR(SOUND_MIXER_LINE1);
    IOCTL_TO_STR(SOUND_MIXER_LINE2);
    IOCTL_TO_STR(SOUND_MIXER_LINE3);
    IOCTL_TO_STR(SOUND_MIXER_DIGITAL1);
    IOCTL_TO_STR(SOUND_MIXER_DIGITAL2);
    IOCTL_TO_STR(SOUND_MIXER_DIGITAL3);
162
#ifdef SOUND_MIXER_PHONEIN
163
    IOCTL_TO_STR(SOUND_MIXER_PHONEIN);
164 165
#endif
#ifdef SOUND_MIXER_PHONEOUT
166
    IOCTL_TO_STR(SOUND_MIXER_PHONEOUT);
167
#endif
168 169
    IOCTL_TO_STR(SOUND_MIXER_VIDEO);
    IOCTL_TO_STR(SOUND_MIXER_RADIO);
170
#ifdef SOUND_MIXER_MONITOR
171
    IOCTL_TO_STR(SOUND_MIXER_MONITOR);
172
#endif
173 174 175 176 177 178 179 180 181
    }
#undef IOCTL_TO_STR
    sprintf(str, "UNKNOWN(%08x)", command);
    return str;
}

static const char * getControlType(DWORD dwControlType)
{
    static char str[64];
182
#define TYPE_TO_STR(x) case x: return #x
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    switch (dwControlType) {
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_CUSTOM);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEANMETER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNEDMETER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PEAKMETER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNEDMETER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BOOLEAN);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_ONOFF);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUTE);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MONO);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_LOUDNESS);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_STEREOENH);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS_BOOST);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BUTTON);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_DECIBELS);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SIGNED);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_UNSIGNED);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PERCENT);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SLIDER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_PAN);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_QSOUNDPAN);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_FADER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_VOLUME);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_BASS);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_TREBLE);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_EQUALIZER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_SINGLESELECT);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MUX);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MULTIPLESELECT);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MIXER);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MICROTIME);
    TYPE_TO_STR(MIXERCONTROL_CONTROLTYPE_MILLITIME);
    }
#undef TYPE_TO_STR
217
    sprintf(str, "UNKNOWN(%08x)", dwControlType);
218 219 220
    return str;
}

221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
static const char * getComponentType(DWORD dwComponentType)
{
    static char str[64];
#define TYPE_TO_STR(x) case x: return #x;
    switch (dwComponentType) {
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_UNDEFINED);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_DIGITAL);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_LINE);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_MONITOR);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_SPEAKERS);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_HEADPHONES);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_TELEPHONE);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_DST_VOICEIN);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_DIGITAL);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_LINE);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY);
    TYPE_TO_STR(MIXERLINE_COMPONENTTYPE_SRC_ANALOG);
    }
#undef TYPE_TO_STR
248
    sprintf(str, "UNKNOWN(%08x)", dwComponentType);
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    return str;
}

static const char * getTargetType(DWORD dwType)
{
    static char str[64];
#define TYPE_TO_STR(x) case x: return #x;
    switch (dwType) {
    TYPE_TO_STR(MIXERLINE_TARGETTYPE_UNDEFINED);
    TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEOUT);
    TYPE_TO_STR(MIXERLINE_TARGETTYPE_WAVEIN);
    TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIOUT);
    TYPE_TO_STR(MIXERLINE_TARGETTYPE_MIDIIN);
    TYPE_TO_STR(MIXERLINE_TARGETTYPE_AUX);
    }
#undef TYPE_TO_STR
265
    sprintf(str, "UNKNOWN(%08x)", dwType);
266 267 268
    return str;
}

269 270 271 272 273
static const WCHAR sz_short_volume [] = {'V','o','l',0};
static const WCHAR sz_long_volume  [] = {'V','o','l','u','m','e',0};
static const WCHAR sz_shrtlng_mute [] = {'M','u','t','e',0};
static const WCHAR sz_shrtlng_mixer[] = {'M','i','x','e','r',0};

274 275 276
/**************************************************************************
 * 				MIX_FillLineControls		[internal]
 */
277
static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID,
278
                                 DWORD dwControlType)
279 280 281 282
{
    struct mixerCtrl* 	mc = &mix->ctrl[c];
    int			j;

283
    TRACE("(%p, %d, %08x, %s)\n", mix, c, lineID,
284
          getControlType(dwControlType));
285

286
    mc->dwLineID = lineID;
287
    mc->ctrl.cbStruct = sizeof(MIXERCONTROLW);
288
    mc->ctrl.dwControlID = c + 1;
289
    mc->ctrl.dwControlType = dwControlType;
290

291
    switch (dwControlType)
292 293 294 295
    {
    case MIXERCONTROL_CONTROLTYPE_VOLUME:
	mc->ctrl.fdwControl = 0;
	mc->ctrl.cMultipleItems = 0;
296 297
	strcpyW(mc->ctrl.szShortName, sz_short_volume);
	strcpyW(mc->ctrl.szName, sz_long_volume);
298
	memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
299
	/* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct,
300 301 302 303 304 305 306
	 * [0, 100] is the range supported by OSS
	 * whatever the min and max values are they must match
	 * conversions done in (Get|Set)ControlDetails to stay in [0, 100] range
	 */
	mc->ctrl.Bounds.s1.dwMinimum = 0;
	mc->ctrl.Bounds.s1.dwMaximum = 65535;
	memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
307
        mc->ctrl.Metrics.cSteps = 656;
308 309 310 311 312
	break;
    case MIXERCONTROL_CONTROLTYPE_MUTE:
    case MIXERCONTROL_CONTROLTYPE_ONOFF:
	mc->ctrl.fdwControl = 0;
	mc->ctrl.cMultipleItems = 0;
313 314
	strcpyW(mc->ctrl.szShortName, sz_shrtlng_mute);
	strcpyW(mc->ctrl.szName, sz_shrtlng_mute);
315 316 317 318 319 320 321 322 323 324 325 326
	memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
	mc->ctrl.Bounds.s1.dwMinimum = 0;
	mc->ctrl.Bounds.s1.dwMaximum = 1;
	memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
	break;
    case MIXERCONTROL_CONTROLTYPE_MUX:
    case MIXERCONTROL_CONTROLTYPE_MIXER:
	mc->ctrl.fdwControl = MIXERCONTROL_CONTROLF_MULTIPLE;
	mc->ctrl.cMultipleItems = 0;
	for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
	    if (WINE_CHN_SUPPORTS(mix->recMask, j))
		mc->ctrl.cMultipleItems++;
327 328
	strcpyW(mc->ctrl.szShortName, sz_shrtlng_mixer);
	strcpyW(mc->ctrl.szName, sz_shrtlng_mixer);
329
	memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds));
Robert Reif's avatar
Robert Reif committed
330
        mc->ctrl.Bounds.s1.dwMaximum = mc->ctrl.cMultipleItems - 1;
331
	memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics));
Robert Reif's avatar
Robert Reif committed
332
        mc->ctrl.Metrics.cSteps = mc->ctrl.cMultipleItems;
333
	break;
334

335
    default:
336
	FIXME("Internal error: unknown type: %08x\n", dwControlType);
337
    }
338
    TRACE("ctrl[%2d]: typ=%08x lin=%08x\n", c + 1, dwControlType, lineID);
339 340 341 342 343 344 345 346 347
}

/******************************************************************
 *		MIX_GetMixer
 *
 *
 */
static struct mixer*	MIX_Get(WORD wDevID)
{
348 349
    TRACE("(%04x)\n", wDevID);

350
    if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].dev_name == NULL)
351 352
        return NULL;

353 354 355 356 357 358 359 360 361 362 363 364 365
    return &MIX_Mixers[wDevID];
}

/**************************************************************************
 * 				MIX_Open			[internal]
 */
static DWORD MIX_Open(WORD wDevID, LPMIXEROPENDESC lpMod, DWORD flags)
{
    int			mixer, i, j;
    unsigned 		caps;
    struct mixer*	mix;
    DWORD		ret = MMSYSERR_NOERROR;

366
    TRACE("(%04X, %p, %u);\n", wDevID, lpMod, flags);
367

368 369 370 371
    /* as we partly init the mixer with MIX_Open, we can allow null open decs
     * EPP     if (lpMod == NULL) return MMSYSERR_INVALPARAM;
     * anyway, it seems that WINMM/MMSYSTEM doesn't always open the mixer
     * device before sending messages to it... it seems to be linked to all
372
     * the equivalent of mixer identification
373 374
     * (with a reference to a wave, midi.. handle
     */
375 376
    if ((mix = MIX_Get(wDevID)) == NULL) {
        WARN("bad device ID: %04X\n", wDevID);
377 378
        return MMSYSERR_BADDEVICEID;
    }
379

380
    if ((mixer = open(mix->dev_name, O_RDWR)) < 0)
381
    {
382
	ERR("open(%s, O_RDWR) failed (%s)\n",
383
            mix->dev_name, strerror(errno));
384

385 386
	if (errno == ENODEV || errno == ENXIO)
	{
387
	    /* no driver present */
388
            WARN("no driver\n");
389 390 391 392
	    return MMSYSERR_NODRIVER;
	}
	return MMSYSERR_ERROR;
    }
393 394

    if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1)
395
    {
396
	ERR("ioctl(%s, SOUND_MIXER_DEVMASK) failed (%s)\n",
397
            mix->dev_name, strerror(errno));
398 399 400
	ret = MMSYSERR_ERROR;
	goto error;
    }
401

402 403 404
    mix->devMask &= WINE_MIXER_MASK_SPEAKER;
    if (mix->devMask == 0)
    {
405
        WARN("no driver\n");
406 407 408 409
	ret = MMSYSERR_NODRIVER;
	goto error;
    }

410
    if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1)
411
    {
412
	ERR("ioctl(%s, SOUND_MIXER_STEREODEVS) failed (%s)\n",
413
            mix->dev_name, strerror(errno));
414 415 416 417 418
	ret = MMSYSERR_ERROR;
	goto error;
    }
    mix->stereoMask &= WINE_MIXER_MASK_SPEAKER;

419
    if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1)
420
    {
421
	ERR("ioctl(%s, SOUND_MIXER_RECMASK) failed (%s)\n",
422
            mix->dev_name, strerror(errno));
423 424 425 426 427 428 429 430 431 432 433
	ret = MMSYSERR_ERROR;
	goto error;
    }
    mix->recMask &= WINE_MIXER_MASK_RECORD;
    /* FIXME: we may need to support both rec lev & igain */
    if (!WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_RECLEV))
    {
	WARN("The sound card doesn't support rec level\n");
	if (WINE_CHN_SUPPORTS(mix->recMask, SOUND_MIXER_IGAIN))
	    WARN("but it does support IGain, please report\n");
    }
434
    if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1)
435
    {
436
	ERR("ioctl(%s, SOUND_MIXER_READ_CAPS) failed (%s)\n",
437
            mix->dev_name, strerror(errno));
438 439 440 441
	ret = MMSYSERR_ERROR;
	goto error;
    }
    mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT;
442 443
    TRACE("dev=%04x rec=%04x stereo=%04x %s\n",
	  mix->devMask, mix->recMask, mix->stereoMask,
444
	  mix->singleRecChannel ? "single" : "multiple");
445
    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
446 447 448 449 450 451 452 453 454 455 456
    {
	mix->volume[i] = -1;
    }
    mix->numCtrl = 4; /* dst lines... vol&mute on speakers, vol&onoff on rec */
    /* FIXME: do we always have RECLEV on all cards ??? */
    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    {
	if (WINE_CHN_SUPPORTS(mix->devMask, i))
	    mix->numCtrl += 2; /* volume & mute */
	if (WINE_CHN_SUPPORTS(mix->recMask, i))
	    mix->numCtrl += 2; /* volume & onoff */
457

458
    }
459 460
    if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                                sizeof(mix->ctrl[0]) * mix->numCtrl)))
461 462 463 464
    {
	ret = MMSYSERR_NOMEM;
	goto error;
    }
465

466
    j = 0;
467 468 469 470
    MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST),
                         MIXERCONTROL_CONTROLTYPE_VOLUME);
    MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST),
                         MIXERCONTROL_CONTROLTYPE_MUTE);
471
    MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
472
			 mix->singleRecChannel ?
473 474 475 476
			     MIXERCONTROL_CONTROLTYPE_MUX :
                             MIXERCONTROL_CONTROLTYPE_MIXER);
    MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST),
                         MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    {
	if (WINE_CHN_SUPPORTS(mix->devMask, i))
	{
	    MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
				 MIXERCONTROL_CONTROLTYPE_VOLUME);
	    MIX_FillLineControls(mix, j++, MAKELONG(LINEID_SPEAKER, i),
				 MIXERCONTROL_CONTROLTYPE_MUTE);
	}
    }
    for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
    {
	if (WINE_CHN_SUPPORTS(mix->recMask, i))
	{
	    MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
				 MIXERCONTROL_CONTROLTYPE_VOLUME);
	    MIX_FillLineControls(mix, j++, MAKELONG(LINEID_RECORD, i),
				 MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/);
	}
    }
    assert(j == mix->numCtrl);
 error:
    close(mixer);
    return ret;
}
502

Alexandre Julliard's avatar
Alexandre Julliard committed
503
/**************************************************************************
504
 * 				MIX_GetVal			[internal]
Alexandre Julliard's avatar
Alexandre Julliard committed
505
 */
506
static	BOOL	MIX_GetVal(struct mixer* mix, int chn, int* val)
Alexandre Julliard's avatar
Alexandre Julliard committed
507
{
508 509
    int		mixer;
    BOOL	ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
510

511
    TRACE("(%p, %s, %p\n", mix, getIoctlCommand(chn), val);
512

513
    if ((mixer = open(mix->dev_name, O_RDWR)) < 0) {
514 515
	/* FIXME: ENXIO => no mixer installed */
	WARN("mixer device not available !\n");
516 517 518
    } else {
	if (ioctl(mixer, MIXER_READ(chn), val) >= 0) {
	    TRACE("Reading %04x for %s\n", *val, getIoctlCommand(chn));
519
	    ret = TRUE;
520 521
	} else {
            ERR("ioctl(%s, MIXER_READ(%s)) failed (%s)\n",
522
                mix->dev_name, getIoctlCommand(chn), strerror(errno));
523
        }
524 525 526 527
	close(mixer);
    }
    return ret;
}
528

529 530 531
/**************************************************************************
 * 				MIX_SetVal			[internal]
 */
532
static	BOOL	MIX_SetVal(struct mixer* mix, int chn, int val)
533 534 535
{
    int		mixer;
    BOOL	ret = FALSE;
536

537
    TRACE("(%p, %s, %x)\n", mix, getIoctlCommand(chn), val);
538

539
    if ((mixer = open(mix->dev_name, O_RDWR)) < 0) {
540 541
	/* FIXME: ENXIO => no mixer installed */
	WARN("mixer device not available !\n");
542 543 544
    } else {
	if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0) {
	    TRACE("Set %s to %04x\n", getIoctlCommand(chn), val);
545
	    ret = TRUE;
546 547
	} else {
            ERR("ioctl(%s, MIXER_WRITE(%s)) failed (%s)\n",
548
                mix->dev_name, getIoctlCommand(chn), strerror(errno));
549
        }
550 551 552 553
	close(mixer);
    }
    return ret;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
554

555 556 557 558 559 560 561 562 563 564
/******************************************************************
 *		MIX_GetRecSrc
 *
 *
 */
static BOOL	MIX_GetRecSrc(struct mixer* mix, unsigned* mask)
{
    int		mixer;
    BOOL	ret = FALSE;

565 566
    TRACE("(%p, %p)\n", mix, mask);

567
    if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) {
568 569 570 571
	if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) {
            ret = TRUE;
	} else {
            ERR("ioctl(%s, SOUND_MIXER_READ_RECSRC) failed (%s)\n",
572
                mix->dev_name, strerror(errno));
573
        }
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
	close(mixer);
    }
    return ret;
}

/******************************************************************
 *		MIX_SetRecSrc
 *
 *
 */
static BOOL	MIX_SetRecSrc(struct mixer* mix, unsigned mask)
{
    int		mixer;
    BOOL	ret = FALSE;

589 590
    TRACE("(%p, %08x)\n", mix, mask);

591
    if ((mixer = open(mix->dev_name, O_RDWR)) >= 0) {
592
	if (ioctl(mixer, SOUND_MIXER_WRITE_RECSRC, &mask) >= 0) {
593
	    ret = TRUE;
594 595
	} else {
            ERR("ioctl(%s, SOUND_MIXER_WRITE_RECSRC) failed (%s)\n",
596
                mix->dev_name, strerror(errno));
597
        }
598 599 600 601 602
	close(mixer);
    }
    return ret;
}

603 604 605
/**************************************************************************
 * 				MIX_GetDevCaps			[internal]
 */
606
static DWORD MIX_GetDevCaps(WORD wDevID, LPMIXERCAPSW lpCaps, DWORD dwSize)
607
{
608
    struct mixer*	mix;
609 610
    MIXERCAPSW		capsW;
    const char*         name;
611

612
    TRACE("(%04X, %p, %u);\n", wDevID, lpCaps, dwSize);
613

614 615 616 617 618
    if (lpCaps == NULL) {
        WARN("invalid parameter: lpCaps == NULL\n");
        return MMSYSERR_INVALPARAM;
    }

619 620
    if ((mix = MIX_Get(wDevID)) == NULL) {
        WARN("bad device ID: %04X\n", wDevID);
621 622
        return MMSYSERR_BADDEVICEID;
    }
623

624 625 626 627 628 629 630
    capsW.wMid = WINE_MIXER_MANUF_ID;
    capsW.wPid = WINE_MIXER_PRODUCT_ID;
    capsW.vDriverVersion = WINE_MIXER_VERSION;
    if (!(name = mix->name)) name = WINE_MIXER_NAME;
    MultiByteToWideChar(CP_ACP, 0, name, -1, capsW.szPname, sizeof(capsW.szPname) / sizeof(WCHAR));
    capsW.cDestinations = 2; /* speakers & record */
    capsW.fdwSupport = 0; /* No bits defined yet */
631

632
    memcpy(lpCaps, &capsW, min(dwSize, sizeof(capsW)));
633

634
    return MMSYSERR_NOERROR;
Alexandre Julliard's avatar
Alexandre Julliard committed
635 636
}

637
/**************************************************************************
638 639
 * 				MIX_GetLineInfoDst	[internal]
 */
640
static	DWORD	MIX_GetLineInfoDst(struct mixer* mix, LPMIXERLINEW lpMl,
641
                                   DWORD dst)
642
{
643 644 645
    unsigned mask;
    int	j;

646
    TRACE("(%p, %p, %08x)\n", mix, lpMl, dst);
647

648 649 650
    lpMl->dwDestination = dst;
    switch (dst)
    {
Robert Reif's avatar
Robert Reif committed
651
    case LINEID_SPEAKER:
652 653 654
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
	mask = mix->devMask;
	j = SOUND_MIXER_VOLUME;
Robert Reif's avatar
Robert Reif committed
655
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
656
	break;
Robert Reif's avatar
Robert Reif committed
657
    case LINEID_RECORD:
658 659 660
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
	mask = mix->recMask;
	j = SOUND_MIXER_RECLEV;
Robert Reif's avatar
Robert Reif committed
661
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
662 663 664 665 666 667
	break;
    default:
	FIXME("shouldn't happen\n");
	return MMSYSERR_ERROR;
    }
    lpMl->dwSource = 0xFFFFFFFF;
668 669
    MultiByteToWideChar(CP_ACP, 0, MIX_Labels[j], -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, MIX_Names[j],  -1, lpMl->szName,      sizeof(lpMl->szName) / sizeof(WCHAR));
670

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
    /* we have all connections found in the MIX_DevMask */
    lpMl->cConnections = 0;
    for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
	if (WINE_CHN_SUPPORTS(mask, j))
	    lpMl->cConnections++;
    lpMl->cChannels = 1;
    if (WINE_CHN_SUPPORTS(mix->stereoMask, lpMl->dwLineID))
	lpMl->cChannels++;
    lpMl->dwLineID = MAKELONG(dst, LINEID_DST);
    lpMl->cControls = 0;
    for (j = 0; j < mix->numCtrl; j++)
	if (mix->ctrl[j].dwLineID == lpMl->dwLineID)
	    lpMl->cControls++;

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				MIX_GetLineInfoSrc	[internal]
690
 */
691
static	DWORD	MIX_GetLineInfoSrc(struct mixer* mix, LPMIXERLINEW lpMl,
692
                                   DWORD idx, DWORD dst)
693
{
694 695 696
    int		i, j;
    unsigned	mask = (dst) ? mix->recMask : mix->devMask;

697
    TRACE("(%p, %p, %d, %08x)\n", mix, lpMl, idx, dst);
698

699 700
    MultiByteToWideChar(CP_ACP, 0, MIX_Labels[idx], -1, lpMl->szShortName, sizeof(lpMl->szShortName) / sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, MIX_Names[idx],  -1, lpMl->szName,      sizeof(lpMl->szName) / sizeof(WCHAR));
701 702
    lpMl->dwLineID = MAKELONG(dst, idx);
    lpMl->dwDestination = dst;
703
    lpMl->cConnections = 1;
704 705 706 707 708
    lpMl->cControls = 0;
    for (i = 0; i < mix->numCtrl; i++)
	if (mix->ctrl[i].dwLineID == lpMl->dwLineID)
	    lpMl->cControls++;

709
    switch (idx)
710
    {
711 712 713
    case SOUND_MIXER_SYNTH:
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
	lpMl->fdwLine	 |= MIXERLINE_LINEF_SOURCE;
Robert Reif's avatar
Robert Reif committed
714
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_MIDIOUT;
715 716 717 718
	break;
    case SOUND_MIXER_CD:
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
	lpMl->fdwLine	 |= MIXERLINE_LINEF_SOURCE;
Robert Reif's avatar
Robert Reif committed
719
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
720 721 722 723
	break;
    case SOUND_MIXER_LINE:
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
	lpMl->fdwLine	 |= MIXERLINE_LINEF_SOURCE;
Robert Reif's avatar
Robert Reif committed
724
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
725 726 727 728
	break;
    case SOUND_MIXER_MIC:
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
	lpMl->fdwLine	 |= MIXERLINE_LINEF_SOURCE;
Robert Reif's avatar
Robert Reif committed
729
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
730 731 732 733
	break;
    case SOUND_MIXER_PCM:
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
	lpMl->fdwLine	 |= MIXERLINE_LINEF_SOURCE;
Robert Reif's avatar
Robert Reif committed
734
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
735
	break;
736 737 738
    case SOUND_MIXER_IMIX:
	lpMl->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
	lpMl->fdwLine	 |= MIXERLINE_LINEF_SOURCE;
Robert Reif's avatar
Robert Reif committed
739
        lpMl->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
740
	break;
741
    default:
742
	WARN("Index %d not handled.\n", idx);
743
	return MIXERR_INVALLINE;
744
    }
745 746 747
    lpMl->cChannels = 1;
    if (dst == 0 && WINE_CHN_SUPPORTS(mix->stereoMask, idx))
	lpMl->cChannels++;
748
    for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
749 750 751 752 753 754
    {
	if (WINE_CHN_SUPPORTS(mask, j))
	{
	    if (j == idx) break;
	    i++;
	}
755
    }
756
    lpMl->dwSource = i;
757
    return MMSYSERR_NOERROR;
758
}
Alexandre Julliard's avatar
Alexandre Julliard committed
759

760 761 762 763 764
/******************************************************************
 *		MIX_CheckLine
 */
static BOOL MIX_CheckLine(DWORD lineID)
{
765
    TRACE("(%08x)\n",lineID);
766

767
    return ((HIWORD(lineID) < SOUND_MIXER_NRDEVICES && LOWORD(lineID) < 2) ||
768
	    (HIWORD(lineID) == LINEID_DST &&
769
             LOWORD(lineID) < SOUND_MIXER_NRDEVICES));
770 771
}

Alexandre Julliard's avatar
Alexandre Julliard committed
772
/**************************************************************************
773
 * 				MIX_GetLineInfo			[internal]
Alexandre Julliard's avatar
Alexandre Julliard committed
774
 */
775
static DWORD MIX_GetLineInfo(WORD wDevID, LPMIXERLINEW lpMl, DWORD fdwInfo)
Alexandre Julliard's avatar
Alexandre Julliard committed
776
{
777
    int 		i, j;
778
    DWORD		ret = MMSYSERR_NOERROR;
779 780 781
    unsigned		mask;
    struct mixer*	mix;

782
    TRACE("(%04X, %p, %u);\n", wDevID, lpMl, fdwInfo);
783

784 785 786 787 788 789
    if (lpMl == NULL) {
        WARN("invalid parameter: lpMl = NULL\n");
	return MMSYSERR_INVALPARAM;
    }

    if (lpMl->cbStruct != sizeof(*lpMl)) {
790 791
        WARN("invalid parameter: lpMl->cbStruct = %d\n",
             lpMl->cbStruct);
792
	return MMSYSERR_INVALPARAM;
793 794 795
    }

    if ((mix = MIX_Get(wDevID)) == NULL) {
796
        WARN("bad device ID: %04X\n", wDevID);
797 798
        return MMSYSERR_BADDEVICEID;
    }
799

800 801 802 803 804
    /* FIXME: set all the variables correctly... the lines below
     * are very wrong...
     */
    lpMl->fdwLine	= MIXERLINE_LINEF_ACTIVE;
    lpMl->dwUser	= 0;
805 806

    switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK)
807
    {
808
    case MIXER_GETLINEINFOF_DESTINATION:
809
	TRACE("MIXER_GETLINEINFOF_DESTINATION (%08x)\n", lpMl->dwDestination);
810
	if (lpMl->dwDestination >= 2) {
811
            WARN("invalid parameter: lpMl->dwDestination = %d >= 2\n",
812
                 lpMl->dwDestination);
813
	    return MMSYSERR_INVALPARAM;
814
        }
815 816 817
	ret = MIX_GetLineInfoDst(mix, lpMl, lpMl->dwDestination);
	if (ret != MMSYSERR_NOERROR) {
            WARN("error\n");
818
	    return ret;
819
        }
820 821
	break;
    case MIXER_GETLINEINFOF_SOURCE:
822
	TRACE("MIXER_GETLINEINFOF_SOURCE (%08x), dst=%08x\n", lpMl->dwSource,
823
              lpMl->dwDestination);
824 825
	switch (lpMl->dwDestination)
	{
Robert Reif's avatar
Robert Reif committed
826 827
	case LINEID_SPEAKER: mask = mix->devMask; break;
	case LINEID_RECORD: mask = mix->recMask; break;
828
	default:
829 830
            WARN("invalid parameter\n");
            return MMSYSERR_INVALPARAM;
831
	}
832
	i = lpMl->dwSource;
833
	for (j = 0; j < SOUND_MIXER_NRDEVICES; j++)
834 835
	{
	    if (WINE_CHN_SUPPORTS(mask, j) && (i-- == 0))
Alexandre Julliard's avatar
Alexandre Julliard committed
836 837
		break;
	}
838 839
	if (j >= SOUND_MIXER_NRDEVICES) {
            WARN("invalid line\n");
840
	    return MIXERR_INVALLINE;
841
        }
842 843 844
	ret = MIX_GetLineInfoSrc(mix, lpMl, j, lpMl->dwDestination);
	if (ret != MMSYSERR_NOERROR) {
            WARN("error\n");
845
	    return ret;
846
        }
847 848
	break;
    case MIXER_GETLINEINFOF_LINEID:
849
	TRACE("MIXER_GETLINEINFOF_LINEID (%08x)\n", lpMl->dwLineID);
850

851 852
	if (!MIX_CheckLine(lpMl->dwLineID)) {
            WARN("invalid line\n");
853
	    return MIXERR_INVALLINE;
854
        }
855 856 857
	if (HIWORD(lpMl->dwLineID) == LINEID_DST)
	    ret = MIX_GetLineInfoDst(mix, lpMl, LOWORD(lpMl->dwLineID));
	else
858 859 860 861
	    ret = MIX_GetLineInfoSrc(mix, lpMl, HIWORD(lpMl->dwLineID),
                                     LOWORD(lpMl->dwLineID));
	if (ret != MMSYSERR_NOERROR) {
            WARN("error\n");
862
	    return ret;
863
        }
864 865
	break;
    case MIXER_GETLINEINFOF_COMPONENTTYPE:
866 867
	TRACE("MIXER_GETLINEINFOF_COMPONENTTYPE (%s)\n",
              getComponentType(lpMl->dwComponentType));
868
	switch (lpMl->dwComponentType)
869
	{
870
	case MIXERLINE_COMPONENTTYPE_DST_HEADPHONES:
871
	case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
872
	    ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_SPEAKER);
873
	    break;
874 875
	case MIXERLINE_COMPONENTTYPE_DST_LINE:
	case MIXERLINE_COMPONENTTYPE_DST_VOICEIN:
876
	case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
877
	    ret = MIX_GetLineInfoDst(mix, lpMl, LINEID_RECORD);
878 879
	    break;
	case MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
880
	    ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_SYNTH, 0);
881 882
	    break;
	case MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
883
	    ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_CD, 0);
884 885
	    break;
	case MIXERLINE_COMPONENTTYPE_SRC_LINE:
886
	    ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_LINE, 0);
887 888
	    break;
	case MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
889
	    ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_MIC, 1);
890 891
	    break;
	case MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
892 893 894 895
	    ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_PCM, 0);
	    break;
	case MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
	    ret = MIX_GetLineInfoSrc(mix, lpMl, SOUND_MIXER_IMIX, 1);
896 897
	    break;
	default:
898 899
	    FIXME("Unhandled component type (%s)\n",
                  getComponentType(lpMl->dwComponentType));
900 901 902 903
	    return MMSYSERR_INVALPARAM;
	}
	break;
    case MIXER_GETLINEINFOF_TARGETTYPE:
904
	FIXME("MIXER_GETLINEINFOF_TARGETTYPE not implemented yet.\n");
905 906 907 908 909 910 911 912 913 914 915 916 917 918
        TRACE("MIXER_GETLINEINFOF_TARGETTYPE (%s)\n",
              getTargetType(lpMl->Target.dwType));
        switch (lpMl->Target.dwType) {
        case MIXERLINE_TARGETTYPE_UNDEFINED:
        case MIXERLINE_TARGETTYPE_WAVEOUT:
        case MIXERLINE_TARGETTYPE_WAVEIN:
        case MIXERLINE_TARGETTYPE_MIDIOUT:
        case MIXERLINE_TARGETTYPE_MIDIIN:
        case MIXERLINE_TARGETTYPE_AUX:
	default:
	    FIXME("Unhandled target type (%s)\n",
                  getTargetType(lpMl->Target.dwType));
	    return MMSYSERR_INVALPARAM;
        }
919 920 921 922 923
	break;
    default:
	WARN("Unknown flag (%08lx)\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
	break;
    }
924

Robert Reif's avatar
Robert Reif committed
925
    if ((fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) != MIXER_GETLINEINFOF_TARGETTYPE) {
926
        const char* name;
Robert Reif's avatar
Robert Reif committed
927 928 929 930
        lpMl->Target.dwDeviceID = 0xFFFFFFFF;
        lpMl->Target.wMid = WINE_MIXER_MANUF_ID;
        lpMl->Target.wPid = WINE_MIXER_PRODUCT_ID;
        lpMl->Target.vDriverVersion = WINE_MIXER_VERSION;
931 932
        if (!(name = mix->name)) name = WINE_MIXER_NAME;
        MultiByteToWideChar(CP_ACP, 0, name, -1, lpMl->Target.szPname, sizeof(lpMl->Target.szPname) / sizeof(WCHAR));
Robert Reif's avatar
Robert Reif committed
933
    }
934

935
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
936 937
}

938 939 940
/******************************************************************
 *		MIX_CheckControl
 *
941
 */
942
static BOOL	MIX_CheckControl(struct mixer* mix, DWORD ctrlID)
943
{
944
    TRACE("(%p, %08x)\n", mix, ctrlID);
945

946
    return (ctrlID >= 1 && ctrlID <= mix->numCtrl);
Alexandre Julliard's avatar
Alexandre Julliard committed
947 948
}

949 950 951
/**************************************************************************
 * 				MIX_GetLineControls		[internal]
 */
952
static	DWORD	MIX_GetLineControls(WORD wDevID, LPMIXERLINECONTROLSW lpMlc,
953
                                    DWORD flags)
954
{
955
    DWORD		dwRet = MMSYSERR_NOERROR;
956
    struct mixer*	mix;
957

958
    TRACE("(%04X, %p, %u);\n", wDevID, lpMlc, flags);
959

960 961 962 963 964
    if (lpMlc == NULL) {
        WARN("invalid parameter: lpMlc == NULL\n");
        return MMSYSERR_INVALPARAM;
    }

965
    if (lpMlc->cbStruct < sizeof(*lpMlc)) {
966 967
        WARN("invalid parameter: lpMlc->cbStruct = %d\n",
             lpMlc->cbStruct);
968 969 970
	return MMSYSERR_INVALPARAM;
    }

971
    if (lpMlc->cbmxctrl < sizeof(MIXERCONTROLW)) {
972 973
        WARN("invalid parameter: lpMlc->cbmxctrl = %d\n",
             lpMlc->cbmxctrl);
974
	return MMSYSERR_INVALPARAM;
975 976 977
    }

    if ((mix = MIX_Get(wDevID)) == NULL) {
978
        WARN("bad device ID: %04X\n", wDevID);
979 980
        return MMSYSERR_BADDEVICEID;
    }
981

982
    switch (flags & MIXER_GETLINECONTROLSF_QUERYMASK)
983
    {
984
    case MIXER_GETLINECONTROLSF_ALL:
985 986 987
        {
	    int		i, j;

988
            TRACE("line=%08x MIXER_GETLINECONTROLSF_ALL (%d)\n",
989
                  lpMlc->dwLineID, lpMlc->cControls);
990

991 992 993 994 995
	    for (i = j = 0; i < mix->numCtrl; i++)
	    {
		if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
		    j++;
	    }
996 997 998 999 1000 1001 1002 1003

	    if (!j || lpMlc->cControls != j) {
                WARN("invalid parameter\n");
                dwRet = MMSYSERR_INVALPARAM;
	    } else if (!MIX_CheckLine(lpMlc->dwLineID)) {
                WARN("invalid line\n");
                dwRet = MIXERR_INVALLINE;
	    } else {
1004 1005 1006 1007
		for (i = j = 0; i < mix->numCtrl; i++)
		{
		    if (mix->ctrl[i].dwLineID == lpMlc->dwLineID)
		    {
1008
			TRACE("[%d] => [%2d]: typ=%08x\n", j, i + 1,
1009
                              mix->ctrl[i].ctrl.dwControlType);
1010
			lpMlc->pamxctrl[j++] = mix->ctrl[i].ctrl;
1011
                    }
1012 1013
		}
	    }
1014
	}
1015 1016
	break;
    case MIXER_GETLINECONTROLSF_ONEBYID:
1017
	TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYID (%x)\n",
1018
              lpMlc->dwLineID, lpMlc->u.dwControlID);
1019 1020

	if (!MIX_CheckControl(mix, lpMlc->u.dwControlID) ||
1021 1022
	    mix->ctrl[lpMlc->u.dwControlID - 1].dwLineID != lpMlc->dwLineID) {
            WARN("invalid parameter\n");
1023
	    dwRet = MMSYSERR_INVALPARAM;
1024
	} else
1025
	    lpMlc->pamxctrl[0] = mix->ctrl[lpMlc->u.dwControlID - 1].ctrl;
1026 1027
	break;
    case MIXER_GETLINECONTROLSF_ONEBYTYPE:
1028
	TRACE("line=%08x MIXER_GETLINECONTROLSF_ONEBYTYPE (%s)\n",
1029
              lpMlc->dwLineID, getControlType(lpMlc->u.dwControlType));
1030 1031 1032 1033 1034 1035 1036
	if (!MIX_CheckLine(lpMlc->dwLineID)) {
            WARN("invalid line\n");
            dwRet = MIXERR_INVALLINE;
	} else {
	    int	  i;
	    DWORD ct = lpMlc->u.dwControlType & MIXERCONTROL_CT_CLASS_MASK;
	    for (i = 0; i < mix->numCtrl; i++) {
1037
		if (mix->ctrl[i].dwLineID == lpMlc->dwLineID &&
1038 1039
		    ct == (mix->ctrl[i].ctrl.dwControlType &
                    MIXERCONTROL_CT_CLASS_MASK)) {
1040 1041 1042 1043
		    lpMlc->pamxctrl[0] = mix->ctrl[i].ctrl;
		    break;
		}
	    }
1044 1045

	    if (i == mix->numCtrl) {
1046
                WARN("invalid parameter: control not found\n");
1047 1048
                dwRet = MMSYSERR_INVALPARAM;
            }
1049
	}
1050 1051
	break;
    default:
Eric Pouech's avatar
Eric Pouech committed
1052
	ERR("Unknown flag %08lx\n", flags & MIXER_GETLINECONTROLSF_QUERYMASK);
1053
	dwRet = MMSYSERR_INVALPARAM;
1054
    }
1055 1056

    return dwRet;
1057
}
1058 1059 1060 1061

/**************************************************************************
 * 				MIX_GetControlDetails		[internal]
 */
1062 1063
static	DWORD	MIX_GetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd,
                                      DWORD fdwDetails)
1064
{
1065
    DWORD		ret = MMSYSERR_NOTSUPPORTED;
1066
    DWORD		chnl;
1067
    struct mixer*	mix;
1068

1069
    TRACE("(%04X, %p, %u);\n", wDevID, lpmcd, fdwDetails);
1070

1071 1072 1073 1074 1075 1076
    if (lpmcd == NULL) {
        WARN("invalid parameter: lpmcd == NULL\n");
        return MMSYSERR_INVALPARAM;
    }

    if ((mix = MIX_Get(wDevID)) == NULL) {
1077
        WARN("bad device ID: %04X\n", wDevID);
1078 1079
        return MMSYSERR_BADDEVICEID;
    }
1080

1081
    switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
1082
    {
1083
    case MIXER_GETCONTROLDETAILSF_VALUE:
1084
	TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd->dwControlID);
1085 1086
	if (MIX_CheckControl(mix, lpmcd->dwControlID))
	{
1087
	    DWORD c = lpmcd->dwControlID - 1;
1088
	    chnl = HIWORD(mix->ctrl[c].dwLineID);
1089
	    if (chnl == LINEID_DST)
1090 1091
		chnl = LOWORD(mix->ctrl[c].dwLineID) ? SOUND_MIXER_RECLEV :
                    SOUND_MIXER_VOLUME;
1092
	    switch (mix->ctrl[c].ctrl.dwControlType)
1093
	    {
1094 1095 1096 1097 1098
	    case MIXERCONTROL_CONTROLTYPE_VOLUME:
		{
		    LPMIXERCONTROLDETAILS_UNSIGNED	mcdu;
		    int					val;

1099
                    if (lpmcd->cbDetails !=
1100
                        sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
1101 1102
                        WARN("invalid parameter: cbDetails = %d\n",
                             lpmcd->cbDetails);
1103 1104 1105
                        return MMSYSERR_INVALPARAM;
                    }

1106
                    TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n",
1107 1108 1109
                          getControlType(mix->ctrl[c].ctrl.dwControlType),
                          lpmcd->cChannels);

1110
                    mcdu = lpmcd->paDetails;
1111

1112
		    /* return value is 00RL (4 bytes)... */
1113 1114 1115
		    if ((val = mix->volume[chnl]) == -1 &&
                        !MIX_GetVal(mix, chnl, &val)) {
                        WARN("invalid parameter\n");
1116
			return MMSYSERR_INVALPARAM;
1117
                    }
1118 1119

		    switch (lpmcd->cChannels)
1120
		    {
1121 1122
		    case 1:
			/* mono... so R = L */
1123
			mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1124
			TRACE("Reading RL = %d\n", mcdu->dwValue);
1125 1126 1127
			break;
		    case 2:
			/* stereo, left is paDetails[0] */
1128
			mcdu->dwValue = ((LOBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1129
			TRACE("Reading L = %d\n", mcdu->dwValue);
1130
                        mcdu++;
1131
			mcdu->dwValue = ((HIBYTE(LOWORD(val)) * 65536.0) / 100.0) + 0.5;
1132
			TRACE("Reading R = %d\n", mcdu->dwValue);
1133 1134
			break;
		    default:
1135
			WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
1136 1137
			return MMSYSERR_INVALPARAM;
		    }
1138
                    TRACE("=> %08x\n", mcdu->dwValue);
1139
		}
1140
		break;
1141
	    case MIXERCONTROL_CONTROLTYPE_MUTE:
1142
	    case MIXERCONTROL_CONTROLTYPE_ONOFF:
1143 1144
		{
		    LPMIXERCONTROLDETAILS_BOOLEAN	mcdb;
1145

1146
                    if (lpmcd->cbDetails !=
1147
                        sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1148 1149
                        WARN("invalid parameter: cbDetails = %d\n",
                             lpmcd->cbDetails);
1150 1151 1152
                        return MMSYSERR_INVALPARAM;
                    }

1153
                    TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1154 1155 1156
                          getControlType(mix->ctrl[c].ctrl.dwControlType),
                          lpmcd->cChannels);

1157
		    /* we mute both channels at the same time */
1158
                    mcdb = lpmcd->paDetails;
1159 1160
		    mcdb->fValue = (mix->volume[chnl] != -1);
		    TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
1161
		}
1162
		break;
1163 1164 1165 1166 1167
	    case MIXERCONTROL_CONTROLTYPE_MIXER:
	    case MIXERCONTROL_CONTROLTYPE_MUX:
		{
		    unsigned				mask;

1168
                    if (lpmcd->cbDetails !=
1169
                        sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1170 1171
                        WARN("invalid parameter: cbDetails = %d\n",
                             lpmcd->cbDetails);
1172 1173 1174
                        return MMSYSERR_INVALPARAM;
                    }

1175
                    TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1176 1177 1178
                          getControlType(mix->ctrl[c].ctrl.dwControlType),
                          lpmcd->cChannels);

1179 1180 1181 1182 1183 1184
		    if (!MIX_GetRecSrc(mix, &mask))
		    {
			/* FIXME: ENXIO => no mixer installed */
			WARN("mixer device not available !\n");
			ret = MMSYSERR_ERROR;
		    }
1185
		    else
1186 1187 1188 1189 1190
		    {
			LPMIXERCONTROLDETAILS_BOOLEAN	mcdb;
			int				i, j;

			/* we mute both channels at the same time */
1191
			mcdb = lpmcd->paDetails;
1192 1193 1194 1195 1196

			for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
			{
			    if (WINE_CHN_SUPPORTS(mix->recMask, j))
			    {
1197
				if (i >= lpmcd->u.cMultipleItems)
1198 1199 1200 1201 1202 1203 1204 1205
				    return MMSYSERR_INVALPARAM;
				mcdb[i++].fValue = WINE_CHN_SUPPORTS(mask, j);
			    }
			}
		    }
		}
		break;
	    default:
1206 1207
		WARN("%s Unsupported\n",
                     getControlType(mix->ctrl[c].ctrl.dwControlType));
1208
	    }
1209
	    ret = MMSYSERR_NOERROR;
1210
	}
1211
	else
1212
	{
1213
            WARN("invalid parameter\n");
1214
	    ret = MMSYSERR_INVALPARAM;
1215 1216 1217
	}
	break;
    case MIXER_GETCONTROLDETAILSF_LISTTEXT:
1218
	TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%08x)\n",
1219
              lpmcd->dwControlID);
1220 1221 1222 1223

	ret = MMSYSERR_INVALPARAM;
	if (MIX_CheckControl(mix, lpmcd->dwControlID))
	{
1224
	    DWORD c = lpmcd->dwControlID - 1;
1225 1226 1227 1228

	    if (mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX ||
		mix->ctrl[c].ctrl.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
	    {
1229
		LPMIXERCONTROLDETAILS_LISTTEXTW	mcdlt;
1230 1231
		int i, j;

1232
		mcdlt = lpmcd->paDetails;
1233 1234 1235 1236 1237 1238
		for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
		{
		    if (WINE_CHN_SUPPORTS(mix->recMask, j))
		    {
			mcdlt[i].dwParam1 = MAKELONG(LINEID_RECORD, j);
			mcdlt[i].dwParam2 = 0;
1239 1240
			MultiByteToWideChar(CP_ACP, 0, MIX_Names[j], -1, 
                                            mcdlt[i].szName, sizeof(mcdlt[i]) / sizeof(WCHAR));
1241 1242 1243 1244 1245 1246 1247
			i++;
		    }
		}
		if (i != lpmcd->u.cMultipleItems) FIXME("bad count\n");
		ret = MMSYSERR_NOERROR;
	    }
	}
1248 1249
	break;
    default:
1250 1251
	WARN("Unknown flag (%08lx)\n",
             fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1252 1253 1254 1255 1256 1257 1258
    }
    return ret;
}

/**************************************************************************
 * 				MIX_SetControlDetails		[internal]
 */
1259 1260
static	DWORD	MIX_SetControlDetails(WORD wDevID, LPMIXERCONTROLDETAILS lpmcd,
                                      DWORD fdwDetails)
1261
{
1262 1263 1264 1265
    DWORD		ret = MMSYSERR_NOTSUPPORTED;
    DWORD		c, chnl;
    int			val;
    struct mixer*	mix;
1266

1267
    TRACE("(%04X, %p, %u);\n", wDevID, lpmcd, fdwDetails);
1268

1269 1270 1271 1272 1273 1274
    if (lpmcd == NULL) {
        TRACE("invalid parameter: lpmcd == NULL\n");
        return MMSYSERR_INVALPARAM;
    }

    if ((mix = MIX_Get(wDevID)) == NULL) {
1275
        WARN("bad device ID: %04X\n", wDevID);
1276 1277
        return MMSYSERR_BADDEVICEID;
    }
1278 1279

    switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK)
1280
    {
1281
    case MIXER_GETCONTROLDETAILSF_VALUE:
1282
	TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%08x)\n", lpmcd->dwControlID);
1283 1284 1285
	if (MIX_CheckControl(mix, lpmcd->dwControlID))
	{
	    c = lpmcd->dwControlID - 1;
1286

1287
            TRACE("dwLineID=%08x\n",mix->ctrl[c].dwLineID);
1288

1289
	    chnl = HIWORD(mix->ctrl[c].dwLineID);
1290
	    if (chnl == LINEID_DST)
1291 1292
		chnl = LOWORD(mix->ctrl[c].dwLineID) ?
                    SOUND_MIXER_RECLEV : SOUND_MIXER_VOLUME;
1293

1294
	    switch (mix->ctrl[c].ctrl.dwControlType)
1295
	    {
1296 1297 1298
	    case MIXERCONTROL_CONTROLTYPE_VOLUME:
		{
		    LPMIXERCONTROLDETAILS_UNSIGNED	mcdu;
1299

1300
                    if (lpmcd->cbDetails !=
1301
                        sizeof(MIXERCONTROLDETAILS_UNSIGNED)) {
1302 1303
                        WARN("invalid parameter: cbDetails = %d\n",
                             lpmcd->cbDetails);
1304 1305 1306
                        return MMSYSERR_INVALPARAM;
                    }

1307
                    TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n",
1308 1309 1310
                          getControlType(mix->ctrl[c].ctrl.dwControlType),
                          lpmcd->cChannels);

1311
                    mcdu = lpmcd->paDetails;
1312
		    /* val should contain 00RL */
1313
		    switch (lpmcd->cChannels)
1314
		    {
1315 1316
		    case 1:
			/* mono... so R = L */
1317
			TRACE("Setting RL to %d\n", mcdu->dwValue);
1318 1319 1320 1321
			val = 0x101 * ((mcdu->dwValue * 100) >> 16);
			break;
		    case 2:
			/* stereo, left is paDetails[0] */
1322
			TRACE("Setting L to %d\n", mcdu->dwValue);
1323
			val = ((mcdu->dwValue * 100.0) / 65536.0) + 0.5;
1324
                        mcdu++;
1325
			TRACE("Setting R to %d\n", mcdu->dwValue);
1326
			val += (int)(((mcdu->dwValue * 100) / 65536.0) + 0.5) << 8;
1327 1328
			break;
		    default:
1329
			WARN("Unsupported cChannels (%d)\n", lpmcd->cChannels);
1330 1331
			return MMSYSERR_INVALPARAM;
		    }
1332 1333

		    if (mix->volume[chnl] == -1)
1334
		    {
1335 1336
			if (!MIX_SetVal(mix, chnl, val)) {
                            WARN("invalid parameter\n");
1337
			    return MMSYSERR_INVALPARAM;
1338
                        }
1339
		    }
1340
		    else
1341 1342
		    {
			mix->volume[chnl] = val;
1343 1344 1345
		    }
		}
		ret = MMSYSERR_NOERROR;
1346
		break;
1347
	    case MIXERCONTROL_CONTROLTYPE_MUTE:
1348
	    case MIXERCONTROL_CONTROLTYPE_ONOFF:
1349 1350
		{
		    LPMIXERCONTROLDETAILS_BOOLEAN	mcdb;
1351

1352
                    if (lpmcd->cbDetails !=
1353
                        sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1354 1355
                        WARN("invalid parameter: cbDetails = %d\n",
                             lpmcd->cbDetails);
1356 1357 1358
                        return MMSYSERR_INVALPARAM;
                    }

1359
                    TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1360 1361 1362
                          getControlType(mix->ctrl[c].ctrl.dwControlType),
                          lpmcd->cChannels);

1363
                    mcdb = lpmcd->paDetails;
1364
		    if (mcdb->fValue)
1365
		    {
1366 1367 1368 1369
                        /* save the volume and then set it to 0 */
			if (!MIX_GetVal(mix, chnl, &mix->volume[chnl]) ||
                            !MIX_SetVal(mix, chnl, 0)) {
                            WARN("invalid parameter\n");
1370
			    return MMSYSERR_INVALPARAM;
1371
                        }
1372 1373
		    }
		    else
1374
		    {
1375
			if (mix->volume[chnl] == -1)
1376 1377 1378
			{
			    ret = MMSYSERR_NOERROR;
			    break;
Ed Snow's avatar
Ed Snow committed
1379
			}
1380 1381
			if (!MIX_SetVal(mix, chnl, mix->volume[chnl])) {
                            WARN("invalid parameter\n");
1382
			    return MMSYSERR_INVALPARAM;
1383
                        }
1384
			mix->volume[chnl] = -1;
1385 1386 1387
		    }
		}
		ret = MMSYSERR_NOERROR;
1388
		break;
1389 1390 1391 1392 1393 1394 1395
	    case MIXERCONTROL_CONTROLTYPE_MIXER:
	    case MIXERCONTROL_CONTROLTYPE_MUX:
		{
		    LPMIXERCONTROLDETAILS_BOOLEAN	mcdb;
		    unsigned				mask;
		    int					i, j;

1396
                    if (lpmcd->cbDetails !=
1397
                        sizeof(MIXERCONTROLDETAILS_BOOLEAN)) {
1398 1399
                        WARN("invalid parameter: cbDetails = %d\n",
                             lpmcd->cbDetails);
1400 1401 1402
                        return MMSYSERR_INVALPARAM;
                    }

1403
                    TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n",
1404 1405 1406
                          getControlType(mix->ctrl[c].ctrl.dwControlType),
                          lpmcd->cChannels);

1407
		    /* we mute both channels at the same time */
1408
                    mcdb = lpmcd->paDetails;
1409 1410 1411
		    mask = 0;
		    for (i = j = 0; j < SOUND_MIXER_NRDEVICES; j++)
		    {
1412 1413
			if (WINE_CHN_SUPPORTS(mix->recMask, j) &&
                            mcdb[i++].fValue)
1414 1415
			{
			    /* a mux can only select one line at a time... */
1416
			    if (mix->singleRecChannel && mask != 0)
1417 1418 1419 1420 1421 1422 1423
			    {
				FIXME("!!!\n");
				return MMSYSERR_INVALPARAM;
			    }
			    mask |= WINE_CHN_MASK(j);
			}
		    }
1424 1425
		    if (i != lpmcd->u.cMultipleItems)
                        FIXME("bad count\n");
1426
		    TRACE("writing %04x as rec src\n", mask);
1427
		    if (!MIX_SetRecSrc(mix, mask))
1428 1429 1430 1431 1432
			ERR("Can't write new mixer settings\n");
		    else
			ret = MMSYSERR_NOERROR;
		}
		break;
1433 1434 1435 1436
	    }
	}
	break;
    default:
1437 1438
	WARN("Unknown SetControlDetails flag (%08lx)\n",
             fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK);
1439
    }
1440
    return ret;
1441 1442 1443 1444 1445
}

/**************************************************************************
 * 				MIX_Init			[internal]
 */
1446
LRESULT OSS_MixerInit(void)
1447
{
1448
    int	i, mixer;
1449

1450 1451
    TRACE("()\n");

1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
    MIX_NumMixers = 0;

    for (i = 0; i < MAX_MIXERDRV; i++) {
        char name[32];

        if (i == 0)
            sprintf(name, "/dev/mixer");
        else
            sprintf(name, "/dev/mixer%d", i);

        if ((mixer = open(name, O_RDWR)) >= 0) {
#ifdef SOUND_MIXER_INFO
            mixer_info info;
            if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
1466 1467
                MIX_Mixers[MIX_NumMixers].name = HeapAlloc(GetProcessHeap(),0,strlen(info.name) + 1);
                strcpy(MIX_Mixers[MIX_NumMixers].name, info.name);
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
            } else {
                /* FreeBSD up to at least 5.2 provides this ioctl, but does not
                 * implement it properly, and there are probably similar issues
                 * on other platforms, so we warn but try to go ahead.
                 */
                WARN("%s: cannot read SOUND_MIXER_INFO!\n", name);
            }
#endif
            close(mixer);

1478 1479
            MIX_Mixers[MIX_NumMixers].dev_name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
            strcpy(MIX_Mixers[MIX_NumMixers].dev_name, name);
1480
            MIX_NumMixers++;
1481
            MIX_Open(MIX_NumMixers - 1, NULL, 0); /* FIXME */
1482 1483 1484
        } else {
            WARN("couldn't open %s\n", name);
        }
1485
    }
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497

    if (MIX_NumMixers == 0) {
        WARN("no driver\n");
        return MMSYSERR_NODRIVER;
    }

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				MIX_Exit			[internal]
 */
1498
LRESULT OSS_MixerExit(void)
1499 1500 1501 1502 1503 1504
{
    int	i;

    TRACE("()\n");

    for (i = 0; i < MIX_NumMixers; i++) {
1505 1506
        HeapFree(GetProcessHeap(),0,MIX_Mixers[i].name);
        HeapFree(GetProcessHeap(),0,MIX_Mixers[i].dev_name);
1507 1508
    }

1509
    return MMSYSERR_NOERROR;
1510
}
1511

1512 1513 1514 1515 1516
/**************************************************************************
 * 				MIX_GetNumDevs			[internal]
 */
static	DWORD	MIX_GetNumDevs(void)
{
1517 1518
    TRACE("()\n");

1519 1520 1521
    return MIX_NumMixers;
}

1522
#endif /* HAVE_OSS */
1523

Alexandre Julliard's avatar
Alexandre Julliard committed
1524
/**************************************************************************
1525
 * 				mxdMessage (WINEOSS.3)
Alexandre Julliard's avatar
Alexandre Julliard committed
1526
 */
1527 1528
DWORD WINAPI OSS_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
			    DWORD_PTR dwParam1, DWORD_PTR dwParam2)
Alexandre Julliard's avatar
Alexandre Julliard committed
1529
{
1530
#ifdef HAVE_OSS
1531
    TRACE("(%04X, %s, %08lX, %08lX, %08lX);\n", wDevID, getMessage(wMsg),
1532
          dwUser, dwParam1, dwParam2);
1533 1534

    switch (wMsg)
1535
    {
1536 1537 1538 1539 1540 1541
    case DRVM_INIT:
    case DRVM_EXIT:
    case DRVM_ENABLE:
    case DRVM_DISABLE:
	/* FIXME: Pretend this is supported */
	return 0;
1542
    case MXDM_GETDEVCAPS:
1543
        return MIX_GetDevCaps(wDevID, (LPMIXERCAPSW)dwParam1, dwParam2);
1544
    case MXDM_GETLINEINFO:
1545
	return MIX_GetLineInfo(wDevID, (LPMIXERLINEW)dwParam1, dwParam2);
1546
    case MXDM_GETNUMDEVS:
1547
	return MIX_GetNumDevs();
1548
    case MXDM_OPEN:
1549 1550
	return MMSYSERR_NOERROR;
	/* MIX_Open(wDevID, (LPMIXEROPENDESC)dwParam1, dwParam2); */
1551 1552 1553
    case MXDM_CLOSE:
	return MMSYSERR_NOERROR;
    case MXDM_GETLINECONTROLS:
1554
	return MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2);
1555 1556 1557 1558 1559 1560
    case MXDM_GETCONTROLDETAILS:
	return MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
    case MXDM_SETCONTROLDETAILS:
	return MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2);
    default:
	WARN("unknown message %d!\n", wMsg);
1561
	return MMSYSERR_NOTSUPPORTED;
1562 1563
    }
#else
1564
    TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg,
1565 1566
          dwUser, dwParam1, dwParam2);

1567
    return MMSYSERR_NOTENABLED;
1568
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
1569
}