Fixed DevStudio 2003 build with memory check code.
[pwlib.git] / src / ptlib / unix / beaudio.cxx
blobda20fb29feb0ba296e51d26ea5cc1863e177f688
1 /*
2 * beaudio.cxx
4 * Sound driver implementation.
6 * Portable Windows Library
8 * Copyright (c) 1993-2001 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s):
28 * Yuri Kiryanov, ykiryanov at users.sourceforge.net,
29 * Jac Goudsmit <jac@be.com>.
31 * $Log$
32 * Revision 1.16 2004/10/26 18:08:54 ykiryanov
33 * Added code for old Media Kit, to be backwards compatible with R5, and Zeta ifdef
35 * Revision 1.15 2004/06/16 01:55:10 ykiryanov
36 * Added usage of lastReadCount - sound capture now works
38 * Revision 1.14 2004/05/30 04:48:45 ykiryanov
39 * Stable version
41 * Revision 1.12 2004/05/14 05:26:57 ykiryanov
42 * Fixed dynamic cast bug
44 * Revision 1.11 2004/04/18 00:32:26 ykiryanov
45 * Fized compiler choking on <dynamic_cast>.
47 * Revision 1.10 2004/04/02 03:29:07 ykiryanov
48 * New improved code
50 * Revision 1.9 2002/02/09 00:52:01 robertj
51 * Slight adjustment to API and documentation for volume functions.
53 * Revision 1.8 2002/02/07 20:57:21 dereks
54 * add SetVolume and GetVolume methods to PSoundChannelBeOS
56 * Revision 1.7 2001/07/09 06:16:15 yurik
57 * Jac Goudsmit's BeOS changes of July,6th. Cleaning up media subsystem etc.
59 * Revision 1.6 2000/12/16 13:08:56 rogerh
60 * BeOS changes from Yuri Kiryanov <openh323@kiryanov.com>
62 * Revision 1.5 2000/04/19 00:13:52 robertj
63 * BeOS port changes.
65 * Revision 1.4 1999/09/21 00:56:29 robertj
66 * Added more sound support for BeOS (thanks again Yuri!)
68 * Revision 1.3 1999/06/28 09:28:02 robertj
69 * Portability issues, especially n BeOS (thanks Yuri!)
71 * Revision 1.2 1999/03/05 07:03:27 robertj
72 * Some more BeOS port changes.
74 * Revision 1.1 1999/03/02 05:41:59 robertj
75 * More BeOS changes
79 #include <ptlib.h>
80 #include <ptlib/unix/ptlib/beaudio.h>
82 PCREATE_SOUND_PLUGIN(BeOS, PSoundChannelBeOS);
84 /////////////// Debugging stuff ///////////////
85 #define TL (7)
87 #define PRINT(x) //do { printf(__FILE__ ":%d %s ", __LINE__, __FUNCTION__); printf x; printf("\n"); } while(0)
89 #define STATUS(x) // PRINT((x "=%ld", (long)dwLastError))
91 #define PRINTCB(x) // PRINT(x)
93 //#define SOUNDDETECT 1 define this for printed output of first pb/rec audio
95 //#define FILEDUMP 1 define this for dumping audio to wav file
97 // Macros and global vars for debugging
99 #ifdef SOUNDDETECT
100 #define DETECTVARS(buffer,numframes) short *detbuf=(short*)buffer; size_t detframes=numframes;
101 #define DETECTSOUND() \
102 do { \
103 static bool silence=true; \
104 if (silence) \
106 for (size_t i=0; i<detframes; i++) \
108 if (detbuf[i]>=255) \
110 PRINT(("SOUND DETECTED at %p",detbuf)); \
111 for (size_t j=0; j<detframes && j<30; j++) \
113 char *x; \
114 asprintf(&x,"%%%ds\n",(detbuf[j]>>10)+32); \
115 printf(x,"."); \
116 free(x); \
118 silence=false; \
119 break; \
123 } while(0)
124 #else
125 #define DETECTVARS(buffer,numframes)
126 #define DETECTSOUND()
127 #endif
129 #ifdef FILEDUMP
130 #include "beaudio/AudioFileWriter.h"
131 BAudioFileWriter *playwriter=NULL;
132 BAudioFileWriter *recwriter=NULL;
133 #endif
135 ////////////////////////////////////////////////////////////////////////////////
136 // PSound
138 PSound::PSound(unsigned channels,
139 unsigned samplesPerSecond,
140 unsigned bitsPerSample,
141 PINDEX bufferSize,
142 const BYTE * buffer)
144 encoding = 0;
145 SetFormat(channels, samplesPerSecond, bitsPerSample);
147 if (buffer != NULL)
149 memcpy(GetPointer(bufferSize), buffer, bufferSize);
154 PSound::PSound(const PFilePath & filename)
156 encoding = 0;
158 // Set the default format
159 SetFormat(1, 8000, 16);
161 // The format is changed if the file is succesfully loaded.
162 Load(filename);
166 PSound & PSound::operator=(const PBYTEArray & data)
168 PBYTEArray::operator=(data);
169 return *this;
173 void PSound::SetFormat(unsigned channels,
174 unsigned samplesPerSecond,
175 unsigned bitsPerSample)
177 // NOTE: all constructors should call this to initialize
178 // the local members, especially formatInfo.
179 // Do NOT call the function with any parameter set to 0!
180 sampleSize=bitsPerSample;
181 sampleRate=samplesPerSecond;
182 numChannels = channels;
184 // We don't use the encoding member (although we could probably set it to 0=PCM)
185 // Let the application know it shouldn't assume anything
186 encoding = 1;
188 // The formatInfo member to us is a media_format structure.
189 BOOL setsize_formatInfo=formatInfo.SetSize(sizeof(media_format));
190 PAssert(setsize_formatInfo, "Unable to set size for sound info array");
192 // Initialize the media_format struct
193 // The numbers of bits that we support here are 8, 16 or 32 bits (signed),
194 // results for other sizes are not defined.
195 media_format &format=*(media_format*)(const BYTE *)formatInfo;
197 format.type = B_MEDIA_RAW_AUDIO;
198 format.u.raw_audio = media_raw_audio_format::wildcard;
199 format.u.raw_audio.frame_rate=(float)sampleRate;
200 format.u.raw_audio.channel_count=numChannels;
201 format.u.raw_audio.format=(sampleSize / 8) & 0xF;
202 format.u.raw_audio.byte_order=B_MEDIA_HOST_ENDIAN;
203 format.u.raw_audio.buffer_size=(channels * samplesPerSecond * (bitsPerSample/8))/10; // 1/10 sec buffer
206 BOOL PSound::Load(const PFilePath & filename)
208 // format is a reference to the formatInfo member which stores info
209 // about the media format. This is needed for writing the data back
210 // or for playing the sound.
211 media_format &format=*(media_format *)(const BYTE *)formatInfo;
213 // Create BEntry from file name
214 BEntry entry(filename, true);
215 if ((dwLastError=entry.InitCheck())!=B_OK)
217 STATUS("entry.InitCheck()");
218 return FALSE;
221 // Create entry_ref from BEntry
222 entry_ref ref;
223 if ((dwLastError=entry.GetRef(&ref))!=B_OK)
225 STATUS("entry.GetRef()");
226 return FALSE;
229 // Create BMediaFile for read access from the entry_ref
230 BMediaFile file(&ref);
231 if ((dwLastError=file.InitCheck())!=B_OK)
233 STATUS("file.InitCheck()");
234 return FALSE;
237 // Search for the first media track that can be decoded
238 BMediaTrack *ptrack = NULL;
239 for (int index=0; (index<file.CountTracks()) && (dwLastError==B_OK); index++)
241 ptrack = file.TrackAt(index);
242 if (ptrack)
244 dwLastError = ptrack->InitCheck();
246 else
248 dwLastError = B_ERROR; //todo: change error code
251 if (dwLastError==B_OK)
253 // Get media format; we're looking for a raw audio track.
254 format.type = B_MEDIA_RAW_AUDIO;
255 format.u.raw_audio = media_raw_audio_format::wildcard;
256 dwLastError = ptrack->DecodedFormat(&format);
258 if ((dwLastError==B_OK) && (format.type==B_MEDIA_RAW_AUDIO))
260 break; // found a decodable track
263 else
265 STATUS("TrackAt() failed, error");
268 // if we found a track and arrived at this point, the track we found
269 // was not decodable
270 if (ptrack)
272 dwLastError=file.ReleaseTrack(ptrack); // destroys ptrack
276 // if an error occurred during track scanning, leave now
277 if (dwLastError!=B_OK)
279 return FALSE;
282 // Get a reference to the raw output format
283 media_raw_audio_format &rawformat = format.u.raw_audio;
285 // Fill in our fields from the format
286 sampleSize = (rawformat.format & 0xF) * 8;
287 numChannels = rawformat.channel_count;
288 if (rawformat.frame_rate>0.0 && rawformat.frame_rate<=(float)0xFFFFFFFFU)
290 sampleRate = (unsigned)(rawformat.frame_rate);
292 else
294 // unknown or unrepresentable sample rate.
295 // It's not really documented what we should do in this case but
296 // it probably doesn't matter either...
297 sampleRate = 0;
300 // Get the number of frames for the track and determine how much
301 // memory we need to store the file's data
302 // The multiplication might overflow for huge files but we don't
303 // want to read them into memory anyway so I guess it's ok...
304 int64 numframes = ptrack->CountFrames();
305 int64 framesize = numChannels * (sampleSize/8);
306 int64 numbytes = numframes * framesize;
308 // Set the size of the object's data area
309 if (!SetSize(numbytes))
311 PRINT(("Can't set size of sound to %Ld", numbytes));
312 dwLastError = B_ERROR; //todo replace by better error code
313 return FALSE; // BMediaFile will destroy ptrack
316 // Read all frames into memory. NOTE: not thread safe!
317 BYTE* dest = GetPointer(); // destination pointer
318 int64 framecount = numframes; // number of frames left to read
319 int64 framesread; // number of actual frames done
320 while ((framecount!=0) && (dwLastError==B_OK))
322 framesread = framecount;
323 dwLastError = ptrack->ReadFrames(dest, &framesread);
324 dest += framesread * framesize;
325 framecount -= framesread;
328 // return true for success
329 return (dwLastError==B_OK); // BMediaFile will destroy ptrack
333 BOOL PSound::Save(const PFilePath & filename)
335 // format is a reference to the formatInfo member which stores info
336 // about the media format. This is needed for writing the data back
337 // or for playing the sound.
338 media_format &format=*(media_format *)(const BYTE *)formatInfo;
340 // Get the file type from the file name's extension; if none, use wav
341 PFilePathString filetype=filename.GetType(); // e.g. ".wav"
342 if (filetype=="")
344 filetype="wav";
346 else
348 filetype=filetype.Mid(1); // cut off the '.'
351 // Try to find the file format in BeOS's list of formats
352 media_file_format mfi;
353 int32 cookie=0;
354 while ((dwLastError=get_next_file_format(&cookie, &mfi))==B_OK)
356 if (!strcasecmp(mfi.file_extension, (const char *)filetype))
358 break;
361 if (dwLastError!=B_OK)
363 // didn't find file format
364 PRINT(("Couldn't find media_file_format for \"%s\"", (const char *)filetype));
365 return FALSE;
368 // Create BEntry from file name
369 BEntry entry(filename, true);
370 if ((dwLastError=entry.InitCheck())!=B_OK)
372 STATUS("entry.InitCheck()");
373 return FALSE;
376 // Create entry_ref from BEntry
377 entry_ref ref;
378 if ((dwLastError=entry.GetRef(&ref))!=B_OK)
380 STATUS("entry.GetRef()");
381 return FALSE;
384 // Create BMediaFile for write access from the entry_ref
385 BMediaFile file(&ref, &mfi, B_MEDIA_FILE_REPLACE_MODE);
386 if ((dwLastError=file.InitCheck())!=B_OK)
388 STATUS("file.InitCheck()");
389 return FALSE;
392 // Find an encoder. The input format is the format we have stored in
393 // our formatInfo member.
394 cookie=0;
395 media_format outformat;
396 media_codec_info mci,validmci,rawmci, *pmci;
397 bool found_encoder = false;
398 bool found_raw_encoder = false;
399 while (get_next_encoder(&cookie, &mfi, &format, &outformat, &mci)==B_OK)
401 found_encoder=true;
403 if (outformat.type==B_MEDIA_RAW_AUDIO)
405 rawmci=mci;
406 found_raw_encoder=true;
408 else
410 validmci=mci;
414 // Choose an encoder:
415 // If a raw-output encoder was found, use it.
416 // Else, use the last found encoded-output encoder, if any.
417 // This method of choosing will make sure that most file formats
418 // will get the most common encoding (PCM) whereas it's still possible
419 // to choose another output format like MP3, if so dictated by the
420 // file format.
421 // BeOS is smart enough not to return an encoder that produces raw audio
422 // for e.g. the MP3 file format, but it knows that there are many ways
423 // to encode e.g. a WAV file and we don't want to put anything
424 // unexpected into a WAV file, do we?
425 BMediaTrack *ptrack = NULL;
426 if (found_encoder)
428 if (found_raw_encoder)
430 PRINT(("Using raw encoder"));
431 pmci=&rawmci;
433 else
435 // don't use mci instead of validmci,
436 // it could be unreliable after the last call to get_next_encoder
437 PRINT(("Using non-raw encoder"));
438 pmci=&validmci;
441 // Create a BMediaTrack in the file using the selected encoder
442 ptrack = file.CreateTrack(&format, pmci);
443 if (ptrack)
445 dwLastError = ptrack->InitCheck();
447 else
449 dwLastError = B_ERROR; //todo: change error code
452 else
454 dwLastError=B_ERROR; //todo: change error code
457 if (dwLastError!=B_OK)
459 STATUS("Encoder not found or file.CreateTrack() error");
460 return FALSE; // BMediaFile will destroy ptrack
463 // We're only creating one track so commit the header now
464 if ((dwLastError = file.CommitHeader())!=B_OK)
466 STATUS("file.CommitHeader()");
467 return FALSE;
470 // Determine how many frames we have to write
471 // There is a small possibility of a divide by zero but this only
472 // happens if the object is not properly initialized.
473 PINDEX numbytes = GetSize();
474 int32 framesize = numChannels * (sampleSize/8);
475 int32 numframes = numbytes / framesize; // divide by zero possibility ignored.
477 if ((dwLastError=ptrack->WriteFrames((const BYTE *)*this, numframes))!=B_OK)
479 STATUS("ptrack->WriteFrames()");
480 return FALSE; // BMediaFile will destroy ptrack
483 return (file.CloseFile()==B_OK); // BMediaFile will destroy ptrack
486 BOOL PSound::Play()
488 PSoundChannelBeOS player(PSoundChannelBeOS::GetDefaultDevice(PSoundChannelBeOS::Player), PSoundChannelBeOS::Player, numChannels, sampleRate, sampleSize);
490 if (!player.IsOpen())
492 PRINT(("PSoundChannelBeOS constructor failed to open"));
493 return FALSE;
496 return player.PlaySound(*this, TRUE);
499 BOOL PSound::PlayFile(const PFilePath & file, BOOL wait)
501 entry_ref ref;
502 status_t err; // can't use dwLastError because this function is static
504 // using pointers for these objects so that we don't have to
505 // construct them here but can nevertheless use the if(ok)'s
506 BEntry *pentry = NULL;
509 // Create BEntry from file name
510 pentry = new BEntry(file, true);
511 err = pentry->InitCheck();
514 if (err==B_OK)
516 // Create entry_ref from BEntry
517 err = pentry->GetRef(&ref);
520 if (err==B_OK)
522 // Play the sound. Return value is a handle or a negative value for errors
523 // Errors in BeOS are always negative values
524 err=play_sound(&ref, true, !wait, wait);
525 if (err>=0)
527 err=B_OK;
531 return (err==B_OK);
534 void PSound::Beep()
536 ::beep();
539 ////////////////////////////////////////////////////////////////////////////////
540 // CircularBuffer
542 class Guard
544 private:
545 sem_id mSem;
546 public:
547 Guard(sem_id sem) { acquire_sem(mSem=sem); }
548 ~Guard() { release_sem(mSem); }
552 This class represents a circular FIFO buffer.
553 The buffer has a head and a tail that chase each other.
554 The data is added to the buffer at the tail side by using Fill.
555 The data from the buffer can be read starting at the head side using
556 Drain.
557 It is possible to use two threads to fill and drain the buffer but
558 there should not be more than 2 threads doing draining and filling.
559 Resetting (flushing) or destroying from a third thread is allowed;
560 do make sure that any threads that operate on buffer data are stopped
561 before destroying a buffer.
562 Normally, filling and draining operations block the thread as short as
563 possible (i.e. only when the other thread needs to update the head and
564 tail pointers etc). If the filling thread tries to put data into a full
565 or almost full buffer, it just returns after filling as much data as
566 it can, and if the draining thread tries to get more data out than is
567 in the buffer, it will simply return with the data that is there.
568 In order to move all the data from an external buffer into an object
569 of this class, the caller would have to call Fill repeatedly until
570 all the data has been processed (similarly it would have to call Drain
571 until it receives sufficient data). But if the application has nothing
572 else to do in the mean time, this constitutes a Busy Waiting loop
573 on either the filling or draining side of the FIFO buffer that slurps
574 up as much CPU time as possible.
575 To improve this behaviour, it's possible to specify a threshold value
576 that is used to change the state to FullEnough and EmptyEnough. By using
577 these states (instead of Full and Empty), one thread can block until the
578 other thread has determined that there is enough data or enough room for
579 data.
581 class CircularBuffer
583 public:
584 // Internal state for the buffer
585 // Note the nifty bit patterns for comparing the current state
586 // with a desired state
587 typedef enum
588 { // Headspace Tailspace
589 Empty =1, // 0 size
590 Filled =2, // 0<h<size 0<t<size
591 NotFull =3, // 0<=h<size 0<t<=size (for comparing)
592 Full =4, // size 0
593 NotEmpty =6, // 0<h<=size 0<=t<size (for comparing)
594 Flushed =8, // (extra signal to threads waiting on full)
595 FullEnough =16, // h>=drainthreshold
596 EmptyEnough =32, // t>=fillthreshold
597 } State;
599 protected:
600 friend class ResamplingBuffer; // needed for one of their constructors
602 BYTE *mBuffer; // the buffer
603 PINDEX mSize; // size of the buffer in bytes
605 volatile PINDEX mHead; // index where to start reading
606 volatile PINDEX mTail; // index where to start writing
607 volatile PINDEX mHeadRoom; // consecutive space from head to end-of-buffer or tail
608 volatile PINDEX mTailRoom; // consecutive space from tail to end-of-buffer or head
609 volatile PINDEX mSizeUsed; // total bytes in use
610 volatile PINDEX mFillThreshold; // see above
611 volatile PINDEX mDrainThreshold; // see above
613 volatile State mState; // current state of the buffer
615 sem_id mSemInUse; // used to guard data integrity
616 sem_id mSemStateChange; // used to wait for state changes
618 protected:
619 // Check if the state changed. Private because it's not guarded by semaphore
620 void UpdateState(void)
622 // Determine current state
623 State newstate;
625 if (mSizeUsed==mSize)
627 PRINTCB(("State is FULL"));
628 newstate=Full;
630 else if (mSizeUsed==0)
632 PRINTCB(("State is EMPTY"));
633 newstate=Empty;
635 else
637 PRINTCB(("State is FILLED"));
638 newstate=Filled;
641 // Check thresholds
642 if (mSize-mSizeUsed>=mFillThreshold)
644 PRINTCB(("...and EMPTYENOUGH"));
645 newstate=(State)(newstate | EmptyEnough);
647 if (mSizeUsed>=mDrainThreshold)
649 PRINTCB(("...and FULLENOUGH"));
650 newstate=(State)(newstate | FullEnough);
653 // Check if the state changed
654 if (newstate!=mState)
656 PRINTCB(("Updating state from %X to %X", mState, newstate));
658 // Set the new state
659 mState=newstate;
661 // Signal state change
662 release_sem(mSemStateChange);
666 virtual size_t Write(
667 BYTE *dest, // destination
668 const BYTE **extbuf, // source, to be updated
669 size_t size, // space in destination
670 size_t *extsize) // data in source, to be updated
672 // This function is called to put data into the buffer
673 size_t todo=MIN(size, *extsize);
674 memcpy(dest, *extbuf, todo);
675 *extbuf +=todo; // The external pointer moves forward...
676 *extsize -=todo; // ... so the remaining size decreases
677 return todo;
680 virtual size_t Read(
681 BYTE **extbuf, // destination, to be updated
682 const BYTE *src, // source
683 size_t *extsize, // space in destination, to be updated
684 size_t size) // data in source
686 // This function is called to read data out of the buffer
687 size_t todo=MIN(size, *extsize);
688 memcpy(*extbuf, src, todo);
689 *extbuf +=todo; // The external pointer moves forward...
690 *extsize -=todo; // ... so the remaining size decreases
691 return todo;
694 public:
695 // Reset buffer so that it can be filled again
696 void Reset(void)
698 Guard _(mSemInUse); // guard data integrity
700 mHead=mHeadRoom=mTail=mSizeUsed=0;
701 mTailRoom=GetSize();
702 mState=(State)(Flushed|Empty|EmptyEnough);
705 // Constructor
706 CircularBuffer(
707 PINDEX size,
708 PINDEX fillthreshold = 0,
709 PINDEX drainthreshold = 0)
710 : mFillThreshold(fillthreshold), mDrainThreshold(drainthreshold), mState(Empty)
712 PAssert(size!=0, "Attempting to create a buffer with size 0");
714 mSemInUse=create_sem(1, "mSemInUse");
715 mSemStateChange=create_sem(0, "mSemStateChange");
717 PAssert(mSemInUse>=0 && mSemStateChange>=0, "Unable to create semaphores");
719 mBuffer=new BYTE[(mSize=size)];
721 Reset();
724 // Destructor
725 virtual ~CircularBuffer()
727 // make sure the in-use semaphore is free and stays free
728 while (acquire_sem_etc(mSemInUse,1,B_RELATIVE_TIMEOUT,0)==B_WOULD_BLOCK)
730 // nothing to do, just busy-wait
733 delete_sem(mSemInUse);
735 delete_sem(mSemStateChange);
737 Reset();
739 if(mBuffer)
740 delete[] mBuffer;
743 // Check if buffer is empty
744 bool IsEmpty() { return (mState==Empty); }
746 // Check if buffer is full
747 bool IsFull() { return (mState==Full); }
749 // Get the size of the buffer
750 PINDEX GetSize(void) { return mSize; }
752 // Wait asynchronously for a buffer state or one of a number of states
753 void WaitForState(State state)
755 PRINTCB(("Waiting for state %X, current state=%X this=%p", state, mState, this));
756 // reset the Flushed bit so it only stops the loop if the buffer
757 // is flushed DURING an operation
759 Guard _(mSemInUse);
760 mState=(State)(mState & ~Flushed);
762 for(;;)
764 if ((mState & (state|Flushed))!=0) // bit patterns allowed
766 PRINTCB(("Detected state %X, wanted %X, returning", mState, state));
767 return;
769 PRINTCB(("Waiting for %X; headroom=%u tailroom=%u this=%p",state,mHeadRoom,mTailRoom,this));
770 // To prevent a race condition here in case the state
771 // gets changed just after the GetState call, the next
772 // semaphore call has a timeout.
773 acquire_sem_etc(mSemStateChange,1,B_RELATIVE_TIMEOUT,1000000);
777 // Fill buffer with data.
778 void Fill(const BYTE **extbuf, size_t *extsize)
780 PRINTCB(("start: head %d tail %d headroom %d tailroom %d extsize %d buffer %p this %p", mHead, mTail, mHeadRoom, mTailRoom, *extsize, mBuffer, this));
782 // Make a local copy of the queue.
783 // This is ok because there is only one filler thread and
784 // one drainer thread. The drainer is not going to make the
785 // free area for the filler any smaller and the filler is not
786 // going to overwrite the drainer's data if we do this.
787 // This way we can keep the semaphore busy as short as possible.
788 PINDEX lTail;
789 PINDEX lTailRoom;
790 PINDEX lHead; // read only
792 Guard _(mSemInUse); // guard data integrity
793 lTail=mTail;
794 lTailRoom=mTailRoom;
795 lHead=mHead;
798 bool needhousekeeping=false;
799 PINDEX totaldone=0;
801 while (*extsize!=0 && lTailRoom!=0 && totaldone<mSize)
803 needhousekeeping=true;
805 PINDEX done=Write(
806 mBuffer+lTail,
807 extbuf,
808 lTailRoom,
809 extsize);
811 totaldone +=done;
813 lTail +=done; // The tail moves forward...
814 lTailRoom -=done; // ... so there will be less room at the tail
816 // Check if we should wrap around
817 if (lTail==mSize)
819 lTail=0;
820 lTailRoom=lHead;
824 if (needhousekeeping)
826 Guard _(mSemInUse);
828 // Copy the local values back
829 mTail=lTail;
830 mTailRoom=lTailRoom;
831 mSizeUsed+=totaldone;
833 // Recalculate headroom
834 if (mTail>mHead)
836 mHeadRoom=mTail-mHead;
838 else
840 mHeadRoom=mSize-mHead;
843 // Check if we need to change the state
844 UpdateState();
846 PRINTCB((" end: head %d tail %d headroom %d tailroom %d extsize %d", mHead, mTail, mHeadRoom, mTailRoom, *extsize));
850 // Empty data out of buffer
851 void Drain(BYTE **extbuf, size_t *extsize)
853 PTRACE(7, "Drain: head " << mHead
854 << " tail " << mTail
855 << " headroom " << mHeadRoom
856 << " tailroom " << mTailRoom
857 << " extsize " << *extsize
858 << " buffer " << mBuffer
859 << " this " << this);
861 // Make a local copy of the queue.
862 // This is ok because there is only one filler thread and
863 // one drainer thread. The drainer is not going to make the
864 // free area for the filler any smaller and the filler is not
865 // going to overwrite the drainer's data if we do this.
866 // This way we can keep the semaphore busy as short as possible.
867 PINDEX lHead;
868 PINDEX lHeadRoom;
869 PINDEX lTail; // read only
871 Guard _(mSemInUse); // guard data integrity
872 lHead=mHead;
873 lHeadRoom=mHeadRoom;
874 lTail=mTail;
877 bool needhousekeeping=false;
878 PINDEX totaldone=0;
880 while (*extsize!=0 && lHeadRoom!=0 && totaldone<mSize)
882 needhousekeeping=true;
884 size_t done=Read(
885 extbuf,
886 mBuffer+lHead,
887 extsize,
888 lHeadRoom);
890 totaldone +=done;
892 lHead +=done; // The head moves forward...
893 lHeadRoom -=done; // ... so there will be less room at the head
895 // Check if we should wrap around
896 if (lHead==mSize)
898 lHead=0;
899 lHeadRoom=mTail;
903 if (needhousekeeping)
905 Guard _(mSemInUse);
907 // Copy the local values back
908 mHead=lHead;
909 mHeadRoom=lHeadRoom;
910 mSizeUsed-=totaldone;
912 // Recalculate tailroom
913 if (mHead>mTail)
915 mTailRoom=mHead-mTail;
917 else
919 mTailRoom=GetSize()-mTail;
922 // Check if we need to change the state
923 UpdateState();
925 PRINTCB((" end: head %d tail %d headroom %d tailroom %d extsize %d", mHead, mTail, mHeadRoom, mTailRoom, *extsize));
930 ////////////////////////////////////////////////////////////////////////////////
932 class ResamplingBuffer : public CircularBuffer
934 protected:
935 Resampler *mResampler;
937 protected:
938 virtual size_t Write(
939 BYTE *dest, // destination
940 const BYTE **extbuf, // source, to be updated
941 size_t size, // space in destination
942 size_t *extsize) // data in source, to be updated
944 size_t todo=*extsize/mResampler->InFrameSize();
945 size_t done=mResampler->InFrames(
946 (const short **)extbuf,
947 (short **)&dest,
948 &todo,
949 size/mResampler->OutFrameSize());
950 done*=mResampler->OutFrameSize();
951 *extsize=todo*mResampler->InFrameSize();
953 return done;
956 public:
957 void SetResampler(Resampler *resampler)
959 Guard _(mSemInUse); // guard data integrity
961 mResampler=resampler;
964 ResamplingBuffer(
965 Resampler *resampler,
966 PINDEX size,
967 PINDEX fillthreshold=0,
968 PINDEX drainthreshold=0)
969 : CircularBuffer(size, fillthreshold, drainthreshold), mResampler(NULL)
971 SetResampler(resampler);
974 ResamplingBuffer(
975 Resampler *resampler,
976 CircularBuffer *other)
977 : CircularBuffer(other->mSize, other->mFillThreshold, other->mDrainThreshold), mResampler(NULL)
979 SetResampler(resampler);
983 ////////////////////////////////////////////////////////////////////////////////
984 static void PlayBuffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format)
986 // This function is called by the BSoundPlayer object whenever it needs some more
987 // data to play.
988 DETECTVARS(buffer, size/2)
990 ((CircularBuffer *)cookie)->Drain((BYTE **)&buffer, &size);
992 DETECTSOUND();
995 static void RecordBuffer(void *cookie, const void *buffer, size_t size, const media_header &header)
997 // This function is called by the BMediaRecorder object whenever it has a buffer
998 // with recorded data ready.
999 DETECTVARS(buffer, size/2)
1000 DETECTSOUND();
1002 ((CircularBuffer *)cookie)->Fill((const BYTE **)&buffer, &size);
1005 ////////////////////////////////////////////////////////////////////////////////
1006 // PSoundChannelBeOS
1008 // This defines the number of times we would like to be called per second
1009 // to play/record data
1010 #define PLAYRECFREQ 20
1012 // Macro to let the default buffer size correspond neatly with the
1013 // setting we put into the format.
1014 #define DEFAULT_BUFSIZE(channels, rate, bits) 480
1015 //((channels*rate*(bits/8))/PLAYRECFREQ)
1017 PSoundChannelBeOS::PSoundChannelBeOS() :
1018 mRecorder(NULL),
1019 mPlayer(NULL),
1020 mBuffer(NULL),
1021 mNumBuffers(1),
1022 mResampler(NULL)
1024 PRINT(("default constructor"));
1026 InternalSetBuffers(DEFAULT_BUFSIZE(1, 8000, 16),DEFAULT_BUFSIZE(1, 8000, 16)/2);
1027 SetFormat(1, 8000, 16);
1029 // Nothing else to do here. Notice that the channel is not open for
1030 // playing/recording yet.
1034 PSoundChannelBeOS::PSoundChannelBeOS(const PString & dev,
1035 Directions dir,
1036 unsigned numChannels,
1037 unsigned sampleRate,
1038 unsigned bitsPerSample) :
1039 mRecorder(NULL),
1040 mPlayer(NULL),
1041 mBuffer(NULL),
1042 mNumBuffers(1),
1043 mResampler(NULL)
1045 PRINT(("constructor %s %u %u %u", dir==Player ? "Player" : "Recorder", numChannels, sampleRate, bitsPerSample));
1047 InternalSetBuffers(DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample), DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample)/2);
1048 Open(dev, dir, numChannels, sampleRate, bitsPerSample);
1049 // ignore result; user will need to find out whether this succeeds using IsOpen
1053 PSoundChannelBeOS::~PSoundChannelBeOS()
1055 PRINT((""));
1057 Close(); // destroys player and recorder
1058 InternalSetBuffers(0,0); // destroys buffer
1061 static const PStringArray GetRecorderDevicesList(BMediaRecorder *Recorder)
1063 // Array to hold the list.
1064 PStringArray devlist;
1065 BMediaRecorder* bRecorder = NULL;
1067 if(Recorder != NULL)
1068 bRecorder = Recorder;
1070 #ifdef MEDIA_KIT_UPDATE
1071 BMediaRecorder localRecorder("GetRecorderDevicesList");
1072 bool result=true;
1073 status_t status;
1076 if(bRecorder == NULL)
1078 bRecorder = &localRecorder;
1081 if (bRecorder == NULL || bRecorder->InitCheck()!=B_OK)
1083 PRINT(("Error constructing recorder to fetch device names"));
1084 result=false;
1088 if (result)
1090 media_format format;
1091 format.type = B_MEDIA_RAW_AUDIO;
1092 format.u.raw_audio=media_raw_audio_format::wildcard;
1094 // The resampler can only handle 16-bit audio
1095 format.u.raw_audio.format=media_raw_audio_format::B_AUDIO_SHORT;
1097 // Let the media recorder determine which sources are available
1098 if ((status = bRecorder->FetchSources(format, false))!=B_OK)
1100 PRINT(("Couldn't fetch BMediaRecorder sources; status=%d", status));
1101 result=false;
1105 if (result)
1107 // Fetch the names of all output devices
1108 media_format format;
1109 BString outname;
1110 for (int i=0; i< bRecorder->CountSources(); i++)
1112 if ((status = bRecorder->GetSourceAt(i, &outname, &format))==B_OK)
1114 PRINT(("Device found: %s", outname.String()));
1115 devlist[i] = PString(outname.String());
1117 else
1119 PRINT(("error %d retrieving data for device %d", status, i));
1120 result=false;
1125 if (!result)
1127 devlist.RemoveAll();
1130 return devlist;
1131 #else
1132 // Media Kit is the only device
1133 devlist[0] = "MediaKit";
1134 return devlist;
1135 #endif
1138 PStringArray PSoundChannelBeOS::GetDeviceNames(Directions dir)
1140 if (dir==Recorder)
1142 return GetRecorderDevicesList(NULL);
1144 else
1146 // not supported yet
1147 return PStringArray("MediaKit");
1151 PString PSoundChannelBeOS::GetDefaultDevice(Directions dir)
1153 if (dir==Recorder)
1155 const PStringArray &devlist = GetRecorderDevicesList(NULL);
1157 if (devlist.GetSize()!=0)
1159 return devlist[0];
1161 else
1163 return PString("MediaKit");
1166 else
1168 // not supported yet
1169 return PString("MediaKit");
1173 BOOL PSoundChannelBeOS::OpenPlayer(void)
1175 // We're using cascaded "if result"s here for clarity
1176 BOOL result = TRUE;
1178 #ifdef FILEDUMP
1179 media_format format;
1180 format.type=B_MEDIA_RAW_AUDIO;
1181 memcpy(&format.u.raw_audio, &mFormat, sizeof(mFormat));
1183 delete playwriter;
1184 playwriter=new BAudioFileWriter("play.wav", format, 441000);
1185 #endif
1187 // Must have a buffer
1188 if (!mBuffer)
1190 result = FALSE;
1191 PRINT(("Trying to open as player without setting buffers first"));
1194 if (result)
1196 // Create the player
1197 //was: mPlayer=new BSoundPlayer(&mFormat, NULL, PlayBuffer, NULL, mBuffer);
1199 mPlayer = new BSoundPlayer(
1200 &mFormat,
1201 NULL,
1202 PlayBuffer,
1203 NULL,
1204 mBuffer);
1206 if ((mPlayer == NULL) || (mPlayer->InitCheck() != B_OK))
1208 result = FALSE;
1209 PRINT(("Couldn't construct player"));
1213 if (result)
1215 // Start the player
1216 if (mPlayer->Start() != B_OK)
1218 result = FALSE;
1219 PRINT(("Couldn't start the player"));
1223 if (result)
1225 // Enable the fetching of data by PlayBuffer
1226 mPlayer->SetHasData(true);
1229 PRINT(("Returning %s", result?"success":"failure"));
1230 return result;
1233 BOOL PSoundChannelBeOS::OpenRecorder(const PString &dev)
1235 // We're using cascaded "if result"s here for clarity
1236 BOOL result=TRUE;
1239 if (!mBuffer)
1241 result=FALSE;
1242 PRINT(("Trying to open as recorder without setting buffers first"));
1246 if (result)
1248 // Create the recorder
1249 mRecorder=new BMediaRecorder("PWLIB PSoundChannel recorder");
1251 if ((mRecorder==NULL) || (mRecorder->InitCheck()!=B_OK))
1253 result=FALSE;
1254 PRINT(("Couldn't construct recorder"));
1258 #ifdef MEDIA_KIT_UPDATE
1259 int32 sourceindex;
1260 if (result)
1262 // Find the specified device in the list of input devices
1263 PINDEX x=GetRecorderDevicesList(mRecorder).GetStringsIndex(dev);
1264 if (x==P_MAX_INDEX)
1266 result=FALSE;
1267 PRINT(("Couldn't find device %s in the list",(const char *)dev));
1269 else
1271 sourceindex=(int32)x;
1275 #ifdef _DEBUG
1276 if (result)
1278 // Get information for the device
1279 BString outname;
1280 media_format xformat;
1281 status_t err;
1283 if ((err=mRecorder->GetSourceAt(sourceindex, &outname, &xformat))==B_OK)
1285 PRINT(("%s", outname.String()));
1286 PRINT((" type %d", (int)xformat.type));
1287 PRINT((" AudioFormat 0x%X", (int)xformat.AudioFormat()));
1288 PRINT((" u.raw_audio:"));
1289 PRINT((" frame_rate: %f", xformat.u.raw_audio.frame_rate));
1290 PRINT((" channel_count: %d", xformat.u.raw_audio.channel_count));
1291 PRINT((" byte_order: %d", xformat.u.raw_audio.byte_order));
1292 PRINT((" buffer_size: %d", xformat.u.raw_audio.buffer_size));
1294 else
1296 result=FALSE;
1297 PRINT(("couldn't get details for source %d: err=0x%X",sourceindex,err));
1300 #endif
1302 if (result)
1304 // Try to connect to the source
1305 if (mRecorder->ConnectSourceAt(sourceindex)!=B_OK)
1307 result=FALSE;
1308 PRINT(("Couldn't connect BMediaRecorder to source"));
1312 #else
1313 if (result)
1315 // Connect the recorder to the default input device
1316 media_format format;
1317 format.type=B_MEDIA_RAW_AUDIO;
1318 format.u.raw_audio=media_raw_audio_format::wildcard;
1319 // The resampler can only handle 16-bit audio
1320 format.u.raw_audio.format=media_raw_audio_format::B_AUDIO_SHORT;
1321 if (mRecorder->Connect(format,0)!=B_OK)
1323 result=FALSE;
1324 PRINT(("couldn't connect the recorder to the default source"));
1327 #endif
1329 if (result)
1331 // Create resampler
1332 media_format format=mRecorder->Format();
1334 delete mResampler;
1335 mResampler=new Resampler(
1336 format.u.raw_audio.frame_rate,
1337 mFormat.frame_rate,
1338 format.u.raw_audio.channel_count,
1339 mFormat.channel_count,
1343 #ifdef FILEDUMP
1345 media_format format;
1346 format.type=B_MEDIA_RAW_AUDIO;
1347 memcpy(&format.u.raw_audio, &mFormat, sizeof(mFormat));
1349 delete recwriter;
1350 recwriter=new BAudioFileWriter("record.wav", format);
1352 #endif
1354 // If the current buffer is not a resamplin buffer, re-create it
1355 ResamplingBuffer *buf=dynamic_cast<ResamplingBuffer*>(mBuffer);
1357 if (buf==NULL)
1359 PRINT(("re-creating buffer"));
1361 CircularBuffer *old=mBuffer;
1362 mBuffer=new ResamplingBuffer(mResampler, old);
1363 delete old;
1365 else
1367 buf->SetResampler(mResampler);
1371 if (result)
1373 // Set the hook function to our data processing function
1374 PRINT(("Setting buffer hook, cookie=%p",mBuffer));
1375 if (mRecorder->SetBufferHook(RecordBuffer, mBuffer)!=B_OK)
1377 result=FALSE;
1378 PRINT(("Couldn't set buffer hook on BMediaRecorder"));
1382 // If something went wrong, delete the recorder.
1383 if (!result)
1385 if (mRecorder)
1387 delete mRecorder;
1388 mRecorder=NULL;
1392 return result;
1395 BOOL PSoundChannelBeOS::Open(const PString & dev,
1396 Directions dir,
1397 unsigned numChannels,
1398 unsigned sampleRate,
1399 unsigned bitsPerSample)
1401 // We're using cascaded "if result"s here for clarity
1402 BOOL result = TRUE;
1403 PRINT(("%s %u %u %u", dir==Player?"Player":"Recorder", numChannels, sampleRate, bitsPerSample));
1405 // Close the channel first, just in case
1406 Close();
1408 // Initialize the format struct, necessary to create player or recorder
1409 if (!SetFormat(numChannels, sampleRate, bitsPerSample))
1411 result = FALSE;
1412 PRINT(("Couldn't set format"));
1415 if (result)
1417 switch (dir)
1419 case Player:
1420 PRINT(("... trying to open player"));
1421 result=OpenPlayer();
1422 break;
1424 case Recorder:
1425 PRINT(("...trying to open recorder"));
1426 result=OpenRecorder(dev);
1427 break;
1429 default:
1430 PRINT(("Unknown direction parameter"));
1431 result=FALSE;
1435 if (!result)
1437 // If anything went wrong, clean up
1438 PRINT(("... can't open, cleaning up"));
1439 Close();
1442 ::snooze(1*1000*1000);
1444 PRINT(("Returning %s", result?"success":"failure"));
1445 return result;
1448 BOOL PSoundChannelBeOS::Abort()
1450 return FALSE;
1454 BOOL PSoundChannelBeOS::SetFormat(unsigned numChannels,
1455 unsigned sampleRate,
1456 unsigned bitsPerSample)
1458 PRINT(("%u %u %u", numChannels, sampleRate, bitsPerSample));
1460 // NOTE: all constructors should call this to initialize
1461 // the local members
1462 // Do NOT call the function with any parameter set to 0!
1464 // The function only fails if the channel is open.
1465 // This is because the player or recorder needs to be re-created when the
1466 // format changes.
1467 if (IsOpen())
1469 PRINT(("Not allowed to set format on open channel"));
1470 return FALSE;
1473 // Initialize the format struct
1474 // The numbers of bits that we support here are 8, 16 or 32 bits (signed),
1475 // results for other sizes are not defined.
1476 mFormat = media_raw_audio_format::wildcard;
1477 mFormat.frame_rate=(float)sampleRate;
1478 mFormat.channel_count=numChannels;
1479 mFormat.format=(bitsPerSample / 8) & 0xF;
1480 mFormat.byte_order=B_HOST_IS_BENDIAN ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
1481 mFormat.buffer_size=DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample);
1483 return TRUE;
1487 unsigned PSoundChannelBeOS::GetChannels() const
1489 return mFormat.channel_count;
1493 unsigned PSoundChannelBeOS::GetSampleRate() const
1495 return (unsigned)mFormat.frame_rate;
1499 unsigned PSoundChannelBeOS::GetSampleSize() const
1501 return (mFormat.format & 0xF)*8; // return number of BITS
1505 BOOL PSoundChannelBeOS::Read(void *buf, PINDEX len)
1507 PINDEX bufSize = len;
1509 // Can only read from a recorder
1510 if (mRecorder!=NULL)
1512 // A Read starts the recording, if it's not running already
1513 if (!mRecorder->IsRunning())
1515 mRecorder->Start();
1518 // Wait until there's a buffer recorded
1519 mBuffer->WaitForState(CircularBuffer::NotEmpty);
1521 #ifdef FILEDUMP
1522 void *dumpbuf=buf;
1523 size_t dumpsize=len;
1524 #endif
1525 lastReadCount = 0;
1527 while(lastReadCount < bufSize)
1529 len = bufSize - lastReadCount;
1530 if(len <= 0)
1531 break;
1533 // Get data from the buffer
1534 mBuffer->Drain((BYTE**)&buf, (size_t*) &len);
1536 lastReadCount += len;
1539 #ifdef FILEDUMP
1540 if (recwriter)
1542 recwriter->writewavfile(dumpbuf, dumpsize);
1544 #endif
1546 return TRUE;
1548 return FALSE;
1551 BOOL PSoundChannelBeOS::Write(const void *buf, PINDEX len)
1553 // can only write to a player
1554 if (mPlayer!=NULL)
1556 // Wait until there is space
1557 mBuffer->WaitForState(CircularBuffer::EmptyEnough);
1559 // This function needs to update the last write count
1560 // Store len before it gets modified
1561 lastWriteCount=len;
1563 #ifdef FILEDUMP
1564 if (playwriter)
1566 playwriter->writewavfile(buf,len);
1568 #endif
1570 // Store data into the buffer
1571 mBuffer->Fill((const BYTE **)&buf, (size_t*) &len);
1573 // Update last write count
1574 lastWriteCount-=len;
1576 return TRUE;
1579 return FALSE;
1583 BOOL PSoundChannelBeOS::Close()
1585 PRINT((""));
1587 // Flush the buffer first
1588 Abort();
1590 // Stop the player
1591 if ((mPlayer!=NULL) && (mPlayer->InitCheck()==B_OK))
1593 mPlayer->Stop();
1595 #ifdef FILEDUMP
1596 delete playwriter;
1597 playwriter=NULL;
1598 #endif
1601 if(mPlayer)
1603 // Destroy the player
1604 delete mPlayer;
1605 mPlayer=NULL; // make sure that another Close won't crash the system
1608 // Stop the recorder
1609 if ((mRecorder!=NULL) && (mRecorder->InitCheck()==B_OK))
1611 mRecorder->Stop(); // Not really necessary
1612 mRecorder->Disconnect();
1614 #ifdef FILEDUMP
1615 delete recwriter;
1616 recwriter=NULL;
1617 #endif
1620 if(mRecorder)
1622 // Destroy the recorder
1623 delete mRecorder;
1624 mRecorder=NULL; // make sure that another Close won't crash the system
1627 return TRUE;
1631 BOOL PSoundChannelBeOS::SetBuffers(PINDEX size, PINDEX count)
1633 return InternalSetBuffers(size*(mNumBuffers=count),size);
1637 BOOL PSoundChannelBeOS::InternalSetBuffers(PINDEX size, PINDEX threshold)
1639 if (mPlayer)
1641 mPlayer->SetHasData(false);
1643 else if (mRecorder)
1645 mRecorder->Stop();
1648 // Delete the current buffer
1649 if(mBuffer != NULL)
1651 delete mBuffer;
1652 mBuffer = NULL;
1655 // Create the new buffer
1656 if (size != 0)
1658 if (mRecorder)
1660 if (!mResampler)
1662 PTRACE(TL, "Creating default resampler");
1663 mResampler = new Resampler(1.0,1.0,1,1,0,1);
1666 PTRACE(TL, "Creating resampling buffer, size " << size);
1667 mBuffer = new ResamplingBuffer(mResampler, size, threshold, threshold);
1669 // In case we use resampler, size must be set to resampled buffer size
1672 else
1674 PTRACE(TL, "Creating playback buffer, size " << size);
1675 mBuffer = new CircularBuffer(size, threshold, threshold);
1678 // If we have a player, set the cookie again and restart it
1679 if (mPlayer)
1681 mPlayer->SetCookie(mBuffer);
1682 PTRACE(TL, "Tried to set player buffer cookie");
1683 mPlayer->SetHasData(true);
1684 PTRACE(TL, "Tried to set player has data");
1687 // If we have a recorder, set the cookie again
1688 // Note that the recorder is not restarted, even if it was running.
1689 // It's not a good idea for the program to change the buffers during
1690 // recording anyway because it would at least lose some data.
1691 if (mRecorder)
1693 if(B_OK != mRecorder->SetBufferHook(RecordBuffer, mBuffer))
1694 PTRACE(TL, "Can't set recorder buffer hook");
1697 return TRUE;
1700 if (IsOpen())
1702 PTRACE(TL, "Can't continue without buffers - closing channel");
1703 Close(); // should give errors on subsequent read/writes
1706 mBuffer = NULL;
1708 return FALSE;
1712 BOOL PSoundChannelBeOS::GetBuffers(PINDEX &size, PINDEX &count)
1714 if (mBuffer)
1716 size=mBuffer->GetSize();
1717 count=mNumBuffers;
1718 return TRUE;
1721 return FALSE;
1725 BOOL PSoundChannelBeOS::PlaySound(const PSound &sound, BOOL wait)
1727 PRINT(("wait=%s", wait?"true":"false"));
1729 if (mPlayer==NULL)
1731 PRINT(("Playing a sound on a closed (or recording) PSoundChannelBeOS"));
1732 return FALSE;
1735 #ifdef FILEDUMP
1736 playwriter->writewavfile((void *)(const BYTE*)sound, sound.GetSize());
1737 #endif
1739 // create a local buffer that references the PSound
1740 // NOTE: no conversion between the PSound's format and the
1741 // PSoundChannelBeOS's format is done.
1742 const BYTE *buf=(const BYTE *)sound;
1743 PINDEX size=sound.GetSize();
1745 // Play the sound by doing successive Writes until the sound is done.
1746 // Note that write will return when either the buffer is full or the
1747 // given data is written. We want to return after the entire sound
1748 // has been buffered. So we repeatedly call Write until there is
1749 // no data left
1750 while (size!=0)
1752 // Wait until there is space
1753 mBuffer->WaitForState(CircularBuffer::EmptyEnough);
1755 // Write the data
1756 mBuffer->Fill(&buf, (size_t*) &size);
1759 // Wait until the sound is finished, if requested
1760 if (wait)
1762 PRINT(("Waiting for sound"));
1763 mBuffer->WaitForState(CircularBuffer::Empty);
1766 return TRUE;
1770 BOOL PSoundChannelBeOS::PlayFile(const PFilePath &file, BOOL wait)
1772 entry_ref ref;
1773 status_t err;
1775 // using pointers for these objects so that we don't have to
1776 // construct them here but can nevertheless use the if(ok)'s
1777 BEntry *pentry = NULL;
1780 // Create BEntry from file name
1781 pentry = new BEntry(file, true);
1782 err = pentry->InitCheck();
1785 if (err==B_OK)
1787 // Create entry_ref from BEntry
1788 err = pentry->GetRef(&ref);
1791 if (err==B_OK)
1793 // Play the sound. Return value is a handle or a negative value for errors
1794 // Errors in BeOS are always negative values
1795 err=play_sound(&ref, true, !wait, wait);
1796 if (err>=0)
1798 err=B_OK;
1802 return (err==B_OK);
1806 BOOL PSoundChannelBeOS::HasPlayCompleted()
1808 if (mPlayer!=NULL)
1810 return mBuffer->IsEmpty();
1813 return FALSE;
1817 BOOL PSoundChannelBeOS::WaitForPlayCompletion()
1819 if (mPlayer!=NULL)
1821 mBuffer->WaitForState(CircularBuffer::Empty);
1824 return TRUE;
1828 BOOL PSoundChannelBeOS::RecordSound(PSound &sound)
1830 PRINT((""));
1832 if (mRecorder==NULL)
1834 PRINT(("Recording a sound on a closed (or playing) PSoundChannelBeOS"));
1835 return FALSE;
1838 // Flush the buffer first
1839 Abort();
1841 // Start recording
1842 if (mRecorder->Start()!=B_OK)
1844 PRINT(("BMediaRecorder::Start() returned error"));
1845 return FALSE;
1848 // Wait until buffer is filled
1849 mBuffer->WaitForState(CircularBuffer::Full);
1850 PRINT(("Buffer full: size=%lu",mBuffer->GetSize()));
1852 // Stop the recorder
1853 if (mRecorder->Stop()!=B_OK)
1855 PRINT(("Uh-oh, recorder is unstoppable!"));
1856 //return FALSE;
1859 // Set the sound's format to ours
1860 sound.SetFormat(GetChannels(), GetSampleRate(), GetSampleSize());
1862 // Resize the sound and set up local buffer references
1863 PINDEX size=mBuffer->GetSize();
1864 BYTE *buf=sound.GetPointer(size);
1866 #ifdef FILEDUMP
1867 void *dumpbuf=buf;
1868 size_t dumpsize=size;
1869 #endif
1871 // Read the data
1872 mBuffer->Drain(&buf, (size_t*) &size);
1874 #ifdef FILEDUMP
1875 recwriter->writewavfile(dumpbuf, dumpsize);
1876 #endif
1878 PRINT(("Recording succesful"));
1879 return TRUE;
1883 BOOL PSoundChannelBeOS::RecordFile(const PFilePath & filename)
1885 // Not implemented for now
1886 return FALSE;
1890 BOOL PSoundChannelBeOS::StartRecording()
1892 if (mRecorder==NULL)
1894 PRINT(("Recording to a closed (or playing) PSoundChannelBeOS"));
1895 return FALSE;
1898 // Flush the buffers
1899 Abort();
1901 // Start recording
1902 if (mRecorder->Start()!=B_OK)
1904 PRINT(("BMediaRecorder::Start returned error"));
1905 return FALSE;
1908 return TRUE;
1912 BOOL PSoundChannelBeOS::IsRecordBufferFull()
1914 if (mRecorder)
1916 return !mBuffer->IsEmpty();
1919 return FALSE;
1923 BOOL PSoundChannelBeOS::AreAllRecordBuffersFull()
1925 if (mRecorder)
1927 return mBuffer->IsFull();
1930 return FALSE;
1934 BOOL PSoundChannelBeOS::WaitForRecordBufferFull()
1936 if (mRecorder==NULL)
1938 PRINT(("Waiting for record buffer on playing or closed PSoundChannelBeOS"));
1939 return FALSE;
1942 mBuffer->WaitForState(CircularBuffer::FullEnough);
1944 return PXSetIOBlock(PXReadBlock, readTimeout);
1948 BOOL PSoundChannelBeOS::WaitForAllRecordBuffersFull()
1950 if (mRecorder==NULL)
1952 PRINT(("Waiting for record buffers on playing or closed PSoundChannelBeOS"));
1953 return FALSE;
1956 mBuffer->WaitForState(CircularBuffer::Full);
1958 return TRUE;
1962 BOOL PSoundChannelBeOS::IsOpen() const
1964 BOOL result=((mPlayer!=NULL) || (mRecorder!=NULL));
1965 PRINT(("returning %s, player 0x%X recorder 0x%X", result?"true":"false", mPlayer, mRecorder));
1966 return result;
1971 BOOL PSoundChannelBeOS::SetVolume(unsigned newVolume)
1973 #ifdef TODO
1974 cerr << __FILE__<< "PSoundChannelBeOS :: SetVolume called in error. Please fix" << endl;
1975 #endif
1977 return TRUE;
1980 BOOL PSoundChannelBeOS::GetVolume(unsigned & volume)
1982 #ifdef TODO
1983 cerr << __FILE__<< "PSoundChannelBeOS :: GetVolume called in error. Please fix" << endl;
1984 #endif
1986 return TRUE;
1990 // End of file