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