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