1 /*
2 * PROJECT: ReactOS Sound System
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/wdmaud.drv/mixer.c
5 *
6 * PURPOSE: WDM Audio Driver (User-mode part)
7 * PROGRAMMERS: Johannes Anderwald
8 */
9
10 #include "wdmaud.h"
11
12 #include <samplerate.h>
13 #include <float_cast.h>
14
15 #define NDEBUG
16 #include <debug.h>
17 #include <mmebuddy_debug.h>
18
19 extern HANDLE KernelHandle;
20
21 DWORD
PerformSampleRateConversion(PUCHAR Buffer,ULONG BufferLength,ULONG OldRate,ULONG NewRate,ULONG BytesPerSample,ULONG NumChannels,PVOID * Result,PULONG ResultLength)22 PerformSampleRateConversion(
23 PUCHAR Buffer,
24 ULONG BufferLength,
25 ULONG OldRate,
26 ULONG NewRate,
27 ULONG BytesPerSample,
28 ULONG NumChannels,
29 PVOID * Result,
30 PULONG ResultLength)
31 {
32 ULONG Index;
33 SRC_STATE * State;
34 SRC_DATA Data;
35 PUCHAR ResultOut;
36 int error;
37 PFLOAT FloatIn, FloatOut;
38 ULONG NumSamples;
39 ULONG NewSamples;
40
41 //SND_TRACE(L"PerformSampleRateConversion OldRate %u NewRate %u BytesPerSample %u NumChannels %u\n", OldRate, NewRate, BytesPerSample, NumChannels);
42
43 ASSERT(BytesPerSample == 1 || BytesPerSample == 2 || BytesPerSample == 4);
44
45 NumSamples = BufferLength / (BytesPerSample * NumChannels);
46
47 FloatIn = HeapAlloc(GetProcessHeap(), 0, NumSamples * NumChannels * sizeof(FLOAT));
48 if (!FloatIn)
49 {
50 return ERROR_NOT_ENOUGH_MEMORY;
51 }
52
53 NewSamples = ((((ULONG64)NumSamples * NewRate) + (OldRate / 2)) / OldRate) + 2;
54
55 FloatOut = HeapAlloc(GetProcessHeap(), 0, NewSamples * NumChannels * sizeof(FLOAT));
56 if (!FloatOut)
57 {
58 HeapFree(GetProcessHeap(), 0,FloatIn);
59 return ERROR_NOT_ENOUGH_MEMORY;
60 }
61
62 ResultOut = HeapAlloc(GetProcessHeap(), 0, NewSamples * NumChannels * BytesPerSample);
63 if (!ResultOut)
64 {
65 HeapFree(GetProcessHeap(), 0,FloatIn);
66 HeapFree(GetProcessHeap(), 0,FloatOut);
67 return ERROR_NOT_ENOUGH_MEMORY;
68 }
69
70 State = src_new(SRC_SINC_FASTEST, NumChannels, &error);
71 if (!State)
72 {
73 HeapFree(GetProcessHeap(), 0,FloatIn);
74 HeapFree(GetProcessHeap(), 0,FloatOut);
75 HeapFree(GetProcessHeap(), 0,ResultOut);
76 return ERROR_NOT_ENOUGH_MEMORY;
77 }
78
79 /* fixme use asm */
80 if (BytesPerSample == 1)
81 {
82 for(Index = 0; Index < NumSamples * NumChannels; Index++)
83 FloatIn[Index] = (float)(Buffer[Index] / (1.0 * 0x80));
84 }
85 else if (BytesPerSample == 2)
86 {
87 src_short_to_float_array((short*)Buffer, FloatIn, NumSamples * NumChannels);
88 }
89 else if (BytesPerSample == 4)
90 {
91 src_int_to_float_array((int*)Buffer, FloatIn, NumSamples * NumChannels);
92 }
93
94 Data.data_in = FloatIn;
95 Data.data_out = FloatOut;
96 Data.input_frames = NumSamples;
97 Data.output_frames = NewSamples;
98 Data.src_ratio = (double)NewRate / (double)OldRate;
99
100 error = src_process(State, &Data);
101 if (error)
102 {
103 DPRINT1("src_process failed with %x\n", error);
104 HeapFree(GetProcessHeap(), 0,FloatIn);
105 HeapFree(GetProcessHeap(), 0,FloatOut);
106 HeapFree(GetProcessHeap(), 0,ResultOut);
107 return ERROR_INVALID_DATA;
108 }
109
110 if (BytesPerSample == 1)
111 {
112 /* FIXME perform over/under clipping */
113
114 for(Index = 0; Index < Data.output_frames_gen * NumChannels; Index++)
115 ResultOut[Index] = (lrintf(FloatOut[Index]) >> 24);
116 }
117 else if (BytesPerSample == 2)
118 {
119 PUSHORT Res = (PUSHORT)ResultOut;
120
121 src_float_to_short_array(FloatOut, (short*)Res, Data.output_frames_gen * NumChannels);
122 }
123 else if (BytesPerSample == 4)
124 {
125 PULONG Res = (PULONG)ResultOut;
126
127 src_float_to_int_array(FloatOut, (int*)Res, Data.output_frames_gen * NumChannels);
128 }
129
130
131 *Result = ResultOut;
132 *ResultLength = Data.output_frames_gen * BytesPerSample * NumChannels;
133 HeapFree(GetProcessHeap(), 0,FloatIn);
134 HeapFree(GetProcessHeap(), 0,FloatOut);
135 src_delete(State);
136 return ERROR_SUCCESS;
137 }
138
139 DWORD
PerformChannelConversion(PUCHAR Buffer,ULONG BufferLength,ULONG OldChannels,ULONG NewChannels,ULONG BitsPerSample,PVOID * Result,PULONG ResultLength)140 PerformChannelConversion(
141 PUCHAR Buffer,
142 ULONG BufferLength,
143 ULONG OldChannels,
144 ULONG NewChannels,
145 ULONG BitsPerSample,
146 PVOID * Result,
147 PULONG ResultLength)
148 {
149 ULONG Samples;
150 ULONG NewIndex, OldIndex;
151
152 Samples = BufferLength / (BitsPerSample / 8) / OldChannels;
153
154 SND_TRACE(L"PerformChannelConversion OldChannels %u NewChannels %u\n", OldChannels, NewChannels);
155
156 if (NewChannels > OldChannels)
157 {
158 if (BitsPerSample == 8)
159 {
160 PUCHAR BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * NewChannels);
161 if (!BufferOut)
162 return ERROR_NOT_ENOUGH_MEMORY;
163
164 for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
165 {
166 ULONG SubIndex = 0;
167
168 RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * sizeof(UCHAR));
169
170 do
171 {
172 /* 2 channel stretched to 4 looks like LRLR */
173 BufferOut[NewIndex+OldChannels + SubIndex] = Buffer[OldIndex + (SubIndex % OldChannels)];
174 }while(SubIndex++ < NewChannels - OldChannels);
175 }
176 *Result = BufferOut;
177 *ResultLength = Samples * NewChannels;
178 }
179 else if (BitsPerSample == 16)
180 {
181 PUSHORT BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * NewChannels);
182 if (!BufferOut)
183 return ERROR_NOT_ENOUGH_MEMORY;
184
185 for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
186 {
187 ULONG SubIndex = 0;
188
189 RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * sizeof(USHORT));
190
191 do
192 {
193 BufferOut[NewIndex+OldChannels + SubIndex] = Buffer[OldIndex + (SubIndex % OldChannels)];
194 }while(SubIndex++ < NewChannels - OldChannels);
195 }
196 *Result = BufferOut;
197 *ResultLength = Samples * NewChannels;
198 }
199 else if (BitsPerSample == 24)
200 {
201 PUCHAR BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * NewChannels);
202 if (!BufferOut)
203 return ERROR_NOT_ENOUGH_MEMORY;
204
205 for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
206 {
207 ULONG SubIndex = 0;
208
209 RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * 3);
210
211 do
212 {
213 RtlMoveMemory(&BufferOut[(NewIndex+OldChannels + SubIndex) * 3], &Buffer[(OldIndex + (SubIndex % OldChannels)) * 3], 3);
214 }while(SubIndex++ < NewChannels - OldChannels);
215 }
216 *Result = BufferOut;
217 *ResultLength = Samples * NewChannels;
218 }
219 else if (BitsPerSample == 32)
220 {
221 PULONG BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * NewChannels);
222 if (!BufferOut)
223 return ERROR_NOT_ENOUGH_MEMORY;
224
225 for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
226 {
227 ULONG SubIndex = 0;
228
229 RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], OldChannels * sizeof(ULONG));
230
231 do
232 {
233 BufferOut[NewIndex+OldChannels + SubIndex] = Buffer[OldIndex + (SubIndex % OldChannels)];
234 }while(SubIndex++ < NewChannels - OldChannels);
235 }
236 *Result = BufferOut;
237 *ResultLength = Samples * NewChannels;
238 }
239
240 }
241 else
242 {
243 PUSHORT BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * NewChannels);
244 if (!BufferOut)
245 return ERROR_NOT_ENOUGH_MEMORY;
246
247 for(NewIndex = 0, OldIndex = 0; OldIndex < Samples * OldChannels; NewIndex += NewChannels, OldIndex += OldChannels)
248 {
249 /* TODO
250 * mix stream instead of just dumping part of it ;)
251 */
252 RtlMoveMemory(&BufferOut[NewIndex], &Buffer[OldIndex], NewChannels * (BitsPerSample/8));
253 }
254
255 *Result = BufferOut;
256 *ResultLength = Samples * NewChannels;
257 }
258 return ERROR_SUCCESS;
259 }
260
261
262 DWORD
PerformQualityConversion(PUCHAR Buffer,ULONG BufferLength,ULONG OldWidth,ULONG NewWidth,PVOID * Result,PULONG ResultLength)263 PerformQualityConversion(
264 PUCHAR Buffer,
265 ULONG BufferLength,
266 ULONG OldWidth,
267 ULONG NewWidth,
268 PVOID * Result,
269 PULONG ResultLength)
270 {
271 ULONG Samples;
272 ULONG Index;
273
274 ASSERT(OldWidth != NewWidth);
275
276 Samples = BufferLength / (OldWidth / 8);
277 //DPRINT("Samples %u BufferLength %u\n", Samples, BufferLength);
278
279 //SND_TRACE(L"PerformQualityConversion OldWidth %u NewWidth %u\n", OldWidth, NewWidth);
280
281 if (OldWidth == 8 && NewWidth == 16)
282 {
283 USHORT Sample;
284 PUSHORT BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * sizeof(USHORT));
285 if (!BufferOut)
286 return ERROR_NOT_ENOUGH_MEMORY;
287
288 for(Index = 0; Index < Samples; Index++)
289 {
290 Sample = Buffer[Index];// & 0xFF);
291 Sample *= 2;
292 #ifdef _X86_
293 Sample = _byteswap_ushort(Sample);
294 #endif
295 BufferOut[Index] = Sample;
296 }
297 *Result = BufferOut;
298 *ResultLength = Samples * sizeof(USHORT);
299 }
300 else if (OldWidth == 8 && NewWidth == 32)
301 {
302 ULONG Sample;
303 PULONG BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * sizeof(ULONG));
304 if (!BufferOut)
305 return ERROR_NOT_ENOUGH_MEMORY;
306
307 for(Index = 0; Index < Samples; Index++)
308 {
309 Sample = Buffer[Index];
310 Sample *= 16777216;
311 #ifdef _X86_
312 Sample = _byteswap_ulong(Sample);
313 #endif
314 BufferOut[Index] = Sample;
315 }
316 *Result = BufferOut;
317 *ResultLength = Samples * sizeof(ULONG);
318 }
319 else if (OldWidth == 16 && NewWidth == 32)
320 {
321 ULONG Sample;
322 PUSHORT BufferIn = (PUSHORT)Buffer;
323 PULONG BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * sizeof(ULONG));
324 if (!BufferOut)
325 return ERROR_NOT_ENOUGH_MEMORY;
326
327 for(Index = 0; Index < Samples; Index++)
328 {
329 Sample = BufferIn[Index];
330 Sample *= 65536;
331 #ifdef _X86_
332 Sample = _byteswap_ulong(Sample);
333 #endif
334 BufferOut[Index] = Sample;
335 }
336 *Result = BufferOut;
337 *ResultLength = Samples * sizeof(ULONG);
338 }
339
340 else if (OldWidth == 16 && NewWidth == 8)
341 {
342 USHORT Sample;
343 PUSHORT BufferIn = (PUSHORT)Buffer;
344 PUCHAR BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * sizeof(UCHAR));
345 if (!BufferOut)
346 return ERROR_NOT_ENOUGH_MEMORY;
347
348 for(Index = 0; Index < Samples; Index++)
349 {
350 Sample = BufferIn[Index];
351 #ifdef _X86_
352 Sample = _byteswap_ushort(Sample);
353 #endif
354 Sample /= 256;
355 BufferOut[Index] = (Sample & 0xFF);
356 }
357 *Result = BufferOut;
358 *ResultLength = Samples * sizeof(UCHAR);
359 }
360 else if (OldWidth == 32 && NewWidth == 8)
361 {
362 ULONG Sample;
363 PULONG BufferIn = (PULONG)Buffer;
364 PUCHAR BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * sizeof(UCHAR));
365 if (!BufferOut)
366 return ERROR_NOT_ENOUGH_MEMORY;
367
368 for(Index = 0; Index < Samples; Index++)
369 {
370 Sample = BufferIn[Index];
371 #ifdef _X86_
372 Sample = _byteswap_ulong(Sample);
373 #endif
374 Sample /= 16777216;
375 BufferOut[Index] = (Sample & 0xFF);
376 }
377 *Result = BufferOut;
378 *ResultLength = Samples * sizeof(UCHAR);
379 }
380 else if (OldWidth == 32 && NewWidth == 16)
381 {
382 USHORT Sample;
383 PULONG BufferIn = (PULONG)Buffer;
384 PUSHORT BufferOut = HeapAlloc(GetProcessHeap(), 0, Samples * sizeof(USHORT));
385 if (!BufferOut)
386 return ERROR_NOT_ENOUGH_MEMORY;
387
388 for(Index = 0; Index < Samples; Index++)
389 {
390 Sample = BufferIn[Index];
391 #ifdef _X86_
392 Sample = _byteswap_ulong(Sample);
393 #endif
394 Sample /= 65536;
395 BufferOut[Index] = (Sample & 0xFFFF);
396 }
397 *Result = BufferOut;
398 *ResultLength = Samples * sizeof(USHORT);
399 }
400 else
401 {
402 DPRINT1("Not implemented conversion OldWidth %u NewWidth %u\n", OldWidth, NewWidth);
403 return ERROR_NOT_SUPPORTED;
404 }
405
406 return ERROR_SUCCESS;
407 }
408
409 VOID
410 CALLBACK
MixerCompletionRoutine(IN DWORD dwErrorCode,IN DWORD dwNumberOfBytesTransferred,IN LPOVERLAPPED lpOverlapped)411 MixerCompletionRoutine(
412 IN DWORD dwErrorCode,
413 IN DWORD dwNumberOfBytesTransferred,
414 IN LPOVERLAPPED lpOverlapped)
415 {
416 PSOUND_OVERLAPPED Overlap = (PSOUND_OVERLAPPED)lpOverlapped;
417
418 /* Call mmebuddy overlap routine */
419 Overlap->OriginalCompletionRoutine(dwErrorCode, PtrToUlong(Overlap->CompletionContext), lpOverlapped);
420 }
421
422 MMRESULT
WriteFileEx_Remixer(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PVOID OffsetPtr,IN DWORD Length,IN PSOUND_OVERLAPPED Overlap,IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)423 WriteFileEx_Remixer(
424 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
425 IN PVOID OffsetPtr,
426 IN DWORD Length,
427 IN PSOUND_OVERLAPPED Overlap,
428 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
429 {
430 HANDLE Handle;
431 WDMAUD_DEVICE_INFO DeviceInfo;
432 DWORD BufferLength, BufferLengthTemp;
433 PVOID BufferOut, BufferOutTemp;
434 DWORD Status;
435 BOOL Result;
436
437 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
438 VALIDATE_MMSYS_PARAMETER( OffsetPtr );
439 VALIDATE_MMSYS_PARAMETER( Overlap );
440 VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
441
442 GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
443
444 SND_ASSERT(Handle);
445
446 BufferOut = OffsetPtr;
447 BufferLength = Length;
448
449 if (SoundDeviceInstance->WaveFormatEx.wBitsPerSample != 16)
450 {
451 Status = PerformQualityConversion(OffsetPtr,
452 Length,
453 SoundDeviceInstance->WaveFormatEx.wBitsPerSample,
454 16,
455 &BufferOut,
456 &BufferLength);
457 if (Status)
458 {
459 SND_TRACE(L"PerformQualityConversion failed\n");
460 return MMSYSERR_NOERROR;
461 }
462 }
463
464 if (SoundDeviceInstance->WaveFormatEx.nChannels != 2)
465 {
466 Status = PerformChannelConversion(BufferOut,
467 BufferLength,
468 SoundDeviceInstance->WaveFormatEx.nChannels,
469 2,
470 16,
471 &BufferOutTemp,
472 &BufferLengthTemp);
473
474 if (BufferOut != OffsetPtr)
475 {
476 HeapFree(GetProcessHeap(), 0, BufferOut);
477 }
478
479 if (Status)
480 {
481 SND_TRACE(L"PerformChannelConversion failed\n");
482 return MMSYSERR_NOERROR;
483 }
484
485 BufferOut = BufferOutTemp;
486 BufferLength = BufferLengthTemp;
487 }
488
489 if (SoundDeviceInstance->WaveFormatEx.nSamplesPerSec != 44100)
490 {
491 Status = PerformSampleRateConversion(BufferOut,
492 BufferLength,
493 SoundDeviceInstance->WaveFormatEx.nSamplesPerSec,
494 44100,
495 2,
496 2,
497 &BufferOutTemp,
498 &BufferLengthTemp);
499
500 if (BufferOut != OffsetPtr)
501 {
502 HeapFree(GetProcessHeap(), 0, BufferOut);
503 }
504
505 if (Status)
506 {
507 SND_TRACE(L"PerformSampleRateConversion failed\n");
508 return MMSYSERR_NOERROR;
509 }
510
511 BufferOut = BufferOutTemp;
512 BufferLength = BufferLengthTemp;
513 }
514
515 ZeroMemory(&DeviceInfo, sizeof(WDMAUD_DEVICE_INFO));
516 DeviceInfo.hDevice = Handle;
517 DeviceInfo.DeviceType = WAVE_OUT_DEVICE_TYPE; //FIXME
518 DeviceInfo.Header.FrameExtent = BufferLength;
519 DeviceInfo.Header.DataUsed = BufferLength;
520 DeviceInfo.Header.Data = BufferOut;
521 DeviceInfo.Header.Size = sizeof(KSSTREAM_HEADER);
522 DeviceInfo.Header.PresentationTime.Numerator = 1;
523 DeviceInfo.Header.PresentationTime.Denominator = 1;
524
525 Overlap->CompletionContext = UlongToPtr(Length);
526 Overlap->OriginalCompletionRoutine = CompletionRoutine;
527
528 Overlap->Standard.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
529
530 //SND_TRACE(L"OriginalLength %u NewLength %u\n", Length, BufferLength);
531
532 #if 0
533 Result = WriteFileEx(KernelHandle, &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), (LPOVERLAPPED)Overlap, CompletionRoutine);
534 #else
535 Result = WriteFileEx(KernelHandle, &DeviceInfo, sizeof(WDMAUD_DEVICE_INFO), (LPOVERLAPPED)Overlap, MixerCompletionRoutine);
536 #endif
537
538 if ( ! Result )
539 {
540 SND_TRACE(L"WriteFileEx failed with %x\n", GetLastError());
541 return MMSYSERR_NOERROR;
542 }
543
544 WaitForSingleObjectEx (KernelHandle, INFINITE, TRUE);
545
546 #ifdef USERMODE_MIXER
547 // if (BufferOut != OffsetPtr)
548 // HeapFree(GetProcessHeap(), 0, BufferOut);
549 #endif
550
551
552 return MMSYSERR_NOERROR;
553 }
554
555