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