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