📄 mixer.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample MIXER Wine Driver for Linux * * Copyright 1997 Marcus Meissner * 1999,2001 Eric Pouech * * 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 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "config.h"#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <fcntl.h>#include <errno.h>#include <assert.h>#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif#define NONAMELESSUNION#define NONAMELESSSTRUCT#include "windef.h"#include "winbase.h"#include "mmddk.h"#include "oss.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(mmaux);#ifdef HAVE_OSS#define WINE_MIXER_MANUF_ID 0xAA#define WINE_MIXER_PRODUCT_ID 0x55#define WINE_MIXER_VERSION 0x0100#define WINE_MIXER_NAME "WINE OSS Mixer"#define WINE_CHN_MASK(_x) (1L << (_x))#define WINE_CHN_SUPPORTS(_c, _x) ((_c) & WINE_CHN_MASK(_x))/* Bass and Treble are no longer in the mask as Windows does not handle them */#define WINE_MIXER_MASK_SPEAKER (WINE_CHN_MASK(SOUND_MIXER_SYNTH) | \ WINE_CHN_MASK(SOUND_MIXER_PCM) | \ WINE_CHN_MASK(SOUND_MIXER_LINE) | \ WINE_CHN_MASK(SOUND_MIXER_MIC) | \ 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) )/* 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 */static const char* MIX_Labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;static const char* MIX_Names [SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;struct mixerCtrl{ DWORD dwLineID; MIXERCONTROLA ctrl;};struct mixer{ const char* name; 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 0x0001static int MIX_NumMixers;static struct mixer MIX_Mixers[1];/************************************************************************** * MIX_FillLineControls [internal] */static void MIX_FillLineControls(struct mixer* mix, int c, DWORD lineID, DWORD dwType){ struct mixerCtrl* mc = &mix->ctrl[c]; int j; mc->dwLineID = lineID; mc->ctrl.cbStruct = sizeof(MIXERCONTROLA); mc->ctrl.dwControlID = c + 1; mc->ctrl.dwControlType = dwType; switch (dwType) { case MIXERCONTROL_CONTROLTYPE_VOLUME: mc->ctrl.fdwControl = 0; mc->ctrl.cMultipleItems = 0; lstrcpynA(mc->ctrl.szShortName, "Vol", MIXER_SHORT_NAME_CHARS); lstrcpynA(mc->ctrl.szName, "Volume", MIXER_LONG_NAME_CHARS); memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds)); /* CONTROLTYPE_VOLUME uses the MIXER_CONTROLDETAILS_UNSIGNED struct, * [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)); break; case MIXERCONTROL_CONTROLTYPE_MUTE: case MIXERCONTROL_CONTROLTYPE_ONOFF: mc->ctrl.fdwControl = 0; mc->ctrl.cMultipleItems = 0; lstrcpynA(mc->ctrl.szShortName, "Mute", MIXER_SHORT_NAME_CHARS); lstrcpynA(mc->ctrl.szName, "Mute", MIXER_LONG_NAME_CHARS); 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++; lstrcpynA(mc->ctrl.szShortName, "Mixer", MIXER_SHORT_NAME_CHARS); lstrcpynA(mc->ctrl.szName, "Mixer", MIXER_LONG_NAME_CHARS); memset(&mc->ctrl.Bounds, 0, sizeof(mc->ctrl.Bounds)); memset(&mc->ctrl.Metrics, 0, sizeof(mc->ctrl.Metrics)); break; default: FIXME("Internal error: unknown type: %08lx\n", dwType); } TRACE("ctrl[%2d]: typ=%08lx lin=%08lx\n", c + 1, dwType, lineID);}/****************************************************************** * MIX_GetMixer * * */static struct mixer* MIX_Get(WORD wDevID){ if (wDevID >= MIX_NumMixers || MIX_Mixers[wDevID].name == NULL) return NULL; 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; TRACE("(%04X, %p, %lu);\n", wDevID, lpMod, flags); /* 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 the equivalent of mixer identification * (with a reference to a wave, midi.. handle */ if (!(mix = MIX_Get(wDevID))) return MMSYSERR_BADDEVICEID; if ((mixer = open(mix->name, O_RDWR)) < 0) { if (errno == ENODEV || errno == ENXIO) { /* no driver present */ return MMSYSERR_NODRIVER; } return MMSYSERR_ERROR; } if (ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &mix->devMask) == -1) { perror("ioctl mixer SOUND_MIXER_DEVMASK"); ret = MMSYSERR_ERROR; goto error; } mix->devMask &= WINE_MIXER_MASK_SPEAKER; if (mix->devMask == 0) { ret = MMSYSERR_NODRIVER; goto error; } if (ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &mix->stereoMask) == -1) { perror("ioctl mixer SOUND_MIXER_STEREODEVS"); ret = MMSYSERR_ERROR; goto error; } mix->stereoMask &= WINE_MIXER_MASK_SPEAKER; if (ioctl(mixer, SOUND_MIXER_READ_RECMASK, &mix->recMask) == -1) { perror("ioctl mixer SOUND_MIXER_RECMASK"); 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"); } if (ioctl(mixer, SOUND_MIXER_READ_CAPS, &caps) == -1) { perror("ioctl mixer SOUND_MIXER_READ_CAPS"); ret = MMSYSERR_ERROR; goto error; } mix->singleRecChannel = caps & SOUND_CAP_EXCL_INPUT; TRACE("dev=%04x rec=%04x stereo=%04x %s\n", mix->devMask, mix->recMask, mix->stereoMask, mix->singleRecChannel ? "single" : "multiple"); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 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 */ } if (!(mix->ctrl = HeapAlloc(GetProcessHeap(), 0, sizeof(mix->ctrl[0]) * mix->numCtrl))) { ret = MMSYSERR_NOMEM; goto error; } j = 0; MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_VOLUME); MIX_FillLineControls(mix, j++, MAKELONG(0, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE); MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), mix->singleRecChannel ? MIXERCONTROL_CONTROLTYPE_MUX : MIXERCONTROL_CONTROLTYPE_MIXER); MIX_FillLineControls(mix, j++, MAKELONG(1, LINEID_DST), MIXERCONTROL_CONTROLTYPE_MUTE/*EPP*/); 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;}/************************************************************************** * MIX_GetVal [internal] */static BOOL MIX_GetVal(struct mixer* mix, int chn, int* val){ int mixer; BOOL ret = FALSE; if ((mixer = open(mix->name, O_RDWR)) < 0) { /* FIXME: ENXIO => no mixer installed */ WARN("mixer device not available !\n"); } else { if (ioctl(mixer, MIXER_READ(chn), val) >= 0) { TRACE("Reading volume %x on %d\n", *val, chn); ret = TRUE; } close(mixer); } return ret;}/************************************************************************** * MIX_SetVal [internal] */static BOOL MIX_SetVal(struct mixer* mix, int chn, int val){ int mixer; BOOL ret = FALSE; TRACE("Writing volume %x on %d\n", val, chn); if ((mixer = open(mix->name, O_RDWR)) < 0) { /* FIXME: ENXIO => no mixer installed */ WARN("mixer device not available !\n"); } else { if (ioctl(mixer, MIXER_WRITE(chn), &val) >= 0) { ret = TRUE; } close(mixer); } return ret;}/****************************************************************** * MIX_GetRecSrc * * */static BOOL MIX_GetRecSrc(struct mixer* mix, unsigned* mask){ int mixer; BOOL ret = FALSE; if ((mixer = open(mix->name, O_RDWR)) >= 0) { if (ioctl(mixer, SOUND_MIXER_READ_RECSRC, &mask) >= 0) ret = TRUE; close(mixer); } return ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -