示例#1
0
static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
	internalPortAudioStream   *past;
	PaHostSoundControl  *pahsc;
#if PA_SIMULATE_UNDERFLOW
	gUnderCallbackCounter++;
	if( (gUnderCallbackCounter >= UNDER_START_GAP) &&
		(gUnderCallbackCounter <= UNDER_STOP_GAP) )
	{
		if( gUnderCallbackCounter == UNDER_START_GAP)
		{
			AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
		}
		if( gUnderCallbackCounter == UNDER_STOP_GAP)
		{
			AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
		}
		return;
	}
#endif
	past = (internalPortAudioStream *) dwUser;
	if( past == NULL ) return;
	pahsc = (PaHostSoundControl *) past->past_DeviceData;
	if( pahsc == NULL ) return;
	if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive )
	{
		if( past->past_StopNow )
		{
			past->past_IsActive = 0;
		}
		else if( past->past_StopSoon )
		{
			DSoundWrapper   *dsw = &pahsc->pahsc_DSoundWrapper;
			if( past->past_NumOutputChannels > 0 )
			{
				DSW_ZeroEmptySpace( dsw ); 
				AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten );
				AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed );
		/* clear past_IsActive when all sound played */
				if( dsw->dsw_FramesPlayed >= past->past_FrameCount )
				{
					past->past_IsActive = 0;
				}
			}
			else
			{
				past->past_IsActive = 0;
			}
		}
		else
		{
			pahsc->pahsc_IfInsideCallback = 1;
			if( Pa_TimeSlice( past ) != 0)  /* Call time slice independant of timing method. */
			{
				past->past_StopSoon = 1;
			}
			pahsc->pahsc_IfInsideCallback = 0;
		}
	}
}
示例#2
0
HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
{
	DWORD          dwDataLen;
	DWORD          playCursor;
	HRESULT        result;
	LPDIRECTSOUNDBUFFER pPrimaryBuffer;
	HWND           hWnd;
	HRESULT        hr;
	WAVEFORMATEX   wfFormat;
	DSBUFFERDESC   primaryDesc;
	DSBUFFERDESC   secondaryDesc;
	unsigned char* pDSBuffData;
	LARGE_INTEGER  counterFrequency;
	dsw->dsw_OutputSize = bytesPerBuffer;
	dsw->dsw_OutputRunning = FALSE;
	dsw->dsw_OutputUnderflows = 0;
	dsw->dsw_FramesWritten = 0;
	dsw->dsw_BytesPerFrame = nChannels * sizeof(short);
// We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
// applications's window. Also if that window is closed before the Buffer is closed
// then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
// So we will use GetDesktopWindow() which was suggested by Miller Puckette.
//	hWnd = GetForegroundWindow();
	hWnd = GetDesktopWindow();
// Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
// Exclusize also prevents unexpected sounds from other apps during a performance.
	if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
			hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
	{
		return hr;
	}
// -----------------------------------------------------------------------
// Create primary buffer and set format just so we can specify our custom format.
// Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
// Setup the primary buffer description
	ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
	primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
	primaryDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
	primaryDesc.dwBufferBytes = 0;
	primaryDesc.lpwfxFormat   = NULL;
// Create the buffer
	if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
			&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
// Define the buffer format
	wfFormat.wFormatTag = WAVE_FORMAT_PCM;
	wfFormat.nChannels = nChannels;
	wfFormat.nSamplesPerSec = nFrameRate;
	wfFormat.wBitsPerSample = 8 * sizeof(short);
	wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
	wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
	wfFormat.cbSize = 0;  /* No extended format info. */
// Set the primary buffer's format
	if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
// ----------------------------------------------------------------------
// Setup the secondary buffer description
	ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
	secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
	secondaryDesc.dwFlags =  DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
	secondaryDesc.dwBufferBytes = bytesPerBuffer;
	secondaryDesc.lpwfxFormat = &wfFormat;
// Create the secondary buffer
	if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
			&secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
// Lock the DS buffer
	if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
	  &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
// Zero the DS buffer
	ZeroMemory(pDSBuffData, dwDataLen);
// Unlock the DS buffer
	if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
	if( QueryPerformanceFrequency( &counterFrequency ) )
	{
		int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
		dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
		AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart );
	}
	else
	{
		dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
	}
// Let DSound set the starting write position because if we set it to zero, it looks like the
// buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
	hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
	if( hr != DS_OK )
	{
		return hr;
	}
	dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame;
	/* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
	return DS_OK;
}
示例#3
0
HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
{
	HRESULT hr;
	DWORD   playCursor;
	DWORD   writeCursor;
	long    numBytesEmpty;
	long    playWriteGap;
// Query to see how much room is in buffer.
// Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing
// under WinNT. The Microsoft documentation says we can pass NULL but apparently not.
// Thanks to Max Rheiner for the fix.
	hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
	if( hr != DS_OK )
	{
		return hr;
	}
	AddTraceMessage("playCursor", playCursor);
	AddTraceMessage("dsw_WriteOffset", dsw->dsw_WriteOffset);
// Determine size of gap between playIndex and WriteIndex that we cannot write into.
	playWriteGap = writeCursor - playCursor;
	if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // 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( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
	{
/* How much time has elapsed since last check. */
		LARGE_INTEGER   currentTime;
		LARGE_INTEGER   elapsedTime;
		long            bytesPlayed;
		long            bytesExpected;
		long            buffersWrapped;
		QueryPerformanceCounter( &currentTime );
		elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
		dsw->dsw_LastPlayTime = currentTime;
/* How many bytes does DirectSound say have been played. */
		bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
		if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
		dsw->dsw_LastPlayCursor = playCursor;
