reactos/lib/drivers/sound/mmebuddy/wave/streaming.c
Amine Khaldi c424146e2c Create a branch for cmake bringup.
svn path=/branches/cmake-bringup/; revision=48236
2010-07-24 18:52:44 +00:00

309 lines
9.6 KiB
C

/*
* PROJECT: ReactOS Sound System "MME Buddy" Library
* LICENSE: GPL - See COPYING in the top level directory
* FILE: lib/drivers/sound/mmebuddy/wave/streaming.c
*
* PURPOSE: Wave streaming
*
* PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
*/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <ntddsnd.h>
#include <mmebuddy.h>
#include <sndtypes.h>
/*
DoWaveStreaming
Check if there is streaming to be done, and if so, do it.
*/
VOID
DoWaveStreaming(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
{
MMRESULT Result;
MMDEVICE_TYPE DeviceType;
PSOUND_DEVICE SoundDevice;
PMMFUNCTION_TABLE FunctionTable;
PWAVEHDR Header;
PWAVEHDR_EXTENSION HeaderExtension;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
SND_ASSERT( MMSUCCESS(Result) );
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
SND_ASSERT( MMSUCCESS(Result) );
Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
SND_ASSERT( MMSUCCESS(Result) );
SND_ASSERT( FunctionTable );
SND_ASSERT( FunctionTable->CommitWaveBuffer );
/* No point in doing anything if no resources available to use */
if ( SoundDeviceInstance->OutstandingBuffers >= SoundDeviceInstance->BufferCount )
{
SND_TRACE(L"DoWaveStreaming: No available buffers to stream with - doing nothing\n");
return;
}
/* Is there any work to do? */
Header = SoundDeviceInstance->HeadWaveHeader;
if ( ! Header )
{
SND_TRACE(L"DoWaveStreaming: No work to do - doing nothing\n");
return;
}
while ( ( SoundDeviceInstance->OutstandingBuffers < SoundDeviceInstance->BufferCount ) &&
( Header ) )
{
HeaderExtension = (PWAVEHDR_EXTENSION) Header->reserved;
SND_ASSERT( HeaderExtension );
/* Saniy checks */
SND_ASSERT(Header->dwFlags & WHDR_PREPARED);
SND_ASSERT(Header->dwFlags & WHDR_INQUEUE);
/* Can never be *above* the length */
SND_ASSERT( HeaderExtension->BytesCommitted <= Header->dwBufferLength );
/* Is this header entirely committed? */
if ( HeaderExtension->BytesCommitted == Header->dwBufferLength )
{
{
/* Move on to the next header */
SND_ASSERT(Header != Header->lpNext);
Header = Header->lpNext;
}
}
else
{
PSOUND_OVERLAPPED Overlap;
LPVOID OffsetPtr;
DWORD BytesRemaining, BytesToCommit;
BOOL OK;
/* Where within the header buffer to stream from */
OffsetPtr = Header->lpData + HeaderExtension->BytesCommitted;
/* How much of this header has not been committed */
BytesRemaining = Header->dwBufferLength - HeaderExtension->BytesCommitted;
/* We can commit anything up to the buffer size limit */
BytesToCommit = BytesRemaining > SoundDeviceInstance->FrameSize ?
SoundDeviceInstance->FrameSize :
BytesRemaining;
/* Should always have something to commit by this point */
SND_ASSERT( BytesToCommit > 0 );
/* We need a new overlapped info structure for each buffer */
Overlap = AllocateStruct(SOUND_OVERLAPPED);
if ( Overlap )
{
ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED));
Overlap->SoundDeviceInstance = SoundDeviceInstance;
Overlap->Header = Header;
/* Don't complete this header if it's part of a loop */
Overlap->PerformCompletion = TRUE;
// ( SoundDeviceInstance->LoopsRemaining > 0 );
/* Adjust the commit-related counters */
HeaderExtension->BytesCommitted += BytesToCommit;
++ SoundDeviceInstance->OutstandingBuffers;
OK = MMSUCCESS(FunctionTable->CommitWaveBuffer(SoundDeviceInstance,
OffsetPtr,
BytesToCommit,
Overlap,
CompleteIO));
if ( ! OK )
{
/* Clean-up and try again on the next iteration (is this OK?) */
SND_WARN(L"FAILED\n");
FreeMemory(Overlap);
HeaderExtension->BytesCommitted -= BytesToCommit;
-- SoundDeviceInstance->OutstandingBuffers;
}
}
}
}
}
/*
CompleteIO
An APC called as a result of a call to CommitWaveHeaderToKernelDevice.
This will count up the number of bytes which have been dealt with,
and when the entire wave header has been dealt with, will call
CompleteWaveHeader to have the wave header returned to the client.
CommitWaveHeaderToKernelDevice
Sends portions of the buffer described by the wave header to a kernel
device. This must only be called from within the context of the sound
thread. The caller supplies either their own commit routine, or uses
WriteFileEx_Committer. The committer is called with portions of the
buffer specified in the wave header.
WriteFileEx_Committer
Commit buffers using the WriteFileEx API.
*/
VOID CALLBACK
CompleteIO(
IN DWORD dwErrorCode,
IN DWORD dwNumberOfBytesTransferred,
IN LPOVERLAPPED lpOverlapped)
{
MMDEVICE_TYPE DeviceType;
PSOUND_DEVICE SoundDevice;
PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
PWAVEHDR WaveHdr;
PWAVEHDR_EXTENSION HdrExtension;
MMRESULT Result;
DWORD Bytes;
WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
SND_ASSERT( WaveHdr );
SND_ASSERT( ERROR_SUCCESS == dwErrorCode );
HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
SND_ASSERT( HdrExtension );
SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
SND_ASSERT( MMSUCCESS(Result) );
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
SND_ASSERT( MMSUCCESS(Result) );
do
{
/* We have an available buffer now */
-- SoundDeviceInstance->OutstandingBuffers;
/* Did we finish a WAVEHDR and aren't looping? */
if ( HdrExtension->BytesCompleted + dwNumberOfBytesTransferred >= WaveHdr->dwBufferLength &&
SoundOverlapped->PerformCompletion )
{
/* Wave buffer fully completed */
Bytes = WaveHdr->dwBufferLength - HdrExtension->BytesCompleted;
HdrExtension->BytesCompleted += Bytes;
dwNumberOfBytesTransferred -= Bytes;
CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
}
else
{
/* Partially completed */
HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
break;
}
/* Move to next wave header */
WaveHdr = WaveHdr->lpNext;
if (!WaveHdr)
{
/* No following WaveHdr */
SND_ASSERT(dwNumberOfBytesTransferred == 0);
break;
}
HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
SND_ASSERT( HdrExtension );
}while(dwNumberOfBytesTransferred);
DoWaveStreaming(SoundDeviceInstance);
//CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
FreeMemory(lpOverlapped);
}
MMRESULT
WriteFileEx_Committer(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
IN PVOID OffsetPtr,
IN DWORD Length,
IN PSOUND_OVERLAPPED Overlap,
IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
{
HANDLE Handle;
VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
VALIDATE_MMSYS_PARAMETER( OffsetPtr );
VALIDATE_MMSYS_PARAMETER( Overlap );
VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
{
// TODO
}
return MMSYSERR_NOERROR;
}
/*
Stream control functions
(External/internal thread pairs)
TODO - Move elsewhere as these shouldn't be wave specific!
*/
MMRESULT
StopStreamingInSoundThread(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
IN PVOID Parameter)
{
/* TODO */
return MMSYSERR_NOTSUPPORTED;
}
MMRESULT
StopStreaming(
IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
{
MMRESULT Result;
PSOUND_DEVICE SoundDevice;
MMDEVICE_TYPE DeviceType;
if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
return MMSYSERR_INVALHANDLE;
Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
Result = GetSoundDeviceType(SoundDevice, &DeviceType);
if ( ! MMSUCCESS(Result) )
return TranslateInternalMmResult(Result);
if ( DeviceType != WAVE_OUT_DEVICE_TYPE && DeviceType != WAVE_IN_DEVICE_TYPE )
return MMSYSERR_NOTSUPPORTED;
return CallSoundThread(SoundDeviceInstance,
StopStreamingInSoundThread,
NULL);
}