⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 csoundoutput.cpp

📁 vc环境下的pgp源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*____________________________________________________________________________
	Copyright (C) 1996-1999 Network Associates, Inc.
	All rights reserved.

	$Id: CSoundOutput.cpp,v 1.5 1999/03/10 02:41:12 heller Exp $
____________________________________________________________________________*/
#include "CSoundOutput.h"

#include "CPFWindow.h"
#include "CControlThread.h"
#include "CStatusPane.h"
#include "CPacketWatch.h"
#include "CMessageQueue.h"

#include "gsm.private.h"
#include "samplerate.h"
#include <string.h>

#ifdef USEVOXWARE
#include "tvgetstr.h"
#endif
#ifdef PGP_MACINTOSH
#include "MacDebug.h"
#endif

#define TOOMANYUNDERFLOWS	2
#define UNDERFLOWLIMIT		5
#define UNDERFLOWTRACKFREQ	5000
#define DEFAULTPLAYPOINT	2
	
#ifdef	PGP_MACINTOSH

	static pascal void
SODoubleBackProc(
	SndChannelPtr		/*theChan*/,
	SndDoubleBufferPtr	doubleBuffer)
{
	((CSoundOutput *)doubleBuffer->dbUserInfo[0])->
		FillDoubleBuffer(doubleBuffer);
}

#elif	PGP_WIN32

#define TONESAMPLES		34							// ~650Hz
#define TONECYCLES		(22050*1/10/TONESAMPLES)	// cycles in 1/10s
#define TONELENGTH		(TONECYCLES*TONESAMPLES)	// samples needed

// 1 cycle of a 400Hz tone sampled at 22050Hz, 16-bits
static short sSwitchTone[TONESAMPLES] =
	{
	0, 6020, 11836, 17249, 22074, 26148, 29331, 31516,	
	32627, 32627, 31516, 29331, 26148, 22074, 17249, 11836,	
	6020, 0, -6020, -11836, -17249, -22074, -26148, -29331,	
	-31516, -32627, -32627, -31516, -29331, -26148, -22074,
	-17249, -11836, -6020
	};
	
static long sBufReturn;

	void CALLBACK
SOCallback(
	HANDLE		hWaveIn,
	WORD		msg,
	DWORD		instance,
	DWORD		param1,
	DWORD		param2)
{
	if(msg==WOM_DONE)
	{
		InterlockedIncrement(&sBufReturn);
		if(sBufReturn==NUMOUTPUTBUFFERS)
			gPacketWatch->Underflow();
	}
}

void
CSoundOutput::CheckWaveError(char *callName, int errCode)
{
	char s[256], t[256];

	if(errCode)
	{
		if(!waveOutGetErrorText(errCode, s, 200))
			sprintf(t, "Error on %s:\n %s", callName, s);
		else
			sprintf(t, "Error on %s:\n %d", callName, errCode);
		PGFAlert(t,0);
		pgpAssert(0);
	}
}

#endif


CSoundOutput::CSoundOutput(CPFWindow *pfWindow, void **outResult)
#ifdef	PGP_MACINTOSH
		: LThread(FALSE, thread_DefaultStack, threadOption_UsePool, outResult)
#else
		: LThread(outResult)