/* Calculate how many bytes we would have expected to been played by now. */
		bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
		buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
		if( buffersWrapped > 0 )
		{
			AddTraceMessage("playCursor wrapped! bytesPlayed", bytesPlayed );
			AddTraceMessage("playCursor wrapped! bytesExpected", bytesExpected );
			playCursor += (buffersWrapped * dsw->dsw_OutputSize);
			bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
		}
	/* Maintain frame output cursor. */
		dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerFrame);
	}
	numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
	if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
/* Have we underflowed? */
	if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
	{
		if( dsw->dsw_OutputRunning )
		{
			dsw->dsw_OutputUnderflows += 1;
			AddTraceMessage("underflow detected! numBytesEmpty", numBytesEmpty );
		}
		dsw->dsw_WriteOffset = writeCursor;
		numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
	}
	*bytesEmpty = numBytesEmpty;
	return hr;
}
示例#4
0
static PaError Pa_TimeSlice( internalPortAudioStream   *past )
{
	PaError           result = 0;
	long              bytesEmpty = 0;
	long              bytesFilled = 0;
	long              bytesToXfer = 0;
	long              numChunks;
	HRESULT           hresult;
	PaHostSoundControl  *pahsc;
	short            *nativeBufPtr;
	past->past_NumCallbacks += 1;
	pahsc = (PaHostSoundControl *) past->past_DeviceData;
	if( pahsc == NULL ) return paInternalError;
/* How much input data is available? */
#if SUPPORT_AUDIO_CAPTURE
	if( past->past_NumInputChannels > 0 )
	{
		DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled );
		bytesToXfer = bytesFilled;
	}
#endif /* SUPPORT_AUDIO_CAPTURE */
/* How much output room is available? */
	if( past->past_NumOutputChannels > 0 )
	{
		DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty );
		bytesToXfer = bytesEmpty;
	}
	AddTraceMessage( "bytesEmpty ", bytesEmpty );
/* Choose smallest value if both are active. */
	if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) )
	{
		bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty;
	}
/*	printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n",
		bytesFilled, bytesEmpty, bytesToXfer);
*/
/* Quantize to multiples of a buffer. */
	numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer;
	if( numChunks > (long)(past->past_NumUserBuffers/2) )
	{
		numChunks = (long)past->past_NumUserBuffers/2;
	}
	else if( numChunks < 0 )
	{
		numChunks = 0;
	}
	AddTraceMessage( "numChunks ", numChunks );
	nativeBufPtr = pahsc->pahsc_NativeBuffer;
	if( numChunks > 0 )
	{
		while( numChunks-- > 0 )
		{
		/* Measure usage based on time to process one user buffer. */
			Pa_StartUsageCalculation( past );
	#if SUPPORT_AUDIO_CAPTURE
	/* Get native data from DirectSound. */
			if( past->past_NumInputChannels > 0 )
			{
				hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
				if( hresult < 0 )
				{
					ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult));
					sPaHostError = hresult;
					break;
				}
			}
	#endif /* SUPPORT_AUDIO_CAPTURE */
	/* Convert 16 bit native data to user data and call user routine. */
			result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr );
			if( result != 0) break;
	/* Pass native data to DirectSound. */
			if( past->past_NumOutputChannels > 0 )
			{
			/*	static short DEBUGHACK = 0;
				DEBUGHACK += 0x0049;
				nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */
				hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
				if( hresult < 0 )
				{
					ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult));
					sPaHostError = hresult;
					break;
				}
			}
			Pa_EndUsageCalculation( past );
		}
	}
	return result;
}
示例#5
0
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int QaCallback( void *inputBuffer, void *outputBuffer,
                       unsigned long framesPerBuffer,
                       PaTimestamp outTime, void *userData )
{
    unsigned long  i;
    short          phase;
    PaQaData *data = (PaQaData *) userData;
    (void) inputBuffer;
    (void) outTime;

    /* Play simple sawtooth wave. */
    if( data->mode == MODE_OUTPUT )
    {
        phase = data->sawPhase;
        switch( data->format )
        {
        case paFloat32:
            {
                float *out =  (float *) outputBuffer;
                for( i=0; i<framesPerBuffer; i++ )
                {
                    phase += 0x123;
                    *out++ = (float) (phase * (1.0 / 32768.0));
                    if( data->numChannels == 2 )
                    {
                        *out++ = (float) (phase * (1.0 / 32768.0));
                    }
                }
            }
            break;

        case paInt32:
            {
                int *out =  (int *) outputBuffer;
                for( i=0; i<framesPerBuffer; i++ )
                {
                    phase += 0x123;
                    *out++ = ((int) phase ) << 16;
                    if( data->numChannels == 2 )
                    {
                        *out++ = ((int) phase ) << 16;
                    }
                }
            }
            break;
        case paInt16:
            {
                short *out =  (short *) outputBuffer;
                for( i=0; i<framesPerBuffer; i++ )
                {
                    phase += 0x123;
                    *out++ = phase;
                    if( data->numChannels == 2 )
                    {
                        *out++ = phase;
                    }
                }
            }
            break;

        default:
            {
                unsigned char *out =  (unsigned char *) outputBuffer;
                unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
                for( i=0; i<numBytes; i++ )
                {
                    *out++ = 0;
                }
            }
            break;
        }
        data->sawPhase = phase;
    }
    /* Are we through yet? */
    if( data->framesLeft > framesPerBuffer )
    {
        AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
        data->framesLeft -= framesPerBuffer;
        return 0;
    }
    else
    {
        AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
        data->framesLeft = 0;
        return 1;
    }
}