From 4bd3460f8ab8a52b51601c079f3ec0bc9ac6fd43 Mon Sep 17 00:00:00 2001 From: Johannes Anderwald Date: Sat, 7 Nov 2009 03:43:56 +0000 Subject: [PATCH] [DSOUND_NEW] - Implement stereo to mono channel conversion - Create a thread which performs the mixing - Fixes messed up voice recording in Skype 3.6 svn path=/trunk/; revision=43994 --- .../dll/directx/dsound_new/capturebuffer.c | 190 +++++++++++++++++- reactos/dll/directx/dsound_new/misc.c | 72 ++++++- reactos/dll/directx/dsound_new/precomp.h | 12 ++ 3 files changed, 268 insertions(+), 6 deletions(-) diff --git a/reactos/dll/directx/dsound_new/capturebuffer.c b/reactos/dll/directx/dsound_new/capturebuffer.c index 8762ff2da49..df042a23e2c 100644 --- a/reactos/dll/directx/dsound_new/capturebuffer.c +++ b/reactos/dll/directx/dsound_new/capturebuffer.c @@ -30,9 +30,102 @@ typedef struct BOOL bMix; BOOL bLoop; KSSTATE State; + PUCHAR MixBuffer; + ULONG MixBufferSize; + HANDLE hStopEvent; + volatile LONG StopMixerThread; + volatile LONG CurrentMixPosition; }CDirectSoundCaptureBufferImpl, *LPCDirectSoundCaptureBufferImpl; +DWORD +WINAPI +MixerThreadRoutine( + LPVOID lpParameter) +{ + KSPROPERTY Request; + KSAUDIO_POSITION Position; + DWORD Result, MixPosition, BufferPosition, BytesWritten, BytesRead, MixLength, BufferLength; + LPCDirectSoundCaptureBufferImpl This = (LPCDirectSoundCaptureBufferImpl)lpParameter; + + /* setup audio position property request */ + Request.Id = KSPROPERTY_AUDIO_POSITION; + Request.Set = KSPROPSETID_Audio; + Request.Flags = KSPROPERTY_TYPE_GET; + + MixPosition = 0; + BufferPosition = 0; + do + { + /* query current position */ + Result = SyncOverlappedDeviceIoControl(This->hPin, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSPROPERTY), (PVOID)&Position, sizeof(KSAUDIO_POSITION), NULL); + + /* sanity check */ + ASSERT(Result == ERROR_SUCCESS); + + /* FIXME implement samplerate conversion */ + ASSERT(This->MixFormat.nSamplesPerSec == This->Format->nSamplesPerSec); + + /* FIXME implement bitrate conversion */ + ASSERT(This->MixFormat.wBitsPerSample == This->Format->wBitsPerSample); + + /* sanity check */ + ASSERT(BufferPosition <= This->BufferSize); + ASSERT(MixPosition <= This->MixBufferSize); + + if (BufferPosition == This->BufferSize) + { + /* restart from front */ + BufferPosition = 0; + } + + if (MixPosition == This->MixBufferSize) + { + /* restart from front */ + MixPosition = 0; + } + + if (This->MixFormat.nChannels != This->Format->nChannels) + { + if ((DWORD)Position.PlayOffset >= MixPosition) + { + /* calculate buffer position difference */ + MixLength = Position.PlayOffset - MixPosition; + } + else + { + /* buffer overlap */ + MixLength = This->MixBufferSize - MixPosition; + } + + BufferLength = This->BufferSize - BufferPosition; + + /* convert the format */ + PerformChannelConversion(&This->MixBuffer[MixPosition], MixLength, &BytesRead, This->MixFormat.nChannels, This->Format->nChannels, This->Format->wBitsPerSample, &This->Buffer[BufferPosition], BufferLength, &BytesWritten); + + /* update buffer offsets */ + MixPosition += BytesRead; + BufferPosition += BytesWritten; + DPRINT("MixPosition %u BufferPosition %u BytesRead %u BytesWritten %u MixLength %u BufferLength %u\n", MixPosition, BufferPosition, BytesRead, BytesWritten, MixLength, BufferLength); + } + /* update offset */ + InterlockedExchange(&This->CurrentMixPosition, (LONG)BufferPosition); + + /* FIXME use timer */ + Sleep(10); + + }while(InterlockedCompareExchange(&This->StopMixerThread, 0, 0) == 0); + + + /* signal stop event */ + SetEvent(This->hStopEvent); + + /* done */ + return 0; +} + + + HRESULT WINAPI IDirectSoundCaptureBufferImpl_QueryInterface( @@ -96,6 +189,18 @@ IDirectSoundCaptureBufferImpl_Release( CloseHandle(This->hPin); } + if (This->hStopEvent) + { + /* close stop event handle */ + CloseHandle(This->hStopEvent); + } + + if (This->MixBuffer) + { + /* free mix buffer */ + HeapFree(GetProcessHeap(), 0, This->MixBuffer); + } + /* free capture buffer */ HeapFree(GetProcessHeap(), 0, This->Buffer); /* free wave format */ @@ -145,6 +250,7 @@ IDirectSoundCaptureBufferImpl_GetCurrentPosition( KSAUDIO_POSITION Position; KSPROPERTY Request; DWORD Result; + DWORD Value; LPCDirectSoundCaptureBufferImpl This = (LPCDirectSoundCaptureBufferImpl)CONTAINING_RECORD(iface, CDirectSoundCaptureBufferImpl, lpVtbl); @@ -160,6 +266,20 @@ IDirectSoundCaptureBufferImpl_GetCurrentPosition( return DS_OK; } + if (This->bMix) + { + /* read current position */ + Value = InterlockedCompareExchange(&This->CurrentMixPosition, 0, 0); + + if (lpdwCapturePosition) + *lpdwCapturePosition = (DWORD)Value; + + if (lpdwReadPosition) + *lpdwReadPosition = (DWORD)Value; + + return DS_OK; + } + /* setup audio position property request */ Request.Id = KSPROPERTY_AUDIO_POSITION; Request.Set = KSPROPSETID_Audio; @@ -326,6 +446,8 @@ IDirectSoundCaptureBufferImpl_Start( DWORD Result, BytesTransferred; OVERLAPPED Overlapped; KSSTATE State; + HANDLE hThread; + LPCDirectSoundCaptureBufferImpl This = (LPCDirectSoundCaptureBufferImpl)CONTAINING_RECORD(iface, CDirectSoundCaptureBufferImpl, lpVtbl); DPRINT("IDirectSoundCaptureBufferImpl_Start Flags %x\n", dwFlags); @@ -367,7 +489,7 @@ IDirectSoundCaptureBufferImpl_Start( /* initialize stream header */ Header.FrameExtent = This->BufferSize; Header.DataUsed = 0; - Header.Data = This->Buffer; + Header.Data = (This->bMix ? This->MixBuffer : This->Buffer); Header.Size = sizeof(KSSTREAM_HEADER); Header.PresentationTime.Numerator = 1; Header.PresentationTime.Denominator = 1; @@ -380,6 +502,34 @@ IDirectSoundCaptureBufferImpl_Start( return DSERR_GENERIC; } + if (This->bMix) + { + if (!This->hStopEvent) + { + /* create stop event */ + This->hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!This->hStopEvent) + { + DPRINT1("Failed to create event object with %x\n", GetLastError()); + return DSERR_GENERIC; + } + } + + /* set state to stop false */ + This->StopMixerThread = FALSE; + + hThread = CreateThread(NULL, 0, MixerThreadRoutine, (PVOID)This, 0, NULL); + if (!hThread) + { + DPRINT1("Failed to create thread with %x\n", GetLastError()); + return DSERR_GENERIC; + } + + /* close thread handle */ + CloseHandle(hThread); + } + + return DS_OK; } @@ -414,6 +564,20 @@ IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER8 iface ) ASSERT(Result == ERROR_SUCCESS); + + if (This->bMix) + { + /* sanity check */ + ASSERT(This->hStopEvent); + /* reset event */ + ResetEvent(This->hStopEvent); + /* signal event to stop */ + This->StopMixerThread = TRUE; + /* Wait for the event to stop */ + WaitForSingleObject(This->hStopEvent, INFINITE); + } + + if (Result == ERROR_SUCCESS) { /* store result */ @@ -492,7 +656,7 @@ NewDirectSoundCaptureBuffer( LPFILTERINFO Filter, LPCDSCBUFFERDESC lpcDSBufferDesc) { - DWORD FormatSize; + DWORD FormatSize, MixBufferSize; ULONG DeviceId = 0, PinId; DWORD Result = ERROR_SUCCESS; WAVEFORMATEX MixFormat; @@ -580,11 +744,33 @@ NewDirectSoundCaptureBuffer( { /* FIXME should not happen */ DPRINT("failed to compute a compatible format\n"); + HeapFree(GetProcessHeap(), 0, This->MixBuffer); HeapFree(GetProcessHeap(), 0, This->Buffer); HeapFree(GetProcessHeap(), 0, This->Format); HeapFree(GetProcessHeap(), 0, This); return DSERR_GENERIC; } + + MixBufferSize = lpcDSBufferDesc->dwBufferBytes; + MixBufferSize /= lpcDSBufferDesc->lpwfxFormat->nChannels; + MixBufferSize /= (lpcDSBufferDesc->lpwfxFormat->wBitsPerSample/8); + + MixBufferSize *= This->MixFormat.nChannels; + MixBufferSize *= (This->MixFormat.wBitsPerSample/8); + + /* allocate buffer for mixing */ + This->MixBuffer = HeapAlloc(GetProcessHeap(), 0, MixBufferSize); + if (!This->Buffer) + { + /* not enough memory */ + CloseHandle(This->hPin); + HeapFree(GetProcessHeap(), 0, This->Buffer); + HeapFree(GetProcessHeap(), 0, This->Format); + HeapFree(GetProcessHeap(), 0, This); + return DSERR_OUTOFMEMORY; + } + This->MixBufferSize = MixBufferSize; + DPRINT1("MixBufferSize %u BufferSize %u\n", MixBufferSize, This->BufferSize); } /* initialize capture buffer */ diff --git a/reactos/dll/directx/dsound_new/misc.c b/reactos/dll/directx/dsound_new/misc.c index ae313c90cbe..73c2e63a283 100644 --- a/reactos/dll/directx/dsound_new/misc.c +++ b/reactos/dll/directx/dsound_new/misc.c @@ -13,6 +13,69 @@ const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, { const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}}; const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}; + +VOID +PerformChannelConversion( + PUCHAR Buffer, + ULONG BufferLength, + PULONG BytesRead, + ULONG OldChannels, + ULONG NewChannels, + ULONG BitsPerSample, + PUCHAR Result, + ULONG ResultLength, + PULONG BytesWritten) +{ + DWORD Samples; + DWORD NewIndex, OldIndex; + DWORD NewLength, Skip; + + Samples = BufferLength / (BitsPerSample / 8) / OldChannels; + + if (NewChannels > OldChannels) + { + UNIMPLEMENTED + ASSERT(0); + } + + /* setup index */ + NewIndex = 0; + OldIndex = 0; + + /* calculate offsets */ + NewLength = NewChannels * (BitsPerSample/8); + Skip = OldChannels * (BitsPerSample/8); + + do + { + if (NewIndex + NewLength>= ResultLength) + { + NewIndex = ResultLength; + break; + } + + if (OldIndex + Skip >= BufferLength) + { + OldIndex = BufferLength; + break; + } + + /* copy first channel */ + RtlMoveMemory(&Result[NewIndex], &Buffer[OldIndex], NewLength); + + /* skip other channels */ + OldIndex += Skip; + + /* increment offset */ + NewIndex += NewLength; + + }while(TRUE); + + *BytesRead = OldIndex; + *BytesWritten = NewIndex; +} + + BOOL SetPinFormat( IN HANDLE hPin, @@ -498,20 +561,21 @@ CreateCompatiblePin( else if (WaveFormatOut->wBitsPerSample > AudioRange->MaximumBitsPerSample) WaveFormatOut->wBitsPerSample = AudioRange->MaximumBitsPerSample; - DPRINT1("MinimumBitsPerSample %u MaximumBitsPerSample %u MinimumSampleFrequency %u MaximumSampleFrequency %u\n", + DPRINT("MinimumBitsPerSample %u MaximumBitsPerSample %u MinimumSampleFrequency %u MaximumSampleFrequency %u\n", AudioRange->MinimumBitsPerSample, AudioRange->MaximumBitsPerSample, AudioRange->MinimumSampleFrequency, AudioRange->MaximumSampleFrequency); for(nChannels = 1; nChannels <= AudioRange->MaximumChannels; nChannels++) { WaveFormatOut->nChannels = nChannels; + dwResult = OpenPin(hFilter, PinId, WaveFormatOut, hPin, TRUE); + if (dwResult == ERROR_SUCCESS) + { DPRINT("InFormat nChannels %u wBitsPerSample %u nSamplesPerSec %u\nOutFormat nChannels %u nBitsPerSample %u nSamplesPerSec %u\n", WaveFormatEx->nChannels, WaveFormatEx->wBitsPerSample, WaveFormatEx->nSamplesPerSec, WaveFormatOut->nChannels, WaveFormatOut->wBitsPerSample, WaveFormatOut->nSamplesPerSec); - dwResult = OpenPin(hFilter, PinId, WaveFormatOut, hPin, TRUE); - if (dwResult == ERROR_SUCCESS) - { + /* free buffer */ HeapFree(GetProcessHeap(), 0, Item); return TRUE; diff --git a/reactos/dll/directx/dsound_new/precomp.h b/reactos/dll/directx/dsound_new/precomp.h index 0bf9b199ada..760c46e6a3f 100644 --- a/reactos/dll/directx/dsound_new/precomp.h +++ b/reactos/dll/directx/dsound_new/precomp.h @@ -110,6 +110,18 @@ NewDirectSound( /* misc.c */ +VOID +PerformChannelConversion( + PUCHAR Buffer, + ULONG BufferLength, + PULONG BytesRead, + ULONG OldChannels, + ULONG NewChannels, + ULONG BitsPerSample, + PUCHAR Result, + ULONG ResultLength, + PULONG BytesWritten); + BOOL SetPinFormat( IN HANDLE hPin,