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

📄 beaudio.cxx

📁 sloedgy open sip stack source code
💻 CXX
📖 第 1 页 / 共 4 页
字号:
}

BOOL PSound::PlayFile(const PFilePath & file, BOOL wait)
{
	entry_ref 			ref;
	status_t			err; // can't use dwLastError because this function is static

	// using pointers for these objects so that we don't have to
	// construct them here but can nevertheless use the if(ok)'s
	BEntry 			   *pentry = NULL;
	
	{
		// Create BEntry from file name
		pentry = new BEntry(file, true);
		err = pentry->InitCheck();
	}

	if (err==B_OK)
	{
		// Create entry_ref from BEntry	
		err = pentry->GetRef(&ref);
	}
	
	if (err==B_OK)
	{
		// Play the sound. Return value is a handle or a negative value for errors
		// Errors in BeOS are always negative values
		err=play_sound(&ref, true, !wait, wait);
		if (err>=0)
		{
			err=B_OK;
		}
	}

	return (err==B_OK);
}

void PSound::Beep()
{
	::beep();
}

////////////////////////////////////////////////////////////////////////////////
// CircularBuffer

class Guard
{
private:
	sem_id mSem;
public:
	Guard(sem_id sem) { acquire_sem(mSem=sem); }
	~Guard() { release_sem(mSem); }
};

/*
	This class represents a circular FIFO buffer.
	The buffer has a head and a tail that chase each other.
	The data is added to the buffer at the tail side by using Fill.
	The data from the buffer can be read starting at the head side using
	Drain.
	It is possible to use two threads to fill and drain the buffer but
	there should not be more than 2 threads doing draining and filling.
	Resetting (flushing) or destroying from a third thread is allowed;
	do make sure that any threads that operate on buffer data are stopped
	before destroying a buffer.
	Normally, filling and draining operations block the thread as short as
	possible (i.e. only when the other thread needs to update the head and
	tail pointers etc). If the filling thread tries to put data into a full
	or almost full buffer, it just returns after filling as much data as
	it can, and if the draining thread tries to get more data out than is
	in the buffer, it will simply return with the data that is there.
	In order to move all the data from an external buffer into an object
	of this class, the caller would have to call Fill repeatedly until
	all the data has been processed (similarly it would have to call Drain
	until it receives sufficient data). But if the application has nothing
	else to do in the mean time, this constitutes a Busy Waiting loop
	on either the filling or draining side of the FIFO buffer that slurps
	up as much CPU time as possible.
	To improve this behaviour, it's possible to specify a threshold value
	that is used to change the state to FullEnough and EmptyEnough. By using
	these states (instead of Full and Empty), one thread can block until the
	other thread has determined that there is enough data or enough room for
	data.
*/
class CircularBuffer
{
public:
	// Internal state for the buffer
	// Note the nifty bit patterns for comparing the current state
	// with a desired state
	typedef enum
	{							// Headspace		Tailspace
		Empty			=1,		// 0                size
		Filled			=2,		// 0<h<size			0<t<size
		NotFull			=3,		// 0<=h<size		0<t<=size	(for comparing)
		Full			=4,		// size				0
		NotEmpty		=6,		// 0<h<=size		0<=t<size	(for comparing)
		Flushed     	=8,		// 								(extra signal to threads waiting on full)
		FullEnough		=16,	// h>=drainthreshold
		EmptyEnough		=32,	//                  t>=fillthreshold
	} State;

protected:
	friend class ResamplingBuffer; // needed for one of their constructors

	BYTE		   *mBuffer;			// the buffer
	PINDEX			mSize;				// size of the buffer in bytes
	
	volatile PINDEX	mHead;				// index where to start reading
	volatile PINDEX	mTail;				// index where to start writing
	volatile PINDEX	mHeadRoom;			// consecutive space from head to end-of-buffer or tail
	volatile PINDEX	mTailRoom;			// consecutive space from tail to end-of-buffer or head
	volatile PINDEX mSizeUsed;			// total bytes in use
	volatile PINDEX mFillThreshold;		// see above
	volatile PINDEX mDrainThreshold;	// see above

	volatile State	mState;				// current state of the buffer

	sem_id			mSemInUse;			// used to guard data integrity
	sem_id			mSemStateChange;	// used to wait for state changes

protected:
	// Check if the state changed. Private because it's not guarded by semaphore
	void UpdateState(void)
	{
		// Determine current state
		State newstate;
		
		if (mSizeUsed==mSize)
		{
			PRINTCB(("State is FULL"));
			newstate=Full;
		}
		else if (mSizeUsed==0)
		{
			PRINTCB(("State is EMPTY"));
			newstate=Empty;
		}
		else
		{
			PRINTCB(("State is FILLED"));
			newstate=Filled;
		}
		
		// Check thresholds
		if (mSize-mSizeUsed>=mFillThreshold)
		{
			PRINTCB(("...and EMPTYENOUGH"));
			newstate=(State)(newstate | EmptyEnough);
		}
		if (mSizeUsed>=mDrainThreshold)
		{
			PRINTCB(("...and FULLENOUGH"));
			newstate=(State)(newstate | FullEnough);
		}
		
		// Check if the state changed
		if (newstate!=mState)
		{
			PRINTCB(("Updating state from %X to %X", mState, newstate));
			
			// Set the new state
			mState=newstate;
			
			// Signal state change
			release_sem(mSemStateChange);
		}
	}

