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