1 /*
2 * beaudio.cxx
3 *
4 * Sound driver implementation.
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 1993-2001 Equivalence Pty. Ltd.
9 *
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/
14 *
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.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
26 *
27 * Contributor(s):
28 * Yuri Kiryanov, ykiryanov at users.sourceforge.net,
29 * Jac Goudsmit <jac@be.com>.
30 *
31 * $Revision: 20385 $
32 * $Author: rjongbloed $
33 * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
34 */
35
36 #include <ptlib.h>
37 #include <ptlib/unix/ptlib/beaudio.h>
38
39 PCREATE_SOUND_PLUGIN(BeOS, PSoundChannelBeOS);
40
41 /////////////// Debugging stuff ///////////////
42 #define TL (7)
43
44 #define PRINT(x) //do { printf(__FILE__ ":%d %s ", __LINE__, __FUNCTION__); printf x; printf("\n"); } while(0)
45
46 #define STATUS(x) // PRINT((x "=%ld", (long)dwLastError))
47
48 #define PRINTCB(x) // PRINT(x)
49
50 //#define SOUNDDETECT 1 define this for printed output of first pb/rec audio
51
52 //#define FILEDUMP 1 define this for dumping audio to wav file
53
54 // Macros and global vars for debugging
55
56 #ifdef SOUNDDETECT
57 #define DETECTVARS(buffer,numframes) short *detbuf=(short*)buffer; size_t detframes=numframes;
58 #define DETECTSOUND() \
59 do { \
60 static bool silence=true; \
61 if (silence) \
62 { \
63 for (size_t i=0; i<detframes; i++) \
64 { \
65 if (detbuf[i]>=255) \
66 { \
67 PRINT(("SOUND DETECTED at %p",detbuf)); \
68 for (size_t j=0; j<detframes && j<30; j++) \
69 { \
70 char *x; \
71 asprintf(&x,"%%%ds\n",(detbuf[j]>>10)+32); \
72 printf(x,"."); \
73 free(x); \
74 } \
75 silence=false; \
76 break; \
77 } \
78 } \
79 } \
80 } while(0)
81 #else
82 #define DETECTVARS(buffer,numframes)
83 #define DETECTSOUND()
84 #endif
85
86 #ifdef FILEDUMP
87 #include "beaudio/AudioFileWriter.h"
88 BAudioFileWriter *playwriter=NULL;
89 BAudioFileWriter *recwriter=NULL;
90 #endif
91
92 ////////////////////////////////////////////////////////////////////////////////
93 // PSound
94
PSound(unsigned channels,unsigned samplesPerSecond,unsigned bitsPerSample,PINDEX bufferSize,const BYTE * buffer)95 PSound::PSound(unsigned channels,
96 unsigned samplesPerSecond,
97 unsigned bitsPerSample,
98 PINDEX bufferSize,
99 const BYTE * buffer)
100 {
101 encoding = 0;
102 SetFormat(channels, samplesPerSecond, bitsPerSample);
103
104 if (buffer != NULL)
105 {
106 memcpy(GetPointer(bufferSize), buffer, bufferSize);
107 }
108 }
109
110
PSound(const PFilePath & filename)111 PSound::PSound(const PFilePath & filename)
112 {
113 encoding = 0;
114
115 // Set the default format
116 SetFormat(1, 8000, 16);
117
118 // The format is changed if the file is succesfully loaded.
119 Load(filename);
120 }
121
122
operator =(const PBYTEArray & data)123 PSound & PSound::operator=(const PBYTEArray & data)
124 {
125 PBYTEArray::operator=(data);
126 return *this;
127 }
128
129
SetFormat(unsigned channels,unsigned samplesPerSecond,unsigned bitsPerSample)130 void PSound::SetFormat(unsigned channels,
131 unsigned samplesPerSecond,
132 unsigned bitsPerSample)
133 {
134 // NOTE: all constructors should call this to initialize
135 // the local members, especially formatInfo.
136 // Do NOT call the function with any parameter set to 0!
137 sampleSize=bitsPerSample;
138 sampleRate=samplesPerSecond;
139 numChannels = channels;
140
141 // We don't use the encoding member (although we could probably set it to 0=PCM)
142 // Let the application know it shouldn't assume anything
143 encoding = 1;
144
145 // The formatInfo member to us is a media_format structure.
146 PBoolean setsize_formatInfo=formatInfo.SetSize(sizeof(media_format));
147 PAssert(setsize_formatInfo, "Unable to set size for sound info array");
148
149 // Initialize the media_format struct
150 // The numbers of bits that we support here are 8, 16 or 32 bits (signed),
151 // results for other sizes are not defined.
152 media_format &format=*(media_format*)(const BYTE *)formatInfo;
153
154 format.type = B_MEDIA_RAW_AUDIO;
155 format.u.raw_audio = media_raw_audio_format::wildcard;
156 format.u.raw_audio.frame_rate=(float)sampleRate;
157 format.u.raw_audio.channel_count=numChannels;
158 format.u.raw_audio.format=(sampleSize / 8) & 0xF;
159 format.u.raw_audio.byte_order=B_MEDIA_HOST_ENDIAN;
160 format.u.raw_audio.buffer_size=(channels * samplesPerSecond * (bitsPerSample/8))/10; // 1/10 sec buffer
161 }
162
Load(const PFilePath & filename)163 PBoolean PSound::Load(const PFilePath & filename)
164 {
165 // format is a reference to the formatInfo member which stores info
166 // about the media format. This is needed for writing the data back
167 // or for playing the sound.
168 media_format &format=*(media_format *)(const BYTE *)formatInfo;
169
170 // Create BEntry from file name
171 BEntry entry(filename, true);
172 if ((dwLastError=entry.InitCheck())!=B_OK)
173 {
174 STATUS("entry.InitCheck()");
175 return PFalse;
176 }
177
178 // Create entry_ref from BEntry
179 entry_ref ref;
180 if ((dwLastError=entry.GetRef(&ref))!=B_OK)
181 {
182 STATUS("entry.GetRef()");
183 return PFalse;
184 }
185
186 // Create BMediaFile for read access from the entry_ref
187 BMediaFile file(&ref);
188 if ((dwLastError=file.InitCheck())!=B_OK)
189 {
190 STATUS("file.InitCheck()");
191 return PFalse;
192 }
193
194 // Search for the first media track that can be decoded
195 BMediaTrack *ptrack = NULL;
196 for (int index=0; (index<file.CountTracks()) && (dwLastError==B_OK); index++)
197 {
198 ptrack = file.TrackAt(index);
199 if (ptrack)
200 {
201 dwLastError = ptrack->InitCheck();
202 }
203 else
204 {
205 dwLastError = B_ERROR; //todo: change error code
206 }
207
208 if (dwLastError==B_OK)
209 {
210 // Get media format; we're looking for a raw audio track.
211 format.type = B_MEDIA_RAW_AUDIO;
212 format.u.raw_audio = media_raw_audio_format::wildcard;
213 dwLastError = ptrack->DecodedFormat(&format);
214
215 if ((dwLastError==B_OK) && (format.type==B_MEDIA_RAW_AUDIO))
216 {
217 break; // found a decodable track
218 }
219 }
220 else
221 {
222 STATUS("TrackAt() failed, error");
223 }
224
225 // if we found a track and arrived at this point, the track we found
226 // was not decodable
227 if (ptrack)
228 {
229 dwLastError=file.ReleaseTrack(ptrack); // destroys ptrack
230 }
231 }
232
233 // if an error occurred during track scanning, leave now
234 if (dwLastError!=B_OK)
235 {
236 return PFalse;
237 }
238
239 // Get a reference to the raw output format
240 media_raw_audio_format &rawformat = format.u.raw_audio;
241
242 // Fill in our fields from the format
243 sampleSize = (rawformat.format & 0xF) * 8;
244 numChannels = rawformat.channel_count;
245 if (rawformat.frame_rate>0.0 && rawformat.frame_rate<=(float)0xFFFFFFFFU)
246 {
247 sampleRate = (unsigned)(rawformat.frame_rate);
248 }
249 else
250 {
251 // unknown or unrepresentable sample rate.
252 // It's not really documented what we should do in this case but
253 // it probably doesn't matter either...
254 sampleRate = 0;
255 }
256
257 // Get the number of frames for the track and determine how much
258 // memory we need to store the file's data
259 // The multiplication might overflow for huge files but we don't
260 // want to read them into memory anyway so I guess it's ok...
261 int64 numframes = ptrack->CountFrames();
262 int64 framesize = numChannels * (sampleSize/8);
263 int64 numbytes = numframes * framesize;
264
265 // Set the size of the object's data area
266 if (!SetSize(numbytes))
267 {
268 PRINT(("Can't set size of sound to %Ld", numbytes));
269 dwLastError = B_ERROR; //todo replace by better error code
270 return PFalse; // BMediaFile will destroy ptrack
271 }
272
273 // Read all frames into memory. NOTE: not thread safe!
274 BYTE* dest = GetPointer(); // destination pointer
275 int64 framecount = numframes; // number of frames left to read
276 int64 framesread; // number of actual frames done
277 while ((framecount!=0) && (dwLastError==B_OK))
278 {
279 framesread = framecount;
280 dwLastError = ptrack->ReadFrames(dest, &framesread);
281 dest += framesread * framesize;
282 framecount -= framesread;
283 }
284
285 // return true for success
286 return (dwLastError==B_OK); // BMediaFile will destroy ptrack
287 }
288
289
Save(const PFilePath & filename)290 PBoolean PSound::Save(const PFilePath & filename)
291 {
292 // format is a reference to the formatInfo member which stores info
293 // about the media format. This is needed for writing the data back
294 // or for playing the sound.
295 media_format &format=*(media_format *)(const BYTE *)formatInfo;
296
297 // Get the file type from the file name's extension; if none, use wav
298 PFilePathString filetype=filename.GetType(); // e.g. ".wav"
299 if (filetype=="")
300 {
301 filetype="wav";
302 }
303 else
304 {
305 filetype=filetype.Mid(1); // cut off the '.'
306 }
307
308 // Try to find the file format in BeOS's list of formats
309 media_file_format mfi;
310 int32 cookie=0;
311 while ((dwLastError=get_next_file_format(&cookie, &mfi))==B_OK)
312 {
313 if (!strcasecmp(mfi.file_extension, (const char *)filetype))
314 {
315 break;
316 }
317 }
318 if (dwLastError!=B_OK)
319 {
320 // didn't find file format
321 PRINT(("Couldn't find media_file_format for \"%s\"", (const char *)filetype));
322 return PFalse;
323 }
324
325 // Create BEntry from file name
326 BEntry entry(filename, true);
327 if ((dwLastError=entry.InitCheck())!=B_OK)
328 {
329 STATUS("entry.InitCheck()");
330 return PFalse;
331 }
332
333 // Create entry_ref from BEntry
334 entry_ref ref;
335 if ((dwLastError=entry.GetRef(&ref))!=B_OK)
336 {
337 STATUS("entry.GetRef()");
338 return PFalse;
339 }
340
341 // Create BMediaFile for write access from the entry_ref
342 BMediaFile file(&ref, &mfi, B_MEDIA_FILE_REPLACE_MODE);
343 if ((dwLastError=file.InitCheck())!=B_OK)
344 {
345 STATUS("file.InitCheck()");
346 return PFalse;
347 }
348
349 // Find an encoder. The input format is the format we have stored in
350 // our formatInfo member.
351 cookie=0;
352 media_format outformat;
353 media_codec_info mci,validmci,rawmci, *pmci;
354 bool found_encoder = false;
355 bool found_raw_encoder = false;
356 while (get_next_encoder(&cookie, &mfi, &format, &outformat, &mci)==B_OK)
357 {
358 found_encoder=true;
359
360 if (outformat.type==B_MEDIA_RAW_AUDIO)
361 {
362 rawmci=mci;
363 found_raw_encoder=true;
364 }
365 else
366 {
367 validmci=mci;
368 }
369 }
370
371 // Choose an encoder:
372 // If a raw-output encoder was found, use it.
373 // Else, use the last found encoded-output encoder, if any.
374 // This method of choosing will make sure that most file formats
375 // will get the most common encoding (PCM) whereas it's still possible
376 // to choose another output format like MP3, if so dictated by the
377 // file format.
378 // BeOS is smart enough not to return an encoder that produces raw audio
379 // for e.g. the MP3 file format, but it knows that there are many ways
380 // to encode e.g. a WAV file and we don't want to put anything
381 // unexpected into a WAV file, do we?
382 BMediaTrack *ptrack = NULL;
383 if (found_encoder)
384 {
385 if (found_raw_encoder)
386 {
387 PRINT(("Using raw encoder"));
388 pmci=&rawmci;
389 }
390 else
391 {
392 // don't use mci instead of validmci,
393 // it could be unreliable after the last call to get_next_encoder
394 PRINT(("Using non-raw encoder"));
395 pmci=&validmci;
396 }
397
398 // Create a BMediaTrack in the file using the selected encoder
399 ptrack = file.CreateTrack(&format, pmci);
400 if (ptrack)
401 {
402 dwLastError = ptrack->InitCheck();
403 }
404 else
405 {
406 dwLastError = B_ERROR; //todo: change error code
407 }
408 }
409 else
410 {
411 dwLastError=B_ERROR; //todo: change error code
412 }
413
414 if (dwLastError!=B_OK)
415 {
416 STATUS("Encoder not found or file.CreateTrack() error");
417 return PFalse; // BMediaFile will destroy ptrack
418 }
419
420 // We're only creating one track so commit the header now
421 if ((dwLastError = file.CommitHeader())!=B_OK)
422 {
423 STATUS("file.CommitHeader()");
424 return PFalse;
425 }
426
427 // Determine how many frames we have to write
428 // There is a small possibility of a divide by zero but this only
429 // happens if the object is not properly initialized.
430 PINDEX numbytes = GetSize();
431 int32 framesize = numChannels * (sampleSize/8);
432 int32 numframes = numbytes / framesize; // divide by zero possibility ignored.
433
434 if ((dwLastError=ptrack->WriteFrames((const BYTE *)*this, numframes))!=B_OK)
435 {
436 STATUS("ptrack->WriteFrames()");
437 return PFalse; // BMediaFile will destroy ptrack
438 }
439
440 return (file.CloseFile()==B_OK); // BMediaFile will destroy ptrack
441 }
442
Play()443 PBoolean PSound::Play()
444 {
445 PSoundChannelBeOS player(PSoundChannelBeOS::GetDefaultDevice(PSoundChannelBeOS::Player), PSoundChannelBeOS::Player, numChannels, sampleRate, sampleSize);
446
447 if (!player.IsOpen())
448 {
449 PRINT(("PSoundChannelBeOS constructor failed to open"));
450 return PFalse;
451 }
452
453 return player.PlaySound(*this, PTrue);
454 }
455
PlayFile(const PFilePath & file,PBoolean wait)456 PBoolean PSound::PlayFile(const PFilePath & file, PBoolean wait)
457 {
458 entry_ref ref;
459 status_t err; // can't use dwLastError because this function is static
460
461 // using pointers for these objects so that we don't have to
462 // construct them here but can nevertheless use the if(ok)'s
463 BEntry *pentry = NULL;
464
465 {
466 // Create BEntry from file name
467 pentry = new BEntry(file, true);
468 err = pentry->InitCheck();
469 }
470
471 if (err==B_OK)
472 {
473 // Create entry_ref from BEntry
474 err = pentry->GetRef(&ref);
475 }
476
477 if (err==B_OK)
478 {
479 // Play the sound. Return value is a handle or a negative value for errors
480 // Errors in BeOS are always negative values
481 err=play_sound(&ref, true, !wait, wait);
482 if (err>=0)
483 {
484 err=B_OK;
485 }
486 }
487
488 return (err==B_OK);
489 }
490
Beep()491 void PSound::Beep()
492 {
493 ::beep();
494 }
495
496 ////////////////////////////////////////////////////////////////////////////////
497 // CircularBuffer
498
499 class Guard
500 {
501 private:
502 sem_id mSem;
503 public:
Guard(sem_id sem)504 Guard(sem_id sem) { acquire_sem(mSem=sem); }
~Guard()505 ~Guard() { release_sem(mSem); }
506 };
507
508 /*
509 This class represents a circular FIFO buffer.
510 The buffer has a head and a tail that chase each other.
511 The data is added to the buffer at the tail side by using Fill.
512 The data from the buffer can be read starting at the head side using
513 Drain.
514 It is possible to use two threads to fill and drain the buffer but
515 there should not be more than 2 threads doing draining and filling.
516 Resetting (flushing) or destroying from a third thread is allowed;
517 do make sure that any threads that operate on buffer data are stopped
518 before destroying a buffer.
519 Normally, filling and draining operations block the thread as short as
520 possible (i.e. only when the other thread needs to update the head and
521 tail pointers etc). If the filling thread tries to put data into a full
522 or almost full buffer, it just returns after filling as much data as
523 it can, and if the draining thread tries to get more data out than is
524 in the buffer, it will simply return with the data that is there.
525 In order to move all the data from an external buffer into an object
526 of this class, the caller would have to call Fill repeatedly until
527 all the data has been processed (similarly it would have to call Drain
528 until it receives sufficient data). But if the application has nothing
529 else to do in the mean time, this constitutes a Busy Waiting loop
530 on either the filling or draining side of the FIFO buffer that slurps
531 up as much CPU time as possible.
532 To improve this behaviour, it's possible to specify a threshold value
533 that is used to change the state to FullEnough and EmptyEnough. By using
534 these states (instead of Full and Empty), one thread can block until the
535 other thread has determined that there is enough data or enough room for
536 data.
537 */
538 class CircularBuffer
539 {
540 public:
541 // Internal state for the buffer
542 // Note the nifty bit patterns for comparing the current state
543 // with a desired state
544 typedef enum
545 { // Headspace Tailspace
546 Empty =1, // 0 size
547 Filled =2, // 0<h<size 0<t<size
548 NotFull =3, // 0<=h<size 0<t<=size (for comparing)
549 Full =4, // size 0
550 NotEmpty =6, // 0<h<=size 0<=t<size (for comparing)
551 Flushed =8, // (extra signal to threads waiting on full)
552 FullEnough =16, // h>=drainthreshold
553 EmptyEnough =32, // t>=fillthreshold
554 } State;
555
556 protected:
557 friend class ResamplingBuffer; // needed for one of their constructors
558
559 BYTE *mBuffer; // the buffer
560 PINDEX mSize; // size of the buffer in bytes
561
562 volatile PINDEX mHead; // index where to start reading
563 volatile PINDEX mTail; // index where to start writing
564 volatile PINDEX mHeadRoom; // consecutive space from head to end-of-buffer or tail
565 volatile PINDEX mTailRoom; // consecutive space from tail to end-of-buffer or head
566 volatile PINDEX mSizeUsed; // total bytes in use
567 volatile PINDEX mFillThreshold; // see above
568 volatile PINDEX mDrainThreshold; // see above
569
570 volatile State mState; // current state of the buffer
571
572 sem_id mSemInUse; // used to guard data integrity
573 sem_id mSemStateChange; // used to wait for state changes
574
575 protected:
576 // Check if the state changed. Private because it's not guarded by semaphore
UpdateState(void)577 void UpdateState(void)
578 {
579 // Determine current state
580 State newstate;
581
582 if (mSizeUsed==mSize)
583 {
584 PRINTCB(("State is FULL"));
585 newstate=Full;
586 }
587 else if (mSizeUsed==0)
588 {
589 PRINTCB(("State is EMPTY"));
590 newstate=Empty;
591 }
592 else
593 {
594 PRINTCB(("State is FILLED"));
595 newstate=Filled;
596 }
597
598 // Check thresholds
599 if (mSize-mSizeUsed>=mFillThreshold)
600 {
601 PRINTCB(("...and EMPTYENOUGH"));
602 newstate=(State)(newstate | EmptyEnough);
603 }
604 if (mSizeUsed>=mDrainThreshold)
605 {
606 PRINTCB(("...and FULLENOUGH"));
607 newstate=(State)(newstate | FullEnough);
608 }
609
610 // Check if the state changed
611 if (newstate!=mState)
612 {
613 PRINTCB(("Updating state from %X to %X", mState, newstate));
614
615 // Set the new state
616 mState=newstate;
617
618 // Signal state change
619 release_sem(mSemStateChange);
620 }
621 }
622
Write(BYTE * dest,const BYTE ** extbuf,size_t size,size_t * extsize)623 virtual size_t Write(
624 BYTE *dest, // destination
625 const BYTE **extbuf, // source, to be updated
626 size_t size, // space in destination
627 size_t *extsize) // data in source, to be updated
628 {
629 // This function is called to put data into the buffer
630 size_t todo=MIN(size, *extsize);
631 memcpy(dest, *extbuf, todo);
632 *extbuf +=todo; // The external pointer moves forward...
633 *extsize -=todo; // ... so the remaining size decreases
634 return todo;
635 }
636
Read(BYTE ** extbuf,const BYTE * src,size_t * extsize,size_t size)637 virtual size_t Read(
638 BYTE **extbuf, // destination, to be updated
639 const BYTE *src, // source
640 size_t *extsize, // space in destination, to be updated
641 size_t size) // data in source
642 {
643 // This function is called to read data out of the buffer
644 size_t todo=MIN(size, *extsize);
645 memcpy(*extbuf, src, todo);
646 *extbuf +=todo; // The external pointer moves forward...
647 *extsize -=todo; // ... so the remaining size decreases
648 return todo;
649 }
650
651 public:
652 // Reset buffer so that it can be filled again
Reset(void)653 void Reset(void)
654 {
655 Guard _(mSemInUse); // guard data integrity
656
657 mHead=mHeadRoom=mTail=mSizeUsed=0;
658 mTailRoom=GetSize();
659 mState=(State)(Flushed|Empty|EmptyEnough);
660 }
661
662 // Constructor
CircularBuffer(PINDEX size,PINDEX fillthreshold=0,PINDEX drainthreshold=0)663 CircularBuffer(
664 PINDEX size,
665 PINDEX fillthreshold = 0,
666 PINDEX drainthreshold = 0)
667 : mFillThreshold(fillthreshold), mDrainThreshold(drainthreshold), mState(Empty)
668 {
669 PAssert(size!=0, "Attempting to create a buffer with size 0");
670
671 mSemInUse=create_sem(1, "mSemInUse");
672 mSemStateChange=create_sem(0, "mSemStateChange");
673
674 PAssert(mSemInUse>=0 && mSemStateChange>=0, "Unable to create semaphores");
675
676 mBuffer=new BYTE[(mSize=size)];
677
678 Reset();
679 }
680
681 // Destructor
~CircularBuffer()682 virtual ~CircularBuffer()
683 {
684 // make sure the in-use semaphore is free and stays free
685 while (acquire_sem_etc(mSemInUse,1,B_RELATIVE_TIMEOUT,0)==B_WOULD_BLOCK)
686 {
687 // nothing to do, just busy-wait
688 }
689
690 delete_sem(mSemInUse);
691
692 delete_sem(mSemStateChange);
693
694 Reset();
695
696 if(mBuffer)
697 delete[] mBuffer;
698 }
699
700 // Check if buffer is empty
IsEmpty()701 bool IsEmpty() { return (mState==Empty); }
702
703 // Check if buffer is full
IsFull()704 bool IsFull() { return (mState==Full); }
705
706 // Get the size of the buffer
GetSize(void)707 PINDEX GetSize(void) { return mSize; }
708
709 // Wait asynchronously for a buffer state or one of a number of states
WaitForState(State state)710 void WaitForState(State state)
711 {
712 PRINTCB(("Waiting for state %X, current state=%X this=%p", state, mState, this));
713 // reset the Flushed bit so it only stops the loop if the buffer
714 // is flushed DURING an operation
715 {
716 Guard _(mSemInUse);
717 mState=(State)(mState & ~Flushed);
718 }
719 for(;;)
720 {
721 if ((mState & (state|Flushed))!=0) // bit patterns allowed
722 {
723 PRINTCB(("Detected state %X, wanted %X, returning", mState, state));
724 return;
725 }
726 PRINTCB(("Waiting for %X; headroom=%u tailroom=%u this=%p",state,mHeadRoom,mTailRoom,this));
727 // To prevent a race condition here in case the state
728 // gets changed just after the GetState call, the next
729 // semaphore call has a timeout.
730 acquire_sem_etc(mSemStateChange,1,B_RELATIVE_TIMEOUT,1000000);
731 }
732 }
733
734 // Fill buffer with data.
Fill(const BYTE ** extbuf,size_t * extsize)735 void Fill(const BYTE **extbuf, size_t *extsize)
736 {
737 PRINTCB(("start: head %d tail %d headroom %d tailroom %d extsize %d buffer %p this %p", mHead, mTail, mHeadRoom, mTailRoom, *extsize, mBuffer, this));
738
739 // Make a local copy of the queue.
740 // This is ok because there is only one filler thread and
741 // one drainer thread. The drainer is not going to make the
742 // free area for the filler any smaller and the filler is not
743 // going to overwrite the drainer's data if we do this.
744 // This way we can keep the semaphore busy as short as possible.
745 PINDEX lTail;
746 PINDEX lTailRoom;
747 PINDEX lHead; // read only
748 {
749 Guard _(mSemInUse); // guard data integrity
750 lTail=mTail;
751 lTailRoom=mTailRoom;
752 lHead=mHead;
753 }
754
755 bool needhousekeeping=false;
756 PINDEX totaldone=0;
757
758 while (*extsize!=0 && lTailRoom!=0 && totaldone<mSize)
759 {
760 needhousekeeping=true;
761
762 PINDEX done=Write(
763 mBuffer+lTail,
764 extbuf,
765 lTailRoom,
766 extsize);
767
768 totaldone +=done;
769
770 lTail +=done; // The tail moves forward...
771 lTailRoom -=done; // ... so there will be less room at the tail
772
773 // Check if we should wrap around
774 if (lTail==mSize)
775 {
776 lTail=0;
777 lTailRoom=lHead;
778 }
779 }
780
781 if (needhousekeeping)
782 {
783 Guard _(mSemInUse);
784
785 // Copy the local values back
786 mTail=lTail;
787 mTailRoom=lTailRoom;
788 mSizeUsed+=totaldone;
789
790 // Recalculate headroom
791 if (mTail>mHead)
792 {
793 mHeadRoom=mTail-mHead;
794 }
795 else
796 {
797 mHeadRoom=mSize-mHead;
798 }
799
800 // Check if we need to change the state
801 UpdateState();
802
803 PRINTCB((" end: head %d tail %d headroom %d tailroom %d extsize %d", mHead, mTail, mHeadRoom, mTailRoom, *extsize));
804 }
805 }
806
807 // Empty data out of buffer
Drain(BYTE ** extbuf,size_t * extsize)808 void Drain(BYTE **extbuf, size_t *extsize)
809 {
810 PTRACE(7, "Drain: head " << mHead
811 << " tail " << mTail
812 << " headroom " << mHeadRoom
813 << " tailroom " << mTailRoom
814 << " extsize " << *extsize
815 << " buffer " << mBuffer
816 << " this " << this);
817
818 // Make a local copy of the queue.
819 // This is ok because there is only one filler thread and
820 // one drainer thread. The drainer is not going to make the
821 // free area for the filler any smaller and the filler is not
822 // going to overwrite the drainer's data if we do this.
823 // This way we can keep the semaphore busy as short as possible.
824 PINDEX lHead;
825 PINDEX lHeadRoom;
826 PINDEX lTail; // read only
827 {
828 Guard _(mSemInUse); // guard data integrity
829 lHead=mHead;
830 lHeadRoom=mHeadRoom;
831 lTail=mTail;
832 }
833
834 bool needhousekeeping=false;
835 PINDEX totaldone=0;
836
837 while (*extsize!=0 && lHeadRoom!=0 && totaldone<mSize)
838 {
839 needhousekeeping=true;
840
841 size_t done=Read(
842 extbuf,
843 mBuffer+lHead,
844 extsize,
845 lHeadRoom);
846
847 totaldone +=done;
848
849 lHead +=done; // The head moves forward...
850 lHeadRoom -=done; // ... so there will be less room at the head
851
852 // Check if we should wrap around
853 if (lHead==mSize)
854 {
855 lHead=0;
856 lHeadRoom=mTail;
857 }
858 }
859
860 if (needhousekeeping)
861 {
862 Guard _(mSemInUse);
863
864 // Copy the local values back
865 mHead=lHead;
866 mHeadRoom=lHeadRoom;
867 mSizeUsed-=totaldone;
868
869 // Recalculate tailroom
870 if (mHead>mTail)
871 {
872 mTailRoom=mHead-mTail;
873 }
874 else
875 {
876 mTailRoom=GetSize()-mTail;
877 }
878
879 // Check if we need to change the state
880 UpdateState();
881
882 PRINTCB((" end: head %d tail %d headroom %d tailroom %d extsize %d", mHead, mTail, mHeadRoom, mTailRoom, *extsize));
883 }
884 }
885 };
886
887 ////////////////////////////////////////////////////////////////////////////////
888
889 class ResamplingBuffer : public CircularBuffer
890 {
891 protected:
892 Resampler *mResampler;
893
894 protected:
Write(BYTE * dest,const BYTE ** extbuf,size_t size,size_t * extsize)895 virtual size_t Write(
896 BYTE *dest, // destination
897 const BYTE **extbuf, // source, to be updated
898 size_t size, // space in destination
899 size_t *extsize) // data in source, to be updated
900 {
901 size_t todo=*extsize/mResampler->InFrameSize();
902 size_t done=mResampler->InFrames(
903 (const short **)extbuf,
904 (short **)&dest,
905 &todo,
906 size/mResampler->OutFrameSize());
907 done*=mResampler->OutFrameSize();
908 *extsize=todo*mResampler->InFrameSize();
909
910 return done;
911 }
912
913 public:
SetResampler(Resampler * resampler)914 void SetResampler(Resampler *resampler)
915 {
916 Guard _(mSemInUse); // guard data integrity
917
918 mResampler=resampler;
919 }
920
ResamplingBuffer(Resampler * resampler,PINDEX size,PINDEX fillthreshold=0,PINDEX drainthreshold=0)921 ResamplingBuffer(
922 Resampler *resampler,
923 PINDEX size,
924 PINDEX fillthreshold=0,
925 PINDEX drainthreshold=0)
926 : CircularBuffer(size, fillthreshold, drainthreshold), mResampler(NULL)
927 {
928 SetResampler(resampler);
929 }
930
ResamplingBuffer(Resampler * resampler,CircularBuffer * other)931 ResamplingBuffer(
932 Resampler *resampler,
933 CircularBuffer *other)
934 : CircularBuffer(other->mSize, other->mFillThreshold, other->mDrainThreshold), mResampler(NULL)
935 {
936 SetResampler(resampler);
937 }
938 };
939
940 ////////////////////////////////////////////////////////////////////////////////
PlayBuffer(void * cookie,void * buffer,size_t size,const media_raw_audio_format & format)941 static void PlayBuffer(void *cookie, void *buffer, size_t size, const media_raw_audio_format &format)
942 {
943 // This function is called by the BSoundPlayer object whenever it needs some more
944 // data to play.
945 DETECTVARS(buffer, size/2)
946
947 ((CircularBuffer *)cookie)->Drain((BYTE **)&buffer, &size);
948
949 DETECTSOUND();
950 }
951
RecordBuffer(void * cookie,const void * buffer,size_t size,const media_header & header)952 static void RecordBuffer(void *cookie, const void *buffer, size_t size, const media_header &header)
953 {
954 // This function is called by the BMediaRecorder object whenever it has a buffer
955 // with recorded data ready.
956 DETECTVARS(buffer, size/2)
957 DETECTSOUND();
958
959 ((CircularBuffer *)cookie)->Fill((const BYTE **)&buffer, &size);
960 }
961
962 ////////////////////////////////////////////////////////////////////////////////
963 // PSoundChannelBeOS
964
965 // This defines the number of times we would like to be called per second
966 // to play/record data
967 #define PLAYRECFREQ 20
968
969 // Macro to let the default buffer size correspond neatly with the
970 // setting we put into the format.
971 #define DEFAULT_BUFSIZE(channels, rate, bits) 480
972 //((channels*rate*(bits/8))/PLAYRECFREQ)
973
PSoundChannelBeOS()974 PSoundChannelBeOS::PSoundChannelBeOS() :
975 mRecorder(NULL),
976 mPlayer(NULL),
977 mBuffer(NULL),
978 mNumBuffers(1),
979 mResampler(NULL)
980 {
981 PRINT(("default constructor"));
982
983 InternalSetBuffers(DEFAULT_BUFSIZE(1, 8000, 16),DEFAULT_BUFSIZE(1, 8000, 16)/2);
984 SetFormat(1, 8000, 16);
985
986 // Nothing else to do here. Notice that the channel is not open for
987 // playing/recording yet.
988 }
989
990
PSoundChannelBeOS(const PString & dev,Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)991 PSoundChannelBeOS::PSoundChannelBeOS(const PString & dev,
992 Directions dir,
993 unsigned numChannels,
994 unsigned sampleRate,
995 unsigned bitsPerSample) :
996 mRecorder(NULL),
997 mPlayer(NULL),
998 mBuffer(NULL),
999 mNumBuffers(1),
1000 mResampler(NULL)
1001 {
1002 PRINT(("constructor %s %u %u %u", dir==Player ? "Player" : "Recorder", numChannels, sampleRate, bitsPerSample));
1003
1004 InternalSetBuffers(DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample), DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample)/2);
1005 Open(dev, dir, numChannels, sampleRate, bitsPerSample);
1006 // ignore result; user will need to find out whether this succeeds using IsOpen
1007 }
1008
1009
~PSoundChannelBeOS()1010 PSoundChannelBeOS::~PSoundChannelBeOS()
1011 {
1012 PRINT((""));
1013
1014 Close(); // destroys player and recorder
1015 InternalSetBuffers(0,0); // destroys buffer
1016 }
1017
GetRecorderDevicesList(BMediaRecorder * Recorder)1018 static const PStringArray GetRecorderDevicesList(BMediaRecorder *Recorder)
1019 {
1020 // Array to hold the list.
1021 PStringArray devlist;
1022 BMediaRecorder* bRecorder = NULL;
1023
1024 if(Recorder != NULL)
1025 bRecorder = Recorder;
1026
1027 #ifdef MEDIA_KIT_UPDATE
1028 BMediaRecorder localRecorder("GetRecorderDevicesList");
1029 bool result=true;
1030 status_t status;
1031
1032 {
1033 if(bRecorder == NULL)
1034 {
1035 bRecorder = &localRecorder;
1036 }
1037
1038 if (bRecorder == NULL || bRecorder->InitCheck()!=B_OK)
1039 {
1040 PRINT(("Error constructing recorder to fetch device names"));
1041 result=false;
1042 }
1043 }
1044
1045 if (result)
1046 {
1047 media_format format;
1048 format.type = B_MEDIA_RAW_AUDIO;
1049 format.u.raw_audio=media_raw_audio_format::wildcard;
1050
1051 // The resampler can only handle 16-bit audio
1052 format.u.raw_audio.format=media_raw_audio_format::B_AUDIO_SHORT;
1053
1054 // Let the media recorder determine which sources are available
1055 if ((status = bRecorder->FetchSources(format, false))!=B_OK)
1056 {
1057 PRINT(("Couldn't fetch BMediaRecorder sources; status=%d", status));
1058 result=false;
1059 }
1060 }
1061
1062 if (result)
1063 {
1064 // Fetch the names of all output devices
1065 media_format format;
1066 BString outname;
1067 for (int i=0; i< bRecorder->CountSources(); i++)
1068 {
1069 if ((status = bRecorder->GetSourceAt(i, &outname, &format))==B_OK)
1070 {
1071 PRINT(("Device found: %s", outname.String()));
1072 devlist[i] = PString(outname.String());
1073 }
1074 else
1075 {
1076 PRINT(("error %d retrieving data for device %d", status, i));
1077 result=false;
1078 }
1079 }
1080 }
1081
1082 if (!result)
1083 {
1084 devlist.RemoveAll();
1085 }
1086
1087 return devlist;
1088 #else
1089 // Media Kit is the only device
1090 devlist[0] = "MediaKit";
1091 return devlist;
1092 #endif
1093 }
1094
GetDeviceNames(Directions dir)1095 PStringArray PSoundChannelBeOS::GetDeviceNames(Directions dir)
1096 {
1097 if (dir==Recorder)
1098 {
1099 return GetRecorderDevicesList(NULL);
1100 }
1101 else
1102 {
1103 // not supported yet
1104 return PStringArray("MediaKit");
1105 }
1106 }
1107
GetDefaultDevice(Directions dir)1108 PString PSoundChannelBeOS::GetDefaultDevice(Directions dir)
1109 {
1110 if (dir==Recorder)
1111 {
1112 const PStringArray &devlist = GetRecorderDevicesList(NULL);
1113
1114 if (devlist.GetSize()!=0)
1115 {
1116 return devlist[0];
1117 }
1118 else
1119 {
1120 return PString("MediaKit");
1121 }
1122 }
1123 else
1124 {
1125 // not supported yet
1126 return PString("MediaKit");
1127 }
1128 }
1129
OpenPlayer(void)1130 PBoolean PSoundChannelBeOS::OpenPlayer(void)
1131 {
1132 // We're using cascaded "if result"s here for clarity
1133 PBoolean result = PTrue;
1134
1135 #ifdef FILEDUMP
1136 media_format format;
1137 format.type=B_MEDIA_RAW_AUDIO;
1138 memcpy(&format.u.raw_audio, &mFormat, sizeof(mFormat));
1139
1140 delete playwriter;
1141 playwriter=new BAudioFileWriter("play.wav", format, 441000);
1142 #endif
1143
1144 // Must have a buffer
1145 if (!mBuffer)
1146 {
1147 result = PFalse;
1148 PRINT(("Trying to open as player without setting buffers first"));
1149 }
1150
1151 if (result)
1152 {
1153 // Create the player
1154 //was: mPlayer=new BSoundPlayer(&mFormat, NULL, PlayBuffer, NULL, mBuffer);
1155
1156 mPlayer = new BSoundPlayer(
1157 &mFormat,
1158 NULL,
1159 PlayBuffer,
1160 NULL,
1161 mBuffer);
1162
1163 if ((mPlayer == NULL) || (mPlayer->InitCheck() != B_OK))
1164 {
1165 result = PFalse;
1166 PRINT(("Couldn't construct player"));
1167 }
1168 }
1169
1170 if (result)
1171 {
1172 // Start the player
1173 if (mPlayer->Start() != B_OK)
1174 {
1175 result = PFalse;
1176 PRINT(("Couldn't start the player"));
1177 }
1178 }
1179
1180 if (result)
1181 {
1182 // Enable the fetching of data by PlayBuffer
1183 mPlayer->SetHasData(true);
1184 }
1185
1186 PRINT(("Returning %s", result?"success":"failure"));
1187 return result;
1188 }
1189
OpenRecorder(const PString & dev)1190 PBoolean PSoundChannelBeOS::OpenRecorder(const PString &dev)
1191 {
1192 // We're using cascaded "if result"s here for clarity
1193 PBoolean result=PTrue;
1194
1195 {
1196 if (!mBuffer)
1197 {
1198 result=PFalse;
1199 PRINT(("Trying to open as recorder without setting buffers first"));
1200 }
1201 }
1202
1203 if (result)
1204 {
1205 // Create the recorder
1206 mRecorder=new BMediaRecorder("PWLIB PSoundChannel recorder");
1207
1208 if ((mRecorder==NULL) || (mRecorder->InitCheck()!=B_OK))
1209 {
1210 result=PFalse;
1211 PRINT(("Couldn't construct recorder"));
1212 }
1213 }
1214
1215 #ifdef MEDIA_KIT_UPDATE
1216 int32 sourceindex;
1217 if (result)
1218 {
1219 // Find the specified device in the list of input devices
1220 PINDEX x=GetRecorderDevicesList(mRecorder).GetStringsIndex(dev);
1221 if (x==P_MAX_INDEX)
1222 {
1223 result=PFalse;
1224 PRINT(("Couldn't find device %s in the list",(const char *)dev));
1225 }
1226 else
1227 {
1228 sourceindex=(int32)x;
1229 }
1230 }
1231
1232 #ifdef _DEBUG
1233 if (result)
1234 {
1235 // Get information for the device
1236 BString outname;
1237 media_format xformat;
1238 status_t err;
1239
1240 if ((err=mRecorder->GetSourceAt(sourceindex, &outname, &xformat))==B_OK)
1241 {
1242 PRINT(("%s", outname.String()));
1243 PRINT((" type %d", (int)xformat.type));
1244 PRINT((" AudioFormat 0x%X", (int)xformat.AudioFormat()));
1245 PRINT((" u.raw_audio:"));
1246 PRINT((" frame_rate: %f", xformat.u.raw_audio.frame_rate));
1247 PRINT((" channel_count: %d", xformat.u.raw_audio.channel_count));
1248 PRINT((" byte_order: %d", xformat.u.raw_audio.byte_order));
1249 PRINT((" buffer_size: %d", xformat.u.raw_audio.buffer_size));
1250 }
1251 else
1252 {
1253 result=PFalse;
1254 PRINT(("couldn't get details for source %d: err=0x%X",sourceindex,err));
1255 }
1256 }
1257 #endif
1258
1259 if (result)
1260 {
1261 // Try to connect to the source
1262 if (mRecorder->ConnectSourceAt(sourceindex)!=B_OK)
1263 {
1264 result=PFalse;
1265 PRINT(("Couldn't connect BMediaRecorder to source"));
1266 }
1267 }
1268
1269 #else
1270 if (result)
1271 {
1272 // Connect the recorder to the default input device
1273 media_format format;
1274 format.type=B_MEDIA_RAW_AUDIO;
1275 format.u.raw_audio=media_raw_audio_format::wildcard;
1276 // The resampler can only handle 16-bit audio
1277 format.u.raw_audio.format=media_raw_audio_format::B_AUDIO_SHORT;
1278 if (mRecorder->Connect(format,0)!=B_OK)
1279 {
1280 result=PFalse;
1281 PRINT(("couldn't connect the recorder to the default source"));
1282 }
1283 }
1284 #endif
1285
1286 if (result)
1287 {
1288 // Create resampler
1289 media_format format=mRecorder->Format();
1290
1291 delete mResampler;
1292 mResampler=new Resampler(
1293 format.u.raw_audio.frame_rate,
1294 mFormat.frame_rate,
1295 format.u.raw_audio.channel_count,
1296 mFormat.channel_count,
1297 0,
1298 2);
1299
1300 #ifdef FILEDUMP
1301 {
1302 media_format format;
1303 format.type=B_MEDIA_RAW_AUDIO;
1304 memcpy(&format.u.raw_audio, &mFormat, sizeof(mFormat));
1305
1306 delete recwriter;
1307 recwriter=new BAudioFileWriter("record.wav", format);
1308 }
1309 #endif
1310
1311 // If the current buffer is not a resamplin buffer, re-create it
1312 ResamplingBuffer *buf=dynamic_cast<ResamplingBuffer*>(mBuffer);
1313
1314 if (buf==NULL)
1315 {
1316 PRINT(("re-creating buffer"));
1317
1318 CircularBuffer *old=mBuffer;
1319 mBuffer=new ResamplingBuffer(mResampler, old);
1320 delete old;
1321 }
1322 else
1323 {
1324 buf->SetResampler(mResampler);
1325 }
1326 }
1327
1328 if (result)
1329 {
1330 // Set the hook function to our data processing function
1331 PRINT(("Setting buffer hook, cookie=%p",mBuffer));
1332 if (mRecorder->SetBufferHook(RecordBuffer, mBuffer)!=B_OK)
1333 {
1334 result=PFalse;
1335 PRINT(("Couldn't set buffer hook on BMediaRecorder"));
1336 }
1337 }
1338
1339 // If something went wrong, delete the recorder.
1340 if (!result)
1341 {
1342 if (mRecorder)
1343 {
1344 delete mRecorder;
1345 mRecorder=NULL;
1346 }
1347 }
1348
1349 return result;
1350 }
1351
Open(const PString & dev,Directions dir,unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)1352 PBoolean PSoundChannelBeOS::Open(const PString & dev,
1353 Directions dir,
1354 unsigned numChannels,
1355 unsigned sampleRate,
1356 unsigned bitsPerSample)
1357 {
1358 // We're using cascaded "if result"s here for clarity
1359 PBoolean result = PTrue;
1360 PRINT(("%s %u %u %u", dir==Player?"Player":"Recorder", numChannels, sampleRate, bitsPerSample));
1361
1362 // Close the channel first, just in case
1363 Close();
1364
1365 // Initialize the format struct, necessary to create player or recorder
1366 if (!SetFormat(numChannels, sampleRate, bitsPerSample))
1367 {
1368 result = PFalse;
1369 PRINT(("Couldn't set format"));
1370 }
1371
1372 if (result)
1373 {
1374 switch (dir)
1375 {
1376 case Player:
1377 PRINT(("... trying to open player"));
1378 result=OpenPlayer();
1379 break;
1380
1381 case Recorder:
1382 PRINT(("...trying to open recorder"));
1383 result=OpenRecorder(dev);
1384 break;
1385
1386 default:
1387 PRINT(("Unknown direction parameter"));
1388 result=PFalse;
1389 }
1390 }
1391
1392 if (!result)
1393 {
1394 // If anything went wrong, clean up
1395 PRINT(("... can't open, cleaning up"));
1396 Close();
1397 }
1398
1399 ::snooze(1*1000*1000);
1400
1401 PRINT(("Returning %s", result?"success":"failure"));
1402 return result;
1403 }
1404
Abort()1405 PBoolean PSoundChannelBeOS::Abort()
1406 {
1407 return PFalse;
1408 }
1409
1410
SetFormat(unsigned numChannels,unsigned sampleRate,unsigned bitsPerSample)1411 PBoolean PSoundChannelBeOS::SetFormat(unsigned numChannels,
1412 unsigned sampleRate,
1413 unsigned bitsPerSample)
1414 {
1415 PRINT(("%u %u %u", numChannels, sampleRate, bitsPerSample));
1416
1417 // NOTE: all constructors should call this to initialize
1418 // the local members
1419 // Do NOT call the function with any parameter set to 0!
1420
1421 // The function only fails if the channel is open.
1422 // This is because the player or recorder needs to be re-created when the
1423 // format changes.
1424 if (IsOpen())
1425 {
1426 PRINT(("Not allowed to set format on open channel"));
1427 return PFalse;
1428 }
1429
1430 // Initialize the format struct
1431 // The numbers of bits that we support here are 8, 16 or 32 bits (signed),
1432 // results for other sizes are not defined.
1433 mFormat = media_raw_audio_format::wildcard;
1434 mFormat.frame_rate=(float)sampleRate;
1435 mFormat.channel_count=numChannels;
1436 mFormat.format=(bitsPerSample / 8) & 0xF;
1437 mFormat.byte_order=B_HOST_IS_BENDIAN ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
1438 mFormat.buffer_size=DEFAULT_BUFSIZE(numChannels, sampleRate, bitsPerSample);
1439
1440 return PTrue;
1441 }
1442
1443
GetChannels() const1444 unsigned PSoundChannelBeOS::GetChannels() const
1445 {
1446 return mFormat.channel_count;
1447 }
1448
1449
GetSampleRate() const1450 unsigned PSoundChannelBeOS::GetSampleRate() const
1451 {
1452 return (unsigned)mFormat.frame_rate;
1453 }
1454
1455
GetSampleSize() const1456 unsigned PSoundChannelBeOS::GetSampleSize() const
1457 {
1458 return (mFormat.format & 0xF)*8; // return number of BITS
1459 }
1460
1461
Read(void * buf,PINDEX len)1462 PBoolean PSoundChannelBeOS::Read(void *buf, PINDEX len)
1463 {
1464 PINDEX bufSize = len;
1465
1466 // Can only read from a recorder
1467 if (mRecorder!=NULL)
1468 {
1469 // A Read starts the recording, if it's not running already
1470 if (!mRecorder->IsRunning())
1471 {
1472 mRecorder->Start();
1473 }
1474
1475 // Wait until there's a buffer recorded
1476 mBuffer->WaitForState(CircularBuffer::NotEmpty);
1477
1478 #ifdef FILEDUMP
1479 void *dumpbuf=buf;
1480 size_t dumpsize=len;
1481 #endif
1482 lastReadCount = 0;
1483
1484 while(lastReadCount < bufSize)
1485 {
1486 len = bufSize - lastReadCount;
1487 if(len <= 0)
1488 break;
1489
1490 // Get data from the buffer
1491 mBuffer->Drain((BYTE**)&buf, (size_t*) &len);
1492
1493 lastReadCount += len;
1494 }
1495
1496 #ifdef FILEDUMP
1497 if (recwriter)
1498 {
1499 recwriter->writewavfile(dumpbuf, dumpsize);
1500 }
1501 #endif
1502
1503 return PTrue;
1504 }
1505 return PFalse;
1506 }
1507
Write(const void * buf,PINDEX len)1508 PBoolean PSoundChannelBeOS::Write(const void *buf, PINDEX len)
1509 {
1510 // can only write to a player
1511 if (mPlayer!=NULL)
1512 {
1513 // Wait until there is space
1514 mBuffer->WaitForState(CircularBuffer::EmptyEnough);
1515
1516 // This function needs to update the last write count
1517 // Store len before it gets modified
1518 lastWriteCount=len;
1519
1520 #ifdef FILEDUMP
1521 if (playwriter)
1522 {
1523 playwriter->writewavfile(buf,len);
1524 }
1525 #endif
1526
1527 // Store data into the buffer
1528 mBuffer->Fill((const BYTE **)&buf, (size_t*) &len);
1529
1530 // Update last write count
1531 lastWriteCount-=len;
1532
1533 return PTrue;
1534 }
1535
1536 return PFalse;
1537 }
1538
1539
Close()1540 PBoolean PSoundChannelBeOS::Close()
1541 {
1542 PRINT((""));
1543
1544 // Flush the buffer first
1545 Abort();
1546
1547 // Stop the player
1548 if ((mPlayer!=NULL) && (mPlayer->InitCheck()==B_OK))
1549 {
1550 mPlayer->Stop();
1551
1552 #ifdef FILEDUMP
1553 delete playwriter;
1554 playwriter=NULL;
1555 #endif
1556 }
1557
1558 if(mPlayer)
1559 {
1560 // Destroy the player
1561 delete mPlayer;
1562 mPlayer=NULL; // make sure that another Close won't crash the system
1563 }
1564
1565 // Stop the recorder
1566 if ((mRecorder!=NULL) && (mRecorder->InitCheck()==B_OK))
1567 {
1568 mRecorder->Stop(); // Not really necessary
1569 mRecorder->Disconnect();
1570
1571 #ifdef FILEDUMP
1572 delete recwriter;
1573 recwriter=NULL;
1574 #endif
1575 }
1576
1577 if(mRecorder)
1578 {
1579 // Destroy the recorder
1580 delete mRecorder;
1581 mRecorder=NULL; // make sure that another Close won't crash the system
1582 }
1583
1584 return PTrue;
1585 }
1586
1587
SetBuffers(PINDEX size,PINDEX count)1588 PBoolean PSoundChannelBeOS::SetBuffers(PINDEX size, PINDEX count)
1589 {
1590 return InternalSetBuffers(size*(mNumBuffers=count),size);
1591 }
1592
1593
InternalSetBuffers(PINDEX size,PINDEX threshold)1594 PBoolean PSoundChannelBeOS::InternalSetBuffers(PINDEX size, PINDEX threshold)
1595 {
1596 if (mPlayer)
1597 {
1598 mPlayer->SetHasData(false);
1599 }
1600 else if (mRecorder)
1601 {
1602 mRecorder->Stop();
1603 }
1604
1605 // Delete the current buffer
1606 if(mBuffer != NULL)
1607 {
1608 delete mBuffer;
1609 mBuffer = NULL;
1610 }
1611
1612 // Create the new buffer
1613 if (size != 0)
1614 {
1615 if (mRecorder)
1616 {
1617 if (!mResampler)
1618 {
1619 PTRACE(TL, "Creating default resampler");
1620 mResampler = new Resampler(1.0,1.0,1,1,0,1);
1621 }
1622
1623 PTRACE(TL, "Creating resampling buffer, size " << size);
1624 mBuffer = new ResamplingBuffer(mResampler, size, threshold, threshold);
1625
1626 // In case we use resampler, size must be set to resampled buffer size
1627
1628 }
1629 else
1630 {
1631 PTRACE(TL, "Creating playback buffer, size " << size);
1632 mBuffer = new CircularBuffer(size, threshold, threshold);
1633 }
1634
1635 // If we have a player, set the cookie again and restart it
1636 if (mPlayer)
1637 {
1638 mPlayer->SetCookie(mBuffer);
1639 PTRACE(TL, "Tried to set player buffer cookie");
1640 mPlayer->SetHasData(true);
1641 PTRACE(TL, "Tried to set player has data");
1642 }
1643
1644 // If we have a recorder, set the cookie again
1645 // Note that the recorder is not restarted, even if it was running.
1646 // It's not a good idea for the program to change the buffers during
1647 // recording anyway because it would at least lose some data.
1648 if (mRecorder)
1649 {
1650 if(B_OK != mRecorder->SetBufferHook(RecordBuffer, mBuffer))
1651 PTRACE(TL, "Can't set recorder buffer hook");
1652 }
1653
1654 return PTrue;
1655 }
1656
1657 if (IsOpen())
1658 {
1659 PTRACE(TL, "Can't continue without buffers - closing channel");
1660 Close(); // should give errors on subsequent read/writes
1661 }
1662
1663 mBuffer = NULL;
1664
1665 return PFalse;
1666 }
1667
1668
GetBuffers(PINDEX & size,PINDEX & count)1669 PBoolean PSoundChannelBeOS::GetBuffers(PINDEX &size, PINDEX &count)
1670 {
1671 if (mBuffer)
1672 {
1673 size=mBuffer->GetSize();
1674 count=mNumBuffers;
1675 return PTrue;
1676 }
1677
1678 return PFalse;
1679 }
1680
1681
PlaySound(const PSound & sound,PBoolean wait)1682 PBoolean PSoundChannelBeOS::PlaySound(const PSound &sound, PBoolean wait)
1683 {
1684 PRINT(("wait=%s", wait?"true":"false"));
1685
1686 if (mPlayer==NULL)
1687 {
1688 PRINT(("Playing a sound on a closed (or recording) PSoundChannelBeOS"));
1689 return PFalse;
1690 }
1691
1692 #ifdef FILEDUMP
1693 playwriter->writewavfile((void *)(const BYTE*)sound, sound.GetSize());
1694 #endif
1695
1696 // create a local buffer that references the PSound
1697 // NOTE: no conversion between the PSound's format and the
1698 // PSoundChannelBeOS's format is done.
1699 const BYTE *buf=(const BYTE *)sound;
1700 PINDEX size=sound.GetSize();
1701
1702 // Play the sound by doing successive Writes until the sound is done.
1703 // Note that write will return when either the buffer is full or the
1704 // given data is written. We want to return after the entire sound
1705 // has been buffered. So we repeatedly call Write until there is
1706 // no data left
1707 while (size!=0)
1708 {
1709 // Wait until there is space
1710 mBuffer->WaitForState(CircularBuffer::EmptyEnough);
1711
1712 // Write the data
1713 mBuffer->Fill(&buf, (size_t*) &size);
1714 }
1715
1716 // Wait until the sound is finished, if requested
1717 if (wait)
1718 {
1719 PRINT(("Waiting for sound"));
1720 mBuffer->WaitForState(CircularBuffer::Empty);
1721 }
1722
1723 return PTrue;
1724 }
1725
1726
PlayFile(const PFilePath & file,PBoolean wait)1727 PBoolean PSoundChannelBeOS::PlayFile(const PFilePath &file, PBoolean wait)
1728 {
1729 entry_ref ref;
1730 status_t err;
1731
1732 // using pointers for these objects so that we don't have to
1733 // construct them here but can nevertheless use the if(ok)'s
1734 BEntry *pentry = NULL;
1735
1736 {
1737 // Create BEntry from file name
1738 pentry = new BEntry(file, true);
1739 err = pentry->InitCheck();
1740 }
1741
1742 if (err==B_OK)
1743 {
1744 // Create entry_ref from BEntry
1745 err = pentry->GetRef(&ref);
1746 }
1747
1748 if (err==B_OK)
1749 {
1750 // Play the sound. Return value is a handle or a negative value for errors
1751 // Errors in BeOS are always negative values
1752 err=play_sound(&ref, true, !wait, wait);
1753 if (err>=0)
1754 {
1755 err=B_OK;
1756 }
1757 }
1758
1759 return (err==B_OK);
1760 }
1761
1762
HasPlayCompleted()1763 PBoolean PSoundChannelBeOS::HasPlayCompleted()
1764 {
1765 if (mPlayer!=NULL)
1766 {
1767 return mBuffer->IsEmpty();
1768 }
1769
1770 return PFalse;
1771 }
1772
1773
WaitForPlayCompletion()1774 PBoolean PSoundChannelBeOS::WaitForPlayCompletion()
1775 {
1776 if (mPlayer!=NULL)
1777 {
1778 mBuffer->WaitForState(CircularBuffer::Empty);
1779 }
1780
1781 return PTrue;
1782 }
1783
1784
RecordSound(PSound & sound)1785 PBoolean PSoundChannelBeOS::RecordSound(PSound &sound)
1786 {
1787 PRINT((""));
1788
1789 if (mRecorder==NULL)
1790 {
1791 PRINT(("Recording a sound on a closed (or playing) PSoundChannelBeOS"));
1792 return PFalse;
1793 }
1794
1795 // Flush the buffer first
1796 Abort();
1797
1798 // Start recording
1799 if (mRecorder->Start()!=B_OK)
1800 {
1801 PRINT(("BMediaRecorder::Start() returned error"));
1802 return PFalse;
1803 }
1804
1805 // Wait until buffer is filled
1806 mBuffer->WaitForState(CircularBuffer::Full);
1807 PRINT(("Buffer full: size=%lu",mBuffer->GetSize()));
1808
1809 // Stop the recorder
1810 if (mRecorder->Stop()!=B_OK)
1811 {
1812 PRINT(("Uh-oh, recorder is unstoppable!"));
1813 //return PFalse;
1814 }
1815
1816 // Set the sound's format to ours
1817 sound.SetFormat(GetChannels(), GetSampleRate(), GetSampleSize());
1818
1819 // Resize the sound and set up local buffer references
1820 PINDEX size=mBuffer->GetSize();
1821 BYTE *buf=sound.GetPointer(size);
1822
1823 #ifdef FILEDUMP
1824 void *dumpbuf=buf;
1825 size_t dumpsize=size;
1826 #endif
1827
1828 // Read the data
1829 mBuffer->Drain(&buf, (size_t*) &size);
1830
1831 #ifdef FILEDUMP
1832 recwriter->writewavfile(dumpbuf, dumpsize);
1833 #endif
1834
1835 PRINT(("Recording succesful"));
1836 return PTrue;
1837 }
1838
1839
RecordFile(const PFilePath & filename)1840 PBoolean PSoundChannelBeOS::RecordFile(const PFilePath & filename)
1841 {
1842 // Not implemented for now
1843 return PFalse;
1844 }
1845
1846
StartRecording()1847 PBoolean PSoundChannelBeOS::StartRecording()
1848 {
1849 if (mRecorder==NULL)
1850 {
1851 PRINT(("Recording to a closed (or playing) PSoundChannelBeOS"));
1852 return PFalse;
1853 }
1854
1855 // Flush the buffers
1856 Abort();
1857
1858 // Start recording
1859 if (mRecorder->Start()!=B_OK)
1860 {
1861 PRINT(("BMediaRecorder::Start returned error"));
1862 return PFalse;
1863 }
1864
1865 return PTrue;
1866 }
1867
1868
IsRecordBufferFull()1869 PBoolean PSoundChannelBeOS::IsRecordBufferFull()
1870 {
1871 if (mRecorder)
1872 {
1873 return !mBuffer->IsEmpty();
1874 }
1875
1876 return PFalse;
1877 }
1878
1879
AreAllRecordBuffersFull()1880 PBoolean PSoundChannelBeOS::AreAllRecordBuffersFull()
1881 {
1882 if (mRecorder)
1883 {
1884 return mBuffer->IsFull();
1885 }
1886
1887 return PFalse;
1888 }
1889
1890
WaitForRecordBufferFull()1891 PBoolean PSoundChannelBeOS::WaitForRecordBufferFull()
1892 {
1893 if (mRecorder==NULL)
1894 {
1895 PRINT(("Waiting for record buffer on playing or closed PSoundChannelBeOS"));
1896 return PFalse;
1897 }
1898
1899 mBuffer->WaitForState(CircularBuffer::FullEnough);
1900
1901 return PXSetIOBlock(PXReadBlock, readTimeout);
1902 }
1903
1904
WaitForAllRecordBuffersFull()1905 PBoolean PSoundChannelBeOS::WaitForAllRecordBuffersFull()
1906 {
1907 if (mRecorder==NULL)
1908 {
1909 PRINT(("Waiting for record buffers on playing or closed PSoundChannelBeOS"));
1910 return PFalse;
1911 }
1912
1913 mBuffer->WaitForState(CircularBuffer::Full);
1914
1915 return PTrue;
1916 }
1917
1918
IsOpen() const1919 PBoolean PSoundChannelBeOS::IsOpen() const
1920 {
1921 PBoolean result=((mPlayer!=NULL) || (mRecorder!=NULL));
1922 PRINT(("returning %s, player 0x%X recorder 0x%X", result?"true":"false", mPlayer, mRecorder));
1923 return result;
1924 }
1925
1926
1927
SetVolume(unsigned newVolume)1928 PBoolean PSoundChannelBeOS::SetVolume(unsigned newVolume)
1929 {
1930 #ifdef TODO
1931 cerr << __FILE__<< "PSoundChannelBeOS :: SetVolume called in error. Please fix" << endl;
1932 #endif
1933
1934 return PTrue;
1935 }
1936
GetVolume(unsigned & volume)1937 PBoolean PSoundChannelBeOS::GetVolume(unsigned & volume)
1938 {
1939 #ifdef TODO
1940 cerr << __FILE__<< "PSoundChannelBeOS :: GetVolume called in error. Please fix" << endl;
1941 #endif
1942
1943 return PTrue;
1944
1945 }
1946
1947 // End of file
1948