#endif
{
	short err;
	
	mPFWindow = pfWindow;
	mPlayQueue = new CMessageQueue;
	mCodec = 0;
	mIndex = mOutdex = 0;
	mUnderflowRow = 0;
	mUnderflowsExceeded = 0;
	mSendRR = FALSE;
	mUnderStartTime = mLastRR = 0;
	mPlaypoint = DEFAULTPLAYPOINT;
	mPlaying = FALSE;
	mPlay = FALSE;
	mGSM = NIL;
#ifdef	PGP_MACINTOSH
	mOpen = TRUE;
	mUnderflowing = FALSE;
	::GetDefaultOutputVolume(&mVolume);
	mDblBufOne = (SndDoubleBufferPtr)pgp_malloc(sizeof(SndDoubleBuffer) + 3072);
	mDblBufTwo = (SndDoubleBufferPtr)pgp_malloc(sizeof(SndDoubleBuffer) + 3072);
	mDoubleBackProc = NewSndDoubleBackProc(SODoubleBackProc);
	mSndChannel = NIL;	/* allocates default queue of 128 cmds */
	err = SndNewChannel(&mSndChannel, sampledSynth, initMono, NIL);
	pgpAssert(IsntErr(err));
	mSndChannel->userInfo = (long)this;
	mBeepChannel = NIL;
	err = SndNewChannel(&mBeepChannel, squareWaveSynth, initMono, NIL);
	pgpAssert(IsntErr(err));
#elif	PGP_WIN32
	uchar *e;
	short *s, i=TONECYCLES;
	WAVEOUTCAPS devCaps;
	UINT numDevices;
	
	mOpen = FALSE;
	sBufReturn = NUMOUTPUTBUFFERS;
	mHeaderIndex = 0;
	// load buffers for channel switch tone
	// 16-bit, 22050Hz
	mToneHandles[0] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, TONELENGTH*2);
	// 8-bit, 22050Hz
	mToneHandles[1] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, TONELENGTH);
	mToneBuffers[0] = (uchar*)GlobalLock(mToneHandles[0]);
	mToneBuffers[1] = (uchar*)GlobalLock(mToneHandles[1]);
	s = (short*)mToneBuffers[0];
	e = mToneBuffers[1];
	// convert the hard-coded 16-bit sample to 8-bits
	pgp_memcpy(s, sSwitchTone, TONESAMPLES*2);
	for(i=0;i<TONESAMPLES;i++)
		*e++ = (uchar)((ushort)(*s++ + 0x8000) >> 8);
	// make the tone by concatenating several copies of the wave
	for(i=1;i<TONECYCLES;i++)
	{
		pgp_memcpy(s, mToneBuffers[0], TONESAMPLES*2);
		pgp_memcpy(e, mToneBuffers[1], TONESAMPLES);
		s+=TONESAMPLES;
		e+=TONESAMPLES;
	}
	mVolumeSupport = FALSE;
	mVolume = 0x7FFF7FFF;
	for(i=0;i<NUMOUTPUTBUFFERS;i++)
	{
		if(!(mMemHandles[i] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, 3072)))
		{
			pgp_errstring("Error allocating output buffers.");
			return;
		}
		// initialize the header
		mHeaders[i].lpData = (char *)GlobalLock(mMemHandles[i]);
		mHeaders[i].dwFlags = 0;
		mHeaders[i].dwLoops = 0;
		if(!mHeaders[i].lpData)
		{
			pgp_errstring("error locking output buffers");
			return;
		}
	}
	// find out the number of sound devices available
	numDevices = waveOutGetNumDevs();
	if(numDevices < 1)
	{
		PGFAlert("No sound output devices available.", 0);
		return;
	}
	waveOutGetDevCaps(WAVE_MAPPER, &devCaps, sizeof(WAVEOUTCAPS));
	if(devCaps.dwFormats & WAVE_FORMAT_1M16)
	{
		// We'd rather upsample to 11025 than 22050
		mHardwareIs16Bit = TRUE;
		mSampleRate = 11025.0;
	}
	else if(devCaps.dwFormats & WAVE_FORMAT_2M16)
	{
		mHardwareIs16Bit = TRUE;
		mSampleRate = 22050.0;
	}
	else if(devCaps.dwFormats & WAVE_FORMAT_1M08)
	{
		mHardwareIs16Bit = FALSE;
		mSampleRate = 11025.0;
	}
	else if(devCaps.dwFormats & WAVE_FORMAT_2M08)
	{
		mHardwareIs16Bit = FALSE;
		mSampleRate = 22050.0;
	}
	else
		PGFAlert("Sound output device does not support a compatible format.", 0);
	if(devCaps.dwSupport & WAVECAPS_VOLUME ||
		devCaps.dwSupport & WAVECAPS_LRVOLUME)
		mVolumeSupport = TRUE;
	else
		mVolumeSupport = FALSE;
#endif
	SetCodec('GSM7');	// just setting a default coder to init things
}

CSoundOutput::~CSoundOutput()
{
	delete mPlayQueue;
	DisposeCodec();
#ifdef	PGP_MACINTOSH
	::SndDisposeChannel(mSndChannel, TRUE);
	::SndDisposeChannel(mBeepChannel, TRUE);
	DisposeRoutineDescriptor(mDoubleBackProc);
	pgp_free(mDblBufOne);
	pgp_free(mDblBufTwo);
#elif	PGP_WIN32
	short i;

	if(mOpen)
		Close();
	GlobalUnlock(mToneHandles[0]);
	GlobalUnlock(mToneHandles[1]);
	GlobalFree(mToneHandles[0]);
	GlobalFree(mToneHandles[1]);
	for(i=0;i<NUMOUTPUTBUFFERS;i++)
	{
		GlobalUnlock(mMemHandles[i]);
		GlobalFree(mMemHandles[i]);
	}
#endif
}

