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