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