#ifdef	PGP_MACINTOSH
// FillDoubleBuffer
//	We use the double buffering scheme provided by the Sound Manager
//	in order to get the lowest possible latency.  This routine
//	is called whenever we need to fill one of our double buffers
//	with sound from our buffer of uncompressed samples.
void
CSoundOutput::FillDoubleBuffer(SndDoubleBufferPtr buf)
{
	long avail;
	ushort *p, *e;
	ulong now;
	
	now = pgp_getticks();
	buf->dbFlags = 0;
	if(mIndex > mOutdex)
		avail = mIndex - mOutdex;
	else if(mIndex < mOutdex)
		avail = mSampleBufLen - mOutdex + mIndex;
	else
		avail = 0;
	buf->dbFlags |= dbBufferReady;
	if(mUnderflowing ? (avail >= mBufferQuant * mPlaypoint) :
		(avail >= mBufferQuant))
	{
		::BlockMoveData(&mSamples[mOutdex], &buf->dbSoundData[0], mBufferQuant);
		mOutdex += mBufferQuant;
		mOutdex %= mSampleBufLen;
		mUnderflowRow = 0;
		mUnderflowing = FALSE;
	}
	else
	{
		// UNDERFLOW
		
		//	When we run out of sound to play, we fill the buffer with 0's.
		//	This has the great benefit of keeping the double buffer interrupts
		//	going.  There is a delay of up to 100ms between the call to start
		//	double buffering and when the sound actually starts.  This
		//	technique eliminates that delay on the first underflow.
		mUnderflowing = TRUE;
		gPacketWatch->Underflow();
		p=(ushort *)&buf->dbSoundData[0];
		e=(ushort *)((uchar *)p+mBufferQuant);
		while(p<e)
			*p++=0;
		if(!mPlay)
		{
			buf->dbFlags |= dbLastBuffer;
			mPlaying=FALSE;
		}
		if(++mUnderflowRow == TOOMANYUNDERFLOWS)
		{
			// We send an RR when we reach the TOOMANYUNDERFLOWS limit,
			// but only when we exactly reach the limit.  Since it is
			// possible that the sender has only muted their sound, we
			// wait for the next sound packet to actually reset the
			// mUnderflowRow counter.
			
			if((++mUnderflowsExceeded >= UNDERFLOWLIMIT) &&
				(mLastRR+6000 < now))
			{
				mSendRR = TRUE;
				mLastRR = now;
			}
		}
	}
	if(mUnderStartTime + UNDERFLOWTRACKFREQ < now)
	{
		mUnderStartTime = now;
		mUnderflowsExceeded = 0;
	}
}

#endif	PGP_MACINTOSH


void
CSoundOutput::Open(void)
{
#ifdef	PGP_WIN32
	WAVEFORMATEX waveFormat;
	UINT i, did;
	MMRESULT result;
	short err;

	if(mOpen)
		return;
	// open a device for playing
	waveFormat.wFormatTag = WAVE_FORMAT_PCM;
	waveFormat.nChannels = 1;
	waveFormat.nSamplesPerSec = (long)mSampleRate;
	waveFormat.nAvgBytesPerSec = (long)mSampleRate * (mHardwareIs16Bit ? 2 : 1);
	waveFormat.nBlockAlign = mHardwareIs16Bit ? 2 : 1;
	waveFormat.wBitsPerSample = 8 * (mHardwareIs16Bit ? 2 : 1);
	waveFormat.cbSize = 0;
	if(result = waveOutOpen(&mWaveHandle, WAVE_MAPPER,
					&waveFormat, (DWORD)SOCallback,
					(DWORD)this, CALLBACK_FUNCTION))
	{
		char str[200], error[235];
		
		if(waveOutGetErrorText(result, str, 200))
			sprintf(error, "Unknown error opening sound output device: %d", result);
		else
			sprintf(error, "Error opening sound output device: %s", str);
		PGFAlert(error, 0);
		mOpen = FALSE;
		return;
	}
	else
	{
		// The sound device has been successfully opened.
		
		waveOutPause(mWaveHandle);
		for(i=0;i<NUMOUTPUTBUFFERS;i++)
		{
			// Setting a maximum buffer length here, we set it on a per-call
			// basis, but for now we assume that 2048 is a maximum, this is
			// required by the driver.
			mHeaders[i].dwBufferLength = 2048;
			mHeaders[i].dwFlags = 0;
			mHeaders[i].dwLoops = 0;
			err = waveOutPrepareHeader(mWaveHandle, &(mHeaders[i]),
										sizeof(WAVEHDR));
			CheckWaveError("waveOutPrepareHeader", err);
		}
		if(mVolumeSupport)
		{
			// get the device ID for volume control and get the current volume
			if(waveOutGetID(mWaveHandle, &did))
			{
				pgp_errstring("Error getting output device ID.");
				return;
			}
			if(waveOutGetVolume(mWaveHandle, &mOriginalVolume))
			{
				pgp_errstring("Error getting original sound output volume.");
				return;
			}
			SetVolume(mVolume);								   
		}
		mOpen = TRUE;
	}
#endif
}

