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

📄 csoundinput.cpp

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

	$Id: CSoundInput.cpp,v 1.5 1999/03/10 02:39:57 heller Exp $
____________________________________________________________________________*/
#include "CSoundInput.h"

#include "CPFWindow.h"
#include "CStatusPane.h"
#include "CLevelMeter.h"
#include "CPFPackets.h"
#include "CMessageQueue.h"
#include "CPacketWatch.h"
#include "samplerate.h"
#include "fastpool.h"
#include <string.h>

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

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


Boolean gHardwareIsFullDuplex;


#ifdef	PGP_MACINTOSH
#include <Power.h>
#include "PGPFMacUtils.h"

static void SoundReady(SPBPtr spb, char *buf, long len, short /*amplitude*/)
{
	CSoundInput *si;
	long f;

	si=(CSoundInput *)spb->userLong;
	if((f = si->mSampleBufSize - si->mIndex) < len)
	{
		::BlockMoveData(buf, &si->mSamples[si->mIndex], f);
		::BlockMoveData(buf + f, &si->mSamples[0], len - f);
		 si->mIndex = len - f;
	}
	else
	{
		::BlockMoveData(buf, &si->mSamples[si->mIndex], len);
		 si->mIndex += len;
	}
	si->mIndex %=  si->mSampleBufSize;
}

#ifdef	__MC68K__
static asm void SIInterruptProc()
{
	fralloc		+
	movem.l		a0-a2/d0-d2,-(a7)
	move.w		d0, -(a7)
	move.l		d1, -(a7)
	move.l		a1, -(a7)
	move.l		a0, -(a7)
	jsr			SoundReady
	movem.l		(a7)+,a0-a2/d0-d2
	frfree
	rts
}
#else	//__MC68K__
static void SIInterruptProc(SPBPtr spb, Ptr dataBuffer,
							short peakAmplitude, long samplesLen)
{
	SoundReady(spb, dataBuffer, samplesLen, peakAmplitude);
}
#endif	//__MC68K__
#endif	//PGP_MACINTOSH

#ifdef	PGP_WIN32

HANDLE	siSemaphore;
static long bufReturn, sHeaderIndex;
static short sLevel;

//	SoundReady - the callback routine specified when we open the sound
//	device for input.  Whenever Win32 finishes recording a
//	block of sound it calls this routine.

