Logo Search packages:      
Sourcecode: pwlib version File versions  Download package

beaudio.cxx

/*
 * beaudio.cxx
 *
 * Sound driver implementation.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-2001 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
 * All Rights Reserved.
 *
 * Contributor(s): 
 * Yuri Kiryanov, ykiryanov at users.sourceforge.net,
 * Jac Goudsmit <jac@be.com>.
 *
 * $Log: beaudio.cxx,v $
 * Revision 1.16  2004/10/26 18:08:54  ykiryanov
 * Added code for old Media Kit, to be backwards compatible with R5, and Zeta ifdef
 *
 * Revision 1.15  2004/06/16 01:55:10  ykiryanov
 * Added usage of lastReadCount - sound capture now works
 *
 * Revision 1.14  2004/05/30 04:48:45  ykiryanov
 * Stable version
 *
 * Revision 1.12  2004/05/14 05:26:57  ykiryanov
 * Fixed dynamic cast bug
 *
 * Revision 1.11  2004/04/18 00:32:26  ykiryanov
 * Fized compiler choking on <dynamic_cast>.
 *
 * Revision 1.10  2004/04/02 03:29:07  ykiryanov
 * New improved code
 *
 * Revision 1.9  2002/02/09 00:52:01  robertj
 * Slight adjustment to API and documentation for volume functions.
 *
 * Revision 1.8  2002/02/07 20:57:21  dereks
 * add SetVolume and GetVolume methods to PSoundChannelBeOS
 *
 * Revision 1.7  2001/07/09 06:16:15  yurik
 * Jac Goudsmit's BeOS changes of July,6th. Cleaning up media subsystem etc.
 *
 * Revision 1.6  2000/12/16 13:08:56  rogerh
 * BeOS changes from Yuri Kiryanov <openh323@kiryanov.com>
 *
 * Revision 1.5  2000/04/19 00:13:52  robertj
 * BeOS port changes.
 *
 * Revision 1.4  1999/09/21 00:56:29  robertj
 * Added more sound support for BeOS (thanks again Yuri!)
 *
 * Revision 1.3  1999/06/28 09:28:02  robertj
 * Portability issues, especially n BeOS (thanks Yuri!)
 *
 * Revision 1.2  1999/03/05 07:03:27  robertj
 * Some more BeOS port changes.
 *
 * Revision 1.1  1999/03/02 05:41:59  robertj
 * More BeOS changes
 *
 */

#include <ptlib.h>
#include <ptlib/unix/ptlib/beaudio.h>

PCREATE_SOUND_PLUGIN(BeOS, PSoundChannelBeOS);

/////////////// Debugging stuff ///////////////
#define TL (7)

#define PRINT(x) //do { printf(__FILE__ ":%d %s ", __LINE__, __FUNCTION__); printf x; printf("\n"); } while(0)

#define STATUS(x) // PRINT((x "=%ld", (long)dwLastError))

#define PRINTCB(x) // PRINT(x)

//#define SOUNDDETECT 1 define this for printed output of first pb/rec audio

//#define FILEDUMP 1 define this for dumping audio to wav file

// Macros and global vars for debugging

#ifdef SOUNDDETECT
#define DETECTVARS(buffer,numframes) short *detbuf=(short*)buffer; size_t detframes=numframes;
#define DETECTSOUND() \
do { \
      static bool silence=true; \
      if (silence) \
      { \
            for (size_t i=0; i<detframes; i++) \
            { \
                  if (detbuf[i]>=255) \
                  { \
                        PRINT(("SOUND DETECTED at %p",detbuf)); \
                        for (size_t j=0; j<detframes && j<30; j++) \
                        { \
                              char *x; \
                              asprintf(&x,"%%%ds\n",(detbuf[j]>>10)+32); \
                              printf(x,"."); \
                              free(x); \
                        } \
                        silence=false; \
                        break; \
                  } \
            } \
      } \
} while(0)
#else
#define DETECTVARS(buffer,numframes)
#define DETECTSOUND()
#endif

#ifdef FILEDUMP
#include "beaudio/AudioFileWriter.h"
BAudioFileWriter *playwriter=NULL;
BAudioFileWriter *recwriter=NULL;
#endif

////////////////////////////////////////////////////////////////////////////////
// PSound

PSound::PSound(unsigned channels,
               unsigned samplesPerSecond,
               unsigned bitsPerSample,
               PINDEX   bufferSize,
               const BYTE * buffer)
{
      encoding = 0;
      SetFormat(channels, samplesPerSecond, bitsPerSample);

      if (buffer != NULL)
      {
            memcpy(GetPointer(bufferSize), buffer, bufferSize);
      }
}


PSound::PSound(const PFilePath & filename)
{
      encoding = 0;
      
      // Set the default format
      SetFormat(1, 8000, 16);
      
      // The format is changed if the file is succesfully loaded.
      Load(filename);
}


PSound & PSound::operator=(const PBYTEArray & data)
{
      PBYTEArray::operator=(data);
      return *this;
}


void PSound::SetFormat(unsigned channels,
                       unsigned samplesPerSecond,
                       unsigned bitsPerSample)
{
      // NOTE: all constructors should call this to initialize
      // the local members, especially formatInfo.
      // Do NOT call the function with any parameter set to 0!
      sampleSize=bitsPerSample;
      sampleRate=samplesPerSecond;
      numChannels = channels;

      // We don't use the encoding member (although we could probably set it to 0=PCM)
      // Let the application know it shouldn't assume anything
      encoding = 1;
      
      // The formatInfo member to us is a media_format structure.
      BOOL setsize_formatInfo=formatInfo.SetSize(sizeof(media_format));
      PAssert(setsize_formatInfo, "Unable to set size for sound info array");
      
      // Initialize the media_format struct
      // The numbers of bits that we support here are 8, 16 or 32 bits (signed),
      // results for other sizes are not defined.
      media_format &format=*(media_format*)(const BYTE *)formatInfo;

      format.type = B_MEDIA_RAW_AUDIO;
      format.u.raw_audio = media_raw_audio_format::wildcard;
      format.u.raw_audio.frame_rate=(float)sampleRate;
      format.u.raw_audio.channel_count=numChannels;
      format.u.raw_audio.format=(sampleSize / 8) & 0xF; 
      format.u.raw_audio.byte_order=B_MEDIA_HOST_ENDIAN;
      format.u.raw_audio.buffer_size=(channels * samplesPerSecond * (bitsPerSample/8))/10; // 1/10 sec buffer
}

BOOL PSound::Load(const PFilePath & filename)
{
      // format is a reference to the formatInfo member which stores info
      // about the media format. This is needed for writing the data back
      // or for playing the sound.
      media_format         &format=*(media_format *)(const BYTE *)formatInfo;

      // Create BEntry from file name
      BEntry entry(filename, true);
      if ((dwLastError=entry.InitCheck())!=B_OK)
      {
            STATUS("entry.InitCheck()");
            return FALSE;
      }

      // Create entry_ref from BEntry     
      entry_ref ref;
      if ((dwLastError=entry.GetRef(&ref))!=B_OK)
      {
            STATUS("entry.GetRef()");
            return FALSE;
      }

      // Create BMediaFile for read access from the entry_ref
      BMediaFile file(&ref);
      if ((dwLastError=file.InitCheck())!=B_OK)
      {
            STATUS("file.InitCheck()");
            return FALSE;
      }
      
      // Search for the first media track that can be decoded
      BMediaTrack *ptrack = NULL;
      for (int index=0; (index<file.CountTracks()) && (dwLastError==B_OK); index++)
      {
            ptrack = file.TrackAt(index);
            if (ptrack)
            {
                  dwLastError = ptrack->InitCheck();
            }
            else
            {
                  dwLastError = B_ERROR; //todo: change error code
            }

            if (dwLastError==B_OK)
            {
                  // Get media format; we're looking for a raw audio track.
                  format.type = B_MEDIA_RAW_AUDIO;
                  format.u.raw_audio = media_raw_audio_format::wildcard;
                  dwLastError = ptrack->DecodedFormat(&format);
            
                  if ((dwLastError==B_OK) && (format.type==B_MEDIA_RAW_AUDIO))
                  {
                        break; // found a decodable track
                  }
            }
            else
            {
                  STATUS("TrackAt() failed, error");
            }
            
            // if we found a track and arrived at this point, the track we found
            // was not decodable
            if (ptrack)
            {
                  dwLastError=file.ReleaseTrack(ptrack); // destroys ptrack
            }
      }
      
      // if an error occurred during track scanning, leave now
      if (dwLastError!=B_OK)
      {
            return FALSE;
      }

      // Get a reference to the raw output format
      media_raw_audio_format &rawformat = format.u.raw_audio;

      // Fill in our fields from the format           
      sampleSize    = (rawformat.format & 0xF) * 8;
      numChannels   = rawformat.channel_count;
      if (rawformat.frame_rate>0.0 && rawformat.frame_rate<=(float)0xFFFFFFFFU)
      {
            sampleRate = (unsigned)(rawformat.frame_rate);
      }
      else
      {
            // unknown or unrepresentable sample rate.
            // It's not really documented what we should do in this case but
            // it probably doesn't matter either... 
            sampleRate = 0; 
      }
      
      // Get the number of frames for the track and determine how much
      // memory we need to store the file's data
      // The multiplication might overflow for huge files but we don't
      // want to read them into memory anyway so I guess it's ok...
      int64 numframes = ptrack->CountFrames();
      int64 framesize = numChannels * (sampleSize/8);
      int64 numbytes  = numframes * framesize;
      
      // Set the size of the object's data area
      if (!SetSize(numbytes))
      {
            PRINT(("Can't set size of sound to %Ld", numbytes));
            dwLastError = B_ERROR; //todo replace by better error code
            return FALSE; // BMediaFile will destroy ptrack
      }
      
      // Read all frames into memory. NOTE: not thread safe!
      BYTE* dest = GetPointer();          // destination pointer
      int64 framecount = numframes; // number of frames left to read
      int64 framesread;                   // number of actual frames done
      while ((framecount!=0) && (dwLastError==B_OK))
      {
            framesread = framecount;
            dwLastError = ptrack->ReadFrames(dest, &framesread);
            dest += framesread * framesize;
            framecount -= framesread;
      }
      
      // return true for success
      return (dwLastError==B_OK); // BMediaFile will destroy ptrack
}


