From: Johannes Anderwald Date: Fri, 28 Jan 2011 10:17:01 +0000 (+0000) Subject: [MMEBUDDY] X-Git-Tag: backups/ros-branch-0_3_13@51035~86 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=269e92c794baf536aff23689ba36cfb3c7a93f7f [MMEBUDDY] - Merge from audio branch - Handle mixers identified by id, not by handle - Fix opening of mixer devices - Waveformat struct is only provided when type is wave device - Implement wave reset routine, fixes audio recording on ReactOS SndRec, AudaCity etc. Also fixes random hang in WinAmp when skipping audio bytes - Implement wave pausing / restarting, should lead to smoother playback [MMIXER] - Merge from audio branch - Tons of fixes to enumeration of mixerlines, controls / etc - Fix mixer event notification callbacks - For more info, read audio-bringup log svn path=/trunk/; revision=50528 --- 269e92c794baf536aff23689ba36cfb3c7a93f7f diff --cc reactos/lib/drivers/sound/mmebuddy/wave/streaming.c index a28093359b5,00000000000..63ac5e0a89d mode 100644,000000..100644 --- a/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c +++ b/reactos/lib/drivers/sound/mmebuddy/wave/streaming.c @@@ -1,309 -1,0 +1,365 @@@ +/* + * 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 +#include +#include +#include +#include +#include + + +/* + 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); + ++ // AUDIO-BRANCH DIFF ++ // completion callback is performed in a thread + 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; ++ MMDEVICE_TYPE DeviceType; ++ PMMFUNCTION_TABLE FunctionTable; ++ MMRESULT Result; ++ PSOUND_DEVICE SoundDevice; ++ ++ /* set state reset in progress */ ++ SoundDeviceInstance->ResetInProgress = TRUE; ++ ++ /* Get sound device */ ++ Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); ++ SND_ASSERT( Result == MMSYSERR_NOERROR ); ++ ++ /* Obtain the function table */ ++ Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); ++ SND_ASSERT( Result == MMSYSERR_NOERROR ); ++ ++ /* Obtain device instance type */ ++ Result = GetSoundDeviceType(SoundDevice, &DeviceType); ++ SND_ASSERT( Result == MMSYSERR_NOERROR ); ++ ++ /* Check if reset function is supported */ ++ if (FunctionTable->ResetStream) ++ { ++ /* cancel all current audio buffers */ ++ FunctionTable->ResetStream(SoundDeviceInstance, DeviceType, TRUE); ++ } ++ ++ /* complete all current headers */ ++ while( SoundDeviceInstance->HeadWaveHeader ) ++ { ++ SND_TRACE(L"StopStreamingInSoundThread: Completing Header %p\n", SoundDeviceInstance->HeadWaveHeader); ++ CompleteWaveHeader( SoundDeviceInstance, SoundDeviceInstance->HeadWaveHeader ); ++ } ++ ++ /* there should be no oustanding buffers now */ ++ SND_ASSERT(SoundDeviceInstance->OutstandingBuffers == 0); ++ ++ while(SoundDeviceInstance->OutstandingBuffers) ++ { ++ SND_ERR("StopStreamingInSoundThread OutStandingBufferCount %lu\n", SoundDeviceInstance->OutstandingBuffers); ++ /* my hack of doom */ ++ Sleep(10); ++ } ++ ++ /* Check if reset function is supported */ ++ if (FunctionTable->ResetStream) ++ { ++ /* finish the reset */ ++ FunctionTable->ResetStream(SoundDeviceInstance, DeviceType, FALSE); ++ } ++ ++ /* clear state reset in progress */ ++ SoundDeviceInstance->ResetInProgress = FALSE; ++ ++ ++ return MMSYSERR_NOERROR; +} + +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); +} diff --cc reactos/lib/drivers/sound/mmixer/TODO index 00000000000,05cf9c29474..05cf9c29474 mode 000000,100644..100644 --- a/reactos/lib/drivers/sound/mmixer/TODO +++ b/reactos/lib/drivers/sound/mmixer/TODO