void
CSoundOutput::Close(void)
{
#ifdef	PGP_WIN32
	short err;

	if(mOpen)
	{	
		if(mVolumeSupport && waveOutSetVolume(mWaveHandle, mOriginalVolume))
			pgp_errstring("Error restoring original sound output volume.");
  		for(int i=0;i<NUMOUTPUTBUFFERS;i++)
			waveOutUnprepareHeader(mWaveHandle, &(mHeaders[i]),
									sizeof(WAVEHDR));
		err = waveOutClose(mWaveHandle);
		CheckWaveError("waveOutClose", err);
		mOpen = FALSE;
	}
#endif
}

// GetNumPlaying
//	This routine returns the number of samples waiting to be played.
long
CSoundOutput::GetNumPlaying()
{
	long avail, outdex;
	
	outdex = mOutdex;
	if(mIndex > outdex)
		avail = mIndex - outdex;
	else if(mIndex < outdex)
		avail = mSampleBufLen - outdex + mIndex;
	else
		avail = 0;
	if(mSampleSize == 16)
		avail /= 2;
	return avail;
}

void
CSoundOutput::SetJitter(long jitter)
{
	long	coderRate,
			samplesPerMillisecond,
			jitterSamples,
			sampleQuant;
	
#ifdef	PGP_MACINTOSH
	coderRate = mCoderRate >> 16;
#else
	coderRate = mCoderRate;
#endif
	//mFinishTime				= finishTime;
	samplesPerMillisecond	= (coderRate / 1000) + 1;
	jitterSamples			= samplesPerMillisecond * jitter;
	sampleQuant				= mBufferQuant / (mSampleSize == 16 ? 2 : 1);
	mPlaypoint				= jitterSamples / sampleQuant;
	if(jitterSamples % sampleQuant)
		mPlaypoint ++;
	if(mPlaypoint <= 2)
		mPlaypoint = 2;
	else
		mPlaypoint ++;
	if(mPlaypoint > 20)
		mPlaypoint = 20;
	//CStatusPane::GetStatusPane()->AddStatus(0, "PP: %ld", mPlaypoint);
}

void
CSoundOutput::DisposeCodec(void)
{
	switch(mCodec)
	{
		case 'GS4L':
		case 'GS5L':
		case 'GS6L':
		case 'GS7L':
		case 'GL80':
		case 'GS8L':
		case 'GS1L':
		case 'GSM4':
		case 'GSM5':
		case 'GSM6':
		case 'GSM7':
		case 'GS80':
		case 'GSM8':
		case 'GSM1':
			gsm_destroy(mGSM);
			break;
#ifdef USEVOXWARE
		case 'VOXW':{
			VOXWARE_RETCODE voxerr;
			
			voxerr = RTFreeDecoder(&mVoxComVars);	pgpAssertNoErr(voxerr);
			voxerr = RTFreeEncoder(&mtVoxComVars);	pgpAssertNoErr(voxerr);
			break;}
#endif
		case 0:
			break;
	}
}

void
CSoundOutput::SetCodec(ulong type)
{
	short inx;
	
	mSoundMutex.Wait();
	DisposeCodec();
	mCoderHeaderSize = 0;
	switch(type)
	{
		case 'GS4L':
		case 'GS5L':
		case 'GS6L':
		case 'GS7L':
		case 'GL80':
		case 'GS8L':
		case 'GS1L':			// 160 shorts -> 29 bytes
			mGSM = gsm_create();
			mCFrameSize = 29;
			mFrameSamples = 160;
			mSampleSize = 16;
			mBufferQuant = 320;
			switch(type)
			{
				case 'GS4L':
					#ifdef	PGP_MACINTOSH
						mCoderRate = 0x113A0000;
					#elif	PGP_WIN32
						mCoderRate = 4410.0;
					#endif
					break;
				case 'GS5L':
					#ifdef	PGP_MACINTOSH

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -