mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
20 KiB
830 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
play.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code for ioctl play request functions.
|
|
|
|
Author:
|
|
|
|
Nigel Thompson (nigelt) 25-Apr-1991
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
Rewritten by Robin Speed (RobinSp) 10-Dec-1991 - 29-Jan-1992
|
|
|
|
Sameer Dekate ([email protected]) 19-Aug-1992
|
|
- Changes to support the MIPS sound board
|
|
|
|
--*/
|
|
|
|
#include "sound.h"
|
|
|
|
|
|
NTSTATUS
|
|
sndWavePlay(
|
|
IN OUT PLOCAL_DEVICE_INFO pLDI,
|
|
IN PIRP pIrp,
|
|
IN PIO_STACK_LOCATION pIrpStack
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The user has passed in a buffer of wave data to play.
|
|
|
|
If this is not a wave output device return with STATUS_NOT_SUPPORTED
|
|
|
|
This buffer is added to the list of buffers to be played and
|
|
sndStartOutput is called to start playing data if the device is not
|
|
already playing or if the output has not been (temporarily)
|
|
stopped by the application.
|
|
|
|
Arguments:
|
|
|
|
pLDI - Local device info
|
|
pIrp - The IO request packet
|
|
pIrpStack - The current stack location
|
|
|
|
Return Value:
|
|
|
|
Irp status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
dprintf5("In sndIoctlWavePlay pLDI= 0x%x pIrp = 0x%x pIrpStack = 0x%x", pLDI, pIrp, pIrpStack);
|
|
|
|
|
|
// DbgBreakPoint();
|
|
|
|
//
|
|
// confirm we are doing this on the output device!
|
|
//
|
|
|
|
if (pLDI->DeviceType != WAVE_OUT) {
|
|
dprintf1("Attempt to play on input device");
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Initialize data length.
|
|
//
|
|
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Mark the Irp pending before starting processing
|
|
//
|
|
|
|
IoMarkIrpPending(pIrp);
|
|
pIrp->IoStatus.Status = STATUS_PENDING;
|
|
|
|
//
|
|
// Inform debuggers that 0 length buffers are rather strange
|
|
//
|
|
|
|
if (pIrpStack->Parameters.Write.Length == 0) {
|
|
dprintf1("Wave planday buffer is zero length");
|
|
}
|
|
|
|
//
|
|
// Put the request in the queue and start the transfer if possible.
|
|
//
|
|
|
|
//
|
|
// Acquire the spin lock
|
|
//
|
|
|
|
GlobalEnter(pLDI->pGlobalInfo);
|
|
|
|
//
|
|
//
|
|
|
|
InsertTailList(&pLDI->QueueHead, &pIrp->Tail.Overlay.ListEntry);
|
|
dprintf5("irp added");
|
|
|
|
//
|
|
// test if dma is running. If not then start a new transfer.
|
|
// Otherwise try to fill out any silence in the next buffer to
|
|
// go before it starts.
|
|
//
|
|
|
|
sndStartOutput(pLDI);
|
|
|
|
|
|
//
|
|
// Ok to release the spin lock now
|
|
//
|
|
|
|
GlobalLeave(pLDI->pGlobalInfo);
|
|
|
|
//
|
|
// Mark this request as pending completion.
|
|
// The dispatch routine handles the rest of it
|
|
//
|
|
|
|
Status = STATUS_PENDING;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
sndStartOutput(
|
|
PLOCAL_DEVICE_INFO pLDI
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever an event occurs which could
|
|
output more wave data :
|
|
|
|
A new buffer is supplied
|
|
The state is changed from stopped to idle
|
|
|
|
If wave data is already playing it may still be possible to move
|
|
some data into the half buffer which is not playing.
|
|
|
|
If no data is playing then both dma half buffers are primed if
|
|
possible. If no data exists to be played this function is
|
|
a NOOP except that some 0 length buffers may be completed.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
pGDI = pLDI->pGlobalInfo;
|
|
|
|
//
|
|
// Try to fill up both buffers
|
|
//
|
|
|
|
|
|
//
|
|
// If DMA is running we may be able to pad out some silence to
|
|
// stop DMA from dying later. If not just initiate everything
|
|
//
|
|
|
|
if (!pGDI->DMABusy) {
|
|
ASSERT(pGDI->DMABuffer[0].nBytes == 0 &&
|
|
pGDI->DMABuffer[1].nBytes == 0);
|
|
|
|
//
|
|
// Compute the size of the buffer. We want interrupts at least once
|
|
// per 1/8 of a second to match AVI's assumptions. Round to 4 bytes.
|
|
//
|
|
|
|
pGDI->DmaHalfBufferSize =
|
|
((pGDI->SamplesPerSec * pGDI->BytesPerSample) / 8) & ~3;
|
|
|
|
if (pGDI->DmaHalfBufferSize > DMA_BUFFER_SIZE / 2) {
|
|
pGDI->DmaHalfBufferSize = DMA_BUFFER_SIZE / 2;
|
|
}
|
|
|
|
//
|
|
// A paused loop could leave stuff on the transit queue
|
|
// waiting to be looped round again
|
|
//
|
|
|
|
#ifdef WAVE_DD_DO_LOOPS
|
|
ASSERT(IsListEmpty(&pLDI->TransitQueue) || pLDI->LoopBegin);
|
|
#else
|
|
ASSERT(IsListEmpty(&pLDI->TransitQueue));
|
|
#endif // WAVE_DD_DO_LOOPS
|
|
|
|
ASSERT(IsListEmpty(&pLDI->DeadQueue));
|
|
|
|
pGDI->SoundHardware.TcInterruptsPending = 0;
|
|
pGDI->GotUnderFlow = 0;
|
|
|
|
pGDI->NextHalf = LowerHalf; // This causes the code which follows
|
|
// to do the right thing
|
|
}
|
|
|
|
//
|
|
// First try to stoke up the buffer currently being output (or about
|
|
// to be started if DMA is not currently running).
|
|
//
|
|
|
|
if (pGDI->DMABuffer[pGDI->NextHalf].nBytes <
|
|
pGDI->DmaHalfBufferSize) {
|
|
|
|
//
|
|
// Try to stoke up our buffer. We may also succeed in
|
|
// putting data in an empty buffer
|
|
//
|
|
|
|
sndLoadDMABuffer(pLDI,
|
|
&pGDI->DMABuffer[pGDI->NextHalf]);
|
|
|
|
//
|
|
// The buffers processed are now about to
|
|
// be played so move them to the dead queue.
|
|
//
|
|
|
|
while (!IsListEmpty(&pLDI->TransitQueue)) {
|
|
|
|
PLIST_ENTRY pListNode;
|
|
|
|
pListNode = RemoveHeadList(&pLDI->TransitQueue);
|
|
|
|
#ifdef WAVE_DD_DO_LOOPS
|
|
//
|
|
// Do not move loops over to the dead queue - there's
|
|
// still life in them !
|
|
//
|
|
|
|
if (pListNode == pLDI->LoopBegin) {
|
|
InsertHeadList(&pLDI->TransitQueue, pListNode);
|
|
break;
|
|
}
|
|
#endif // WAVE_DD_DO_LOOPS
|
|
|
|
//
|
|
// Move to death row
|
|
//
|
|
|
|
InsertTailList(&pLDI->DeadQueue, pListNode);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Try to fill the second buffer
|
|
//
|
|
|
|
if (pGDI->DMABuffer[UpperHalf + LowerHalf - pGDI->NextHalf].nBytes <
|
|
pGDI->DmaHalfBufferSize) {
|
|
|
|
sndLoadDMABuffer(pLDI,
|
|
&pGDI->DMABuffer[UpperHalf + LowerHalf -
|
|
pGDI->NextHalf]);
|
|
}
|
|
|
|
//
|
|
// See if DMA needs starting
|
|
//
|
|
|
|
if (!pGDI->DMABusy) {
|
|
|
|
if (pGDI->DMABuffer[LowerHalf].nBytes == 0) {
|
|
dprintf4("None loaded");
|
|
|
|
//
|
|
// Free any 0 length buffers to satisfy the testers
|
|
//
|
|
|
|
ASSERT(IsListEmpty(&pLDI->TransitQueue));
|
|
|
|
sndFreeQ(pLDI, &pLDI->DeadQueue, STATUS_SUCCESS);
|
|
|
|
//
|
|
// Note - the Dpc routine RELIES on us returning here and
|
|
// not restarting the DMA which it may have just stopped.
|
|
// This is because it tries to restart the DMA in case we
|
|
// overran or something. This is OK except when we think (on
|
|
// the device user's thread) we stopped the DMA but a Dpc
|
|
// has just been sheduled to run. This 'extra' Dpc restarts the
|
|
// DMA we thought was stopped and we end up in a real mess.
|
|
//
|
|
// The actual crash hit assert failed when DMABusy was TRUE
|
|
// in sndStartDMA in recording.
|
|
//
|
|
// NOTE - this driver is STILL NOT MP safe. When we stop the
|
|
// DMA technically we should flush out any Dpcs like the soundlib
|
|
// code does - but I don't think the hardware for this driver runs
|
|
// on any MP machines.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have something to dma
|
|
// Start the DMA - we're actually going to play something
|
|
//
|
|
|
|
sndStartDMA(pGDI, 1);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
sndLoadDMABuffer(
|
|
PLOCAL_DEVICE_INFO pLDI,
|
|
struct SOUND_DMABUF *pDMA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fill the given DMA buffer with as much data as is available.
|
|
|
|
This is where the supply of bytes is chopped if we're in a
|
|
WAVE_DD_STOPPED state. The supply then dries up and the Dpc routine
|
|
stops the DMA (and posts the pause packet).
|
|
|
|
|
|
Arguments:
|
|
|
|
pLDI - our local device data
|
|
pDMA - The buffer and how full it is now
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
pGDI = pLDI->pGlobalInfo;
|
|
ASSERT(pGDI->Key == GDI_KEY);
|
|
|
|
//
|
|
// Loop, copying data from the request list to the
|
|
// dma buffer. As we complete request blocks we mark
|
|
// them as done.
|
|
//
|
|
|
|
//
|
|
// If wave output is paused, do not process any data.
|
|
//
|
|
|
|
if (pLDI->State != WAVE_DD_STOPPED) {
|
|
|
|
//
|
|
// There should be no pending pauses because WAVE_DD_STOPPED
|
|
// should be the state if there is one
|
|
//
|
|
|
|
ASSERT(pGDI->pIrpPause == NULL);
|
|
|
|
//
|
|
// Loop copying data to the output buffers. Typically the
|
|
// output buffer will be much bigger than the DMA buffer.
|
|
//
|
|
|
|
while (pDMA->nBytes < pGDI->DmaHalfBufferSize) {
|
|
|
|
ULONG BytesToCopy;
|
|
|
|
//
|
|
// We might have completed the last buffer
|
|
// Note that we cope with 0 length buffers here
|
|
//
|
|
|
|
if (pGDI->pUserBuffer == NULL) {
|
|
sndGetNextBuffer(pLDI);
|
|
if (pGDI->pUserBuffer == NULL) {
|
|
|
|
//
|
|
// There REALLY aren't any buffers
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef WAVE_DD_DO_LOOPS
|
|
//
|
|
// See if this request contains any loop information
|
|
//
|
|
|
|
if (IoGetCurrentIrpStackLocation(pGDI->pIrp)->
|
|
Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(WAVEHDR)) {
|
|
|
|
//
|
|
// Check if this is the start of a new loop
|
|
// If so remember where the loop starts and
|
|
// how many iterations there are.
|
|
//
|
|
|
|
LPWAVEHDR pwhd;
|
|
|
|
pwhd = (LPWAVEHDR)pGDI->pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
if ((pwhd->dwFlags & WHDR_BEGINLOOP) &&
|
|
pLDI->LoopBegin != &pGDI->pIrp->Tail.Overlay.ListEntry) {
|
|
pLDI->LoopCount = pwhd->dwLoops;
|
|
if (pwhd->dwLoops == 0) {
|
|
pLDI->LoopBegin = NULL;
|
|
} else {
|
|
pLDI->LoopBegin = &pGDI->pIrp->Tail.Overlay.ListEntry;
|
|
}
|
|
}
|
|
}
|
|
#endif // WAVE_DD_DO_LOOPS
|
|
|
|
|
|
//
|
|
// Find out how much space we have left in the
|
|
// client's buffers
|
|
// Note that BytesToCopy may be 0 - this is OK
|
|
//
|
|
|
|
|
|
BytesToCopy =
|
|
min(pGDI->UserBufferSize - pGDI->UserBufferPosition,
|
|
pGDI->DmaHalfBufferSize - pDMA->nBytes);
|
|
|
|
//
|
|
// Copy the data
|
|
//
|
|
|
|
RtlMoveMemory(pDMA->Buf + pDMA->nBytes,
|
|
pGDI->pUserBuffer + pGDI->UserBufferPosition,
|
|
BytesToCopy);
|
|
|
|
//
|
|
// Update counters etc.
|
|
//
|
|
|
|
pGDI->UserBufferPosition += BytesToCopy;
|
|
pDMA->nBytes += BytesToCopy;
|
|
|
|
//
|
|
// Update our total of bytes
|
|
//
|
|
|
|
pLDI->SampleNumber += BytesToCopy;
|
|
|
|
//
|
|
// See if we've now filled a buffer
|
|
//
|
|
|
|
if (pGDI->UserBufferPosition == pGDI->UserBufferSize) {
|
|
|
|
dprintf4(" finished");
|
|
|
|
//
|
|
// Unmap the users buffer and set data length
|
|
//
|
|
|
|
sndCompleteIoBuffer(pGDI);
|
|
|
|
//
|
|
// Move the request into the transit camp
|
|
//
|
|
|
|
InsertTailList(&pLDI->TransitQueue,
|
|
&pGDI->pIrp->Tail.Overlay.ListEntry);
|
|
|
|
#ifdef WAVE_DD_DO_LOOPS
|
|
//
|
|
// Check for the ends of loops
|
|
//
|
|
|
|
if (IoGetCurrentIrpStackLocation(pGDI->pIrp)->
|
|
Parameters.DeviceIoControl.InputBufferLength >=
|
|
sizeof(WAVEHDR)) {
|
|
|
|
//
|
|
// Set input and output loop
|
|
//
|
|
|
|
LPWAVEHDR pwhd;
|
|
|
|
pwhd =
|
|
(LPWAVEHDR)pGDI->pIrp->AssociatedIrp.SystemBuffer;
|
|
|
|
if (pwhd->dwFlags & WHDR_ENDLOOP) {
|
|
if (pLDI->LoopCount == 0) {
|
|
//
|
|
// Finished looping - there may be some
|
|
// Irps which can be completed now but
|
|
// we leave them until the end of this
|
|
// DMA buffer.
|
|
//
|
|
pLDI->LoopBegin = NULL;
|
|
|
|
} else {
|
|
PLIST_ENTRY pListNode;
|
|
|
|
//
|
|
// There are more loops. Decrement the
|
|
// counter and restart the loop.
|
|
//
|
|
|
|
pLDI->LoopCount--;
|
|
|
|
do {
|
|
ASSERT(!IsListEmpty(&pLDI->TransitQueue));
|
|
pListNode = RemoveTailList(&pLDI->TransitQueue);
|
|
InsertHeadList(&pLDI->QueueHead, pListNode);
|
|
} while (pListNode != pLDI->LoopBegin);
|
|
}
|
|
}
|
|
} // End of loop stuff
|
|
#endif // WAVE_DD_DO_LOOPS
|
|
|
|
|
|
//
|
|
// Move the request into the transit camp
|
|
// Actually this is already done by sndNextBuffer
|
|
//
|
|
}
|
|
|
|
} // Continue around the loop until the request is satisfied
|
|
}
|
|
|
|
//
|
|
// if we transferred something, pad out the request with
|
|
// silence. For 8-bit 0 level is 0x80, for 16-bit it is 0
|
|
//
|
|
|
|
if (pDMA->nBytes < pGDI->DmaHalfBufferSize) {
|
|
dprintf4(" pad %d ", pGDI->DmaHalfBufferSize - pDMA->nBytes);
|
|
RtlFillMemory(pDMA->Buf + pDMA->nBytes,
|
|
pGDI->DmaHalfBufferSize - pDMA->nBytes,
|
|
pGDI->BytesPerSample == 1 ? 0x80 : 0x00);
|
|
}
|
|
|
|
//
|
|
// flush the i/o buffers
|
|
//
|
|
// Actually I386 needs none of this
|
|
// KeFlushIoBuffers(pGDI->pDMABufferMDL, FALSE); // flush for write
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SoundOutDeferred(
|
|
PKDPC pDpc,
|
|
PDEVICE_OBJECT pDeviceObject,
|
|
PIRP pIrp,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deferred procedure call routine for wave output interrupts.
|
|
|
|
The basic job is just to move to the next buffer which consists of
|
|
|
|
-- Completing Irps that made up the buffer just played (in DeadQueue)
|
|
|
|
-- Moving Irps that make up the next buffer to play from
|
|
TransitQueue to DeadQueue and clearing TransitQueue.
|
|
|
|
-- Filling up the next buffer
|
|
|
|
However, if the buffer which is about to play is empty we can deduce
|
|
that there isn't anything to play - either output was stopped by a
|
|
WAVE_DD_STOP or no buffers have arrived (otherwise data would
|
|
have already been moved into the buffer which is about to play).
|
|
|
|
In this case we
|
|
|
|
-- Stop the DMA
|
|
|
|
-- Complete any pause packet
|
|
|
|
-- Set our new state (WAVE_DD_IDLE if not currently WAVE_DD_STOPPED).
|
|
|
|
Arguments:
|
|
|
|
pDPC - pointer to DPC object
|
|
pDeviceObject - pointer to our device object
|
|
pIrp - ???
|
|
Context - our Dpc context (NULL in our case).
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PLOCAL_DEVICE_INFO pLDI;
|
|
PGLOBAL_DEVICE_INFO pGDI;
|
|
|
|
pLDI = (PLOCAL_DEVICE_INFO)pDeviceObject->DeviceExtension;
|
|
pGDI = pLDI->pGlobalInfo;
|
|
|
|
ASSERT(pGDI->Key == GDI_KEY);
|
|
|
|
dprintf4("(");
|
|
|
|
//
|
|
// Acquire the spin lock before we mess with the list
|
|
//
|
|
|
|
GlobalEnter(pGDI);
|
|
|
|
//
|
|
// Kill everything on the dead queue
|
|
// move the transit queue to the dead queue
|
|
// and reinitialize the transit queue ready to receive
|
|
// data from the queue of new buffers
|
|
//
|
|
|
|
sndFreeQ(pLDI, &pLDI->DeadQueue, STATUS_SUCCESS);
|
|
|
|
//
|
|
// Move the Transit queue into the dead queue and empty the
|
|
// transit queue.
|
|
// Note that if a loop is in progress not everything is moved
|
|
// now.
|
|
//
|
|
while (!IsListEmpty(&pLDI->TransitQueue)) {
|
|
|
|
PLIST_ENTRY pListNode;
|
|
|
|
pListNode = RemoveHeadList(&pLDI->TransitQueue);
|
|
|
|
#ifdef WAVE_DD_DO_LOOPS
|
|
//
|
|
// Do not move loops over to the dead queue - there's
|
|
// still life in them !
|
|
//
|
|
|
|
if (pListNode == pLDI->LoopBegin) {
|
|
InsertHeadList(&pLDI->TransitQueue, pListNode);
|
|
break;
|
|
}
|
|
#endif // WAVE_DD_DO_LOOPS
|
|
|
|
//
|
|
// Move to death row
|
|
//
|
|
|
|
InsertTailList(&pLDI->DeadQueue, pListNode);
|
|
}
|
|
|
|
//
|
|
// The block we've just done is now empty, ready for reuse
|
|
//
|
|
|
|
pGDI->DMABuffer[pGDI->NextHalf].nBytes = 0;
|
|
|
|
dprintf4("%d", pGDI->SoundHardware.TcInterruptsPending);
|
|
|
|
//
|
|
// See if we were doing the last block or we got a real underflow
|
|
//
|
|
|
|
if (pGDI->DMABuffer[UpperHalf + LowerHalf - pGDI->NextHalf].nBytes == 0
|
|
|| pGDI->GotUnderFlow) {
|
|
|
|
#ifdef MIPSSND_TAIL_BUG
|
|
|
|
//
|
|
// Mute the sound so that no Click can be heard at the end.
|
|
// We are right now playing a blank block anyway.
|
|
//
|
|
|
|
sndMute(pGDI);
|
|
|
|
//
|
|
// Mute is same as turning off the headphone and lineout
|
|
// But Mute using volume "sounds" better.
|
|
// sndHeadphoneControl(pGDI, OFF);
|
|
// sndLineoutControl(pGDI, OFF);
|
|
//
|
|
|
|
#endif // MIPSSND_TAIL_BUG
|
|
|
|
// We wait for the underflow interrupt to occur and then
|
|
// stop the dma.
|
|
//
|
|
// Overflow can also occur as an error
|
|
//
|
|
|
|
if (pGDI->GotUnderFlow) {
|
|
|
|
pGDI->GotUnderFlow = 0;
|
|
|
|
//
|
|
// That was the end of the last block of the transfer.
|
|
// The MIPSSND are currently sending silence.
|
|
// We can stop DMA and release the adapter channel now.
|
|
//
|
|
|
|
sndStopDMA(pGDI);
|
|
|
|
//
|
|
// It's possible that a request was queued during the last block
|
|
// but that would have caused the LastBlock flag to have been
|
|
// cleared if there were any data, unless we're STOPPED.
|
|
// If a restart occurred after a stop the silence would
|
|
// have been filled out.
|
|
//
|
|
//
|
|
// However, with 0 length buffers possible the dead queue
|
|
// can be non-empty even though the next buffer has nothing
|
|
// to play in it.
|
|
//
|
|
|
|
//
|
|
// Because we can really underflow we must flush out all our
|
|
// data
|
|
//
|
|
|
|
sndFreeQ(pLDI, &pLDI->DeadQueue, STATUS_SUCCESS);
|
|
sndFreeQ(pLDI, &pLDI->TransitQueue, STATUS_SUCCESS);
|
|
|
|
pGDI->DMABuffer[0].nBytes = 0;
|
|
pGDI->DMABuffer[1].nBytes = 0;
|
|
|
|
//
|
|
// Complete any STOP or RESET packet
|
|
//
|
|
|
|
if (pGDI->pIrpPause != NULL) {
|
|
ASSERT(pLDI->State == WAVE_DD_STOPPED);
|
|
|
|
//
|
|
// Complete any RESET. Note that this can delete
|
|
// input received since RESET was issued
|
|
//
|
|
if (*(PULONG)pGDI->pIrpPause->AssociatedIrp.SystemBuffer ==
|
|
WAVE_DD_RESET) {
|
|
sndResetOutput(pLDI);
|
|
}
|
|
|
|
pGDI->pIrpPause->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(pGDI->pIrpPause, IO_SOUND_INCREMENT);
|
|
pGDI->pIrpPause = NULL;
|
|
}
|
|
|
|
//
|
|
// If we really overflowed we can try starting our sound again.
|
|
// sndStartOutput detects if we're really stopped and does
|
|
// nothing.
|
|
|
|
// Just in case
|
|
|
|
if (pLDI->State != WAVE_DD_STOPPED){
|
|
|
|
// In case it had underflowed the volume would have been
|
|
// turned off. So in that case turn on the volume before
|
|
// you start to put out wave data.
|
|
|
|
sndSetOutputVolume( pGDI );
|
|
|
|
sndStartOutput( pLDI );
|
|
}
|
|
|
|
} // if got underflow
|
|
|
|
} else {
|
|
|
|
//
|
|
// That was the end of a normal block.
|
|
// Try to load the next half of the dma buffer.
|
|
// If this is the tail of the request, the load routine
|
|
// will pad it out with silence so we get a full block.
|
|
//
|
|
|
|
sndFlush(pGDI, pGDI->NextHalf);
|
|
sndLoadDMABuffer(pLDI, &pGDI->DMABuffer[pGDI->NextHalf]);
|
|
|
|
//
|
|
// Restart DMA
|
|
//
|
|
sndReStartDMA(pGDI, pGDI->NextHalf);
|
|
|
|
//
|
|
// Move to the next half buffer
|
|
//
|
|
pGDI->NextHalf = LowerHalf + UpperHalf - pGDI->NextHalf;
|
|
|
|
}
|
|
|
|
//
|
|
// Release the spin lock
|
|
//
|
|
|
|
GlobalLeave(pGDI);
|
|
|
|
dprintf4(")");
|
|
|
|
return;
|
|
|
|
DBG_UNREFERENCED_PARAMETER(pDpc);
|
|
DBG_UNREFERENCED_PARAMETER(Context);
|
|
DBG_UNREFERENCED_PARAMETER(pIrp);
|
|
}
|