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
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.
28 * Yuri Kiryanov, ykiryanov at users.sourceforge.net,
29 * Jac Goudsmit <jac@be.com>.
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
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
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
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
80 #include <ptlib/unix/ptlib/beaudio.h>
82 PCREATE_SOUND_PLUGIN(BeOS
, PSoundChannelBeOS
);
84 /////////////// Debugging stuff ///////////////
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
100 #define DETECTVARS(buffer,numframes) short *detbuf=(short*)buffer; size_t detframes=numframes;
101 #define DETECTSOUND() \
103 static bool silence=true; \
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++) \
114 asprintf(&x,"%%%ds\n",(detbuf[j]>>10)+32); \
125 #define DETECTVARS(buffer,numframes)
126 #define DETECTSOUND()
130 #include "beaudio/AudioFileWriter.h"
131 BAudioFileWriter
*playwriter
=NULL
;
132 BAudioFileWriter
*recwriter
=NULL
;
135 ////////////////////////////////////////////////////////////////////////////////
138 PSound::PSound(unsigned channels
,
139 unsigned samplesPerSecond
,
140 unsigned bitsPerSample
,
145 SetFormat(channels
, samplesPerSecond
, bitsPerSample
);
149 memcpy(GetPointer(bufferSize
), buffer
, bufferSize
);
154 PSound::PSound(const PFilePath
& filename
)
158 // Set the default format
159 SetFormat(1, 8000, 16);
161 // The format is changed if the file is succesfully loaded.
166 PSound
& PSound::operator=(const PBYTEArray
& data
)
168 PBYTEArray::operator=(data
);
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
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()");
221 // Create entry_ref from BEntry
223 if ((dwLastError
=entry
.GetRef(&ref
))!=B_OK
)
225 STATUS("entry.GetRef()");
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()");
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
);
244 dwLastError
= ptrack
->InitCheck();
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
265 STATUS("TrackAt() failed, error");
268 // if we found a track and arrived at this point, the track we found
272 dwLastError
=file
.ReleaseTrack(ptrack
); // destroys ptrack
276 // if an error occurred during track scanning, leave now
277 if (dwLastError
!=B_OK
)
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
);
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...
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"
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
;
354 while ((dwLastError
=get_next_file_format(&cookie
, &mfi
))==B_OK
)
356 if (!strcasecmp(mfi
.file_extension
, (const char *)filetype
))
361 if (dwLastError
!=B_OK
)
363 // didn't find file format
364 PRINT(("Couldn't find media_file_format for \"%s\"", (const char *)filetype
));
368 // Create BEntry from file name
369 BEntry
entry(filename
, true);
370 if ((dwLastError
=entry
.InitCheck())!=B_OK
)
372 STATUS("entry.InitCheck()");
376 // Create entry_ref from BEntry
378 if ((dwLastError
=entry
.GetRef(&ref
))!=B_OK
)
380 STATUS("entry.GetRef()");
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()");
392 // Find an encoder. The input format is the format we have stored in
393 // our formatInfo member.
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
)
403 if (outformat
.type
==B_MEDIA_RAW_AUDIO
)
406 found_raw_encoder
=true;
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
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
;
428 if (found_raw_encoder
)
430 PRINT(("Using raw encoder"));
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"));
441 // Create a BMediaTrack in the file using the selected encoder
442 ptrack
= file
.CreateTrack(&format
, pmci
);
445 dwLastError
= ptrack
->InitCheck();
449 dwLastError
= B_ERROR
; //todo: change error code
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()");
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
488 PSoundChannelBeOS
player(PSoundChannelBeOS::GetDefaultDevice(PSoundChannelBeOS::Player
), PSoundChannelBeOS::Player
, numChannels
, sampleRate
, sampleSize
);
490 if (!player
.IsOpen())
492 PRINT(("PSoundChannelBeOS constructor failed to open"));
496 return player
.PlaySound(*this, TRUE
);
499 BOOL
PSound::PlayFile(const PFilePath
& file
, BOOL wait
)
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();
516 // Create entry_ref from BEntry
517 err
= pentry
->GetRef(&ref
);
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
);
539 ////////////////////////////////////////////////////////////////////////////////
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
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
584 // Internal state for the buffer
585 // Note the nifty bit patterns for comparing the current state
586 // with a desired state
588 { // Headspace Tailspace
590 Filled
=2, // 0<h<size 0<t<size
591 NotFull
=3, // 0<=h<size 0<t<=size (for comparing)
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
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
619 // Check if the state changed. Private because it's not guarded by semaphore
620 void UpdateState(void)
622 // Determine current state
625 if (mSizeUsed
==mSize
)
627 PRINTCB(("State is FULL"));
630 else if (mSizeUsed
==0)
632 PRINTCB(("State is EMPTY"));
637 PRINTCB(("State is FILLED"));
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
));
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
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
695 // Reset buffer so that it can be filled again
698 Guard
_(mSemInUse
); // guard data integrity
700 mHead
=mHeadRoom
=mTail
=mSizeUsed
=0;
702 mState
=(State
)(Flushed
|Empty
|EmptyEnough
);
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
)];
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
);
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
760 mState
=(State
)(mState
& ~Flushed
);
764 if ((mState
& (state
|Flushed
))!=0) // bit patterns allowed
766 PRINTCB(("Detected state %X, wanted %X, returning", mState
, state
));
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.
790 PINDEX lHead
; // read only
792 Guard
_(mSemInUse
); // guard data integrity
798 bool needhousekeeping
=false;
801 while (*extsize
!=0 && lTailRoom
!=0 && totaldone
<mSize
)
803 needhousekeeping
=true;
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
824 if (needhousekeeping
)
828 // Copy the local values back
831 mSizeUsed
+=totaldone
;
833 // Recalculate headroom
836 mHeadRoom
=mTail
-mHead
;
840 mHeadRoom
=mSize
-mHead
;
843 // Check if we need to change the state
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
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.
869 PINDEX lTail
; // read only
871 Guard
_(mSemInUse
); // guard data integrity
877 bool needhousekeeping
=false;
880 while (*extsize
!=0 && lHeadRoom
!=0 && totaldone
<mSize
)
882 needhousekeeping
=true;
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
903 if (needhousekeeping
)
907 // Copy the local values back
910 mSizeUsed
-=totaldone
;
912 // Recalculate tailroom
915 mTailRoom
=mHead
-mTail
;
919 mTailRoom
=GetSize()-mTail
;
922 // Check if we need to change the state
925 PRINTCB((" end: head %d tail %d headroom %d tailroom %d extsize %d", mHead
, mTail
, mHeadRoom
, mTailRoom
, *extsize
));
930 ////////////////////////////////////////////////////////////////////////////////
932 class ResamplingBuffer
: public CircularBuffer
935 Resampler
*mResampler
;
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
,
949 size
/mResampler
->OutFrameSize());
950 done
*=mResampler
->OutFrameSize();
951 *extsize
=todo
*mResampler
->InFrameSize();
957 void SetResampler(Resampler
*resampler
)
959 Guard
_(mSemInUse
); // guard data integrity
961 mResampler
=resampler
;
965 Resampler
*resampler
,
967 PINDEX fillthreshold
=0,
968 PINDEX drainthreshold
=0)
969 : CircularBuffer(size
, fillthreshold
, drainthreshold
), mResampler(NULL
)
971 SetResampler(resampler
);
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
988 DETECTVARS(buffer
, size
/2)
990 ((CircularBuffer
*)cookie
)->Drain((BYTE
**)&buffer
, &size
);
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)
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() :
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
,
1036 unsigned numChannels
,
1037 unsigned sampleRate
,
1038 unsigned bitsPerSample
) :
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()
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");
1076 if(bRecorder
== NULL
)
1078 bRecorder
= &localRecorder
;
1081 if (bRecorder
== NULL
|| bRecorder
->InitCheck()!=B_OK
)
1083 PRINT(("Error constructing recorder to fetch device names"));
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
));
1107 // Fetch the names of all output devices
1108 media_format format
;
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());
1119 PRINT(("error %d retrieving data for device %d", status
, i
));
1127 devlist
.RemoveAll();
1132 // Media Kit is the only device
1133 devlist
[0] = "MediaKit";
1138 PStringArray
PSoundChannelBeOS::GetDeviceNames(Directions dir
)
1142 return GetRecorderDevicesList(NULL
);
1146 // not supported yet
1147 return PStringArray("MediaKit");
1151 PString
PSoundChannelBeOS::GetDefaultDevice(Directions dir
)
1155 const PStringArray
&devlist
= GetRecorderDevicesList(NULL
);
1157 if (devlist
.GetSize()!=0)
1163 return PString("MediaKit");
1168 // not supported yet
1169 return PString("MediaKit");
1173 BOOL
PSoundChannelBeOS::OpenPlayer(void)
1175 // We're using cascaded "if result"s here for clarity
1179 media_format format
;
1180 format
.type
=B_MEDIA_RAW_AUDIO
;
1181 memcpy(&format
.u
.raw_audio
, &mFormat
, sizeof(mFormat
));
1184 playwriter
=new BAudioFileWriter("play.wav", format
, 441000);
1187 // Must have a buffer
1191 PRINT(("Trying to open as player without setting buffers first"));
1196 // Create the player
1197 //was: mPlayer=new BSoundPlayer(&mFormat, NULL, PlayBuffer, NULL, mBuffer);
1199 mPlayer
= new BSoundPlayer(
1206 if ((mPlayer
== NULL
) || (mPlayer
->InitCheck() != B_OK
))
1209 PRINT(("Couldn't construct player"));
1216 if (mPlayer
->Start() != B_OK
)
1219 PRINT(("Couldn't start the player"));
1225 // Enable the fetching of data by PlayBuffer
1226 mPlayer
->SetHasData(true);
1229 PRINT(("Returning %s", result
?"success":"failure"));
1233 BOOL
PSoundChannelBeOS::OpenRecorder(const PString
&dev
)
1235 // We're using cascaded "if result"s here for clarity
1242 PRINT(("Trying to open as recorder without setting buffers first"));
1248 // Create the recorder
1249 mRecorder
=new BMediaRecorder("PWLIB PSoundChannel recorder");
1251 if ((mRecorder
==NULL
) || (mRecorder
->InitCheck()!=B_OK
))
1254 PRINT(("Couldn't construct recorder"));
1258 #ifdef MEDIA_KIT_UPDATE
1262 // Find the specified device in the list of input devices
1263 PINDEX x
=GetRecorderDevicesList(mRecorder
).GetStringsIndex(dev
);
1267 PRINT(("Couldn't find device %s in the list",(const char *)dev
));
1271 sourceindex
=(int32
)x
;
1278 // Get information for the device
1280 media_format xformat
;
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
));
1297 PRINT(("couldn't get details for source %d: err=0x%X",sourceindex
,err
));
1304 // Try to connect to the source
1305 if (mRecorder
->ConnectSourceAt(sourceindex
)!=B_OK
)
1308 PRINT(("Couldn't connect BMediaRecorder to source"));
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
)
1324 PRINT(("couldn't connect the recorder to the default source"));
1332 media_format format
=mRecorder
->Format();
1335 mResampler
=new Resampler(
1336 format
.u
.raw_audio
.frame_rate
,
1338 format
.u
.raw_audio
.channel_count
,
1339 mFormat
.channel_count
,
1345 media_format format
;
1346 format
.type
=B_MEDIA_RAW_AUDIO
;
1347 memcpy(&format
.u
.raw_audio
, &mFormat
, sizeof(mFormat
));
1350 recwriter
=new BAudioFileWriter("record.wav", format
);
1354 // If the current buffer is not a resamplin buffer, re-create it
1355 ResamplingBuffer
*buf
=dynamic_cast<ResamplingBuffer
*>(mBuffer
);
1359 PRINT(("re-creating buffer"));
1361 CircularBuffer
*old
=mBuffer
;
1362 mBuffer
=new ResamplingBuffer(mResampler
, old
);
1367 buf
->SetResampler(mResampler
);
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
)
1378 PRINT(("Couldn't set buffer hook on BMediaRecorder"));
1382 // If something went wrong, delete the recorder.
1395 BOOL
PSoundChannelBeOS::Open(const PString
& dev
,
1397 unsigned numChannels
,
1398 unsigned sampleRate
,
1399 unsigned bitsPerSample
)
1401 // We're using cascaded "if result"s here for clarity
1403 PRINT(("%s %u %u %u", dir
==Player
?"Player":"Recorder", numChannels
, sampleRate
, bitsPerSample
));
1405 // Close the channel first, just in case
1408 // Initialize the format struct, necessary to create player or recorder
1409 if (!SetFormat(numChannels
, sampleRate
, bitsPerSample
))
1412 PRINT(("Couldn't set format"));
1420 PRINT(("... trying to open player"));
1421 result
=OpenPlayer();
1425 PRINT(("...trying to open recorder"));
1426 result
=OpenRecorder(dev
);
1430 PRINT(("Unknown direction parameter"));
1437 // If anything went wrong, clean up
1438 PRINT(("... can't open, cleaning up"));
1442 ::snooze(1*1000*1000);
1444 PRINT(("Returning %s", result
?"success":"failure"));
1448 BOOL
PSoundChannelBeOS::Abort()
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
1469 PRINT(("Not allowed to set format on open channel"));
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
);
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())
1518 // Wait until there's a buffer recorded
1519 mBuffer
->WaitForState(CircularBuffer::NotEmpty
);
1523 size_t dumpsize
=len
;
1527 while(lastReadCount
< bufSize
)
1529 len
= bufSize
- lastReadCount
;
1533 // Get data from the buffer
1534 mBuffer
->Drain((BYTE
**)&buf
, (size_t*) &len
);
1536 lastReadCount
+= len
;
1542 recwriter
->writewavfile(dumpbuf
, dumpsize
);
1551 BOOL
PSoundChannelBeOS::Write(const void *buf
, PINDEX len
)
1553 // can only write to a player
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
1566 playwriter
->writewavfile(buf
,len
);
1570 // Store data into the buffer
1571 mBuffer
->Fill((const BYTE
**)&buf
, (size_t*) &len
);
1573 // Update last write count
1574 lastWriteCount
-=len
;
1583 BOOL
PSoundChannelBeOS::Close()
1587 // Flush the buffer first
1591 if ((mPlayer
!=NULL
) && (mPlayer
->InitCheck()==B_OK
))
1603 // Destroy the player
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();
1622 // Destroy the recorder
1624 mRecorder
=NULL
; // make sure that another Close won't crash the system
1631 BOOL
PSoundChannelBeOS::SetBuffers(PINDEX size
, PINDEX count
)
1633 return InternalSetBuffers(size
*(mNumBuffers
=count
),size
);
1637 BOOL
PSoundChannelBeOS::InternalSetBuffers(PINDEX size
, PINDEX threshold
)
1641 mPlayer
->SetHasData(false);
1648 // Delete the current buffer
1655 // Create the new buffer
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
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
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.
1693 if(B_OK
!= mRecorder
->SetBufferHook(RecordBuffer
, mBuffer
))
1694 PTRACE(TL
, "Can't set recorder buffer hook");
1702 PTRACE(TL
, "Can't continue without buffers - closing channel");
1703 Close(); // should give errors on subsequent read/writes
1712 BOOL
PSoundChannelBeOS::GetBuffers(PINDEX
&size
, PINDEX
&count
)
1716 size
=mBuffer
->GetSize();
1725 BOOL
PSoundChannelBeOS::PlaySound(const PSound
&sound
, BOOL wait
)
1727 PRINT(("wait=%s", wait
?"true":"false"));
1731 PRINT(("Playing a sound on a closed (or recording) PSoundChannelBeOS"));
1736 playwriter
->writewavfile((void *)(const BYTE
*)sound
, sound
.GetSize());
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
1752 // Wait until there is space
1753 mBuffer
->WaitForState(CircularBuffer::EmptyEnough
);
1756 mBuffer
->Fill(&buf
, (size_t*) &size
);
1759 // Wait until the sound is finished, if requested
1762 PRINT(("Waiting for sound"));
1763 mBuffer
->WaitForState(CircularBuffer::Empty
);
1770 BOOL
PSoundChannelBeOS::PlayFile(const PFilePath
&file
, BOOL wait
)
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();
1787 // Create entry_ref from BEntry
1788 err
= pentry
->GetRef(&ref
);
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
);
1806 BOOL
PSoundChannelBeOS::HasPlayCompleted()
1810 return mBuffer
->IsEmpty();
1817 BOOL
PSoundChannelBeOS::WaitForPlayCompletion()
1821 mBuffer
->WaitForState(CircularBuffer::Empty
);
1828 BOOL
PSoundChannelBeOS::RecordSound(PSound
&sound
)
1832 if (mRecorder
==NULL
)
1834 PRINT(("Recording a sound on a closed (or playing) PSoundChannelBeOS"));
1838 // Flush the buffer first
1842 if (mRecorder
->Start()!=B_OK
)
1844 PRINT(("BMediaRecorder::Start() returned error"));
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!"));
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
);
1868 size_t dumpsize
=size
;
1872 mBuffer
->Drain(&buf
, (size_t*) &size
);
1875 recwriter
->writewavfile(dumpbuf
, dumpsize
);
1878 PRINT(("Recording succesful"));
1883 BOOL
PSoundChannelBeOS::RecordFile(const PFilePath
& filename
)
1885 // Not implemented for now
1890 BOOL
PSoundChannelBeOS::StartRecording()
1892 if (mRecorder
==NULL
)
1894 PRINT(("Recording to a closed (or playing) PSoundChannelBeOS"));
1898 // Flush the buffers
1902 if (mRecorder
->Start()!=B_OK
)
1904 PRINT(("BMediaRecorder::Start returned error"));
1912 BOOL
PSoundChannelBeOS::IsRecordBufferFull()
1916 return !mBuffer
->IsEmpty();
1923 BOOL
PSoundChannelBeOS::AreAllRecordBuffersFull()
1927 return mBuffer
->IsFull();
1934 BOOL
PSoundChannelBeOS::WaitForRecordBufferFull()
1936 if (mRecorder
==NULL
)
1938 PRINT(("Waiting for record buffer on playing or closed PSoundChannelBeOS"));
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"));
1956 mBuffer
->WaitForState(CircularBuffer::Full
);
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
));
1971 BOOL
PSoundChannelBeOS::SetVolume(unsigned newVolume
)
1974 cerr
<< __FILE__
<< "PSoundChannelBeOS :: SetVolume called in error. Please fix" << endl
;
1980 BOOL
PSoundChannelBeOS::GetVolume(unsigned & volume
)
1983 cerr
<< __FILE__
<< "PSoundChannelBeOS :: GetVolume called in error. Please fix" << endl
;