BOOL PSound::Save(const PFilePath & filename)
{
      // format is a reference to the formatInfo member which stores info
      // about the media format. This is needed for writing the data back
      // or for playing the sound.
      media_format         &format=*(media_format *)(const BYTE *)formatInfo;

      // Get the file type from the file name's extension; if none, use wav
      PFilePathString filetype=filename.GetType(); // e.g. ".wav"
      if (filetype=="")
      {
            filetype="wav";
      }
      else
      {
            filetype=filetype.Mid(1); // cut off the '.'
      }
      
      // Try to find the file format in BeOS's list of formats
      media_file_format mfi;
      int32 cookie=0;
      while ((dwLastError=get_next_file_format(&cookie, &mfi))==B_OK)
      {
            if (!strcasecmp(mfi.file_extension, (const char *)filetype))
            {
                  break;
            }
      }
      if (dwLastError!=B_OK)
      {
            // didn't find file format
            PRINT(("Couldn't find media_file_format for \"%s\"", (const char *)filetype));
            return FALSE;
      }
      
      // Create BEntry from file name
      BEntry      entry(filename, true);
      if ((dwLastError=entry.InitCheck())!=B_OK)
      {
            STATUS("entry.InitCheck()");
            return FALSE;
      }
      
      // Create entry_ref from BEntry     
      entry_ref ref;
      if ((dwLastError=entry.GetRef(&ref))!=B_OK)
      {
            STATUS("entry.GetRef()");
            return FALSE;
      }

      // Create BMediaFile for write access from the entry_ref
      BMediaFile file(&ref, &mfi, B_MEDIA_FILE_REPLACE_MODE);
      if ((dwLastError=file.InitCheck())!=B_OK)
      {
            STATUS("file.InitCheck()");
            return FALSE;
      }
      
      // Find an encoder. The input format is the format we have stored in
      // our formatInfo member.
      cookie=0;
      media_format outformat;
      media_codec_info mci,validmci,rawmci, *pmci;
      bool found_encoder = false;
      bool found_raw_encoder = false;
      while (get_next_encoder(&cookie, &mfi, &format, &outformat, &mci)==B_OK)
      {
            found_encoder=true;
            
            if (outformat.type==B_MEDIA_RAW_AUDIO)
            {
                  rawmci=mci;
                  found_raw_encoder=true;
            }
            else
            {
                  validmci=mci;
            }
      }
      
      // Choose an encoder:
      // If a raw-output encoder was found, use it.
      // Else, use the last found encoded-output encoder, if any.
      // This method of choosing will make sure that most file formats 
      // will get the most common encoding (PCM) whereas it's still possible
      // to choose another output format like MP3, if so dictated by the
      // file format.
      // BeOS is smart enough not to return an encoder that produces raw audio
      // for e.g. the MP3 file format, but it knows that there are many ways
      // to encode e.g. a WAV file and we don't want to put anything
      // unexpected into a WAV file, do we?
      BMediaTrack *ptrack = NULL;
      if (found_encoder)
      {
            if (found_raw_encoder)
            {
                  PRINT(("Using raw encoder"));
                  pmci=&rawmci;
            }
            else
            {
                  // don't use mci instead of validmci,
                  // it could be unreliable after the last call to get_next_encoder
                  PRINT(("Using non-raw encoder"));
                  pmci=&validmci;
            }
            
            // Create a BMediaTrack in the file using the selected encoder
            ptrack = file.CreateTrack(&format, pmci);
            if (ptrack)
            {
                  dwLastError = ptrack->InitCheck();
            }
            else
            {
                  dwLastError = B_ERROR; //todo: change error code
            }
      }
      else
      {
            dwLastError=B_ERROR; //todo: change error code
      }

      if (dwLastError!=B_OK)
      {
            STATUS("Encoder not found or file.CreateTrack() error");
            return FALSE; // BMediaFile will destroy ptrack
      }
      
      // We're only creating one track so commit the header now
      if ((dwLastError = file.CommitHeader())!=B_OK)
      {
            STATUS("file.CommitHeader()");
            return FALSE;
      }
      
      // Determine how many frames we have to write
      // There is a small possibility of a divide by zero but this only
      // happens if the object is not properly initialized.
      PINDEX numbytes = GetSize();
      int32 framesize = numChannels * (sampleSize/8);
      int32 numframes = numbytes / framesize; // divide by zero possibility ignored.
            
      if ((dwLastError=ptrack->WriteFrames((const BYTE *)*this, numframes))!=B_OK)
      {
            STATUS("ptrack->WriteFrames()");
            return FALSE; // BMediaFile will destroy ptrack
      }
      
      return (file.CloseFile()==B_OK); // BMediaFile will destroy ptrack
}

BOOL PSound::Play()
{
      PSoundChannelBeOS player(PSoundChannelBeOS::GetDefaultDevice(PSoundChannelBeOS::Player), PSoundChannelBeOS::Player, numChannels, sampleRate, sampleSize);
      
      if (!player.IsOpen())
      {
            PRINT(("PSoundChannelBeOS constructor failed to open"));
            return FALSE;
      }
      
      return player.PlaySound(*this, TRUE);
}

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);
}

00534 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.
*/
00581 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)
{
      // This function is called by the BMediaRecorder object whenever it has a buffer
      // with recorded data ready.
      DETECTVARS(buffer, size/2)
      DETECTSOUND();

      ((CircularBuffer *)cookie)->Fill((const BYTE **)&buffer, &size);
}

////////////////////////////////////////////////////////////////////////////////
// PSoundChannelBeOS

// This defines the number of times we would like to be called per second
// to play/record data
#define PLAYRECFREQ 20 

// Macro to let the default buffer size correspond neatly with the
// setting we put into the format.
#define DEFAULT_BUFSIZE(channels, rate, bits) 480
//((channels*rate*(bits/8))/PLAYRECFREQ)

PSoundChannelBeOS::PSoundChannelBeOS() :
      mRecorder(NULL),
      mPlayer(NULL),
      mBuffer(NULL),
      mNumBuffers(1),
      mResampler(NULL)
{
      PRINT(("default constructor"));

      InternalSetBuffers(DEFAULT_BUFSIZE(1, 8000, 16),DEFAULT_BUFSIZE(1, 8000, 16)/2);
      SetFormat(1, 8000, 16);
      
      // Nothing else to do here. Notice that the channel is not open for
      // playing/recording yet.
}


PSoundChannelBeOS::PSoundChannelBeOS(const PString & dev,
                             Directions dir,
                             unsigned numChannels,
                             unsigned sampleRate,
                             unsigned bitsPerSample) :
      mRecorder(NULL),
      mPlayer(NULL),
      mBuffer(NULL),
      mNumBuffers(1),
      mResampler(NULL)
{
      PRINT(("constructor %s %u %u %u", dir==Player ? "Player" : "Recorder", numChannels, sampleRate, bitsPerSample));
      
      InternalSetBuffers(DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample), DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample)/2);
      Open(dev, dir, numChannels, sampleRate, bitsPerSample);
      // ignore result; user will need to find out whether this succeeds using IsOpen
}


PSoundChannelBeOS::~PSoundChannelBeOS()
{
      PRINT((""));
      
      Close(); // destroys player and recorder
      InternalSetBuffers(0,0); // destroys buffer
}

static const PStringArray GetRecorderDevicesList(BMediaRecorder *Recorder)
{
      // Array to hold the list.
      PStringArray devlist;
    BMediaRecorder* bRecorder = NULL;
    
    if(Recorder != NULL)
      bRecorder = Recorder;

#ifdef MEDIA_KIT_UPDATE
    BMediaRecorder localRecorder("GetRecorderDevicesList");    
      bool result=true;
      status_t status;
      
      {
            if(bRecorder == NULL)
            {
                  bRecorder = &localRecorder;
            }
      
            if (bRecorder == NULL || bRecorder->InitCheck()!=B_OK)
            {
                  PRINT(("Error constructing recorder to fetch device names"));
                  result=false;
            }
      }
      
      if (result)
      {
            media_format format;
            format.type = B_MEDIA_RAW_AUDIO;
            format.u.raw_audio=media_raw_audio_format::wildcard;
            
            // The resampler can only handle 16-bit audio
            format.u.raw_audio.format=media_raw_audio_format::B_AUDIO_SHORT;

            // Let the media recorder determine which sources are available         
            if ((status = bRecorder->FetchSources(format, false))!=B_OK)
            {
                  PRINT(("Couldn't fetch BMediaRecorder sources; status=%d", status));
                  result=false;
            }
      }
      
      if (result)
      {
            // Fetch the names of all output devices
            media_format format;
            BString outname;
            for (int i=0; i< bRecorder->CountSources(); i++)
            {
                  if ((status = bRecorder->GetSourceAt(i, &outname, &format))==B_OK)
                  {
                        PRINT(("Device found: %s", outname.String()));
                        devlist[i] = PString(outname.String());
                  }
                  else
                  {
                        PRINT(("error %d retrieving data for device %d", status, i));
                        result=false;
                  }
            }
      }

      if (!result)
      {
            devlist.RemoveAll();
      }
      
      return devlist;
#else
    // Media Kit is the only device
    devlist[0] = "MediaKit";
      return devlist;
#endif
}

PStringArray PSoundChannelBeOS::GetDeviceNames(Directions dir)
{
      if (dir==Recorder)
      {
            return GetRecorderDevicesList(NULL);
      }
      else
      {
            // not supported yet
            return PStringArray("MediaKit");
      }
}

PString PSoundChannelBeOS::GetDefaultDevice(Directions dir)
{
      if (dir==Recorder)
      {
            const PStringArray &devlist = GetRecorderDevicesList(NULL);
            
            if (devlist.GetSize()!=0)
            {
                  return devlist[0];
            }
            else
            {
                  return PString("MediaKit");
            }
      }
      else
      {
            // not supported yet
            return PString("MediaKit");
      }
}

BOOL PSoundChannelBeOS::OpenPlayer(void)
{
      // We're using cascaded "if result"s here for clarity
      BOOL result = TRUE;

#ifdef FILEDUMP
      media_format format;
      format.type=B_MEDIA_RAW_AUDIO;
      memcpy(&format.u.raw_audio, &mFormat, sizeof(mFormat));
                  
      delete playwriter;
      playwriter=new BAudioFileWriter("play.wav", format, 441000);
#endif                  
      
      // Must have a buffer
      if (!mBuffer)
      {
            result = FALSE;
            PRINT(("Trying to open as player without setting buffers first"));
      }
      
      if (result)       
      {
            // Create the player
            //was: mPlayer=new BSoundPlayer(&mFormat, NULL, PlayBuffer, NULL, mBuffer);
      
            mPlayer = new BSoundPlayer(
                        &mFormat, 
                        NULL,
                        PlayBuffer,
                        NULL,
                        mBuffer);
                        
            if ((mPlayer == NULL) || (mPlayer->InitCheck() != B_OK))
            {
                  result = FALSE;
                  PRINT(("Couldn't construct player"));
            }
      }
      
      if (result)
      {
            // Start the player
            if (mPlayer->Start() != B_OK)
            {
                  result = FALSE;
                  PRINT(("Couldn't start the player"));
            }
      }
      
      if (result)
      {
            // Enable the fetching of data by PlayBuffer
            mPlayer->SetHasData(true);
      }

      PRINT(("Returning %s", result?"success":"failure"));
      return result;
}

BOOL PSoundChannelBeOS::OpenRecorder(const PString &dev)
{
      // We're using cascaded "if result"s here for clarity
      BOOL result=TRUE;

      {
            if (!mBuffer)
            {
                  result=FALSE;
                  PRINT(("Trying to open as recorder without setting buffers first"));
            }
      }
      
      if (result)             
      {
            // Create the recorder
            mRecorder=new BMediaRecorder("PWLIB PSoundChannel recorder");
      
            if ((mRecorder==NULL) || (mRecorder->InitCheck()!=B_OK))
            {
                  result=FALSE;
                  PRINT(("Couldn't construct recorder"));
            }
      }

#ifdef MEDIA_KIT_UPDATE
      int32 sourceindex;
      if (result)
      {
            // Find the specified device in the list of input devices
            PINDEX x=GetRecorderDevicesList(mRecorder).GetStringsIndex(dev);
            if (x==P_MAX_INDEX)
            {
                  result=FALSE;
                  PRINT(("Couldn't find device %s in the list",(const char *)dev));
            }
            else
            {
                  sourceindex=(int32)x;
            }
      }
      
#ifdef _DEBUG     
      if (result)
      {
            // Get information for the device
            BString outname;
            media_format xformat;
            status_t err;

            if ((err=mRecorder->GetSourceAt(sourceindex, &outname, &xformat))==B_OK)
            {
                  PRINT(("%s", outname.String()));
                  PRINT(("    type %d", (int)xformat.type));
                  PRINT(("    AudioFormat 0x%X", (int)xformat.AudioFormat()));
                  PRINT(("    u.raw_audio:"));
                  PRINT(("        frame_rate: %f", xformat.u.raw_audio.frame_rate));
                  PRINT(("        channel_count: %d", xformat.u.raw_audio.channel_count));
                  PRINT(("        byte_order: %d", xformat.u.raw_audio.byte_order));
                  PRINT(("        buffer_size: %d", xformat.u.raw_audio.buffer_size));
            }
            else
            {
                  result=FALSE;
                  PRINT(("couldn't get details for source %d: err=0x%X",sourceindex,err));
            }
      }
#endif

      if (result)
      {
            // Try to connect to the source
            if (mRecorder->ConnectSourceAt(sourceindex)!=B_OK)
            {
                  result=FALSE;
                  PRINT(("Couldn't connect BMediaRecorder to source"));
            }
      }

#else
      if (result)
      {
            // Connect the recorder to the default input device
            media_format format;
            format.type=B_MEDIA_RAW_AUDIO;
            format.u.raw_audio=media_raw_audio_format::wildcard;
            // The resampler can only handle 16-bit audio
            format.u.raw_audio.format=media_raw_audio_format::B_AUDIO_SHORT;
            if (mRecorder->Connect(format,0)!=B_OK)
            {
                  result=FALSE;
                  PRINT(("couldn't connect the recorder to the default source"));
            }
      }
#endif

      if (result)
      {
            // Create resampler
            media_format format=mRecorder->Format();

            delete mResampler;
            mResampler=new Resampler(
                  format.u.raw_audio.frame_rate,
                  mFormat.frame_rate,
                  format.u.raw_audio.channel_count,
                  mFormat.channel_count,
                  0,
                  2);

#ifdef FILEDUMP
            {
                  media_format format;
                  format.type=B_MEDIA_RAW_AUDIO;
                  memcpy(&format.u.raw_audio, &mFormat, sizeof(mFormat));
                  
                  delete recwriter;
                  recwriter=new BAudioFileWriter("record.wav", format);
            }
#endif

            // If the current buffer is not a resamplin buffer, re-create it 
            ResamplingBuffer *buf=dynamic_cast<ResamplingBuffer*>(mBuffer);
            
            if (buf==NULL)
            {
                  PRINT(("re-creating buffer"));
                  
                  CircularBuffer *old=mBuffer;
                  mBuffer=new ResamplingBuffer(mResampler, old);
                  delete old;
            }
            else
            {
                  buf->SetResampler(mResampler);
            }
      }
      
      if (result)
      {
            // Set the hook function to our data processing function
            PRINT(("Setting buffer hook, cookie=%p",mBuffer));
            if (mRecorder->SetBufferHook(RecordBuffer, mBuffer)!=B_OK)
            {
                  result=FALSE;
                  PRINT(("Couldn't set buffer hook on BMediaRecorder"));
            }
      }

      // If something went wrong, delete the recorder.
      if (!result)
      {
            if (mRecorder)
            {
                  delete mRecorder;
                  mRecorder=NULL;
            }
      }
      
      return result;          
}

BOOL PSoundChannelBeOS::Open(const PString & dev,
                         Directions dir,
                         unsigned numChannels,
                         unsigned sampleRate,
                         unsigned bitsPerSample)
{
      // We're using cascaded "if result"s here for clarity
      BOOL result = TRUE;
      PRINT(("%s %u %u %u", dir==Player?"Player":"Recorder", numChannels, sampleRate, bitsPerSample));
      
      // Close the channel first, just in case  
        Close();

      // Initialize the format struct, necessary to create player or recorder 
      if (!SetFormat(numChannels, sampleRate, bitsPerSample))
      {
        result = FALSE;
        PRINT(("Couldn't set format"));
      }

      if (result)
      {
            switch (dir)
            {
            case Player:
                    PRINT(("... trying to open player"));
                  result=OpenPlayer();
                  break;
                  
            case Recorder:
                        PRINT(("...trying to open recorder"));
                  result=OpenRecorder(dev);
                  break;

            default:
                  PRINT(("Unknown direction parameter"));
                  result=FALSE;
            }
      }

      if (!result)
      {
            // If anything went wrong, clean up
            PRINT(("... can't open, cleaning up"));
                Close();
      }

        ::snooze(1*1000*1000);
      
        PRINT(("Returning %s", result?"success":"failure"));
      return result;
}

BOOL PSoundChannelBeOS::Abort()
{
      return FALSE;
}


BOOL PSoundChannelBeOS::SetFormat(unsigned numChannels,
                              unsigned sampleRate,
                              unsigned bitsPerSample)
{
      PRINT(("%u %u %u", numChannels, sampleRate, bitsPerSample));
      
      // NOTE: all constructors should call this to initialize
      // the local members
      // Do NOT call the function with any parameter set to 0!
      
      // The function only fails if the channel is open.
      // This is because the player or recorder needs to be re-created when the
      // format changes.
      if (IsOpen())
      {
            PRINT(("Not allowed to set format on open channel"));
            return FALSE;
      }
      
      // Initialize the format struct
      // The numbers of bits that we support here are 8, 16 or 32 bits (signed),
      // results for other sizes are not defined.
      mFormat = media_raw_audio_format::wildcard;
      mFormat.frame_rate=(float)sampleRate;
      mFormat.channel_count=numChannels;
      mFormat.format=(bitsPerSample / 8) & 0xF; 
      mFormat.byte_order=B_HOST_IS_BENDIAN ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
      mFormat.buffer_size=DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample);

      return TRUE;
}


unsigned PSoundChannelBeOS::GetChannels() const
{
      return mFormat.channel_count;
}


unsigned PSoundChannelBeOS::GetSampleRate() const
{
      return (unsigned)mFormat.frame_rate;
}


unsigned PSoundChannelBeOS::GetSampleSize() const
{
      return (mFormat.format & 0xF)*8; // return number of BITS
}


BOOL PSoundChannelBeOS::Read(void *buf, PINDEX len)
{
    PINDEX bufSize = len;

      // Can only read from a recorder
      if (mRecorder!=NULL)
      {
            // A Read starts the recording, if it's not running already
            if (!mRecorder->IsRunning())
            {
                  mRecorder->Start();
            }

            // Wait until there's a buffer recorded
            mBuffer->WaitForState(CircularBuffer::NotEmpty);

#ifdef FILEDUMP
            void *dumpbuf=buf;
            size_t dumpsize=len;
#endif
        lastReadCount = 0;

            while(lastReadCount < bufSize)
            {
          len = bufSize - lastReadCount; 
          if(len <= 0)
            break;

              // Get data from the buffer
              mBuffer->Drain((BYTE**)&buf, (size_t*) &len);

              lastReadCount += len;
            }
            
#ifdef FILEDUMP
            if (recwriter)
            {
                  recwriter->writewavfile(dumpbuf, dumpsize);
            }
#endif

            return TRUE;
      }
      return FALSE;
}

BOOL PSoundChannelBeOS::Write(const void *buf, PINDEX len)
{
      // can only write to a player
      if (mPlayer!=NULL)
      {
            // Wait until there is space
            mBuffer->WaitForState(CircularBuffer::EmptyEnough);

            // This function needs to update the last write count
            // Store len before it gets modified
            lastWriteCount=len;

#ifdef FILEDUMP
            if (playwriter)
            {
                  playwriter->writewavfile(buf,len);
            }
#endif
      
            // Store data into the buffer
            mBuffer->Fill((const BYTE **)&buf, (size_t*) &len);

            // Update last write count
            lastWriteCount-=len;
            
            return TRUE;
      }
      
      return FALSE;
}


BOOL PSoundChannelBeOS::Close()
{
      PRINT((""));
      
      // Flush the buffer first
      Abort();

      // Stop the player
      if ((mPlayer!=NULL) && (mPlayer->InitCheck()==B_OK))
      {
            mPlayer->Stop();

#ifdef FILEDUMP
            delete playwriter;
            playwriter=NULL;
#endif            
      }

        if(mPlayer)
        {
            // Destroy the player
              delete mPlayer;
            mPlayer=NULL; // make sure that another Close won't crash the system
        }

      // Stop the recorder
      if ((mRecorder!=NULL) && (mRecorder->InitCheck()==B_OK))
      {
            mRecorder->Stop(); // Not really necessary
            mRecorder->Disconnect();
            
#ifdef FILEDUMP
            delete recwriter;
            recwriter=NULL;
#endif
      }
      
     if(mRecorder)
     {
         // Destroy the recorder
         delete mRecorder;
         mRecorder=NULL; // make sure that another Close won't crash the system
       }

      return TRUE;
}


BOOL PSoundChannelBeOS::SetBuffers(PINDEX size, PINDEX count)
{
      return InternalSetBuffers(size*(mNumBuffers=count),size);
}


BOOL PSoundChannelBeOS::InternalSetBuffers(PINDEX size, PINDEX threshold)
{
  if (mPlayer)
  {
    mPlayer->SetHasData(false);
  }
  else if (mRecorder)
  {
    mRecorder->Stop();
  }

  // Delete the current buffer
  if(mBuffer != NULL)
  {
    delete mBuffer;
    mBuffer = NULL;
  }
      
  // Create the new buffer
  if (size != 0)
  {
    if (mRecorder)
      {
      if (!mResampler)
      {
        PTRACE(TL, "Creating default resampler");     
        mResampler = new Resampler(1.0,1.0,1,1,0,1);
      }

       PTRACE(TL, "Creating resampling buffer, size " << size);
       mBuffer = new ResamplingBuffer(mResampler, size, threshold, threshold);
       
       // In case we use resampler, size must be set to resampled buffer size
        
    }
    else 
      {
      PTRACE(TL, "Creating playback buffer, size " << size);
      mBuffer = new CircularBuffer(size, threshold, threshold);
    }

    // If we have a player, set the cookie again and restart it
    if (mPlayer)
    {
      mPlayer->SetCookie(mBuffer);
      PTRACE(TL, "Tried to set player buffer cookie");
      mPlayer->SetHasData(true);
      PTRACE(TL, "Tried to set player has data");
    }
            
    // If we have a recorder, set the cookie again
    // Note that the recorder is not restarted, even if it was running.
    // It's not a good idea for the program to change the buffers during
    // recording anyway because it would at least lose some data.
    if (mRecorder)
    {
      if(B_OK != mRecorder->SetBufferHook(RecordBuffer, mBuffer))
      PTRACE(TL, "Can't set recorder buffer hook");
    }
            
    return TRUE;
  }

  if (IsOpen())
  {
    PTRACE(TL, "Can't continue without buffers - closing channel");
    Close(); // should give errors on subsequent read/writes
  }

  mBuffer = NULL;

  return FALSE;
}


BOOL PSoundChannelBeOS::GetBuffers(PINDEX &size, PINDEX &count)
{
      if (mBuffer)
      {
            size=mBuffer->GetSize();
            count=mNumBuffers;
            return TRUE;
      }
      
      return FALSE;
}


BOOL PSoundChannelBeOS::PlaySound(const PSound &sound, BOOL wait)
{
      PRINT(("wait=%s", wait?"true":"false"));
      
      if (mPlayer==NULL)
      {
            PRINT(("Playing a sound on a closed (or recording) PSoundChannelBeOS"));
            return FALSE;
      }

#ifdef FILEDUMP
      playwriter->writewavfile((void *)(const BYTE*)sound, sound.GetSize());
#endif

      // create a local buffer that references the PSound
      // NOTE: no conversion between the PSound's format and the
      // PSoundChannelBeOS's format is done.
      const BYTE *buf=(const BYTE *)sound;
      PINDEX size=sound.GetSize();
      
      // Play the sound by doing successive Writes until the sound is done.
      // Note that write will return when either the buffer is full or the
      // given data is written. We want to return after the entire sound
      // has been buffered. So we repeatedly call Write until there is
      // no data left
      while (size!=0)
      {
            // Wait until there is space
            mBuffer->WaitForState(CircularBuffer::EmptyEnough);
            
            // Write the data
            mBuffer->Fill(&buf, (size_t*) &size);
      }
      
      // Wait until the sound is finished, if requested
      if (wait)
      {
            PRINT(("Waiting for sound"));
            mBuffer->WaitForState(CircularBuffer::Empty);
      }

      return TRUE;
}


