📄 beaudio.cxx
字号:
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();}////////////////////////////////////////////////////////////////////////////////// CircularBufferclass 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 changesprotected: // 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 + -