2 * PROJECT: ReactOS Sound System
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/wdmaud.drv/mmixer.c
6 * PURPOSE: WDM Audio Mixer API (User-mode part)
7 * PROGRAMMERS: Johannes Anderwald
13 PVOID
Alloc(ULONG NumBytes
);
14 MIXER_STATUS
Close(HANDLE hDevice
);
15 VOID
Free(PVOID Block
);
16 VOID
Copy(PVOID Src
, PVOID Dst
, ULONG NumBytes
);
17 MIXER_STATUS
Open(IN LPWSTR DevicePath
, OUT PHANDLE hDevice
);
18 MIXER_STATUS
Control(IN HANDLE hMixer
, IN ULONG dwIoControlCode
, IN PVOID lpInBuffer
, IN ULONG nInBufferSize
, OUT PVOID lpOutBuffer
, ULONG nOutBufferSize
, PULONG lpBytesReturned
);
19 MIXER_STATUS
Enum(IN PVOID EnumContext
, IN ULONG DeviceIndex
, OUT LPWSTR
* DeviceName
, OUT PHANDLE OutHandle
, OUT PHANDLE OutKey
);
20 MIXER_STATUS
OpenKey(IN HANDLE hKey
, IN LPWSTR SubKey
, IN ULONG DesiredAccess
, OUT PHANDLE OutKey
);
21 MIXER_STATUS
CloseKey(IN HANDLE hKey
);
22 MIXER_STATUS
QueryKeyValue(IN HANDLE hKey
, IN LPWSTR KeyName
, OUT PVOID
* ResultBuffer
, OUT PULONG ResultLength
, OUT PULONG KeyType
);
23 PVOID
AllocEventData(IN ULONG ExtraSize
);
24 VOID
FreeEventData(IN PVOID EventData
);
26 MIXER_CONTEXT MixerContext
=
28 sizeof(MIXER_CONTEXT
),
43 GUID CategoryGuid
= {STATIC_KSCATEGORY_AUDIO
};
49 OUT PVOID
* ResultBuffer
,
50 OUT PULONG ResultLength
,
53 if (RegQueryValueExW((HKEY
)hKey
, KeyName
, NULL
, KeyType
, NULL
, ResultLength
) == ERROR_FILE_NOT_FOUND
)
54 return MM_STATUS_UNSUCCESSFUL
;
56 *ResultBuffer
= HeapAlloc(GetProcessHeap(), 0, *ResultLength
);
57 if (*ResultBuffer
== NULL
)
58 return MM_STATUS_NO_MEMORY
;
60 if (RegQueryValueExW((HKEY
)hKey
, KeyName
, NULL
, KeyType
, *ResultBuffer
, ResultLength
) != ERROR_SUCCESS
)
62 HeapFree(GetProcessHeap(), 0, *ResultBuffer
);
63 return MM_STATUS_UNSUCCESSFUL
;
65 return MM_STATUS_SUCCESS
;
72 IN ULONG DesiredAccess
,
75 if (RegOpenKeyExW((HKEY
)hKey
, SubKey
, 0, DesiredAccess
, (PHKEY
)OutKey
) == ERROR_SUCCESS
)
76 return MM_STATUS_SUCCESS
;
78 return MM_STATUS_UNSUCCESSFUL
;
85 RegCloseKey((HKEY
)hKey
);
86 return MM_STATUS_SUCCESS
;
90 PVOID
Alloc(ULONG NumBytes
)
92 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, NumBytes
);
98 if (CloseHandle(hDevice
))
99 return MM_STATUS_SUCCESS
;
101 return MM_STATUS_UNSUCCESSFUL
;
107 HeapFree(GetProcessHeap(), 0, Block
);
111 Copy(PVOID Src
, PVOID Dst
, ULONG NumBytes
)
113 CopyMemory(Src
, Dst
, NumBytes
);
118 IN LPWSTR DevicePath
,
121 DevicePath
[1] = L
'\\';
122 *hDevice
= CreateFileW(DevicePath
,
123 GENERIC_READ
| GENERIC_WRITE
,
127 FILE_FLAG_OVERLAPPED
,
129 if (*hDevice
== INVALID_HANDLE_VALUE
)
131 return MM_STATUS_UNSUCCESSFUL
;
134 return MM_STATUS_SUCCESS
;
140 IN ULONG dwIoControlCode
,
142 IN ULONG nInBufferSize
,
143 OUT PVOID lpOutBuffer
,
144 ULONG nOutBufferSize
,
145 PULONG lpBytesReturned
)
147 OVERLAPPED Overlapped
;
149 DWORD Transferred
= 0;
151 /* Overlapped I/O is done here - this is used for waiting for completion */
152 ZeroMemory(&Overlapped
, sizeof(OVERLAPPED
));
153 Overlapped
.hEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
155 if ( ! Overlapped
.hEvent
)
156 return MM_STATUS_NO_MEMORY
;
158 /* Talk to the device */
159 IoResult
= DeviceIoControl(hMixer
,
168 /* If failure occurs, make sure it's not just due to the overlapped I/O */
171 if ( GetLastError() != ERROR_IO_PENDING
)
173 CloseHandle(Overlapped
.hEvent
);
175 if (GetLastError() == ERROR_MORE_DATA
|| GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
177 if ( lpBytesReturned
)
178 *lpBytesReturned
= Transferred
;
179 return MM_STATUS_MORE_ENTRIES
;
182 return MM_STATUS_UNSUCCESSFUL
;
186 /* Wait for the I/O to complete */
187 IoResult
= GetOverlappedResult(hMixer
,
192 /* Don't need this any more */
193 CloseHandle(Overlapped
.hEvent
);
196 return MM_STATUS_UNSUCCESSFUL
;
198 if ( lpBytesReturned
)
199 *lpBytesReturned
= Transferred
;
201 return MM_STATUS_SUCCESS
;
206 IN PVOID EnumContext
,
207 IN ULONG DeviceIndex
,
208 OUT LPWSTR
* DeviceName
,
209 OUT PHANDLE OutHandle
,
212 SP_DEVICE_INTERFACE_DATA InterfaceData
;
213 SP_DEVINFO_DATA DeviceData
;
214 PSP_DEVICE_INTERFACE_DETAIL_DATA_W DetailData
;
219 //printf("Enum EnumContext %p DeviceIndex %lu OutHandle %p\n", EnumContext, DeviceIndex, OutHandle);
221 InterfaceData
.cbSize
= sizeof(InterfaceData
);
222 InterfaceData
.Reserved
= 0;
224 Result
= SetupDiEnumDeviceInterfaces(EnumContext
,
232 if (GetLastError() == ERROR_NO_MORE_ITEMS
)
234 return MM_STATUS_NO_MORE_DEVICES
;
236 return MM_STATUS_UNSUCCESSFUL
;
239 Length
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
) + MAX_PATH
* sizeof(WCHAR
);
240 DetailData
= (PSP_DEVICE_INTERFACE_DETAIL_DATA_W
)HeapAlloc(GetProcessHeap(),
243 DetailData
->cbSize
= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
);
244 DeviceData
.cbSize
= sizeof(DeviceData
);
245 DeviceData
.Reserved
= 0;
247 Result
= SetupDiGetDeviceInterfaceDetailW(EnumContext
,
256 DPRINT("SetupDiGetDeviceInterfaceDetailW failed with %lu\n", GetLastError());
257 return MM_STATUS_UNSUCCESSFUL
;
261 *OutKey
= SetupDiOpenDeviceInterfaceRegKey(EnumContext
, &InterfaceData
, 0, KEY_READ
);
262 if ((HKEY
)*OutKey
== INVALID_HANDLE_VALUE
)
264 HeapFree(GetProcessHeap(), 0, DetailData
);
265 return MM_STATUS_UNSUCCESSFUL
;
268 Status
= Open(DetailData
->DevicePath
, OutHandle
);
270 if (Status
!= MM_STATUS_SUCCESS
)
272 RegCloseKey((HKEY
)*OutKey
);
273 HeapFree(GetProcessHeap(), 0, DetailData
);
277 *DeviceName
= (LPWSTR
)HeapAlloc(GetProcessHeap(), 0, (wcslen(DetailData
->DevicePath
)+1) * sizeof(WCHAR
));
278 if (*DeviceName
== NULL
)
280 CloseHandle(*OutHandle
);
281 RegCloseKey((HKEY
)*OutKey
);
282 HeapFree(GetProcessHeap(), 0, DetailData
);
283 return MM_STATUS_NO_MEMORY
;
286 wcscpy(*DeviceName
, DetailData
->DevicePath
);
287 HeapFree(GetProcessHeap(), 0, DetailData
);
296 PKSEVENTDATA Data
= (PKSEVENTDATA
)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(KSEVENTDATA
) + ExtraSize
);
300 Data
->EventHandle
.Event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
301 if (!Data
->EventHandle
.Event
)
303 HeapFree(GetProcessHeap(), 0, Data
);
307 Data
->NotificationType
= KSEVENTF_EVENT_HANDLE
;
312 FreeEventData(IN PVOID EventData
)
314 PKSEVENTDATA Data
= (PKSEVENTDATA
)EventData
;
316 CloseHandle(Data
->EventHandle
.Event
);
317 HeapFree(GetProcessHeap(), 0, Data
);
322 WdmAudInitUserModeMixer()
324 HDEVINFO DeviceHandle
;
327 /* create a device list */
328 DeviceHandle
= SetupDiGetClassDevs(&CategoryGuid
,
331 DIGCF_DEVICEINTERFACE
/* FIXME |DIGCF_PRESENT*/);
333 if (DeviceHandle
== INVALID_HANDLE_VALUE
)
335 /* failed to create a device list */
340 /* initialize the mixer library */
341 Status
= MMixerInitialize(&MixerContext
, Enum
, (PVOID
)DeviceHandle
);
343 /* free device list */
344 SetupDiDestroyDeviceInfoList(DeviceHandle
);
346 if (Status
!= MM_STATUS_SUCCESS
)
348 /* failed to initialize mixer library */
349 DPRINT1("Failed to initialize mixer library with %x\n", Status
);
353 /* completed successfully */
358 WdmAudCleanupMMixer()
364 WdmAudGetMixerCapabilties(
366 LPMIXERCAPSW Capabilities
)
368 if (MMixerGetCapabilities(&MixerContext
, DeviceId
, Capabilities
) == MM_STATUS_SUCCESS
)
369 return MMSYSERR_NOERROR
;
371 return MMSYSERR_BADDEVICEID
;
377 IN LPMIXERLINEW MixLine
,
380 if (MMixerGetLineInfo(&MixerContext
, hMixer
, Flags
, MixLine
) == MM_STATUS_SUCCESS
)
381 return MMSYSERR_NOERROR
;
383 return MMSYSERR_ERROR
;
387 WdmAudGetLineControls(
389 IN LPMIXERLINECONTROLSW MixControls
,
392 if (MMixerGetLineControls(&MixerContext
, hMixer
, Flags
, MixControls
) == MM_STATUS_SUCCESS
)
393 return MMSYSERR_NOERROR
;
395 return MMSYSERR_ERROR
;
399 WdmAudSetControlDetails(
401 IN LPMIXERCONTROLDETAILS MixDetails
,
404 if (MMixerSetControlDetails(&MixerContext
, hMixer
, Flags
, MixDetails
) == MM_STATUS_SUCCESS
)
405 return MMSYSERR_NOERROR
;
407 return MMSYSERR_ERROR
;
412 WdmAudGetControlDetails(
414 IN LPMIXERCONTROLDETAILS MixDetails
,
417 if (MMixerGetControlDetails(&MixerContext
, hMixer
, Flags
, MixDetails
) == MM_STATUS_SUCCESS
)
418 return MMSYSERR_NOERROR
;
420 return MMSYSERR_ERROR
;
424 WdmAudGetWaveOutCapabilities(
426 LPWAVEOUTCAPSW Capabilities
)
428 if (MMixerWaveOutCapabilities(&MixerContext
, DeviceId
, Capabilities
) == MM_STATUS_SUCCESS
)
429 return MMSYSERR_NOERROR
;
431 return MMSYSERR_ERROR
;
436 WdmAudGetWaveInCapabilities(
438 LPWAVEINCAPSW Capabilities
)
440 if (MMixerWaveInCapabilities(&MixerContext
, DeviceId
, Capabilities
) == MM_STATUS_SUCCESS
)
441 return MMSYSERR_NOERROR
;
443 return MMSYSERR_ERROR
;
447 WdmAudSetWdmWaveDeviceFormatByMMixer(
448 IN PSOUND_DEVICE_INSTANCE Instance
,
450 IN PWAVEFORMATEX WaveFormat
,
451 IN DWORD WaveFormatSize
)
453 MMDEVICE_TYPE DeviceType
;
454 PSOUND_DEVICE SoundDevice
;
458 Result
= GetSoundDeviceFromInstance(Instance
, &SoundDevice
);
460 if ( ! MMSUCCESS(Result
) )
462 return TranslateInternalMmResult(Result
);
465 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
466 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
468 bWaveIn
= (DeviceType
== WAVE_IN_DEVICE_TYPE
? TRUE
: FALSE
);
470 if (MMixerOpenWave(&MixerContext
, DeviceId
, bWaveIn
, WaveFormat
, &Instance
->Handle
) == MM_STATUS_SUCCESS
)
472 if (DeviceType
== WAVE_OUT_DEVICE_TYPE
)
474 MMixerSetWaveStatus(&MixerContext
, Instance
->Handle
, KSSTATE_ACQUIRE
);
475 MMixerSetWaveStatus(&MixerContext
, Instance
->Handle
, KSSTATE_PAUSE
);
476 MMixerSetWaveStatus(&MixerContext
, Instance
->Handle
, KSSTATE_RUN
);
478 return MMSYSERR_NOERROR
;
480 return MMSYSERR_ERROR
;
485 WdmAudGetCapabilitiesByMMixer(
486 IN PSOUND_DEVICE SoundDevice
,
488 OUT PVOID Capabilities
,
489 IN DWORD CapabilitiesSize
)
491 MMDEVICE_TYPE DeviceType
;
494 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
495 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
497 if (DeviceType
== MIXER_DEVICE_TYPE
)
499 return WdmAudGetMixerCapabilties(DeviceId
, (LPMIXERCAPSW
)Capabilities
);
501 else if (DeviceType
== WAVE_OUT_DEVICE_TYPE
)
503 return WdmAudGetWaveOutCapabilities(DeviceId
, (LPWAVEOUTCAPSW
)Capabilities
);
505 else if (DeviceType
== WAVE_IN_DEVICE_TYPE
)
507 return WdmAudGetWaveInCapabilities(DeviceId
, (LPWAVEINCAPSW
)Capabilities
);
512 return MMSYSERR_ERROR
;
517 WdmAudOpenSoundDeviceByMMixer(
518 IN
struct _SOUND_DEVICE
* SoundDevice
,
521 return WdmAudInitUserModeMixer();
525 WdmAudCloseSoundDeviceByMMixer(
526 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
529 MMDEVICE_TYPE DeviceType
;
530 PSOUND_DEVICE SoundDevice
;
533 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
535 if ( ! MMSUCCESS(Result
) )
537 return TranslateInternalMmResult(Result
);
540 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
541 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
543 if (DeviceType
== MIXER_DEVICE_TYPE
)
546 return MMSYSERR_NOERROR
;
548 else if (DeviceType
== WAVE_IN_DEVICE_TYPE
|| DeviceType
== WAVE_OUT_DEVICE_TYPE
)
550 /* make sure the pin is stopped */
551 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
552 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
553 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_STOP
);
556 return MMSYSERR_NOERROR
;
559 /* midi is not supported */
560 return MMSYSERR_ERROR
;
564 WdmAudGetNumDevsByMMixer(
565 IN MMDEVICE_TYPE DeviceType
,
566 OUT DWORD
* DeviceCount
)
570 case MIXER_DEVICE_TYPE
:
571 *DeviceCount
= MMixerGetCount(&MixerContext
);
573 case WAVE_OUT_DEVICE_TYPE
:
574 *DeviceCount
= MMixerGetWaveOutCount(&MixerContext
);
576 case WAVE_IN_DEVICE_TYPE
:
577 *DeviceCount
= MMixerGetWaveInCount(&MixerContext
);
582 return MMSYSERR_NOERROR
;
586 WdmAudQueryMixerInfoByMMixer(
587 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
592 LPMIXERLINEW MixLine
;
593 LPMIXERLINECONTROLSW MixControls
;
594 LPMIXERCONTROLDETAILS MixDetails
;
596 MixLine
= (LPMIXERLINEW
)Parameter
;
597 MixControls
= (LPMIXERLINECONTROLSW
)Parameter
;
598 MixDetails
= (LPMIXERCONTROLDETAILS
)Parameter
;
600 /* FIXME param checks */
604 case MXDM_GETLINEINFO
:
605 return WdmAudGetLineInfo(SoundDeviceInstance
->Handle
, MixLine
, Flags
);
606 case MXDM_GETLINECONTROLS
:
607 return WdmAudGetLineControls(SoundDeviceInstance
->Handle
, MixControls
, Flags
);
608 case MXDM_SETCONTROLDETAILS
:
609 return WdmAudSetControlDetails(SoundDeviceInstance
->Handle
, MixDetails
, Flags
);
611 case MXDM_GETCONTROLDETAILS
:
612 return WdmAudGetControlDetails(SoundDeviceInstance
->Handle
, MixDetails
, Flags
);
616 return MMSYSERR_NOTSUPPORTED
;
621 WdmAudGetDeviceInterfaceStringByMMixer(
622 IN MMDEVICE_TYPE DeviceType
,
625 IN DWORD InterfaceLength
,
626 OUT DWORD
* InterfaceSize
)
629 return MMSYSERR_NOTSUPPORTED
;
633 WdmAudSetMixerDeviceFormatByMMixer(
634 IN PSOUND_DEVICE_INSTANCE Instance
,
636 IN PWAVEFORMATEX WaveFormat
,
637 IN DWORD WaveFormatSize
)
639 Instance
->hNotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
640 if ( ! Instance
->hNotifyEvent
)
641 return MMSYSERR_NOMEM
;
643 if (MMixerOpen(&MixerContext
, DeviceId
, Instance
->hNotifyEvent
, NULL
/* FIXME */, &Instance
->Handle
) == MM_STATUS_SUCCESS
)
644 return MMSYSERR_NOERROR
;
646 return MMSYSERR_BADDEVICEID
;
650 WdmAudSetWdmWaveStateByMMixer(
651 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
656 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
657 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
658 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_RUN
);
662 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_PAUSE
);
663 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_ACQUIRE
);
664 MMixerSetWaveStatus(&MixerContext
, SoundDeviceInstance
->Handle
, KSSTATE_STOP
);
667 return MMSYSERR_NOERROR
;
671 WdmAudResetStreamByMMixer(
672 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
673 IN MMDEVICE_TYPE DeviceType
,
674 IN BOOLEAN bStartReset
)
677 return MMSYSERR_NOTSUPPORTED
;
681 WdmAudGetWdmPositionByMMixer(
682 IN
struct _SOUND_DEVICE_INSTANCE
* SoundDeviceInstance
,
686 return MMSYSERR_NOTSUPPORTED
;
690 WdmAudCommitWaveBufferByMMixer(
691 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
,
694 IN PSOUND_OVERLAPPED Overlap
,
695 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine
)
697 KSSTREAM_HEADER Packet
;
698 PSOUND_DEVICE SoundDevice
;
699 MMDEVICE_TYPE DeviceType
;
702 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
704 if ( ! MMSUCCESS(Result
) )
706 return TranslateInternalMmResult(Result
);
709 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
710 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
712 /* setup stream packet */
713 ZeroMemory(&Packet
, sizeof(KSSTREAM_HEADER
));
714 Packet
.Size
= sizeof(KSSTREAM_HEADER
);
715 Packet
.PresentationTime
.Numerator
= 1;
716 Packet
.PresentationTime
.Denominator
= 1;
717 Packet
.Data
= OffsetPtr
;
718 Packet
.FrameExtent
= Length
;
720 if (DeviceType
== WAVE_OUT_DEVICE_TYPE
)
722 Packet
.DataUsed
= Length
;
725 Result
= SyncOverlappedDeviceIoControl(SoundDeviceInstance
->Handle
,
726 IOCTL_KS_WRITE_STREAM
,
730 sizeof(KSSTREAM_HEADER
),
734 * don't call completion routine directly
736 CompletionRoutine(ERROR_SUCCESS
, Length
, (LPOVERLAPPED
)Overlap
);
738 return MMSYSERR_NOERROR
;