BOOL PSoundChannelBeOS::PlayFile(const PFilePath &file, BOOL wait)
{
      entry_ref               ref;
      status_t                err;

      // 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);
}


BOOL PSoundChannelBeOS::HasPlayCompleted()
{
      if (mPlayer!=NULL)
      {
            return mBuffer->IsEmpty();
      }
      
      return FALSE;
}


BOOL PSoundChannelBeOS::WaitForPlayCompletion()
{
      if (mPlayer!=NULL)
      {
            mBuffer->WaitForState(CircularBuffer::Empty);
      }

      return TRUE;
}


BOOL PSoundChannelBeOS::RecordSound(PSound &sound)
{
      PRINT((""));
      
      if (mRecorder==NULL) 
      {
            PRINT(("Recording a sound on a closed (or playing) PSoundChannelBeOS"));
            return FALSE;
      }

      // Flush the buffer first
      Abort();
      
      // Start recording
      if (mRecorder->Start()!=B_OK)
      {
            PRINT(("BMediaRecorder::Start() returned error"));
            return FALSE;
      }
      
      // Wait until buffer is filled
      mBuffer->WaitForState(CircularBuffer::Full);
      PRINT(("Buffer full: size=%lu",mBuffer->GetSize()));

      // Stop the recorder
      if (mRecorder->Stop()!=B_OK)
      {
            PRINT(("Uh-oh, recorder is unstoppable!"));
            //return FALSE;
      }
      
      // Set the sound's format to ours
      sound.SetFormat(GetChannels(), GetSampleRate(), GetSampleSize());
      
      // Resize the sound and set up local buffer references
      PINDEX size=mBuffer->GetSize();
      BYTE *buf=sound.GetPointer(size);

#ifdef FILEDUMP
      void *dumpbuf=buf;
      size_t dumpsize=size;
#endif

      // Read the data        
      mBuffer->Drain(&buf, (size_t*) &size);

#ifdef FILEDUMP
      recwriter->writewavfile(dumpbuf, dumpsize);
#endif

      PRINT(("Recording succesful"));
      return TRUE;
}


BOOL PSoundChannelBeOS::RecordFile(const PFilePath & filename)
{
      // Not implemented for now
      return FALSE;
}


BOOL PSoundChannelBeOS::StartRecording()
{
      if (mRecorder==NULL) 
      {
            PRINT(("Recording to a closed (or playing) PSoundChannelBeOS"));
            return FALSE;
      }
      
      // Flush the buffers
      Abort();
      
      // Start recording      
      if (mRecorder->Start()!=B_OK)
      {
            PRINT(("BMediaRecorder::Start returned error"));
            return FALSE;
      }
      
      return TRUE;
}


BOOL PSoundChannelBeOS::IsRecordBufferFull()
{
      if (mRecorder)
      {
            return !mBuffer->IsEmpty();
      }
      
      return FALSE;
}


BOOL PSoundChannelBeOS::AreAllRecordBuffersFull()
{
      if (mRecorder)
      {
            return mBuffer->IsFull();
      }

      return FALSE;
}


BOOL PSoundChannelBeOS::WaitForRecordBufferFull()
{
      if (mRecorder==NULL)
      {
            PRINT(("Waiting for record buffer on playing or closed PSoundChannelBeOS"));
            return FALSE;
      }
      
      mBuffer->WaitForState(CircularBuffer::FullEnough);
            
      return PXSetIOBlock(PXReadBlock, readTimeout);
}


BOOL PSoundChannelBeOS::WaitForAllRecordBuffersFull()
{
      if (mRecorder==NULL)
      {
            PRINT(("Waiting for record buffers on playing or closed PSoundChannelBeOS"));
            return FALSE;
      }
      
      mBuffer->WaitForState(CircularBuffer::Full);

      return TRUE;
}


BOOL PSoundChannelBeOS::IsOpen() const
{
      BOOL result=((mPlayer!=NULL) || (mRecorder!=NULL));
      PRINT(("returning %s, player 0x%X recorder 0x%X", result?"true":"false", mPlayer, mRecorder));
      return result;
}



BOOL PSoundChannelBeOS::SetVolume(unsigned newVolume)
{
  #ifdef TODO
  cerr << __FILE__<< "PSoundChannelBeOS :: SetVolume called in error. Please fix" << endl;
  #endif

  return TRUE;
}

BOOL  PSoundChannelBeOS::GetVolume(unsigned & volume)
{
  #ifdef TODO
  cerr << __FILE__<< "PSoundChannelBeOS :: GetVolume called in error. Please fix" << endl;
  #endif

  return TRUE;

}

// End of file

Generated by  Doxygen 1.6.0   Back to index