2 * PROJECT: ReactOS Sound System
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/wdmaud.drv/mixer.c
6 * PURPOSE: WDM Audio Driver (User-mode part)
7 * PROGRAMMERS: Johannes Anderwald
18 #include <samplerate.h>
19 #include <float_cast.h>
21 #include "interface.h"
24 extern HANDLE KernelHandle
;
27 PerformSampleRateConversion(
42 PFLOAT FloatIn
, FloatOut
;
46 //SND_TRACE(L"PerformSampleRateConversion OldRate %u NewRate %u BytesPerSample %u NumChannels %u\n", OldRate, NewRate, BytesPerSample, NumChannels);
48 ASSERT(BytesPerSample
== 1 || BytesPerSample
== 2 || BytesPerSample
== 4);
50 NumSamples
= BufferLength
/ (BytesPerSample
* NumChannels
);
52 FloatIn
= HeapAlloc(GetProcessHeap(), 0, NumSamples
* NumChannels
* sizeof(FLOAT
));
55 return ERROR_NOT_ENOUGH_MEMORY
;
58 NewSamples
= lrintf(((FLOAT
)NumSamples
* ((FLOAT
)NewRate
/ (FLOAT
)OldRate
))) + 2;
60 FloatOut
= HeapAlloc(GetProcessHeap(), 0, NewSamples
* NumChannels
* sizeof(FLOAT
));
63 HeapFree(GetProcessHeap(), 0,FloatIn
);
64 return ERROR_NOT_ENOUGH_MEMORY
;
67 ResultOut
= HeapAlloc(GetProcessHeap(), 0, NewSamples
* NumChannels
* BytesPerSample
);
70 HeapFree(GetProcessHeap(), 0,FloatIn
);
71 HeapFree(GetProcessHeap(), 0,FloatOut
);
72 return ERROR_NOT_ENOUGH_MEMORY
;
75 State
= src_new(SRC_SINC_FASTEST
, NumChannels
, &error
);
78 HeapFree(GetProcessHeap(), 0,FloatIn
);
79 HeapFree(GetProcessHeap(), 0,FloatOut
);
80 HeapFree(GetProcessHeap(), 0,ResultOut
);
81 return ERROR_NOT_ENOUGH_MEMORY
;
85 if (BytesPerSample
== 1)
87 for(Index
= 0; Index
< NumSamples
* NumChannels
; Index
++)
88 FloatIn
[Index
] = (float)(Buffer
[Index
] / (1.0 * 0x80));
90 else if (BytesPerSample
== 2)
92 src_short_to_float_array((short*)Buffer
, FloatIn
, NumSamples
* NumChannels
);
94 else if (BytesPerSample
== 4)
96 src_int_to_float_array((int*)Buffer
, FloatIn
, NumSamples
* NumChannels
);
99 Data
.data_in
= FloatIn
;
100 Data
.data_out
= FloatOut
;
101 Data
.input_frames
= NumSamples
;
102 Data
.output_frames
= NewSamples
;
103 Data
.src_ratio
= (double)NewRate
/ (double)OldRate
;
105 error
= src_process(State
, &Data
);
108 DPRINT1("src_process failed with %x\n", error
);
109 HeapFree(GetProcessHeap(), 0,FloatIn
);
110 HeapFree(GetProcessHeap(), 0,FloatOut
);
111 HeapFree(GetProcessHeap(), 0,ResultOut
);
112 return ERROR_INVALID_DATA
;
115 if (BytesPerSample
== 1)
117 /* FIXME perform over/under clipping */
119 for(Index
= 0; Index
< Data
.output_frames_gen
* NumChannels
; Index
++)
120 ResultOut
[Index
] = (lrintf(FloatOut
[Index
]) >> 24);
122 else if (BytesPerSample
== 2)
124 PUSHORT Res
= (PUSHORT
)ResultOut
;
126 src_float_to_short_array(FloatOut
, (short*)Res
, Data
.output_frames_gen
* NumChannels
);
128 else if (BytesPerSample
== 4)
130 PULONG Res
= (PULONG
)ResultOut
;
132 src_float_to_int_array(FloatOut
, (int*)Res
, Data
.output_frames_gen
* NumChannels
);
137 *ResultLength
= Data
.output_frames_gen
* BytesPerSample
* NumChannels
;
138 HeapFree(GetProcessHeap(), 0,FloatIn
);
139 HeapFree(GetProcessHeap(), 0,FloatOut
);
141 return ERROR_SUCCESS
;
145 PerformChannelConversion(
155 ULONG NewIndex
, OldIndex
;
157 Samples
= BufferLength
/ (BitsPerSample
/ 8) / OldChannels
;
159 SND_TRACE(L
"PerformChannelConversion OldChannels %u NewChannels %u\n", OldChannels
, NewChannels
);
161 if (NewChannels
> OldChannels
)
163 if (BitsPerSample
== 8)
165 PUCHAR BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* NewChannels
);
167 return ERROR_NOT_ENOUGH_MEMORY
;
169 for(NewIndex
= 0, OldIndex
= 0; OldIndex
< Samples
* OldChannels
; NewIndex
+= NewChannels
, OldIndex
+= OldChannels
)
173 RtlMoveMemory(&BufferOut
[NewIndex
], &Buffer
[OldIndex
], OldChannels
* sizeof(UCHAR
));
177 /* 2 channel stretched to 4 looks like LRLR */
178 BufferOut
[NewIndex
+OldChannels
+ SubIndex
] = Buffer
[OldIndex
+ (SubIndex
% OldChannels
)];
179 }while(SubIndex
++ < NewChannels
- OldChannels
);
182 *ResultLength
= Samples
* NewChannels
;
184 else if (BitsPerSample
== 16)
186 PUSHORT BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* NewChannels
);
188 return ERROR_NOT_ENOUGH_MEMORY
;
190 for(NewIndex
= 0, OldIndex
= 0; OldIndex
< Samples
* OldChannels
; NewIndex
+= NewChannels
, OldIndex
+= OldChannels
)
194 RtlMoveMemory(&BufferOut
[NewIndex
], &Buffer
[OldIndex
], OldChannels
* sizeof(USHORT
));
198 BufferOut
[NewIndex
+OldChannels
+ SubIndex
] = Buffer
[OldIndex
+ (SubIndex
% OldChannels
)];
199 }while(SubIndex
++ < NewChannels
- OldChannels
);
202 *ResultLength
= Samples
* NewChannels
;
204 else if (BitsPerSample
== 24)
206 PUCHAR BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* NewChannels
);
208 return ERROR_NOT_ENOUGH_MEMORY
;
210 for(NewIndex
= 0, OldIndex
= 0; OldIndex
< Samples
* OldChannels
; NewIndex
+= NewChannels
, OldIndex
+= OldChannels
)
214 RtlMoveMemory(&BufferOut
[NewIndex
], &Buffer
[OldIndex
], OldChannels
* 3);
218 RtlMoveMemory(&BufferOut
[(NewIndex
+OldChannels
+ SubIndex
) * 3], &Buffer
[(OldIndex
+ (SubIndex
% OldChannels
)) * 3], 3);
219 }while(SubIndex
++ < NewChannels
- OldChannels
);
222 *ResultLength
= Samples
* NewChannels
;
224 else if (BitsPerSample
== 32)
226 PULONG BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* NewChannels
);
228 return ERROR_NOT_ENOUGH_MEMORY
;
230 for(NewIndex
= 0, OldIndex
= 0; OldIndex
< Samples
* OldChannels
; NewIndex
+= NewChannels
, OldIndex
+= OldChannels
)
234 RtlMoveMemory(&BufferOut
[NewIndex
], &Buffer
[OldIndex
], OldChannels
* sizeof(ULONG
));
238 BufferOut
[NewIndex
+OldChannels
+ SubIndex
] = Buffer
[OldIndex
+ (SubIndex
% OldChannels
)];
239 }while(SubIndex
++ < NewChannels
- OldChannels
);
242 *ResultLength
= Samples
* NewChannels
;
248 PUSHORT BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* NewChannels
);
250 return ERROR_NOT_ENOUGH_MEMORY
;
252 for(NewIndex
= 0, OldIndex
= 0; OldIndex
< Samples
* OldChannels
; NewIndex
+= NewChannels
, OldIndex
+= OldChannels
)
255 * mix stream instead of just dumping part of it ;)
257 RtlMoveMemory(&BufferOut
[NewIndex
], &Buffer
[OldIndex
], NewChannels
* (BitsPerSample
/8));
261 *ResultLength
= Samples
* NewChannels
;
263 return ERROR_SUCCESS
;
268 PerformQualityConversion(
279 ASSERT(OldWidth
!= NewWidth
);
281 Samples
= BufferLength
/ (OldWidth
/ 8);
282 //DPRINT("Samples %u BufferLength %u\n", Samples, BufferLength);
284 //SND_TRACE(L"PerformQualityConversion OldWidth %u NewWidth %u\n", OldWidth, NewWidth);
286 if (OldWidth
== 8 && NewWidth
== 16)
289 PUSHORT BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* sizeof(USHORT
));
291 return ERROR_NOT_ENOUGH_MEMORY
;
293 for(Index
= 0; Index
< Samples
; Index
++)
295 Sample
= Buffer
[Index
];// & 0xFF);
298 Sample
= _byteswap_ushort(Sample
);
300 BufferOut
[Index
] = Sample
;
303 *ResultLength
= Samples
* sizeof(USHORT
);
305 else if (OldWidth
== 8 && NewWidth
== 32)
308 PULONG BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* sizeof(ULONG
));
310 return ERROR_NOT_ENOUGH_MEMORY
;
312 for(Index
= 0; Index
< Samples
; Index
++)
314 Sample
= Buffer
[Index
];
317 Sample
= _byteswap_ulong(Sample
);
319 BufferOut
[Index
] = Sample
;
322 *ResultLength
= Samples
* sizeof(ULONG
);
324 else if (OldWidth
== 16 && NewWidth
== 32)
327 PUSHORT BufferIn
= (PUSHORT
)Buffer
;
328 PULONG BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* sizeof(ULONG
));
330 return ERROR_NOT_ENOUGH_MEMORY
;
332 for(Index
= 0; Index
< Samples
; Index
++)
334 Sample
= BufferIn
[Index
];
337 Sample
= _byteswap_ulong(Sample
);
339 BufferOut
[Index
] = Sample
;
342 *ResultLength
= Samples
* sizeof(ULONG
);
345 else if (OldWidth
== 16 && NewWidth
== 8)
348 PUSHORT BufferIn
= (PUSHORT
)Buffer
;
349 PUCHAR BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* sizeof(UCHAR
));
351 return ERROR_NOT_ENOUGH_MEMORY
;
353 for(Index
= 0; Index
< Samples
; Index
++)
355 Sample
= BufferIn
[Index
];
357 Sample
= _byteswap_ushort(Sample
);
360 BufferOut
[Index
] = (Sample
& 0xFF);
363 *ResultLength
= Samples
* sizeof(UCHAR
);
365 else if (OldWidth
== 32 && NewWidth
== 8)
368 PULONG BufferIn
= (PULONG
)Buffer
;
369 PUCHAR BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* sizeof(UCHAR
));
371 return ERROR_NOT_ENOUGH_MEMORY
;
373 for(Index
= 0; Index
< Samples
; Index
++)
375 Sample
= BufferIn
[Index
];
377 Sample
= _byteswap_ulong(Sample
);
380 BufferOut
[Index
] = (Sample
& 0xFF);
383 *ResultLength
= Samples
* sizeof(UCHAR
);
385 else if (OldWidth
== 32 && NewWidth
== 16)
388 PULONG BufferIn
= (PULONG
)Buffer
;
389 PUSHORT BufferOut
= HeapAlloc(GetProcessHeap(), 0, Samples
* sizeof(USHORT
));
391 return ERROR_NOT_ENOUGH_MEMORY
;
393 for(Index
= 0; Index
< Samples
; Index
++)
395 Sample
= BufferIn
[Index
];
397 Sample
= _byteswap_ulong(Sample
);
400 BufferOut
[Index
] = (Sample
& 0xFFFF);
403 *ResultLength
= Samples
* sizeof(USHORT
);
407 DPRINT1("Not implemented conversion OldWidth %u NewWidth %u\n", OldWidth
, NewWidth
);
408 return ERROR_NOT_SUPPORTED
;
411 return ERROR_SUCCESS
;
416 MixerCompletionRoutine(
417 IN DWORD dwErrorCode
,
418 IN DWORD dwNumberOfBytesTransferred
,
419 IN LPOVERLAPPED lpOverlapped
)
421 PSOUND_OVERLAPPED Overlap
= (PSOUND_OVERLAPPED
)lpOverlapped
;
423 /* Call mmebuddy overlap routine */
424 Overlap
->OriginalCompletionRoutine(dwErrorCode
, Overlap
->OriginalBufferSize
, lpOverlapped
);
429 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
,
432 IN PSOUND_OVERLAPPED Overlap
,
433 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine
)
436 WDMAUD_DEVICE_INFO DeviceInfo
;
437 DWORD BufferLength
, BufferLengthTemp
;
438 PVOID BufferOut
, BufferOutTemp
;
442 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance
);
443 VALIDATE_MMSYS_PARAMETER( OffsetPtr
);
444 VALIDATE_MMSYS_PARAMETER( Overlap
);
445 VALIDATE_MMSYS_PARAMETER( CompletionRoutine
);
447 GetSoundDeviceInstanceHandle(SoundDeviceInstance
, &Handle
);
451 BufferOut
= OffsetPtr
;
452 BufferLength
= Length
;
454 if (SoundDeviceInstance
->WaveFormatEx
.wBitsPerSample
!= 16)
456 Status
= PerformQualityConversion(OffsetPtr
,
458 SoundDeviceInstance
->WaveFormatEx
.wBitsPerSample
,
464 SND_TRACE(L
"PerformQualityConversion failed\n");
465 return MMSYSERR_NOERROR
;
469 if (SoundDeviceInstance
->WaveFormatEx
.nChannels
!= 2)
471 Status
= PerformChannelConversion(BufferOut
,
473 SoundDeviceInstance
->WaveFormatEx
.nChannels
,
479 if (BufferOut
!= OffsetPtr
)
481 HeapFree(GetProcessHeap(), 0, BufferOut
);
486 SND_TRACE(L
"PerformChannelConversion failed\n");
487 return MMSYSERR_NOERROR
;
490 BufferOut
= BufferOutTemp
;
491 BufferLength
= BufferLengthTemp
;
494 if (SoundDeviceInstance
->WaveFormatEx
.nSamplesPerSec
!= 44100)
496 Status
= PerformSampleRateConversion(BufferOut
,
498 SoundDeviceInstance
->WaveFormatEx
.nSamplesPerSec
,
505 if (BufferOut
!= OffsetPtr
)
507 HeapFree(GetProcessHeap(), 0, BufferOut
);
512 SND_TRACE(L
"PerformSampleRateConversion failed\n");
513 return MMSYSERR_NOERROR
;
516 BufferOut
= BufferOutTemp
;
517 BufferLength
= BufferLengthTemp
;
520 ZeroMemory(&DeviceInfo
, sizeof(WDMAUD_DEVICE_INFO
));
521 DeviceInfo
.hDevice
= Handle
;
522 DeviceInfo
.DeviceType
= WAVE_OUT_DEVICE_TYPE
; //FIXME
523 DeviceInfo
.Header
.FrameExtent
= BufferLength
;
524 DeviceInfo
.Header
.DataUsed
= BufferLength
;
525 DeviceInfo
.Header
.Data
= BufferOut
;
526 DeviceInfo
.Header
.Size
= sizeof(KSSTREAM_HEADER
);
527 DeviceInfo
.Header
.PresentationTime
.Numerator
= 1;
528 DeviceInfo
.Header
.PresentationTime
.Denominator
= 1;
530 Overlap
->OriginalBufferSize
= Length
;
531 Overlap
->OriginalCompletionRoutine
= CompletionRoutine
;
533 Overlap
->Standard
.hEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
535 //SND_TRACE(L"OriginalLength %u NewLength %u\n", Length, BufferLength);
538 Result
= WriteFileEx(KernelHandle
, &DeviceInfo
, sizeof(WDMAUD_DEVICE_INFO
), (LPOVERLAPPED
)Overlap
, CompletionRoutine
);
540 Result
= WriteFileEx(KernelHandle
, &DeviceInfo
, sizeof(WDMAUD_DEVICE_INFO
), (LPOVERLAPPED
)Overlap
, MixerCompletionRoutine
);
545 SND_TRACE(L
"WriteFileEx failed with %x\n", GetLastError());
546 return MMSYSERR_NOERROR
;
549 WaitForSingleObjectEx (KernelHandle
, INFINITE
, TRUE
);
551 #ifdef USERMODE_MIXER
552 // if (BufferOut != OffsetPtr)
553 // HeapFree(GetProcessHeap(), 0, BufferOut);
557 return MMSYSERR_NOERROR
;