/* * PROJECT: ReactOS Sound System "MME Buddy" Library * LICENSE: GPL - See COPYING in the top level directory * FILE: lib/drivers/sound/mmebuddy/wave/header.c * * PURPOSE: Wave header preparation and submission routines * * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) */ #include #include #include #include #include #include /* This structure gets used locally within functions as a way to shuttle data to the sound thread. It's safe to use locally since CallSoundThread will not return until the operation has been carried out. */ typedef struct { MMWAVEHEADER_FUNC Function; PWAVEHDR Header; } THREADED_WAVEHEADER_PARAMETERS; /* Helper routines to simplify the call to the sound thread for the header functions. */ MMRESULT WaveHeaderOperationInSoundThread( PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID Parameter) { THREADED_WAVEHEADER_PARAMETERS* Parameters = (THREADED_WAVEHEADER_PARAMETERS*) Parameter; return Parameters->Function(SoundDeviceInstance, Parameters->Header); } MMRESULT WaveHeaderOperation( MMWAVEHEADER_FUNC Function, PSOUND_DEVICE_INSTANCE SoundDeviceInstance, PWAVEHDR Header) { THREADED_WAVEHEADER_PARAMETERS Parameters; Parameters.Function = Function; Parameters.Header = Header; return CallSoundThread(SoundDeviceInstance, WaveHeaderOperationInSoundThread, &Parameters); } /* SanitizeWaveHeader Clean up a header / reinitialize */ VOID SanitizeWaveHeader( PWAVEHDR Header) { PWAVEHDR_EXTENSION Extension = (PWAVEHDR_EXTENSION) Header->reserved; SND_ASSERT( Extension ); Header->dwBytesRecorded = 0; Extension->BytesCommitted = 0; Extension->BytesCompleted = 0; } /* The following routines are basically handlers for: - WODM_PREPARE - WODM_UNPREPARE - WODM_WRITE All of these calls are ultimately dealt with in the context of the appropriate sound thread, so the implementation should expect itself to be running in this other thread when any of these operations take place. */ MMRESULT PrepareWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header) { MMRESULT Result; PSOUND_DEVICE SoundDevice; PMMFUNCTION_TABLE FunctionTable; PWAVEHDR_EXTENSION Extension; VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); VALIDATE_MMSYS_PARAMETER( Header ); SND_TRACE(L"Preparing wave header\n"); Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); Extension = AllocateStruct(WAVEHDR_EXTENSION); if ( ! Extension ) return MMSYSERR_NOMEM; Header->reserved = (DWORD_PTR) Extension; Extension->BytesCommitted = 0; Extension->BytesCompleted = 0; /* Configure the flags */ Header->dwFlags |= WHDR_PREPARED; return MMSYSERR_NOERROR; } MMRESULT UnprepareWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header) { MMRESULT Result; PSOUND_DEVICE SoundDevice; PMMFUNCTION_TABLE FunctionTable; PWAVEHDR_EXTENSION Extension; VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); VALIDATE_MMSYS_PARAMETER( Header ); SND_TRACE(L"Un-preparing wave header\n"); Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); SND_ASSERT( Header->reserved ); Extension = (PWAVEHDR_EXTENSION) Header->reserved; FreeMemory(Extension); /* Configure the flags */ Header->dwFlags &= ~WHDR_PREPARED; return MMSYSERR_NOERROR; } MMRESULT WriteWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header) { MMRESULT Result; PSOUND_DEVICE SoundDevice; PMMFUNCTION_TABLE FunctionTable; VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); VALIDATE_MMSYS_PARAMETER( Header ); SND_TRACE(L"Submitting wave header\n"); Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); if ( ! MMSUCCESS(Result) ) return TranslateInternalMmResult(Result); if ( ! FunctionTable->CommitWaveBuffer ) return MMSYSERR_NOTSUPPORTED; /* A few minor sanity checks - any custom checks should've been carried out during wave header preparation etc. */ VALIDATE_MMSYS_PARAMETER( Header->lpData != NULL ); VALIDATE_MMSYS_PARAMETER( Header->dwBufferLength > 0 ); VALIDATE_MMSYS_PARAMETER( Header->dwFlags & WHDR_PREPARED ); VALIDATE_MMSYS_PARAMETER( ! (Header->dwFlags & WHDR_INQUEUE) ); SanitizeWaveHeader(Header); /* Clear the "done" flag for the buffer */ Header->dwFlags &= ~WHDR_DONE; Result = CallSoundThread(SoundDeviceInstance, EnqueueWaveHeader, Header); return Result; } /* EnqueueWaveHeader Put the header in the record/playback queue. This is performed within the context of the sound thread, it must NEVER be called from another thread. CompleteWaveHeader Set the header information to indicate that it has finished playing, and return it to the client application. This again must be called within the context of the sound thread. */ MMRESULT EnqueueWaveHeader( PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID Parameter) { PWAVEHDR WaveHeader = (PWAVEHDR) Parameter; VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance ); VALIDATE_MMSYS_PARAMETER( Parameter ); /* Initialise */ WaveHeader->lpNext = NULL; /* Set the "in queue" flag */ WaveHeader->dwFlags |= WHDR_INQUEUE; if ( ! SoundDeviceInstance->HeadWaveHeader ) { /* This is the first header in the queue */ SND_TRACE(L"Enqueued first wave header\n"); SoundDeviceInstance->HeadWaveHeader = WaveHeader; SoundDeviceInstance->TailWaveHeader = WaveHeader; DoWaveStreaming(SoundDeviceInstance); } else { /* There are already queued headers - make this one the tail */ SND_TRACE(L"Enqueued next wave header\n"); /* FIXME - Make sure that the buffer has not already been added to the list */ if ( SoundDeviceInstance->TailWaveHeader != WaveHeader ) { SND_ASSERT(SoundDeviceInstance->TailWaveHeader != WaveHeader); SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader; SoundDeviceInstance->TailWaveHeader = WaveHeader; DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); DoWaveStreaming(SoundDeviceInstance); } } DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); return MMSYSERR_NOERROR; } VOID CompleteWaveHeader( IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header) { PWAVEHDR PrevHdr = NULL, CurrHdr = NULL; PWAVEHDR_EXTENSION Extension; PSOUND_DEVICE SoundDevice; MMDEVICE_TYPE DeviceType; MMRESULT Result; SND_TRACE(L"BUFFER COMPLETE :)\n"); // TODO: Set header flags? // TODO: Call client // TODO: Streaming //DoWaveStreaming(SoundDeviceInstance); Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); SND_ASSERT( MMSUCCESS(Result) ); Result = GetSoundDeviceType(SoundDevice, &DeviceType); SND_ASSERT( MMSUCCESS(Result) ); Extension = (PWAVEHDR_EXTENSION)Header->reserved; SND_ASSERT( Extension ); /* Remove the header from the queue, like so */ if ( SoundDeviceInstance->HeadWaveHeader == Header ) { SoundDeviceInstance->HeadWaveHeader = Header->lpNext; SND_TRACE(L"Dropping head node\n"); /* If nothing after the head, then there is no tail */ if ( Header->lpNext == NULL ) { SND_TRACE(L"Dropping tail node\n"); SoundDeviceInstance->TailWaveHeader = NULL; } } else { PrevHdr = NULL; CurrHdr = SoundDeviceInstance->HeadWaveHeader; SND_TRACE(L"Relinking nodes\n"); while ( CurrHdr != Header ) { PrevHdr = CurrHdr; CurrHdr = CurrHdr->lpNext; SND_ASSERT( CurrHdr ); } SND_ASSERT( PrevHdr ); PrevHdr->lpNext = CurrHdr->lpNext; /* If this is the tail node, update the tail */ if ( Header->lpNext == NULL ) { SND_TRACE(L"Updating tail node\n"); SoundDeviceInstance->TailWaveHeader = PrevHdr; } } /* Make sure we're not using this as the current buffer any more, either! */ /* if ( SoundDeviceInstance->CurrentWaveHeader == Header ) { SoundDeviceInstance->CurrentWaveHeader = Header->lpNext; } */ DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); SND_TRACE(L"Returning buffer to client...\n"); /* Update the header */ Header->dwFlags &= ~WHDR_INQUEUE; Header->dwFlags |= WHDR_DONE; if ( DeviceType == WAVE_IN_DEVICE_TYPE ) { // FIXME: We won't be called on incomplete buffer! Header->dwBytesRecorded = Extension->BytesCompleted; } /* Safe to do this without thread protection, as we're done with the header */ NotifyMmeClient(SoundDeviceInstance, DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA, (DWORD_PTR)Header); }