667 lines
17 KiB
C
667 lines
17 KiB
C
|
/*++
|
|||
|
"@(#) NEC mixerlib.c 1.1 95/03/22 21:23:32"
|
|||
|
|
|||
|
Copyright (c) 1995 NEC Corporation.
|
|||
|
Copyright (c) 1993 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
mixer.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains code for controlling mixer devices.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <soundlib.h>
|
|||
|
#include <ntddmix.h>
|
|||
|
|
|||
|
//
|
|||
|
// Internal function definitions.
|
|||
|
//
|
|||
|
VOID
|
|||
|
SoundMixerNotify(
|
|||
|
IN OUT PIRP pIrp,
|
|||
|
IN OUT PMIXER_INFO pMixerInfo
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT, SoundInitMixerInfo)
|
|||
|
#pragma alloc_text(INIT, SoundInitDataItem)
|
|||
|
|
|||
|
#pragma alloc_text(PAGE, SoundMixerDispatch)
|
|||
|
#pragma alloc_text(PAGE, SoundSetLineNotify)
|
|||
|
#pragma alloc_text(PAGE, SoundSetVolumeControlId)
|
|||
|
#pragma alloc_text(PAGE, SoundWriteMixerVolume)
|
|||
|
#pragma alloc_text(PAGE, SoundReadMixerCombinedVolume)
|
|||
|
#endif
|
|||
|
//
|
|||
|
// Internal routines
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SoundMixerDispatch(
|
|||
|
IN OUT PLOCAL_DEVICE_INFO pLDI,
|
|||
|
IN PIRP pIrp,
|
|||
|
IN PIO_STACK_LOCATION IrpStack
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Mixer Irp call dispatcher
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pLDI - Pointer to local device data
|
|||
|
pIrp - Pointer to IO request packet
|
|||
|
IrpStack - Pointer to current stack location
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Return status from dispatched routine
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
switch (IrpStack->MajorFunction) {
|
|||
|
|
|||
|
//
|
|||
|
// Anybody can open
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_CREATE:
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MJ_CLOSE:
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MJ_WRITE:
|
|||
|
|
|||
|
Status =
|
|||
|
(*((PMIXER_INFO)pLDI->DeviceSpecificData)->HwSetControlData)(
|
|||
|
(PMIXER_INFO)pLDI->DeviceSpecificData,
|
|||
|
IrpStack->Parameters.Write.ByteOffset.LowPart, // ControlID
|
|||
|
IrpStack->Parameters.Write.Length, // Data length
|
|||
|
pIrp->AssociatedIrp.SystemBuffer); // Data
|
|||
|
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case IRP_MJ_DEVICE_CONTROL:
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the IOCTL function
|
|||
|
// Note that APIs which are possibly asynchronous do not
|
|||
|
// go through the Irp cleanup at the end here because they
|
|||
|
// may get completed before returning here or they are made
|
|||
|
// accessible to other requests by being queued.
|
|||
|
//
|
|||
|
|
|||
|
switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
|
|||
|
case IOCTL_MIX_GET_CONFIGURATION:
|
|||
|
|
|||
|
//
|
|||
|
// say how much we're sending back
|
|||
|
//
|
|||
|
|
|||
|
Status = (*pLDI->DeviceInit->DevCapsRoutine)(pLDI, pIrp, IrpStack);
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IOCTL_MIX_GET_CONTROL_DATA:
|
|||
|
case IOCTL_MIX_GET_LINE_DATA:
|
|||
|
{
|
|||
|
PMIXER_INFO MixerInfo;
|
|||
|
|
|||
|
/*
|
|||
|
** Find out what's being read
|
|||
|
*/
|
|||
|
|
|||
|
if (IrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(MIXER_DD_READ_DATA)) {
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
MixerInfo = ((PMIXER_INFO)pLDI->DeviceSpecificData);
|
|||
|
ASSERT(MixerInfo->Key == MIX_INFO_KEY);
|
|||
|
|
|||
|
Status =
|
|||
|
(IrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_MIX_GET_CONTROL_DATA ? *MixerInfo->HwGetControlData :
|
|||
|
*MixerInfo->HwGetLineData) (
|
|||
|
MixerInfo,
|
|||
|
((PMIXER_DD_READ_DATA)
|
|||
|
pIrp->AssociatedIrp.SystemBuffer)->Id, // Control Id
|
|||
|
IrpStack->Parameters.DeviceIoControl.
|
|||
|
OutputBufferLength, // Data length
|
|||
|
pIrp->AssociatedIrp.SystemBuffer); // Data
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
pIrp->IoStatus.Information =
|
|||
|
IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case IOCTL_MIX_REQUEST_NOTIFY:
|
|||
|
|
|||
|
/*
|
|||
|
** Check the parameters
|
|||
|
*/
|
|||
|
|
|||
|
if (IrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(MIXER_DD_REQUEST_NOTIFY) ||
|
|||
|
IrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(MIXER_DD_REQUEST_NOTIFY)) {
|
|||
|
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
{
|
|||
|
PMIXER_DD_REQUEST_NOTIFY MixerNotifyData;
|
|||
|
PMIXER_INFO MixerInfo;
|
|||
|
|
|||
|
MixerInfo = ((PMIXER_INFO)pLDI->DeviceSpecificData);
|
|||
|
|
|||
|
MixerNotifyData = (PMIXER_DD_REQUEST_NOTIFY)
|
|||
|
pIrp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
/*
|
|||
|
** Check if the 'timer' has been initialized
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
if (!MixerNotifyData->Initialized) {
|
|||
|
|
|||
|
MixerNotifyData->CurrentLogicalTime =
|
|||
|
MixerInfo->CurrentLogicalTime;
|
|||
|
MixerNotifyData->Initialized = (BOOLEAN)TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/*
|
|||
|
** See if there's more data for this IOCTL
|
|||
|
** right now.
|
|||
|
**
|
|||
|
** We complete these Ioctls in 3 places :
|
|||
|
**
|
|||
|
** 1. Here - when they come in and there's data for them
|
|||
|
**
|
|||
|
** -- just this one completes
|
|||
|
**
|
|||
|
** 2. When data changes
|
|||
|
**
|
|||
|
** -- everything completes
|
|||
|
**
|
|||
|
** 3. When a close (cleanup) comes in
|
|||
|
**
|
|||
|
** -- All irps for the handle being closed complete
|
|||
|
*/
|
|||
|
|
|||
|
if (RtlLargeIntegerLessThan(
|
|||
|
MixerNotifyData->CurrentLogicalTime,
|
|||
|
MixerInfo->CurrentLogicalTime)) {
|
|||
|
|
|||
|
/*
|
|||
|
** Generate notification and complete Irp
|
|||
|
** immediately
|
|||
|
*/
|
|||
|
|
|||
|
SoundMixerNotify(pIrp, MixerInfo);
|
|||
|
Status = STATUS_PENDING;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (!RtlLargeIntegerEqualTo(
|
|||
|
MixerNotifyData->CurrentLogicalTime,
|
|||
|
MixerInfo->CurrentLogicalTime)) {
|
|||
|
|
|||
|
/*
|
|||
|
** Don't accept Irps with invalid 'new' times
|
|||
|
** This should catch some rogue users.
|
|||
|
*/
|
|||
|
|
|||
|
Status = STATUS_INVALID_PARAMETER;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Add to our notification list
|
|||
|
*/
|
|||
|
|
|||
|
SoundAddIrpToCancellableQ(&MixerInfo->NotifyQueue, pIrp, FALSE);
|
|||
|
|
|||
|
Status = STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
default:
|
|||
|
dprintf2(("Unimplemented IOCTL (%08lXH) requested", IrpStack->Parameters.DeviceIoControl.IoControlCode));
|
|||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
break;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
case IRP_MJ_CLEANUP:
|
|||
|
/******************************************************************
|
|||
|
*
|
|||
|
* Dispatch anyone waiting for notification from this device now.
|
|||
|
*
|
|||
|
******************************************************************/
|
|||
|
|
|||
|
SoundFreePendingIrps(
|
|||
|
&((PMIXER_INFO)pLDI->DeviceSpecificData)->NotifyQueue,
|
|||
|
IrpStack->FileObject);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
default:
|
|||
|
dprintf1(("Unimplemented major function requested: %08lXH", IrpStack->MajorFunction));
|
|||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SoundMixerChangedItem(
|
|||
|
IN OUT PMIXER_INFO MixerInfo,
|
|||
|
IN OUT PMIXER_DATA_ITEM MixerItem
|
|||
|
)
|
|||
|
{
|
|||
|
ASSERTMSG("Invalid mixer info!", MixerInfo->Key == MIX_INFO_KEY);
|
|||
|
|
|||
|
/*
|
|||
|
** There are 2 tasks :
|
|||
|
**
|
|||
|
** 1. Increment the current 'logical' time
|
|||
|
**
|
|||
|
** 2. Move the item to the head of the list and set its current time
|
|||
|
**
|
|||
|
** 3. Notify all those waiting for notification
|
|||
|
*/
|
|||
|
|
|||
|
RemoveEntryList(&MixerItem->Entry);
|
|||
|
|
|||
|
MixerInfo->CurrentLogicalTime =
|
|||
|
RtlLargeIntegerAdd(MixerInfo->CurrentLogicalTime,
|
|||
|
RtlConvertLongToLargeInteger(1L));
|
|||
|
|
|||
|
MixerItem->LastSet = MixerInfo->CurrentLogicalTime;
|
|||
|
|
|||
|
InsertHeadList(&MixerInfo->ChangedItems, &MixerItem->Entry);
|
|||
|
|
|||
|
/*
|
|||
|
** Complete all notification Irps.
|
|||
|
*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL OldIrql;
|
|||
|
PIRP pIrp;
|
|||
|
|
|||
|
while (TRUE) {
|
|||
|
|
|||
|
pIrp = SoundRemoveFromCancellableQ(&MixerInfo->NotifyQueue);
|
|||
|
|
|||
|
if (pIrp == NULL) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
SoundMixerNotify(pIrp, MixerInfo);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundMixerNotify(
|
|||
|
IN OUT PIRP pIrp,
|
|||
|
IN OUT PMIXER_INFO MixerInfo
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Mixer notification dispatcher
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pIrp - Pointer to IO request packet
|
|||
|
MixerInfo - mixer specific data
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Notes:
|
|||
|
It's ASSUMED that the Irp will be completed by this routine and has been
|
|||
|
removed from any lists etc. This assumption will be valid either because
|
|||
|
|
|||
|
some data has changed so all current notification Irps will become 'old'
|
|||
|
(note - we don't put them on the list if they're 'new' and, because we're
|
|||
|
using buffered IO the application can't change the data once we've got it).
|
|||
|
or
|
|||
|
We're received a new notification Irp which is 'old'.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PMIXER_DD_REQUEST_NOTIFY MixerNotifyData;
|
|||
|
PMIXER_DATA_ITEM LastChanged;
|
|||
|
|
|||
|
MixerNotifyData = (PMIXER_DD_REQUEST_NOTIFY)
|
|||
|
pIrp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
/*
|
|||
|
** Find the oldest item to dispatch. However, there won't be
|
|||
|
** many so start at the beginning.
|
|||
|
**
|
|||
|
** It is assumed that if this routine is called then there must
|
|||
|
** be something to dispatch.
|
|||
|
*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
|
|||
|
/*
|
|||
|
** If we get in here the list must have at least one entry in
|
|||
|
** it otherwise the current time would never have got above 0
|
|||
|
*/
|
|||
|
|
|||
|
ASSERTMSG("No changed items but notify routine called!",
|
|||
|
MixerInfo->ChangedItems.Flink != &MixerInfo->ChangedItems);
|
|||
|
|
|||
|
LastChanged =
|
|||
|
CONTAINING_RECORD(MixerInfo->ChangedItems.Flink,
|
|||
|
MIXER_DATA_ITEM,
|
|||
|
Entry);
|
|||
|
|
|||
|
ASSERTMSG("Last changed not current!",
|
|||
|
RtlLargeIntegerEqualTo(LastChanged->LastSet,
|
|||
|
MixerInfo->CurrentLogicalTime));
|
|||
|
|
|||
|
for (ListEntry = MixerInfo->ChangedItems.Flink->Flink;
|
|||
|
ListEntry != &MixerInfo->ChangedItems;
|
|||
|
ListEntry = ListEntry->Flink) {
|
|||
|
|
|||
|
PMIXER_DATA_ITEM MixerDataItem;
|
|||
|
|
|||
|
MixerDataItem =
|
|||
|
CONTAINING_RECORD(ListEntry, MIXER_DATA_ITEM, Entry);
|
|||
|
|
|||
|
if (RtlLargeIntegerGreaterThan(
|
|||
|
MixerDataItem->LastSet,
|
|||
|
MixerNotifyData->CurrentLogicalTime)
|
|||
|
) {
|
|||
|
|
|||
|
/*
|
|||
|
** The item we're looking at is more recent than the
|
|||
|
** the Irp so it's a candidate - continue search
|
|||
|
*/
|
|||
|
|
|||
|
LastChanged = MixerDataItem;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
/*
|
|||
|
** The item is older (or of equal vintage) to the Irp
|
|||
|
** so stop the search and use the last (cached) item
|
|||
|
*/
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
** Note - we MUST have found something
|
|||
|
**
|
|||
|
** Dispatch the Irp with the requisite data
|
|||
|
*/
|
|||
|
|
|||
|
MixerNotifyData->Message = LastChanged->Message;
|
|||
|
MixerNotifyData->Id = LastChanged->Id;
|
|||
|
MixerNotifyData->CurrentLogicalTime = LastChanged->LastSet;
|
|||
|
|
|||
|
/*
|
|||
|
** Make sure the data gets copied back when this finally
|
|||
|
** completes.
|
|||
|
*/
|
|||
|
|
|||
|
pIrp->IoStatus.Information = sizeof(MIXER_DD_REQUEST_NOTIFY);
|
|||
|
pIrp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
/*
|
|||
|
** There's no need to give the huge IO_SOUND_INCREMENT - this stuff
|
|||
|
** is for user interface update.
|
|||
|
*/
|
|||
|
|
|||
|
IoCompleteRequest(pIrp, IO_KEYBOARD_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundInitMixerInfo(
|
|||
|
PMIXER_INFO MixerInfo,
|
|||
|
PMIXER_DD_GET_SET_DATA HwGetLineData,
|
|||
|
PMIXER_DD_GET_SET_DATA HwGetControlData,
|
|||
|
PMIXER_DD_GET_SET_DATA HwGetCombinedControlData,
|
|||
|
PMIXER_DD_GET_SET_DATA HwSetControlData
|
|||
|
)
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
RtlZeroMemory((PVOID)MixerInfo, sizeof(*MixerInfo));
|
|||
|
|
|||
|
MixerInfo->Key = MIX_INFO_KEY;
|
|||
|
|
|||
|
InitializeListHead(&MixerInfo->NotifyQueue);
|
|||
|
InitializeListHead(&MixerInfo->ChangedItems);
|
|||
|
|
|||
|
MixerInfo->HwGetLineData = HwGetLineData;
|
|||
|
MixerInfo->HwGetControlData = HwGetControlData;
|
|||
|
MixerInfo->HwGetCombinedControlData = HwGetCombinedControlData;
|
|||
|
MixerInfo->HwSetControlData = HwSetControlData;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundInitDataItem(
|
|||
|
PMIXER_INFO MixerInfo,
|
|||
|
PMIXER_DATA_ITEM MixerDataItem,
|
|||
|
USHORT Message,
|
|||
|
USHORT Id
|
|||
|
)
|
|||
|
{
|
|||
|
ASSERTMSG("Mixer info not initialized!", MixerInfo->Key == MIX_INFO_KEY);
|
|||
|
|
|||
|
MixerDataItem->LastSet = RtlConvertLongToLargeInteger(0L);
|
|||
|
|
|||
|
MixerDataItem->Message = Message;
|
|||
|
MixerDataItem->Id = Id;
|
|||
|
InsertTailList(&MixerInfo->ChangedItems, &MixerDataItem->Entry);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundLineNotify(
|
|||
|
PLOCAL_DEVICE_INFO pLDI,
|
|||
|
UCHAR Code
|
|||
|
)
|
|||
|
{
|
|||
|
if (pLDI->LineNotify != NULL) {
|
|||
|
#if DBG
|
|||
|
PMIXER_INFO MixerInfo;
|
|||
|
|
|||
|
MixerInfo = (PMIXER_INFO)pLDI->MixerDevice->DeviceSpecificData;
|
|||
|
|
|||
|
ASSERT(MixerInfo->Key == MIX_INFO_KEY);
|
|||
|
#endif // DBG
|
|||
|
|
|||
|
/*
|
|||
|
** Synchronize with the mixer
|
|||
|
*/
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, TRUE);
|
|||
|
|
|||
|
(*pLDI->LineNotify)(pLDI, Code);
|
|||
|
|
|||
|
/*
|
|||
|
** Synchronize with the mixer
|
|||
|
*/
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, FALSE);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundSetLineNotify(
|
|||
|
PLOCAL_DEVICE_INFO pLDI,
|
|||
|
PSOUND_LINE_NOTIFY LineNotify
|
|||
|
)
|
|||
|
{
|
|||
|
pLDI->LineNotify = LineNotify;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SoundReadMixerVolume(
|
|||
|
PLOCAL_DEVICE_INFO pLDI,
|
|||
|
PWAVE_DD_VOLUME Volume
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PMIXER_INFO MixerInfo;
|
|||
|
ULONG Vol[2];
|
|||
|
|
|||
|
ASSERT(pLDI->MixerDevice != NULL &&
|
|||
|
pLDI->VolumeControlId != SOUND_MIXER_INVALID_CONTROL_ID);
|
|||
|
|
|||
|
/*
|
|||
|
** Synchronize with the mixer
|
|||
|
*/
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, TRUE);
|
|||
|
|
|||
|
MixerInfo = (PMIXER_INFO)pLDI->MixerDevice->DeviceSpecificData;
|
|||
|
|
|||
|
Status = (* MixerInfo->HwGetControlData)(MixerInfo,
|
|||
|
pLDI->VolumeControlId,
|
|||
|
sizeof(Vol),
|
|||
|
Vol);
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, FALSE);
|
|||
|
|
|||
|
/*
|
|||
|
** Adjust for 32-bit volumes
|
|||
|
*/
|
|||
|
|
|||
|
Volume->Left = Vol[0] << 16;
|
|||
|
Volume->Right = Vol[1] << 16;
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(Status));
|
|||
|
}
|
|||
|
VOID
|
|||
|
SoundReadMixerCombinedVolume(
|
|||
|
PLOCAL_DEVICE_INFO pLDI,
|
|||
|
PWAVE_DD_VOLUME Volume
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Read the 'real' volume specified by the control id for this device.
|
|||
|
This includes merging in any mute or master settings.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PMIXER_INFO MixerInfo;
|
|||
|
ULONG Vol[2];
|
|||
|
|
|||
|
ASSERT(pLDI->MixerDevice != NULL &&
|
|||
|
pLDI->VolumeControlId != SOUND_MIXER_INVALID_CONTROL_ID);
|
|||
|
|
|||
|
MixerInfo = (PMIXER_INFO)pLDI->MixerDevice->DeviceSpecificData;
|
|||
|
|
|||
|
ASSERT(MixerInfo->HwGetCombinedControlData != NULL);
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, TRUE);
|
|||
|
|
|||
|
Status = (* MixerInfo->HwGetCombinedControlData)(MixerInfo,
|
|||
|
pLDI->VolumeControlId,
|
|||
|
sizeof(Vol),
|
|||
|
Vol);
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, FALSE);
|
|||
|
|
|||
|
/*
|
|||
|
** Adjust for 32-bit volumes
|
|||
|
*/
|
|||
|
|
|||
|
Volume->Left = Vol[0] << 16;
|
|||
|
Volume->Right = Vol[1] << 16;
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(Status));
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundWriteMixerVolume(
|
|||
|
PLOCAL_DEVICE_INFO pLDI,
|
|||
|
PWAVE_DD_VOLUME Volume
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PMIXER_INFO MixerInfo;
|
|||
|
ULONG Vol[2];
|
|||
|
|
|||
|
Vol[0] = Volume->Left >> 16;
|
|||
|
Vol[1] = Volume->Right >> 16;
|
|||
|
|
|||
|
ASSERT(pLDI->MixerDevice != NULL &&
|
|||
|
pLDI->VolumeControlId != SOUND_MIXER_INVALID_CONTROL_ID);
|
|||
|
|
|||
|
MixerInfo = (PMIXER_INFO)pLDI->MixerDevice->DeviceSpecificData;
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, TRUE);
|
|||
|
|
|||
|
Status = (* MixerInfo->HwSetControlData)(MixerInfo,
|
|||
|
pLDI->VolumeControlId,
|
|||
|
sizeof(Vol),
|
|||
|
Vol);
|
|||
|
|
|||
|
SoundEnter(pLDI->MixerDevice, FALSE);
|
|||
|
|
|||
|
ASSERT(NT_SUCCESS(Status));
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
SoundSetVolumeControlId(
|
|||
|
PLOCAL_DEVICE_INFO pLDI,
|
|||
|
UCHAR VolumeControlId
|
|||
|
)
|
|||
|
{
|
|||
|
pLDI->VolumeControlId = VolumeControlId;
|
|||
|
}
|
|||
|
|