	virtual size_t Write(
		BYTE *dest,					// destination
		const BYTE **extbuf,		// source, to be updated
		size_t size,				// space in destination
		size_t *extsize)			// data in source, to be updated
	{
		// This function is called to put data into the buffer
		size_t todo=MIN(size, *extsize);
		memcpy(dest, *extbuf, todo);
		*extbuf   +=todo; // The external pointer moves forward...
		*extsize  -=todo; // ... so the remaining size decreases
		return todo;
	}
	
	virtual size_t Read(
		BYTE **extbuf,				// destination, to be updated
		const BYTE *src,			// source
		size_t *extsize,			// space in destination, to be updated
		size_t size)				// data in source
	{
		// This function is called to read data out of the buffer
		size_t todo=MIN(size, *extsize);
		memcpy(*extbuf, src, todo);
		*extbuf   +=todo; // The external pointer moves forward...
		*extsize  -=todo; // ... so the remaining size decreases
		return todo;
	}
	
public:
	// Reset buffer so that it can be filled again
	void Reset(void)
	{
		Guard _(mSemInUse); // guard data integrity

		mHead=mHeadRoom=mTail=mSizeUsed=0;
		mTailRoom=GetSize();
		mState=(State)(Flushed|Empty|EmptyEnough);
	}

	// Constructor
	CircularBuffer(
		PINDEX size,
		PINDEX fillthreshold = 0,
		PINDEX drainthreshold = 0) 
	: mFillThreshold(fillthreshold), mDrainThreshold(drainthreshold), mState(Empty)
	{
		PAssert(size!=0, "Attempting to create a buffer with size 0");
		
		mSemInUse=create_sem(1, "mSemInUse");
		mSemStateChange=create_sem(0, "mSemStateChange");
		
		PAssert(mSemInUse>=0 && mSemStateChange>=0, "Unable to create semaphores");
		
		mBuffer=new BYTE[(mSize=size)];
		
		Reset();
	}

	// Destructor
	virtual ~CircularBuffer()
	{
	  // make sure the in-use semaphore is free and stays free
	  while (acquire_sem_etc(mSemInUse,1,B_RELATIVE_TIMEOUT,0)==B_WOULD_BLOCK)
	  {
	    // nothing to do, just busy-wait
	  }

	  delete_sem(mSemInUse);
	
	  delete_sem(mSemStateChange);
	
	  Reset();
		
	  if(mBuffer)
	    delete[] mBuffer;
	}

	// Check if buffer is empty
	bool IsEmpty() { return (mState==Empty); }
	
	// Check if buffer is full
	bool IsFull() { return (mState==Full); }
	
	// Get the size of the buffer
	PINDEX GetSize(void) { return mSize; }

	// Wait asynchronously for a buffer state or one of a number of states
	void WaitForState(State state)
	{
		PRINTCB(("Waiting for state %X, current state=%X this=%p", state, mState, this));
		// reset the Flushed bit so it only stops the loop if the buffer
		// is flushed DURING an operation
		{
			Guard _(mSemInUse);
			mState=(State)(mState & ~Flushed);
		}
		for(;;)
		{
			if ((mState & (state|Flushed))!=0) // bit patterns allowed
			{
				PRINTCB(("Detected state %X, wanted %X, returning", mState, state));
				return;
			}
			PRINTCB(("Waiting for %X; headroom=%u tailroom=%u this=%p",state,mHeadRoom,mTailRoom,this));
			// To prevent a race condition here in case the state
			// gets changed just after the GetState call, the next
			// semaphore call has a timeout.
			acquire_sem_etc(mSemStateChange,1,B_RELATIVE_TIMEOUT,1000000);
		}
	}
	
	// Fill buffer with data.
	void Fill(const BYTE **extbuf, size_t *extsize)
	{
		PRINTCB(("start: head %d tail %d headroom %d tailroom %d extsize %d buffer %p this %p", mHead, mTail, mHeadRoom, mTailRoom, *extsize, mBuffer, this));

		// Make a local copy of the queue.
		// This is ok because there is only one filler thread and
		// one drainer thread. The drainer is not going to make the
		// free area for the filler any smaller and the filler is not
		// going to overwrite the drainer's data if we do this.
		// This way we can keep the semaphore busy as short as possible.
		PINDEX lTail;
		PINDEX lTailRoom;
		PINDEX lHead; // read only
		{
			Guard _(mSemInUse); // guard data integrity
			lTail=mTail;
			lTailRoom=mTailRoom;
			lHead=mHead;
		}
		
		bool needhousekeeping=false;
		PINDEX totaldone=0;
		
		while (*extsize!=0 && lTailRoom!=0 && totaldone<mSize)
		{
			needhousekeeping=true;
			
			PINDEX done=Write(
				mBuffer+lTail,
				extbuf,
			    lTailRoom,
				extsize);
			
			totaldone +=done;
			
			lTail     +=done; // The tail moves forward...
			lTailRoom -=done; // ... so there will be less room at the tail

			// Check if we should wrap around
			if (lTail==mSize)
			{
				lTail=0;
				lTailRoom=lHead;
			}
		}

		if (needhousekeeping)
		{
			Guard _(mSemInUse);
			
			// Copy the local values back
			mTail=lTail;
			mTailRoom=lTailRoom;
			mSizeUsed+=totaldone;
			
			// Recalculate headroom
			if (mTail>mHead)
			{
				mHeadRoom=mTail-mHead;
			}
			else
			{
				mHeadRoom=mSize-mHead;
			}

			// Check if we need to change the state
			UpdateState();

			PRINTCB(("  end: head %d tail %d headroom %d tailroom %d extsize %d", mHead, mTail, mHeadRoom, mTailRoom, *extsize));
		}
	}

