[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
This commit is contained in:
Johannes Anderwald 2009-11-07 03:43:56 +00:00
parent c74f994e2f
commit 4bd3460f8a
3 changed files with 268 additions and 6 deletions

View 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 */

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

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