void CALLBACK
SoundReady(HANDLE hWaveIn, UINT msg, DWORD instance, DWORD param1, DWORD param2)
{
	CSoundInput *si;
	long f, count;
	short tLevel, *p, *e;

	if(msg == WIM_DATA)
	{
		si=(CSoundInput *)instance;
		if((f = si->mSampleBufSize - si->mIndex) < RAWRECORDQUANT)
		{
			memcpy(&si->mSamples[si->mIndex], si->mHeaders[sHeaderIndex].lpData, f);
			memcpy(&si->mSamples[0], si->mHeaders[sHeaderIndex].lpData + f, RAWRECORDQUANT - f);
			si->mIndex = RAWRECORDQUANT - f;
		}
		else
		{
			memcpy(&si->mSamples[si->mIndex], si->mHeaders[sHeaderIndex].lpData, RAWRECORDQUANT);
			si->mIndex += RAWRECORDQUANT;
		}
		// Calculate a level meter position.
		// Look at every fourth sample and take
		// the max sample value to send to
		// the level meter.  This is done for us
		// on the Macintosh.
		tLevel = 0;
		p = (short *)si->mHeaders[sHeaderIndex].lpData;
		e = (short *)(si->mHeaders[sHeaderIndex].lpData + RAWRECORDQUANT);
		for(;p<e;p+=4)	// every fourth sample
		{
			if(*p > tLevel)
				tLevel = *p;
		}
		if(tLevel<256)
			sLevel = 0;
		else
			sLevel = (tLevel / 2048) + 1;	// scale it from 0-16
		if(sLevel > 16)
			sLevel = 16;
		sHeaderIndex++;
		sHeaderIndex %= NUMINPUTBUFFERS;
		si->mIndex %= si->mSampleBufSize;
		InterlockedIncrement(&bufReturn);
		ReleaseSemaphore(siSemaphore, 1, &count);
	}
}

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

	if(errCode)
	{
		if(!waveInGetErrorText(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	//PGP_WIN32

void Amplify(short *samples, int numSamples, double gain);

CSoundInput::CSoundInput(CPFWindow *pfWindow, CLevelMeter *levelMeter, void **outResult)
#ifndef PGP_WIN32
		: LThread(0, thread_DefaultStack, threadOption_UsePool, outResult)
#else
		: LThread(outResult)		
#endif
{
	long gestValue;
	
	mPFWindow = pfWindow;
	mAbort = FALSE;
	mOpen = FALSE;
	mRecording = FALSE;
	mPaused = FALSE;
	mLevelMeter = levelMeter;
	mGSM = NIL;
	mOutQueue = NIL;
	mOutThread = NIL;
	mPacketThread = NIL;
	mIndex = mOutdex = 0;
	mSampleSize = 16;
	mDefaultTrail = mTrailSamples = 0;
	mHardwareIs16Bit = TRUE;
	mCodec = 0;
	mGain = 0;
	SetGain(gPGFOpts.popt.siGain);
	mThreshold = 0;
	mDownDone = mCompDone = 0;
	mCompSnd = NIL;
	gHardwareIsFullDuplex = TRUE;
	
#ifdef PGP_MACINTOSH
	mSoundRef = 0;
	Gestalt(gestaltSoundAttr, &gestValue);
	gHardwareIsFullDuplex = !!(gestValue & (1<<gestaltPlayAndRecord));
	mHardwareIs16Bit = !!(gestValue & (1<<gestalt16BitSoundIO));
	mSIIntProc = NewSIInterruptProc(SIInterruptProc);
	mSPB.completionRoutine = NIL;
	mSPB.interruptRoutine = mSIIntProc;
	mSPB.userLong = (long)this;
	mSPB.unused1 = 0;
	mPMExists = PowerManagerExists();
	if(mPMExists)
		::DisableIdle();// recording sucks with idle mode enabled
#elif PGP_WIN32
	WAVEFORMATEX waveFormat;
	WAVEINCAPS devCaps;
	MMRESULT result;
	Boolean flag = TRUE;
	short i;

	memset(mHeaders, 0, sizeof(mHeaders));
	for(i=0;i<NUMINPUTBUFFERS;i++)
	{
		mMemHandles[i] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,RAWRECORDQUANT);
		mMemPtrs[i] = (uchar*)GlobalLock(mMemHandles[i]);

		// initialize the header
		mHeaders[i].lpData = (char *)mMemPtrs[i];
		mHeaders[i].dwBufferLength = RAWRECORDQUANT;
	}
	bufReturn = 0;
	mHeaderIndex = sHeaderIndex = 0;
	siSemaphore = CreateSemaphore(NULL, 0, NUMINPUTBUFFERS, NULL);
	if(!siSemaphore)
		pgp_errstring("Error creating semaphore.");

	// Now lets test the hardware to see whether we are running on a full duplex
	// machine or a half duplex one.  Lets open sound input and then we'll see if
	// there's an error when we open sound output.  If there is, the hardware is
	// half duplex.

	if(waveInGetNumDevs() < 1)
		PGFAlert("No sound input devices found!", 0);
	waveInGetDevCaps(WAVE_MAPPER, &devCaps, sizeof(WAVEINCAPS));
	if(devCaps.dwFormats & WAVE_FORMAT_1M16)
	{
		mHardwareIs16Bit = TRUE;
		mSampleRate = 11025.0;
	}
	else if(devCaps.dwFormats & WAVE_FORMAT_2M16)
	{
		mHardwareIs16Bit = TRUE;
		mSampleRate = 22050.0;
	}
	else if(devCaps.dwFormats & WAVE_FORMAT_2M08)
	{
		mHardwareIs16Bit = FALSE;
		mSampleRate = 22050.0;
	}
	else if(devCaps.dwFormats & WAVE_FORMAT_1M08)
	{
		mHardwareIs16Bit = FALSE;
		mSampleRate = 11025.0;
	}
	else
	{
		PGFAlert("Sound input device does not support a compatible format.", 0);
		flag = FALSE;
	}
	if(flag)
	{
		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(!waveInOpen(&mWaveHandle, WAVE_MAPPER, &waveFormat, 
						(DWORD)SoundReady, NIL, CALLBACK_FUNCTION))
		{
			HWAVEOUT mOWaveHandle = NULL;
			float mOSampleRate;
			Boolean mOHardwareIs16Bit;
			WAVEOUTCAPS devCaps;
	
			flag = TRUE;
			if(waveOutGetNumDevs() < 1)
				PGFAlert("No sound output devices available.", 0);
			waveOutGetDevCaps(WAVE_MAPPER, &devCaps, sizeof(WAVEOUTCAPS));
			if(devCaps.dwFormats & WAVE_FORMAT_1M16)
			{
				// We'd rather upsample to 11025 than 22050
				mOHardwareIs16Bit = TRUE;
				mOSampleRate = 11025.0;
			}
			else if(devCaps.dwFormats & WAVE_FORMAT_2M16)
			{
				mOHardwareIs16Bit = TRUE;
				mOSampleRate = 22050.0;
			}
			else if(devCaps.dwFormats & WAVE_FORMAT_1M08)
			{
				mOHardwareIs16Bit = FALSE;
				mOSampleRate = 11025.0;
			}
			else if(devCaps.dwFormats & WAVE_FORMAT_2M08)
			{
				mOHardwareIs16Bit = FALSE;
				mOSampleRate = 22050.0;
			}
			else
			{
				PGFAlert("Sound output device does not support a compatible format.", 0);
				flag = FALSE;
			}
			if(flag)
			{
				MMRESULT err;
				waveFormat.wFormatTag = WAVE_FORMAT_PCM;
				waveFormat.nChannels = 1;
				waveFormat.nSamplesPerSec = (long)mOSampleRate;
				waveFormat.nAvgBytesPerSec = (long)mOSampleRate * (mOHardwareIs16Bit ? 2 : 1);
				waveFormat.nBlockAlign = mOHardwareIs16Bit ? 2 : 1;
				waveFormat.wBitsPerSample = 8 * (mOHardwareIs16Bit ? 2 : 1);
				waveFormat.cbSize = 0;
				err=waveOutOpen(&mOWaveHandle, WAVE_MAPPER,
						&waveFormat,
						(DWORD)SoundReady, NIL, CALLBACK_FUNCTION);
				if(!err || !(err=waveOutOpen(&mOWaveHandle, WAVE_MAPPER,
						&waveFormat,
						(DWORD)SoundReady, NIL, CALLBACK_FUNCTION)))
				{
					gHardwareIsFullDuplex = TRUE;
					waveOutClose(mOWaveHandle);
				}
				//else
				//	CheckWaveError("waveOutOpen", err);
			}
			waveInClose(mWaveHandle);
		}
	}
#endif
	SetCodec('GSM7');	// just setting a default coder for testing only
}

CSoundInput::~CSoundInput()
{
	Close();
	DisposeCodec();
	if(mCompSnd)
		safe_free(mCompSnd);
#ifdef	PGP_MACINTOSH
	DisposeRoutineDescriptor(mSIIntProc);
	if(mPMExists)
		::EnableIdle();
#elif	PGP_WIN32
	for(short i=0;i<NUMINPUTBUFFERS;i++)
	{
		GlobalUnlock(mMemHandles[i]);
		GlobalFree(mMemHandles[i]);
	}
	if(siSemaphore)
		CloseHandle(siSemaphore);
#endif
}

// Open is called when a call begins upon connection before configuration,
// full duplex CPUs are always open so that the level meter can be always on

void
CSoundInput::Open(void)
{
#ifdef PGP_MACINTOSH
	OSErr result;
	OSType comp;
	Fixed rate;
	short lm;

	if(!mOpen)
	{
		if(result = ::SPBOpenDevice(NIL, siWritePermission, &mSoundRef))
			PGFAlert("Sound input device is already open!  Recording disabled.", 0);
		else
		{
			mSPB.inRefNum = mSoundRef;
			//SPBSetDeviceInfo(mSoundRef, siOptionsDialog, NIL);
			rate = rate22050hz;
			::SPBSetDeviceInfo(mSoundRef, siSampleRate, &rate);
			::SPBGetDeviceInfo(mSoundRef, siSampleRate, &rate);
			mSampleRate = rate;
			result = ::SPBSetDeviceInfo(mSoundRef, siInputGain, &mGain);
			//pgpAssert(!result);
			comp = 'NONE';
			result = ::SPBSetDeviceInfo(mSoundRef, siCompressionType, &comp);
			pgpAssert(IsntErr(result));
			lm = 1;
			result = ::SPBSetDeviceInfo(mSoundRef, siNumberChannels, &lm);
			pgpAssert(IsntErr(result));
			lm = 1;
			result = ::SPBSetDeviceInfo(mSoundRef, siLevelMeterOnOff, &lm);
			pgpAssert(IsntErr(result));
			if(mHardwareIs16Bit)
			{
				result = ::SPBSetDeviceInfo(mSoundRef, siSampleSize, &mSampleSize);
				if(result)
					mHardwareIs16Bit = false;
				//pgpAssert(IsntErr(result));
			}
			mOpen = TRUE;
		}
	}
#elif	PGP_WIN32
	WAVEFORMATEX waveFormat;
	WAVEINCAPS devCaps;
	MMRESULT result;

	if(!mOpen)
	{
		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 = waveInOpen(&mWaveHandle, WAVE_MAPPER, &waveFormat, 
						(DWORD)SoundReady, (DWORD)this, CALLBACK_FUNCTION))
		{
			char str[200], error[220];

			if(!waveInGetErrorText(result, str, 200))
			{
				if(!str[0])
					sprintf(error, "Unknown error opening sound input device: %d", result);
				else
					sprintf(error, "Error opening sound input device:\n %s", str);
			}
			else
				sprintf(error, "Error opening sound input device:\n %s", str);
			PGFAlert(error, 0);
			mOpen = FALSE;
		}
		else
			mOpen = TRUE;
	}
#endif
}

// Close is called when a call is terminated, or when the program is exited
// on a full duplex CPU

void
CSoundInput::Close(void)
{
	if(mOpen)
	{
		mLevelMeter->SetLevel(0);
		if(mRecording)
			Record(FALSE);
		mOpen = FALSE;
		mPaused = FALSE;
		
#ifdef	PGP_MACINTOSH
		::SPBCloseDevice(mSoundRef);
#elif	PGP_WIN32
		short err;
		
		err = waveInReset(mWaveHandle);	CheckWaveError("waveInReset", err);
		for(short i=0;i<NUMINPUTBUFFERS;i++)
		{
			while(mHeaders[i].dwFlags && !(mHeaders[i].dwFlags & WHDR_DONE))
				Sleep(0);
			err = waveInUnprepareHeader(mWaveHandle, &(mHeaders[i]), sizeof(WAVEHDR));
			CheckWaveError("waveInUnprepareHeader", err);
		}
		err = waveInClose(mWaveHandle);	CheckWaveError("waveInClose", err);
		mWaveHandle = NIL;
#endif	//PGP_WIN32
	}
}

void *
CSoundInput::Run(void)
{
	long levelInfo, newlen, index, downSize;
	short *src, *s, *p, *e;
	uchar *d, *de;
	Boolean passedThreshold;
	
	while(!mAbort)
	{
#ifdef	PGP_WIN32
		int err;
		WaitForSingleObject(siSemaphore, INFINITE);
		if(mAbort)
			break;
		while(bufReturn>0)
		{
			mSoundMutex.Wait();
			if(mOpen && mRecording)
			{
				if(!(mHeaders[mHeaderIndex].dwFlags & WHDR_PREPARED))
				{
					err = waveInPrepareHeader(mWaveHandle, &(mHeaders[mHeaderIndex]), sizeof(WAVEHDR));
					CheckWaveError("waveInPrepareHeader", err);
				}
				err = waveInAddBuffer(mWaveHandle, &(mHeaders[mHeaderIndex]), sizeof(WAVEHDR));
				CheckWaveError("waveInAddBuffer", err);
			}
			mHeaderIndex++;
			mHeaderIndex %= NUMINPUTBUFFERS;
			mSoundMutex.Signal();
			InterlockedDecrement(&bufReturn);
		}
#endif
		mSoundMutex.Wait();
#ifdef	PGP_WIN32
		if(mOpen)
			mLevelMeter->SetLevel(sLevel);
#endif
		index = mIndex;
		if(mOpen && mRecording && mOutdex != index)
		{
			if(index < mOutdex)
				newlen = mSampleBufSize - mOutdex;
			else
				newlen = index - mOutdex;
			newlen = minl(DOWNBUFSIZE - mDownDone, newlen);
			if(!mPaused && mOutQueue && mCodec)
			{
				src = (short *)&mSamples[mOutdex];
				if(mCodec == 'rand')
					randPoolAddBytes((uchar *)src, newlen);
				else
				{
					switch(mSampleSize)
					{
						case 16:
							if(mUpSampleSize)
							{
								char *p, *e;

								p = (char *)src;
								e = p + newlen;
								if(e-p > UPSAMPBUFSIZE/2)
									newlen = UPSAMPBUFSIZE/2;
								src = s = (short *)mUpSampleBuf;
								for(;p<e;p++)
									*s++ = (*p - 0x80) * 0x100;
								downSize = RateChange(src, (short *)(mDownBuf + mDownDone),
													newlen, mSampleRate, mCoderRate);
							}
							else
								downSize = RateChange(src, (short *)(mDownBuf + mDownDone),
													newlen / 2, mSampleRate, mCoderRate);
#ifdef PGP_WIN32
							// Amplify the sound samples based on the user's setting
							if(mGain)
								Amplify((short *)(mDownBuf + mDownDone), downSize * 2, mGain);
#endif
							mDownDone += downSize * 2;

							// Handle silence detection
							if(mThreshold)
							{
								// If mThreshold is 0, silence detection is deactivated
								
								if(mTrailSamples > 0)
								{
									// mTrailSamples allows us to have a number of
									// samples guaranteed to be sent any time we
									// go above the minimum amplitude.
									// This will probably trail a bit more than
									// its value because mDownDone may be greater
									// than mTrailSamples, but thats okay.
									
									mTrailSamples -= mDownDone;
									if(mTrailSamples < 0)
										mTrailSamples = 0;
								}
								else
								{
									// This is the meat of the silence detection.
									// See whether every 2nd sample is above or below
									// 0 and then measure its amplitude difference from
									// our threshold.
									
									passedThreshold = FALSE;
									p = (short *)mDownBuf;
									e = (short *)(mDownBuf + mDownDone);
									for(;!passedThreshold && (p<e);p+=2)
									{
										if(*p < 0)
										{
											if(*p <= -mThreshold)
											{

⌨️ 快捷键说明

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