	// Empty data out of buffer
	void Drain(BYTE **extbuf, size_t *extsize)
	{
		PTRACE(7, "Drain: head " << mHead 
	    << " tail " << mTail 
	    << " headroom " << mHeadRoom 
	    << " tailroom " << mTailRoom 
	    << " extsize " << *extsize
	    << " buffer " << mBuffer
	    << " this " << this);

		// Make a local copy of the queue.
		// This is ok because there is only one filler thread and
		// one drainer thread. The drainer is not going to make the
		// free area for the filler any smaller and the filler is not
		// going to overwrite the drainer's data if we do this.
		// This way we can keep the semaphore busy as short as possible.
		PINDEX lHead;
		PINDEX lHeadRoom;
		PINDEX lTail; // read only
		{
			Guard _(mSemInUse); // guard data integrity
			lHead=mHead;
			lHeadRoom=mHeadRoom;
			lTail=mTail;
		}

		bool needhousekeeping=false;
		PINDEX totaldone=0;
		
		while (*extsize!=0 && lHeadRoom!=0 && totaldone<mSize)
		{
			needhousekeeping=true;
			
			size_t done=Read(
				extbuf,
				mBuffer+lHead,
				extsize,
				lHeadRoom);
				
			totaldone +=done;
			
			lHead     +=done; // The head moves forward...
			lHeadRoom -=done; // ... so there will be less room at the head
		
			// Check if we should wrap around
			if (lHead==mSize)
			{
				lHead=0;
				lHeadRoom=mTail;
			}
		}

		if (needhousekeeping)
		{
			Guard _(mSemInUse);
			
			// Copy the local values back
			mHead=lHead;
			mHeadRoom=lHeadRoom;
			mSizeUsed-=totaldone;
			
			// Recalculate tailroom
			if (mHead>mTail)
			{
				mTailRoom=mHead-mTail;
			}
			else
			{
				mTailRoom=GetSize()-mTail;
			}
			
			// Check if we need to change the state
			UpdateState();

			PRINTCB(("  end: head %d tail %d headroom %d tailroom %d extsize %d", mHead, mTail, mHeadRoom, mTailRoom, *extsize));
		}
	}
};

////////////////////////////////////////////////////////////////////////////////

class ResamplingBuffer : public CircularBuffer
{
protected:
	Resampler	   *mResampler;
	
protected:
	virtual size_t Write(
		BYTE *dest,					// destination
		const BYTE **extbuf,		// source, to be updated
		size_t size,				// space in destination
		size_t *extsize)			// data in source, to be updated
	{
		size_t todo=*extsize/mResampler->InFrameSize();
		size_t done=mResampler->InFrames(
			(const short **)extbuf,
			(short **)&dest,
			&todo,
			size/mResampler->OutFrameSize());
		done*=mResampler->OutFrameSize();
		*extsize=todo*mResampler->InFrameSize();
		
		return done;
	}
	
public:
	void SetResampler(Resampler *resampler)
	{
		Guard _(mSemInUse); // guard data integrity
		
		mResampler=resampler;
	}

	ResamplingBuffer(
		Resampler *resampler,
		PINDEX size,
		PINDEX fillthreshold=0,
		PINDEX drainthreshold=0) 
		: CircularBuffer(size, fillthreshold, drainthreshold), mResampler(NULL)
	{
		SetResampler(resampler);
	}

	ResamplingBuffer(
		Resampler *resampler,
		CircularBuffer *other) 
	  : CircularBuffer(other->mSize, other->mFillThreshold, other->mDrainThreshold), mResampler(NULL)
	{
		SetResampler(resampler);
	}
};

////////////////////////////////////////////////////////////////////////////////
static void PlayBuffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format)
{
	// This function is called by the BSoundPlayer object whenever it needs some more
	// data to play.
	DETECTVARS(buffer, size/2)

	((CircularBuffer *)cookie)->Drain((BYTE **)&buffer, &size);
	
	DETECTSOUND();
}

static void RecordBuffer(void *cookie, const void *buffer, size_t size, const media_header &header)

⌨️ 快捷键说明

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