xref: /haiku/src/kits/media/SoundPlayer.cpp (revision 1026b0a1a76dc88927bb8175c470f638dc5464ee)
1 /*
2  * Copyright 2002-2009, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marcus Overhagen
7  *		Jérôme Duval
8  */
9 
10 
11 #include <SoundPlayer.h>
12 
13 #include <math.h>
14 #include <string.h>
15 
16 #include <Autolock.h>
17 #include <MediaRoster.h>
18 #include <ParameterWeb.h>
19 #include <Sound.h>
20 #include <TimeSource.h>
21 
22 #include "SoundPlayNode.h"
23 
24 #include "debug.h"
25 
26 
27 // Flags used internally in BSoundPlayer
28 enum {
29 	F_NODES_CONNECTED	= (1 << 0),
30 	F_HAS_DATA			= (1 << 1),
31 	F_IS_STARTED		= (1 << 2),
32 	F_MUST_RELEASE_MIXER = (1 << 3),
33 };
34 
35 
36 static BSoundPlayer::play_id sCurrentPlayID = 1;
37 
38 
39 BSoundPlayer::BSoundPlayer(const char* name, BufferPlayerFunc playerFunction,
40 	EventNotifierFunc eventNotifierFunction, void* cookie)
41 {
42 	CALLED();
43 
44 	TRACE("BSoundPlayer::BSoundPlayer: default constructor used\n");
45 
46 	media_multi_audio_format format = media_multi_audio_format::wildcard;
47 
48 	_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
49 		cookie);
50 }
51 
52 
53 BSoundPlayer::BSoundPlayer(const media_raw_audio_format* _format,
54 	const char* name, BufferPlayerFunc playerFunction,
55 	EventNotifierFunc eventNotifierFunction, void* cookie)
56 {
57 	CALLED();
58 
59 	TRACE("BSoundPlayer::BSoundPlayer: raw audio format constructor used\n");
60 
61 	media_multi_audio_format format = media_multi_audio_format::wildcard;
62 	*(media_raw_audio_format*)&format = *_format;
63 
64 #if DEBUG > 0
65 	char buf[100];
66 	media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = format;
67 	string_for_format(tmp, buf, sizeof(buf));
68 	TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
69 #endif
70 
71 	_Init(NULL, &format, name, NULL, playerFunction, eventNotifierFunction,
72 		cookie);
73 }
74 
75 
76 BSoundPlayer::BSoundPlayer(const media_node& toNode,
77 	const media_multi_audio_format* format, const char* name,
78 	const media_input* input, BufferPlayerFunc playerFunction,
79 	EventNotifierFunc eventNotifierFunction, void* cookie)
80 {
81 	CALLED();
82 
83 	TRACE("BSoundPlayer::BSoundPlayer: multi audio format constructor used\n");
84 
85 	if ((toNode.kind & B_BUFFER_CONSUMER) == 0)
86 		debugger("BSoundPlayer: toNode must have B_BUFFER_CONSUMER kind!\n");
87 
88 #if DEBUG > 0
89 	char buf[100];
90 	media_format tmp; tmp.type = B_MEDIA_RAW_AUDIO; tmp.u.raw_audio = *format;
91 	string_for_format(tmp, buf, sizeof(buf));
92 	TRACE("BSoundPlayer::BSoundPlayer: format %s\n", buf);
93 #endif
94 
95 	_Init(&toNode, format, name, input, playerFunction, eventNotifierFunction,
96 		cookie);
97 }
98 
99 
100 BSoundPlayer::~BSoundPlayer()
101 {
102 	CALLED();
103 
104 	if ((fFlags & F_IS_STARTED) != 0) {
105 		// block, but don't flush
106 		Stop(true, false);
107 	}
108 
109 	status_t err;
110 	BMediaRoster* roster = BMediaRoster::Roster();
111 	if (roster == NULL) {
112 		TRACE("BSoundPlayer::~BSoundPlayer: Couldn't get BMediaRoster\n");
113 		goto cleanup;
114 	}
115 
116 	if ((fFlags & F_NODES_CONNECTED) != 0) {
117 		// Ordinarily we'd stop *all* of the nodes in the chain before
118 		// disconnecting. However, our node is already stopped, and we can't
119 		// stop the System Mixer.
120 		// So, we just disconnect from it, and release our references to the
121 		// nodes that we're using. We *are* supposed to do that even for global
122 		// nodes like the Mixer.
123 		err = roster->Disconnect(fMediaOutput, fMediaInput);
124 		if (err != B_OK) {
125 			TRACE("BSoundPlayer::~BSoundPlayer: Error disconnecting nodes: "
126 				"%ld (%s)\n", err, strerror(err));
127 		}
128 	}
129 
130 	if ((fFlags & F_MUST_RELEASE_MIXER) != 0) {
131 		// Release the mixer as it was acquired
132 		// through BMediaRoster::GetAudioMixer()
133 		err = roster->ReleaseNode(fMediaInput.node);
134 		if (err != B_OK) {
135 			TRACE("BSoundPlayer::~BSoundPlayer: Error releasing input node: "
136 				"%ld (%s)\n", err, strerror(err));
137 		}
138 	}
139 
140 cleanup:
141 	// Dispose of the player node
142 
143 	// We do not call BMediaRoster::ReleaseNode(), since
144 	// the player was created by using "new". We could
145 	// call BMediaRoster::UnregisterNode(), but this is
146 	// supposed to be done by BMediaNode destructor automatically
147 	delete fPlayerNode;
148 
149 	// do not delete fVolumeSlider, it belongs to the parameter web
150 	delete fParameterWeb;
151 }
152 
153 
154 status_t
155 BSoundPlayer::InitCheck()
156 {
157 	CALLED();
158 	return fInitStatus;
159 }
160 
161 
162 media_raw_audio_format
163 BSoundPlayer::Format() const
164 {
165 	CALLED();
166 
167 	if ((fFlags & F_NODES_CONNECTED) == 0)
168 		return media_raw_audio_format::wildcard;
169 
170 	return fPlayerNode->Format();
171 }
172 
173 
174 status_t
175 BSoundPlayer::Start()
176 {
177 	CALLED();
178 
179 	if ((fFlags & F_NODES_CONNECTED) == 0)
180 		return B_NO_INIT;
181 
182 	if ((fFlags & F_IS_STARTED) != 0)
183 		return B_OK;
184 
185 	BMediaRoster* roster = BMediaRoster::Roster();
186 	if (!roster) {
187 		TRACE("BSoundPlayer::Start: Couldn't get BMediaRoster\n");
188 		return B_ERROR;
189 	}
190 
191 	if (!fPlayerNode->TimeSource()->IsRunning()) {
192 		roster->StartTimeSource(fPlayerNode->TimeSource()->Node(),
193 			fPlayerNode->TimeSource()->RealTime());
194 	}
195 
196 	// Add latency and a few ms to the nodes current time to
197 	// make sure that we give the producer enough time to run
198 	// buffers through the node chain, otherwise it'll start
199 	// up already late
200 
201 	status_t err = roster->StartNode(fPlayerNode->Node(),
202 		fPlayerNode->TimeSource()->Now() + Latency() + 5000);
203 	if (err != B_OK) {
204 		TRACE("BSoundPlayer::Start: StartNode failed, %ld", err);
205 		return err;
206 	}
207 
208 	if (fNotifierFunc != NULL)
209 		fNotifierFunc(fCookie, B_STARTED, this);
210 
211 	SetHasData(true);
212 	atomic_or(&fFlags, F_IS_STARTED);
213 
214 	return B_OK;
215 }
216 
217 
218 void
219 BSoundPlayer::Stop(bool block, bool flush)
220 {
221 	CALLED();
222 
223 	TRACE("BSoundPlayer::Stop: block %d, flush %d\n", (int)block, (int)flush);
224 
225 	if ((fFlags & F_NODES_CONNECTED) == 0)
226 		return;
227 
228 	// TODO: flush is ignored
229 
230 	if ((fFlags & F_IS_STARTED) != 0) {
231 		BMediaRoster* roster = BMediaRoster::Roster();
232 		if (roster == NULL) {
233 			TRACE("BSoundPlayer::Stop: Couldn't get BMediaRoster\n");
234 			return;
235 		}
236 
237 		roster->StopNode(fPlayerNode->Node(), 0, true);
238 
239 		atomic_and(&fFlags, ~F_IS_STARTED);
240 	}
241 
242 	if (block) {
243 		// wait until the node is stopped
244 		int tries;
245 		for (tries = 250; fPlayerNode->IsPlaying() && tries != 0; tries--)
246 			snooze(2000);
247 
248 		DEBUG_ONLY(if (tries == 0)
249 			TRACE("BSoundPlayer::Stop: waiting for node stop failed\n"));
250 
251 		// Wait until all buffers on the way to the physical output have been
252 		// played
253 		snooze(Latency() + 2000);
254 	}
255 
256 	if (fNotifierFunc)
257 		fNotifierFunc(fCookie, B_STOPPED, this);
258 
259 }
260 
261 
262 bigtime_t
263 BSoundPlayer::Latency()
264 {
265 	CALLED();
266 
267 	if ((fFlags & F_NODES_CONNECTED) == 0)
268 		return 0;
269 
270 	BMediaRoster *roster = BMediaRoster::Roster();
271 	if (!roster) {
272 		TRACE("BSoundPlayer::Latency: Couldn't get BMediaRoster\n");
273 		return 0;
274 	}
275 
276 	bigtime_t latency;
277 	status_t err = roster->GetLatencyFor(fMediaOutput.node, &latency);
278 	if (err != B_OK) {
279 		TRACE("BSoundPlayer::Latency: GetLatencyFor failed %ld (%s)\n", err,
280 			strerror(err));
281 		return 0;
282 	}
283 
284 	TRACE("BSoundPlayer::Latency: latency is %Ld\n", latency);
285 
286 	return latency;
287 }
288 
289 
290 void
291 BSoundPlayer::SetHasData(bool hasData)
292 {
293 	CALLED();
294 	if (hasData)
295 		atomic_or(&fFlags, F_HAS_DATA);
296 	else
297 		atomic_and(&fFlags, ~F_HAS_DATA);
298 }
299 
300 
301 bool
302 BSoundPlayer::HasData()
303 {
304 	CALLED();
305 	return (atomic_get(&fFlags) & F_HAS_DATA) != 0;
306 }
307 
308 
309 BSoundPlayer::BufferPlayerFunc
310 BSoundPlayer::BufferPlayer() const
311 {
312 	CALLED();
313 	return fPlayBufferFunc;
314 }
315 
316 
317 void
318 BSoundPlayer::SetBufferPlayer(BufferPlayerFunc playerFunction)
319 {
320 	CALLED();
321 	BAutolock _(fLocker);
322 
323 	fPlayBufferFunc = playerFunction;
324 }
325 
326 
327 BSoundPlayer::EventNotifierFunc
328 BSoundPlayer::EventNotifier() const
329 {
330 	CALLED();
331 	return fNotifierFunc;
332 }
333 
334 
335 void
336 BSoundPlayer::SetNotifier(EventNotifierFunc eventNotifierFunction)
337 {
338 	CALLED();
339 	BAutolock _(fLocker);
340 
341 	fNotifierFunc = eventNotifierFunction;
342 }
343 
344 
345 void*
346 BSoundPlayer::Cookie() const
347 {
348 	CALLED();
349 	return fCookie;
350 }
351 
352 
353 void
354 BSoundPlayer::SetCookie(void *cookie)
355 {
356 	CALLED();
357 	BAutolock _(fLocker);
358 
359 	fCookie = cookie;
360 }
361 
362 
363 void
364 BSoundPlayer::SetCallbacks(BufferPlayerFunc playerFunction,
365 	EventNotifierFunc eventNotifierFunction, void* cookie)
366 {
367 	CALLED();
368 	BAutolock _(fLocker);
369 
370 	SetBufferPlayer(playerFunction);
371 	SetNotifier(eventNotifierFunction);
372 	SetCookie(cookie);
373 }
374 
375 
376 /*!	The BeBook is inaccurate about the meaning of this function.
377 	The probably best interpretation is to return the time that
378 	has elapsed since playing was started, whichs seems to match
379 	"CurrentTime() returns the current media time"
380 */
381 bigtime_t
382 BSoundPlayer::CurrentTime()
383 {
384 	if ((fFlags & F_NODES_CONNECTED) == 0)
385 		return 0;
386 
387 	return fPlayerNode->CurrentTime();
388 }
389 
390 
391 /*!	Returns the current performance time of the sound player node
392 	being used by the BSoundPlayer. Will return B_ERROR if the
393 	BSoundPlayer object hasn't been properly initialized.
394 */
395 bigtime_t
396 BSoundPlayer::PerformanceTime()
397 {
398 	if ((fFlags & F_NODES_CONNECTED) == 0)
399 		return (bigtime_t) B_ERROR;
400 
401 	return fPlayerNode->TimeSource()->Now();
402 }
403 
404 
405 status_t
406 BSoundPlayer::Preroll()
407 {
408 	CALLED();
409 
410 	if ((fFlags & F_NODES_CONNECTED) == 0)
411 		return B_NO_INIT;
412 
413 	BMediaRoster* roster = BMediaRoster::Roster();
414 	if (roster == NULL) {
415 		TRACE("BSoundPlayer::Preroll: Couldn't get BMediaRoster\n");
416 		return B_ERROR;
417 	}
418 
419 	status_t err = roster->PrerollNode(fMediaOutput.node);
420 	if (err != B_OK) {
421 		TRACE("BSoundPlayer::Preroll: Error while PrerollNode:  %ld (%s)\n",
422 			err, strerror(err));
423 		return err;
424 	}
425 
426 	return B_OK;
427 }
428 
429 
430 BSoundPlayer::play_id
431 BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime)
432 {
433 	return StartPlaying(sound, atTime, 1.0);
434 }
435 
436 
437 BSoundPlayer::play_id
438 BSoundPlayer::StartPlaying(BSound* sound, bigtime_t atTime, float withVolume)
439 {
440 	CALLED();
441 
442 	// TODO: support the at_time and with_volume parameters
443 	playing_sound* item = (playing_sound*)malloc(sizeof(playing_sound));
444 	if (item == NULL)
445 		return B_NO_MEMORY;
446 
447 	item->current_offset = 0;
448 	item->sound = sound;
449 	item->id = atomic_add(&sCurrentPlayID, 1);
450 	item->delta = 0;
451 	item->rate = 0;
452 	item->volume = withVolume;
453 
454 	if (!fLocker.Lock()) {
455 		free(item);
456 		return B_ERROR;
457 	}
458 
459 	sound->AcquireRef();
460 	item->next = fPlayingSounds;
461 	fPlayingSounds = item;
462 	fLocker.Unlock();
463 
464 	SetHasData(true);
465 	return item->id;
466 }
467 
468 
469 status_t
470 BSoundPlayer::SetSoundVolume(play_id id, float newVolume)
471 {
472 	CALLED();
473 	if (!fLocker.Lock())
474 		return B_ERROR;
475 
476 	playing_sound *item = fPlayingSounds;
477 	while (item) {
478 		if (item->id == id) {
479 			item->volume = newVolume;
480 			fLocker.Unlock();
481 			return B_OK;
482 		}
483 
484 		item = item->next;
485 	}
486 
487 	fLocker.Unlock();
488 	return B_ENTRY_NOT_FOUND;
489 }
490 
491 
492 bool
493 BSoundPlayer::IsPlaying(play_id id)
494 {
495 	CALLED();
496 	if (!fLocker.Lock())
497 		return B_ERROR;
498 
499 	playing_sound *item = fPlayingSounds;
500 	while (item) {
501 		if (item->id == id) {
502 			fLocker.Unlock();
503 			return true;
504 		}
505 
506 		item = item->next;
507 	}
508 
509 	fLocker.Unlock();
510 	return false;
511 }
512 
513 
514 status_t
515 BSoundPlayer::StopPlaying(play_id id)
516 {
517 	CALLED();
518 	if (!fLocker.Lock())
519 		return B_ERROR;
520 
521 	playing_sound** link = &fPlayingSounds;
522 	playing_sound* item = fPlayingSounds;
523 
524 	while (item != NULL) {
525 		if (item->id == id) {
526 			*link = item->next;
527 			sem_id waitSem = item->wait_sem;
528 			item->sound->ReleaseRef();
529 			free(item);
530 			fLocker.Unlock();
531 
532 			_NotifySoundDone(id, true);
533 			if (waitSem >= 0)
534 				release_sem(waitSem);
535 
536 			return B_OK;
537 		}
538 
539 		link = &item->next;
540 		item = item->next;
541 	}
542 
543 	fLocker.Unlock();
544 	return B_ENTRY_NOT_FOUND;
545 }
546 
547 
548 status_t
549 BSoundPlayer::WaitForSound(play_id id)
550 {
551 	CALLED();
552 	if (!fLocker.Lock())
553 		return B_ERROR;
554 
555 	playing_sound* item = fPlayingSounds;
556 	while (item != NULL) {
557 		if (item->id == id) {
558 			sem_id waitSem = item->wait_sem;
559 			if (waitSem < 0)
560 				waitSem = item->wait_sem = create_sem(0, "wait for sound");
561 
562 			fLocker.Unlock();
563 			return acquire_sem(waitSem);
564 		}
565 
566 		item = item->next;
567 	}
568 
569 	fLocker.Unlock();
570 	return B_ENTRY_NOT_FOUND;
571 }
572 
573 
574 float
575 BSoundPlayer::Volume()
576 {
577 	CALLED();
578 	return pow(10.0, VolumeDB(true) / 20.0);
579 }
580 
581 
582 void
583 BSoundPlayer::SetVolume(float newVolume)
584 {
585 	CALLED();
586 	SetVolumeDB(20.0 * log10(newVolume));
587 }
588 
589 
590 float
591 BSoundPlayer::VolumeDB(bool forcePoll)
592 {
593 	CALLED();
594 	if (!fVolumeSlider)
595 		return -94.0f; // silence
596 
597 	if (!forcePoll && system_time() - fLastVolumeUpdate < 500000)
598 		return fVolumeDB;
599 
600 	int32 count = fVolumeSlider->CountChannels();
601 	float values[count];
602 	size_t size = count * sizeof(float);
603 	fVolumeSlider->GetValue(&values, &size, NULL);
604 	fLastVolumeUpdate = system_time();
605 	fVolumeDB = values[0];
606 
607 	return values[0];
608 }
609 
610 
611 void
612 BSoundPlayer::SetVolumeDB(float volumeDB)
613 {
614 	CALLED();
615 	if (!fVolumeSlider)
616 		return;
617 
618 	float minDB = fVolumeSlider->MinValue();
619 	float maxDB = fVolumeSlider->MaxValue();
620 	if (volumeDB < minDB)
621 		volumeDB = minDB;
622 	if (volumeDB > maxDB)
623 		volumeDB = maxDB;
624 
625 	int count = fVolumeSlider->CountChannels();
626 	float values[count];
627 	for (int i = 0; i < count; i++)
628 		values[i] = volumeDB;
629 	fVolumeSlider->SetValue(values, sizeof(float) * count, 0);
630 
631 	fVolumeDB = volumeDB;
632 	fLastVolumeUpdate = system_time();
633 }
634 
635 
636 status_t
637 BSoundPlayer::GetVolumeInfo(media_node* _node, int32* _parameterID,
638 	float* _minDB, float* _maxDB)
639 {
640 	CALLED();
641 	if (fVolumeSlider == NULL)
642 		return B_NO_INIT;
643 
644 	if (_node != NULL)
645 		*_node = fMediaInput.node;
646 	if (_parameterID != NULL)
647 		*_parameterID = fVolumeSlider->ID();
648 	if (_minDB != NULL)
649 		*_minDB = fVolumeSlider->MinValue();
650 	if (_maxDB != NULL)
651 		*_maxDB = fVolumeSlider->MaxValue();
652 
653 	return B_OK;
654 }
655 
656 
657 // #pragma mark - protected BSoundPlayer
658 
659 
660 void
661 BSoundPlayer::SetInitError(status_t error)
662 {
663 	CALLED();
664 	fInitStatus = error;
665 }
666 
667 
668 // #pragma mark - private BSoundPlayer
669 
670 
671 void
672 BSoundPlayer::_SoundPlayBufferFunc(void *cookie, void *buffer, size_t size,
673 	const media_raw_audio_format &format)
674 {
675 	// TODO: support more than one sound and make use of the format parameter
676 	BSoundPlayer *player = (BSoundPlayer *)cookie;
677 	if (!player->fLocker.Lock()) {
678 		memset(buffer, 0, size);
679 		return;
680 	}
681 
682 	playing_sound *sound = player->fPlayingSounds;
683 	if (sound == NULL) {
684 		player->SetHasData(false);
685 		player->fLocker.Unlock();
686 		memset(buffer, 0, size);
687 		return;
688 	}
689 
690 	size_t used = 0;
691 	if (!sound->sound->GetDataAt(sound->current_offset, buffer, size, &used)) {
692 		// will take care of removing the item and notifying others
693 		player->StopPlaying(sound->id);
694 		player->fLocker.Unlock();
695 		memset(buffer, 0, size);
696 		return;
697 	}
698 
699 	sound->current_offset += used;
700 	player->fLocker.Unlock();
701 
702 	if (used < size)
703 		memset((uint8 *)buffer + used, 0, size - used);
704 }
705 
706 
707 status_t BSoundPlayer::_Reserved_SoundPlayer_0(void*, ...) { return B_ERROR; }
708 status_t BSoundPlayer::_Reserved_SoundPlayer_1(void*, ...) { return B_ERROR; }
709 status_t BSoundPlayer::_Reserved_SoundPlayer_2(void*, ...) { return B_ERROR; }
710 status_t BSoundPlayer::_Reserved_SoundPlayer_3(void*, ...) { return B_ERROR; }
711 status_t BSoundPlayer::_Reserved_SoundPlayer_4(void*, ...) { return B_ERROR; }
712 status_t BSoundPlayer::_Reserved_SoundPlayer_5(void*, ...) { return B_ERROR; }
713 status_t BSoundPlayer::_Reserved_SoundPlayer_6(void*, ...) { return B_ERROR; }
714 status_t BSoundPlayer::_Reserved_SoundPlayer_7(void*, ...) { return B_ERROR; }
715 
716 
717 void
718 BSoundPlayer::_Init(const media_node* node,
719 	const media_multi_audio_format* format, const char* name,
720 	const media_input* input, BufferPlayerFunc playerFunction,
721 	EventNotifierFunc eventNotifierFunction, void* cookie)
722 {
723 	CALLED();
724 	fPlayingSounds = NULL;
725 	fWaitingSounds = NULL;
726 
727 	fPlayerNode = NULL;
728 	if (playerFunction == NULL) {
729 		fPlayBufferFunc = _SoundPlayBufferFunc;
730 		fCookie = this;
731 	} else {
732 		fPlayBufferFunc = playerFunction;
733 		fCookie = cookie;
734 	}
735 
736 	fNotifierFunc = eventNotifierFunction;
737 	fVolumeDB = 0.0f;
738 	fFlags = 0;
739 	fInitStatus = B_ERROR;
740 	fParameterWeb = NULL;
741 	fVolumeSlider = NULL;
742 	fLastVolumeUpdate = 0;
743 
744 	BMediaRoster* roster = BMediaRoster::Roster();
745 	if (roster == NULL) {
746 		TRACE("BSoundPlayer::_Init: Couldn't get BMediaRoster\n");
747 		return;
748 	}
749 
750 	// The inputNode that our player node will be
751 	// connected with is either supplied by the user
752 	// or the system audio mixer
753 	media_node inputNode;
754 	if (node) {
755 		inputNode = *node;
756 	} else {
757 		fInitStatus = roster->GetAudioMixer(&inputNode);
758 		if (fInitStatus != B_OK) {
759 			TRACE("BSoundPlayer::_Init: Couldn't GetAudioMixer\n");
760 			return;
761 		}
762 		fFlags |= F_MUST_RELEASE_MIXER;
763 	}
764 
765 	media_output _output;
766 	media_input _input;
767 	int32 inputCount;
768 	int32 outputCount;
769 	media_format tryFormat;
770 
771 	// Create the player node and register it
772 	fPlayerNode = new BPrivate::SoundPlayNode(name, this);
773 	fInitStatus = roster->RegisterNode(fPlayerNode);
774 	if (fInitStatus != B_OK) {
775 		TRACE("BSoundPlayer::_Init: Couldn't RegisterNode: %s\n",
776 			strerror(fInitStatus));
777 		return;
778 	}
779 
780 	// set the producer's time source to be the "default" time source,
781 	// which the system audio mixer uses too.
782 	media_node timeSource;
783 	fInitStatus = roster->GetTimeSource(&timeSource);
784 	if (fInitStatus != B_OK) {
785 		TRACE("BSoundPlayer::_Init: Couldn't GetTimeSource: %s\n",
786 			strerror(fInitStatus));
787 		return;
788 	}
789 	fInitStatus = roster->SetTimeSourceFor(fPlayerNode->Node().node,
790 		timeSource.node);
791 	if (fInitStatus != B_OK) {
792 		TRACE("BSoundPlayer::_Init: Couldn't SetTimeSourceFor: %s\n",
793 			strerror(fInitStatus));
794 		return;
795 	}
796 
797 	// find a free media_input
798 	if (!input) {
799 		fInitStatus = roster->GetFreeInputsFor(inputNode, &_input, 1,
800 			&inputCount, B_MEDIA_RAW_AUDIO);
801 		if (fInitStatus != B_OK) {
802 			TRACE("BSoundPlayer::_Init: Couldn't GetFreeInputsFor: %s\n",
803 				strerror(fInitStatus));
804 			return;
805 		}
806 		if (inputCount < 1) {
807 			TRACE("BSoundPlayer::_Init: Couldn't find a free input\n");
808 			fInitStatus = B_ERROR;
809 			return;
810 		}
811 	} else {
812 		_input = *input;
813 	}
814 
815 	// find a free media_output
816 	fInitStatus = roster->GetFreeOutputsFor(fPlayerNode->Node(), &_output, 1,
817 		&outputCount, B_MEDIA_RAW_AUDIO);
818 	if (fInitStatus != B_OK) {
819 		TRACE("BSoundPlayer::_Init: Couldn't GetFreeOutputsFor: %s\n",
820 			strerror(fInitStatus));
821 		return;
822 	}
823 	if (outputCount < 1) {
824 		TRACE("BSoundPlayer::_Init: Couldn't find a free output\n");
825 		fInitStatus = B_ERROR;
826 		return;
827 	}
828 
829 	// Set an appropriate run mode for the producer
830 	fInitStatus = roster->SetRunModeNode(fPlayerNode->Node(),
831 		BMediaNode::B_INCREASE_LATENCY);
832 	if (fInitStatus != B_OK) {
833 		TRACE("BSoundPlayer::_Init: Couldn't SetRunModeNode: %s\n",
834 			strerror(fInitStatus));
835 		return;
836 	}
837 
838 	// setup our requested format (can still have many wildcards)
839 	tryFormat.type = B_MEDIA_RAW_AUDIO;
840 	tryFormat.u.raw_audio = *format;
841 
842 #if DEBUG > 0
843 	char buf[100];
844 	string_for_format(tryFormat, buf, sizeof(buf));
845 	TRACE("BSoundPlayer::_Init: trying to connect with format %s\n", buf);
846 #endif
847 
848 	// and connect the nodes
849 	fInitStatus = roster->Connect(_output.source, _input.destination,
850 		&tryFormat, &fMediaOutput, &fMediaInput);
851 	if (fInitStatus != B_OK) {
852 		TRACE("BSoundPlayer::_Init: Couldn't Connect: %s\n",
853 			strerror(fInitStatus));
854 		return;
855 	}
856 
857 	fFlags |= F_NODES_CONNECTED;
858 
859 	_GetVolumeSlider();
860 
861 	TRACE("BSoundPlayer node %ld has timesource %ld\n",
862 		fPlayerNode->Node().node, fPlayerNode->TimeSource()->Node().node);
863 }
864 
865 
866 void
867 BSoundPlayer::_NotifySoundDone(play_id id, bool gotToPlay)
868 {
869 	CALLED();
870 	Notify(B_SOUND_DONE, id, gotToPlay);
871 }
872 
873 
874 void
875 BSoundPlayer::_GetVolumeSlider()
876 {
877 	CALLED();
878 
879 	ASSERT(fVolumeSlider == NULL);
880 
881 	BMediaRoster *roster = BMediaRoster::CurrentRoster();
882 	if (!roster) {
883 		TRACE("BSoundPlayer::_GetVolumeSlider failed to get BMediaRoster");
884 		return;
885 	}
886 
887 	if (!fParameterWeb && roster->GetParameterWebFor(fMediaInput.node, &fParameterWeb) < B_OK) {
888 		TRACE("BSoundPlayer::_GetVolumeSlider couldn't get parameter web");
889 		return;
890 	}
891 
892 	int count = fParameterWeb->CountParameters();
893 	for (int i = 0; i < count; i++) {
894 		BParameter *parameter = fParameterWeb->ParameterAt(i);
895 		if (parameter->Type() != BParameter::B_CONTINUOUS_PARAMETER)
896 			continue;
897 		if ((parameter->ID() >> 16) != fMediaInput.destination.id)
898 			continue;
899 		if  (strcmp(parameter->Kind(), B_GAIN) != 0)
900 			continue;
901 		fVolumeSlider = (BContinuousParameter *)parameter;
902 		break;
903 	}
904 
905 #if DEBUG >0
906 	if (!fVolumeSlider) {
907 		TRACE("BSoundPlayer::_GetVolumeSlider couldn't find volume control");
908 	}
909 #endif
910 }
911 
912 
913 void
914 BSoundPlayer::Notify(sound_player_notification what, ...)
915 {
916 	CALLED();
917 	if (fLocker.Lock()) {
918 		if (fNotifierFunc)
919 			(*fNotifierFunc)(fCookie, what);
920 		fLocker.Unlock();
921 	}
922 }
923 
924 
925 void
926 BSoundPlayer::PlayBuffer(void* buffer, size_t size,
927 	const media_raw_audio_format& format)
928 {
929 	if (fLocker.Lock()) {
930 		if (fPlayBufferFunc)
931 			(*fPlayBufferFunc)(fCookie, buffer, size, format);
932 		fLocker.Unlock();
933 	}
934 }
935 
936 
937 // #pragma mark - public sound_error
938 
939 
940 sound_error::sound_error(const char* string)
941 {
942 	m_str_const = string;
943 }
944 
945 
946 const char*
947 sound_error::what() const throw()
948 {
949 	return m_str_const;
950 }
951 
952