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