xref: /reactos/dll/win32/wdmaud.drv/mixer.c (revision c2c66aff)
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