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 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 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 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 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 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