📄 pa_win_ds.c
字号:
goto error; } memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */ if( streamCallback ) { PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, &winDsHostApi->callbackStreamInterface, streamCallback, userData ); } else { PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, &winDsHostApi->blockingStreamInterface, streamCallback, userData ); } PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); if( inputParameters ) { /* IMPLEMENT ME - establish which host formats are available */ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat ); } else { hostInputSampleFormat = 0; } if( outputParameters ) { /* IMPLEMENT ME - establish which host formats are available */ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat ); } else { hostOutputSampleFormat = 0; } result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, inputChannelCount, inputSampleFormat, hostInputSampleFormat, outputChannelCount, outputSampleFormat, hostOutputSampleFormat, sampleRate, streamFlags, framesPerBuffer, framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */ /* This next mode is required because DS can split the host buffer when it wraps around. */ paUtilVariableHostBufferSizePartialUsageAllowed, streamCallback, userData ); if( result != paNoError ) goto error; stream->streamRepresentation.streamInfo.inputLatency = PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ stream->streamRepresentation.streamInfo.outputLatency = PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ stream->streamRepresentation.streamInfo.sampleRate = sampleRate; /* DirectSound specific initialization */ { HRESULT hr; int bytesPerDirectSoundBuffer; int userLatencyFrames; int minLatencyFrames; stream->timerID = 0; /* Get system minimum latency. */ minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); /* Let user override latency by passing latency parameter. */ userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames) ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames; if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames; /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */ if( framesPerBuffer == paFramesPerBufferUnspecified ) { /* App support variable framesPerBuffer */ stream->framesPerDSBuffer = minLatencyFrames; stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate; } else { /* Round up to number of buffers needed to guarantee that latency. */ int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer; if( numUserBuffers < 1 ) numUserBuffers = 1; numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */ stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers; stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate; } { /** @todo REVIEW: this calculation seems incorrect to me - rossb. */ int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate); PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency )); } /* ------------------ OUTPUT */ if( outputParameters ) { /* PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ]; DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device)); */ bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short); if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) { result = paBufferTooSmall; goto error; } else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) { result = paBufferTooBig; goto error; } hr = paWinDsDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID, &stream->pDirectSound, NULL ); if( hr != DS_OK ) { ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n")); result = paUnanticipatedHostError; PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); goto error; } hr = InitOutputBuffer( stream, (unsigned long) (sampleRate + 0.5), (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer ); DBUG(("InitOutputBuffer() returns %x\n", hr)); if( hr != DS_OK ) { result = paUnanticipatedHostError; PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); goto error; } /* Calculate value used in latency calculation to avoid real-time divides. */ stream->secondsPerHostByte = 1.0 / (stream->bufferProcessor.bytesPerHostOutputSample * outputChannelCount * sampleRate); } /* ------------------ INPUT */ if( inputParameters ) { /* PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ]; DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device)); */ bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short); if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) { result = paBufferTooSmall; goto error; } else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) { result = paBufferTooBig; goto error; } hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID, &stream->pDirectSoundCapture, NULL ); if( hr != DS_OK ) { ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n")); result = paUnanticipatedHostError; PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); goto error; } hr = InitInputBuffer( stream, (unsigned long) (sampleRate + 0.5), (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer ); DBUG(("InitInputBuffer() returns %x\n", hr)); if( hr != DS_OK ) { ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr)); result = paUnanticipatedHostError; PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); goto error; } } } *s = (PaStream*)stream; return result;error: if( stream ) PaUtil_FreeMemory( stream ); return result;}/************************************************************************************ * Determine how much space can be safely written to in DS buffer. * Detect underflows and overflows. * Does not allow writing into safety gap maintained by DirectSound. */static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty ){ HRESULT hr; DWORD playCursor; DWORD writeCursor; long numBytesEmpty; long playWriteGap; // Query to see how much room is in buffer. hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer, &playCursor, &writeCursor ); if( hr != DS_OK ) { return hr; } // Determine size of gap between playIndex and WriteIndex that we cannot write into. playWriteGap = writeCursor - playCursor; if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */ /* Attempt to detect playCursor wrap-around and correct it. */ if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) ) { /* How much time has elapsed since last check. */ LARGE_INTEGER currentTime; LARGE_INTEGER elapsedTime; long bytesPlayed; long bytesExpected; long buffersWrapped; QueryPerformanceCounter( ¤tTime ); elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart; stream->previousPlayTime = currentTime; /* How many bytes does DirectSound say have been played. */ bytesPlayed = playCursor - stream->previousPlayCursor; if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap stream->previousPlayCursor = playCursor; /* Calculate how many bytes we would have expected to been played by now. */ bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart); buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes; if( buffersWrapped > 0 ) { playCursor += (buffersWrapped * stream->outputBufferSizeBytes); bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes); } /* Maintain frame output cursor. */ stream->framesPlayed += (bytesPlayed / stream->bytesPerOutputFrame); } numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes; if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset /* Have we underflowed? */ if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) ) { if( stream->outputIsRunning ) { stream->outputUnderflowCount += 1; } stream->outputBufferWriteOffsetBytes = writeCursor; numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap; } *bytesEmpty = numBytesEmpty; return hr;}/***********************************************************************************/static PaError Pa_TimeSlice( PaWinDsStream *stream ){ PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/ long numFrames = 0; long bytesEmpty = 0; long bytesFilled = 0; long bytesToXfer = 0; long framesToXfer = 0; long numInFramesReady = 0; long numOutFramesReady = 0; long bytesProcessed; HRESULT hresult; double outputLatency = 0; PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */ /* Input */ LPBYTE lpInBuf1 = NULL; LPBYTE lpInBuf2 = NULL; DWORD dwInSize1 = 0; DWORD dwInSize2 = 0;/* Output */ LPBYTE lpOutBuf1 = NULL; LPBYTE lpOutBuf2 = NULL; DWORD dwOutSize1 = 0; DWORD dwOutSize2 = 0; /* How much input data is available? */ if( stream->bufferProcessor.inputChannelCount > 0 ) { HRESULT hr; DWORD capturePos; DWORD readPos; long filled = 0; // Query to see how much data is in buffer. // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly // so let's pass a pointer just to be safe. hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos ); if( hr == DS_OK ) { filled = readPos - stream->readOffset; if( filled < 0 ) filled += stream->inputSize; // unwrap offset bytesFilled = filled; } // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails? framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame; outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; /** @todo Check for overflow */ } /* How much output room is available? */ if( stream->bufferProcessor.outputChannelCount > 0 ) { UINT previousUnderflowCount = stream->outputUnderflowCount; QueryOutputSpace( stream, &bytesEmpty ); framesToXfer = numOutFramesReady = bytesEmpty / stream->bytesPerOutputFrame; /* Check for underflow */ if( stream->outputUnderflowCount != previousUnderflowCount ) stream->callbackFlags |= paOutputUnderflow; } if( (numInFramesReady > 0) && (numOutFramesReady > 0) ) { framesToXfer = (n
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -