[DSOUND_NEW]
authorJohannes Anderwald <johannes.anderwald@reactos.org>
Sat, 7 Nov 2009 03:43:56 +0000 (03:43 +0000)
committerJohannes Anderwald <johannes.anderwald@reactos.org>
Sat, 7 Nov 2009 03:43:56 +0000 (03:43 +0000)
- 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

reactos/dll/directx/dsound_new/capturebuffer.c
reactos/dll/directx/dsound_new/misc.c
reactos/dll/directx/dsound_new/precomp.h

index 8762ff2..df042a2 100644 (file)
@@ -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 */
index ae313c9..73c2e63 100644 (file)
@@ -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;
index 0bf9b19..760c46e 100644 (file)
@@ -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,